LCOV - code coverage report
Current view: top level - include/utils - Shuffle.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 155 182 85.2 %
Date: 2026-05-29 20:35:17 Functions: 16 17 94.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "MooseRandom.h"
      13             : #include "libmesh/communicator.h"
      14             : #include "libmesh/parallel.h"
      15             : #include "libmesh/parallel_sync.h"
      16             : #include "libmesh/libmesh_common.h"
      17             : #include <list>
      18             : #include <memory>
      19             : #include <iterator>
      20             : #include <algorithm>
      21             : 
      22             : namespace MooseUtils
      23             : {
      24             : ///@{
      25             : /**
      26             :  * Swap function for serial or distributed vector of data.
      27             :  * @param data The vector on which the values are to be swapped
      28             :  * @param idx0, idx1 The global indices to be swapped
      29             :  * @param comm_ptr Optional Communicator, if provided and running with multiple processors the
      30             :  *                 vector is assumed to be distributed
      31             :  */
      32             : template <typename T>
      33             : void swap(std::vector<T> & data,
      34             :           const std::size_t idx0,
      35             :           const std::size_t idx1,
      36             :           const libMesh::Parallel::Communicator & comm);
      37             : template <typename T>
      38             : void swap(std::vector<T> & data,
      39             :           const std::size_t idx0,
      40             :           const std::size_t idx1,
      41             :           const libMesh::Parallel::Communicator * comm_ptr = nullptr);
      42             : ///@}
      43             : 
      44             : ///@{
      45             : /**
      46             :  * Shuffle function for serial or distributed vector of data that shuffles in place.
      47             :  * @param data The vector on which the values are to be swapped
      48             :  * @param generator Random number generator to use for shuffle
      49             :  * @param seed_index (default: 0) The seed index to use for calls to randl
      50             :  * @param comm_ptr Optional Communicator, if provided and running with multiple processors the
      51             :  *                 vector is assumed to be distributed
      52             :  *
      53             :  * Both the serial and distributed version implement a Fisher-Yates shuffle
      54             :  * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
      55             :  *
      56             :  * NOTE: This distributed shuffle I have here does a parallel communication with each swap pair
      57             :  *       generated. I am certain that there are more efficient ways to shuffle a distributed vector,
      58             :  *       but there doesn't seem to be an algorithm in the literature (my search was not extensive).
      59             :  *
      60             :  *       The reason I came to this conclusion was because of a 2019 paper, which states the
      61             :  *       following (https://iopscience.iop.org/article/10.1088/1742-6596/1196/1/012035):
      62             :  *
      63             :  *           The study also says that the Fisher-Yates Shuffle can be developed in two ways,
      64             :  *           namely the algorithm's initial assumptions that allow for discrete uniform variables,
      65             :  *           and also with the avent of large core clusters and GPUs, there is an interest in making
      66             :  *           parallel versions of this algorithm.
      67             :  *
      68             :  *       This paper discusses the MergeShuffle (https://arxiv.org/abs/1508.03167), but that is a
      69             :  *       shared memory parallel algorithm.
      70             :  *
      71             :  *       Hence, if you want to become famous create a parallel Fisher-Yates algorithm for MPI.
      72             :  */
      73             : template <typename T>
      74             : void shuffle(std::vector<T> & data, MooseRandom & generator, const std::size_t seed_index = 0);
      75             : template <typename T>
      76             : void shuffle(std::vector<T> & data,
      77             :              MooseRandom & generator,
      78             :              const libMesh::Parallel::Communicator & comm);
      79             : template <typename T>
      80             : void shuffle(std::vector<T> & data,
      81             :              MooseRandom & generator,
      82             :              const std::size_t seed_index,
      83             :              const libMesh::Parallel::Communicator & comm);
      84             : template <typename T>
      85             : void shuffle(std::vector<T> & data,
      86             :              MooseRandom & generator,
      87             :              const std::size_t seed_index,
      88             :              const libMesh::Parallel::Communicator * comm_ptr);
      89             : ///@}
      90             : 
      91             : ///@{
      92             : /**
      93             :  * Randomly resample a vector of data, allowing a value to be repeated.
      94             :  * @param data The vector on which the values are to be swapped
      95             :  * @param generator Random number generator to use for shuffle
      96             :  * @param seed_index (default: 0) The seed index to use for calls to randl
      97             :  * @param comm_ptr Optional Communicator, if provided and running with multiple processors the
      98             :  *                 vector is assumed to be distributed
      99             :  */
     100             : template <typename T>
     101             : std::vector<T>
     102             : resample(const std::vector<T> & data, MooseRandom & generator, const std::size_t seed_index = 0);
     103             : template <typename T>
     104             : std::vector<T> resample(const std::vector<T> & data,
     105             :                         MooseRandom & generator,
     106             :                         const libMesh::Parallel::Communicator & comm);
     107             : template <typename T>
     108             : std::vector<T> resample(const std::vector<T> & data,
     109             :                         MooseRandom & generator,
     110             :                         const std::size_t seed_index,
     111             :                         const libMesh::Parallel::Communicator & comm);
     112             : template <typename T>
     113             : std::vector<T> resample(const std::vector<T> & data,
     114             :                         MooseRandom & generator,
     115             :                         const std::size_t seed_index,
     116             :                         const libMesh::Parallel::Communicator * comm_ptr);
     117             : //@}
     118             : 
     119             : ///@{
     120             : /**
     121             :  * Randomly resample a vector of data and apply a functor, allowing a value to be repeated.
     122             :  * @param data The vector on which the values are to be swapped
     123             :  * @param functor Functor to apply to each entry of the resampled vector
     124             :  * @param generator Random number generator to use for shuffle
     125             :  * @param seed_index (default: 0) The seed index to use for calls to randl
     126             :  * @param comm_ptr Optional Communicator, if provided and running with multiple processors the
     127             :  *                 vector is assumed to be distributed
     128             :  */
     129             : template <typename T, typename ActionFunctor>
     130             : void resampleWithFunctor(const std::vector<T> & data,
     131             :                          const ActionFunctor & functor,
     132             :                          MooseRandom & generator,
     133             :                          const std::size_t seed_index = 0);
     134             : template <typename T, typename ActionFunctor>
     135             : void resampleWithFunctor(const std::vector<T> & data,
     136             :                          const ActionFunctor & functor,
     137             :                          MooseRandom & generator,
     138             :                          const libMesh::Parallel::Communicator & comm);
     139             : template <typename T, typename ActionFunctor>
     140             : void resampleWithFunctor(const std::vector<T> & data,
     141             :                          const ActionFunctor & functor,
     142             :                          MooseRandom & generator,
     143             :                          const std::size_t seed_index,
     144             :                          const libMesh::Parallel::Communicator & comm);
     145             : template <typename T, typename ActionFunctor>
     146             : void resampleWithFunctor(const std::vector<T> & data,
     147             :                          const ActionFunctor & functor,
     148             :                          MooseRandom & generator,
     149             :                          const std::size_t seed_index,
     150             :                          const libMesh::Parallel::Communicator * comm_ptr);
     151             : //@}
     152             : }
     153             : 
     154             : template <typename T>
     155             : void
     156         137 : MooseUtils::swap(std::vector<T> & data,
     157             :                  const std::size_t idx0,
     158             :                  const std::size_t idx1,
     159             :                  const libMesh::Parallel::Communicator * comm_ptr)
     160             : {
     161         137 :   if (!comm_ptr || comm_ptr->size() == 1)
     162             :   {
     163             :     mooseAssert(idx0 < data.size(),
     164             :                 "idx0 (" << idx0 << ") out of range, data.size() is " << data.size());
     165             :     mooseAssert(idx1 < data.size(),
     166             :                 "idx1 (" << idx1 << ") out of range, data.size() is " << data.size());
     167         113 :     std::swap(data[idx0], data[idx1]);
     168             :   }
     169             : 
     170             :   else
     171             :   {
     172             :     // Size of the local input data
     173          24 :     const auto n_local = data.size();
     174          24 :     const auto rank = comm_ptr->rank();
     175             : 
     176             :     // Compute the global size of the vector
     177          24 :     std::size_t n_global = n_local;
     178          24 :     comm_ptr->sum(n_global);
     179             :     mooseAssert(idx0 < n_global,
     180             :                 "idx0 (" << idx0 << ") out of range, the global data size is " << n_global);
     181             :     mooseAssert(idx1 < n_global,
     182             :                 "idx1 (" << idx1 << ") out of range, the global data size is " << n_global);
     183             : 
     184             :     // Compute the vector data offsets, the scope cleans up the "n_local" vector
     185          24 :     std::vector<std::size_t> offsets(comm_ptr->size());
     186             :     {
     187          24 :       std::vector<std::size_t> local_sizes;
     188          24 :       comm_ptr->allgather(n_local, local_sizes);
     189          48 :       for (std::size_t i = 0; i < local_sizes.size() - 1; ++i)
     190          24 :         offsets[i + 1] = offsets[i] + local_sizes[i];
     191          24 :     }
     192             : 
     193             :     // Locate the rank and local index of the data to swap
     194          24 :     auto idx0_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx0));
     195          24 :     auto idx0_rank = std::distance(offsets.begin(), idx0_offset_iter);
     196          24 :     auto idx0_local_idx = idx0 - *idx0_offset_iter;
     197             : 
     198          24 :     auto idx1_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx1));
     199          24 :     auto idx1_rank = std::distance(offsets.begin(), idx1_offset_iter);
     200          24 :     auto idx1_local_idx = idx1 - *idx1_offset_iter;
     201             : 
     202             :     // The values, if any, needed from other rank
     203          24 :     std::unordered_map<processor_id_type, std::vector<std::size_t>> needs;
     204          24 :     if (idx0_rank != rank && idx1_rank == rank)
     205           6 :       needs[idx0_rank].push_back(idx0_local_idx);
     206          24 :     if (idx0_rank == rank && idx1_rank != rank)
     207           6 :       needs[idx1_rank].push_back(idx1_local_idx);
     208             : 
     209             :     // Collect the values needed by this processor
     210          24 :     std::unordered_map<processor_id_type, std::vector<T>> returns;
     211          24 :     auto return_functor =
     212          12 :         [&data, &returns](processor_id_type pid, const std::vector<std::size_t> & indices)
     213             :     {
     214          12 :       auto & returns_pid = returns[pid];
     215          24 :       for (auto idx : indices)
     216          12 :         returns_pid.push_back(data[idx]);
     217             :     };
     218          24 :     Parallel::push_parallel_vector_data(*comm_ptr, needs, return_functor);
     219             : 
     220             :     // Receive needed values from the others processors
     221          24 :     std::vector<T> incoming;
     222          48 :     auto recv_functor = [&incoming](processor_id_type /*pid*/, const std::vector<T> & values)
     223          12 :     { incoming = values; };
     224          24 :     Parallel::push_parallel_vector_data(*comm_ptr, returns, recv_functor);
     225             : 
     226          24 :     if (idx0_rank == rank && idx1_rank == rank)
     227           6 :       MooseUtils::swap(data, idx0_local_idx, idx1_local_idx);
     228             : 
     229          18 :     else if (idx0_rank == rank)
     230             :     {
     231             :       mooseAssert(incoming.size() == 1, "Only one value should be received");
     232           6 :       data[idx0_local_idx] = incoming[0];
     233             :     }
     234          12 :     else if (idx1_rank == rank)
     235             :     {
     236             :       mooseAssert(incoming.size() == 1, "Only one value should be received");
     237           6 :       data[idx1_local_idx] = incoming[0];
     238             :     }
     239          24 :   }
     240         137 : }
     241             : 
     242             : template <typename T>
     243             : void
     244          14 : MooseUtils::shuffle(std::vector<T> & data,
     245             :                     MooseRandom & generator,
     246             :                     const std::size_t seed_index,
     247             :                     const libMesh::Parallel::Communicator * comm_ptr)
     248             : {
     249             :   // REPLICATED data
     250          14 :   if (!comm_ptr || comm_ptr->size() == 1)
     251             :   {
     252           8 :     std::size_t n_global = data.size();
     253          80 :     for (std::size_t i = n_global - 1; i > 0; --i)
     254             :     {
     255          72 :       auto j = generator.randl(seed_index, 0, i);
     256          72 :       MooseUtils::swap(data, i, j, nullptr);
     257             :     }
     258             :   }
     259             : 
     260             :   // DISTRIBUTED data
     261             :   else
     262             :   {
     263             :     // Local/global size
     264           6 :     std::size_t n_local = data.size();
     265           6 :     std::size_t n_global = n_local;
     266           6 :     comm_ptr->sum(n_global);
     267             : 
     268             :     // Compute the vector data offsets, the scope cleans up the "n_local" vector
     269           6 :     std::vector<std::size_t> offsets(comm_ptr->size());
     270             :     {
     271           6 :       std::vector<std::size_t> local_sizes;
     272           6 :       comm_ptr->allgather(n_local, local_sizes);
     273          12 :       for (std::size_t i = 0; i < local_sizes.size() - 1; ++i)
     274           6 :         offsets[i + 1] = offsets[i] + local_sizes[i];
     275           6 :     }
     276             : 
     277             :     // Perform swaps
     278           6 :     auto rank = comm_ptr->rank();
     279          60 :     for (std::size_t idx0 = n_global - 1; idx0 > 0; --idx0)
     280             :     {
     281          54 :       auto idx1 = generator.randl(seed_index, 0, idx0);
     282             : 
     283             :       // Locate the rank and local index of the data to swap
     284          54 :       auto idx0_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx0));
     285          54 :       auto idx0_rank = std::distance(offsets.begin(), idx0_offset_iter);
     286          54 :       auto idx0_local_idx = idx0 - *idx0_offset_iter;
     287             : 
     288          54 :       auto idx1_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx1));
     289          54 :       auto idx1_rank = std::distance(offsets.begin(), idx1_offset_iter);
     290          54 :       auto idx1_local_idx = idx1 - *idx1_offset_iter;
     291             : 
     292             :       // The values, if any, needed from other rank
     293          54 :       std::unordered_map<processor_id_type, std::vector<std::size_t>> needs;
     294          54 :       if (idx0_rank != rank && idx1_rank == rank)
     295          12 :         needs[idx0_rank].push_back(idx0_local_idx);
     296          54 :       if (idx0_rank == rank && idx1_rank != rank)
     297          12 :         needs[idx1_rank].push_back(idx1_local_idx);
     298             : 
     299             :       // Collect the values needed by this processor
     300          54 :       std::unordered_map<processor_id_type, std::vector<T>> returns;
     301          54 :       auto return_functor =
     302          24 :           [&data, &returns](processor_id_type pid, const std::vector<std::size_t> & indices)
     303             :       {
     304          24 :         auto & returns_pid = returns[pid];
     305          48 :         for (auto idx : indices)
     306          24 :           returns_pid.push_back(data[idx]);
     307             :       };
     308          54 :       Parallel::push_parallel_vector_data(*comm_ptr, needs, return_functor);
     309             : 
     310             :       // Receive needed values from the others processors
     311          54 :       std::vector<T> incoming;
     312         102 :       auto recv_functor = [&incoming](processor_id_type /*pid*/, const std::vector<T> & values)
     313          24 :       { incoming = values; };
     314          54 :       Parallel::push_parallel_vector_data(*comm_ptr, returns, recv_functor);
     315             : 
     316          54 :       if (idx0_rank == rank && idx1_rank == rank)
     317          15 :         MooseUtils::swap(data, idx0_local_idx, idx1_local_idx);
     318             : 
     319          39 :       else if (idx0_rank == rank)
     320             :       {
     321             :         mooseAssert(incoming.size() == 1, "Only one value should be received");
     322          12 :         data[idx0_local_idx] = incoming[0];
     323             :       }
     324          27 :       else if (idx1_rank == rank)
     325             :       {
     326             :         mooseAssert(incoming.size() == 1, "Only one value should be received");
     327          12 :         data[idx1_local_idx] = incoming[0];
     328             :       }
     329             :     }
     330           6 :   }
     331          14 : }
     332             : 
     333             : template <typename T>
     334             : std::vector<T>
     335          14 : MooseUtils::resample(const std::vector<T> & data,
     336             :                      MooseRandom & generator,
     337             :                      const std::size_t seed_index,
     338             :                      const libMesh::Parallel::Communicator * comm_ptr)
     339             : {
     340             :   // Size of the local input data
     341          14 :   const std::size_t n_local = data.size();
     342             : 
     343             :   // Re-sampled data vector to be returned
     344          14 :   std::vector<T> replicate(n_local);
     345             : 
     346             :   // REPLICATED data
     347          14 :   if (!comm_ptr || comm_ptr->size() == 1)
     348             :   {
     349           8 :     replicate.resize(n_local);
     350          88 :     for (std::size_t j = 0; j < n_local; ++j)
     351             :     {
     352          80 :       auto index = generator.randl(seed_index, 0, n_local);
     353          80 :       replicate[j] = data[index];
     354             :     }
     355             :   }
     356             : 
     357             :   // DISTRIBUTED data
     358             :   else
     359             :   {
     360             :     // Compute the global size of the vector
     361           6 :     std::size_t n_global = n_local;
     362           6 :     comm_ptr->sum(n_global);
     363             : 
     364             :     // Compute the vector data offsets, the scope cleans up the "n_local" vector
     365           6 :     std::vector<std::size_t> offsets(comm_ptr->size());
     366             :     {
     367           6 :       std::vector<std::size_t> local_sizes;
     368           6 :       comm_ptr->allgather(n_local, local_sizes);
     369          12 :       for (std::size_t i = 0; i < local_sizes.size() - 1; ++i)
     370           6 :         offsets[i + 1] = offsets[i] + local_sizes[i];
     371           6 :     }
     372             : 
     373             :     // Advance the random number generator to the current offset
     374           6 :     const auto rank = comm_ptr->rank();
     375          18 :     for (std::size_t i = 0; i < offsets[rank]; ++i)
     376          12 :       generator.randl(seed_index, 0, n_global);
     377             : 
     378             :     // Compute the needs for this processor
     379           6 :     std::unordered_map<processor_id_type, std::vector<std::pair<std::size_t, std::size_t>>> needs;
     380          36 :     for (std::size_t i = 0; i < n_local; ++i)
     381             :     {
     382          30 :       const auto idx = generator.randl(seed_index, 0, n_global); // random global index
     383             : 
     384             :       // Locate the rank and local index of the data desired
     385          30 :       const auto idx_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx));
     386          30 :       const auto idx_rank = std::distance(offsets.begin(), idx_offset_iter);
     387          30 :       const auto idx_local_idx = idx - *idx_offset_iter;
     388             : 
     389             :       // Local available data can be inserted into the re-sample, non-local data is add the the
     390             :       // needs from other ranks
     391          30 :       if (idx_rank == rank)
     392          24 :         replicate[i] = data[idx_local_idx];
     393             :       else
     394           6 :         needs[idx_rank].emplace_back(idx_local_idx, i);
     395             :     }
     396             : 
     397             :     // Advance the random number generator to the end of the global vector
     398          24 :     for (std::size_t i = offsets[rank] + n_local; i < n_global; ++i)
     399          18 :       generator.randl(seed_index, 0, n_global);
     400             : 
     401             :     // Collect the values to be returned to the various processors
     402           6 :     std::unordered_map<processor_id_type, std::vector<std::pair<T, std::size_t>>> returns;
     403           6 :     auto return_functor =
     404           6 :         [&data, &returns](processor_id_type pid,
     405             :                           const std::vector<std::pair<std::size_t, std::size_t>> & indices)
     406             :     {
     407           6 :       auto & returns_pid = returns[pid];
     408          12 :       for (const auto & idx : indices)
     409           6 :         returns_pid.emplace_back(data[idx.first], idx.second);
     410             :     };
     411           6 :     Parallel::push_parallel_vector_data(*comm_ptr, needs, return_functor);
     412             : 
     413             :     // Receive resampled values from the various processors
     414           6 :     auto recv_functor =
     415          12 :         [&replicate](processor_id_type, const std::vector<std::pair<T, std::size_t>> & values)
     416             :     {
     417          12 :       for (const auto & value : values)
     418           6 :         replicate[value.second] = value.first;
     419             :     };
     420           6 :     Parallel::push_parallel_vector_data(*comm_ptr, returns, recv_functor);
     421           6 :   }
     422          28 :   return replicate;
     423           0 : }
     424             : 
     425             : template <typename T, typename ActionFunctor>
     426             : void
     427           4 : MooseUtils::resampleWithFunctor(const std::vector<T> & data,
     428             :                                 const ActionFunctor & functor,
     429             :                                 MooseRandom & generator,
     430             :                                 const std::size_t seed_index,
     431             :                                 const libMesh::Parallel::Communicator * comm_ptr)
     432             : {
     433           4 :   const std::size_t n_local = data.size();
     434             : 
     435           4 :   if (!comm_ptr || comm_ptr->size() == 1)
     436             :   {
     437          44 :     for (std::size_t j = 0; j < n_local; ++j)
     438             :     {
     439          40 :       auto index = generator.randl(seed_index, 0, n_local);
     440          40 :       functor(data[index]);
     441             :     }
     442             :   }
     443             :   else
     444             :   {
     445             :     // Compute the global size of the vector
     446           0 :     std::size_t n_global = n_local;
     447           0 :     comm_ptr->sum(n_global);
     448             : 
     449             :     // Compute the vector data offsets, the scope cleans up the "n_local" vector
     450           0 :     std::vector<std::size_t> offsets(comm_ptr->size());
     451             :     {
     452           0 :       std::vector<std::size_t> local_sizes;
     453           0 :       comm_ptr->allgather(n_local, local_sizes);
     454           0 :       for (std::size_t i = 0; i < local_sizes.size() - 1; ++i)
     455           0 :         offsets[i + 1] = offsets[i] + local_sizes[i];
     456           0 :     }
     457             : 
     458             :     // Advance the random number generator to the current offset
     459           0 :     const auto rank = comm_ptr->rank();
     460           0 :     for (std::size_t i = 0; i < offsets[rank]; ++i)
     461           0 :       generator.randl(seed_index, 0, n_global);
     462             : 
     463             :     // Compute the needs for this processor
     464           0 :     std::unordered_map<processor_id_type, std::vector<std::size_t>> indices;
     465           0 :     for (std::size_t i = 0; i < n_local; ++i)
     466             :     {
     467           0 :       const auto idx = generator.randl(seed_index, 0, n_global); // random global index
     468             : 
     469             :       // Locate the rank and local index of the data desired
     470           0 :       const auto idx_offset_iter = std::prev(std::upper_bound(offsets.begin(), offsets.end(), idx));
     471           0 :       const auto idx_rank = std::distance(offsets.begin(), idx_offset_iter);
     472           0 :       const auto idx_local_idx = idx - *idx_offset_iter;
     473             : 
     474             :       // Push back the index to appropriate rank
     475           0 :       indices[idx_rank].push_back(idx_local_idx);
     476             :     }
     477             : 
     478             :     // Advance the random number generator to the end of the global vector
     479           0 :     for (std::size_t i = offsets[rank] + n_local; i < n_global; ++i)
     480           0 :       generator.randl(seed_index, 0, n_global);
     481             : 
     482             :     // Send the indices to the appropriate rank and have the calculator do its work
     483           0 :     auto act_functor =
     484           0 :         [&functor, &data](processor_id_type /*pid*/, const std::vector<std::size_t> & indices)
     485             :     {
     486           0 :       for (const auto & idx : indices)
     487           0 :         functor(data[idx]);
     488             :     };
     489           0 :     Parallel::push_parallel_vector_data(*comm_ptr, indices, act_functor);
     490           0 :   }
     491           4 : }
     492             : 
     493             : template <typename T>
     494             : void
     495          40 : MooseUtils::swap(std::vector<T> & data,
     496             :                  const std::size_t idx0,
     497             :                  const std::size_t idx1,
     498             :                  const libMesh::Parallel::Communicator & comm)
     499             : {
     500          40 :   MooseUtils::swap<T>(data, idx0, idx1, &comm);
     501          40 : }
     502             : 
     503             : template <typename T>
     504             : void
     505           4 : MooseUtils::shuffle(std::vector<T> & data, MooseRandom & generator, const std::size_t seed_index)
     506             : {
     507           4 :   return MooseUtils::shuffle(data, generator, seed_index, nullptr);
     508             : }
     509             : 
     510             : template <typename T>
     511             : void
     512          10 : MooseUtils::shuffle(std::vector<T> & data,
     513             :                     MooseRandom & generator,
     514             :                     const libMesh::Parallel::Communicator & comm)
     515             : {
     516          10 :   return MooseUtils::shuffle(data, generator, 0, &comm);
     517             : }
     518             : 
     519             : template <typename T>
     520             : void
     521             : MooseUtils::shuffle(std::vector<T> & data,
     522             :                     MooseRandom & generator,
     523             :                     const std::size_t seed_index,
     524             :                     const libMesh::Parallel::Communicator & comm)
     525             : {
     526             :   return MooseUtils::shuffle(data, generator, seed_index, &comm);
     527             : }
     528             : 
     529             : template <typename T>
     530             : std::vector<T>
     531           4 : MooseUtils::resample(const std::vector<T> & data,
     532             :                      MooseRandom & generator,
     533             :                      const std::size_t seed_index)
     534             : {
     535           4 :   return MooseUtils::resample(data, generator, seed_index, nullptr);
     536             : }
     537             : 
     538             : template <typename T>
     539             : std::vector<T>
     540          10 : MooseUtils::resample(const std::vector<T> & data,
     541             :                      MooseRandom & generator,
     542             :                      const libMesh::Parallel::Communicator & comm)
     543             : {
     544          10 :   return MooseUtils::resample(data, generator, 0, &comm);
     545             : }
     546             : 
     547             : template <typename T>
     548             : std::vector<T>
     549             : MooseUtils::resample(const std::vector<T> & data,
     550             :                      MooseRandom & generator,
     551             :                      const std::size_t seed_index,
     552             :                      const libMesh::Parallel::Communicator & comm)
     553             : {
     554             :   return MooseUtils::resample(data, generator, seed_index, &comm);
     555             : }
     556             : 
     557             : template <typename T, typename ActionFunctor>
     558             : void
     559           4 : MooseUtils::resampleWithFunctor(const std::vector<T> & data,
     560             :                                 const ActionFunctor & functor,
     561             :                                 MooseRandom & generator,
     562             :                                 const std::size_t seed_index)
     563             : {
     564           4 :   return MooseUtils::resampleWithFunctor(data, functor, generator, seed_index, nullptr);
     565             : }
     566             : 
     567             : template <typename T, typename ActionFunctor>
     568             : void
     569             : MooseUtils::resampleWithFunctor(const std::vector<T> & data,
     570             :                                 const ActionFunctor & functor,
     571             :                                 MooseRandom & generator,
     572             :                                 const libMesh::Parallel::Communicator & comm)
     573             : {
     574             :   return MooseUtils::resampleWithFunctor(data, functor, generator, 0, &comm);
     575             : }
     576             : 
     577             : template <typename T, typename ActionFunctor>
     578             : void
     579             : MooseUtils::resampleWithFunctor(const std::vector<T> & data,
     580             :                                 const ActionFunctor & functor,
     581             :                                 MooseRandom & generator,
     582             :                                 const std::size_t seed_index,
     583             :                                 const libMesh::Parallel::Communicator & comm)
     584             : {
     585             :   return MooseUtils::resampleWithFunctor(data, functor, generator, seed_index, &comm);
     586             : }

Generated by: LCOV version 1.14