Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:37:20

0001 // Copyright 2004 The Trustees of Indiana University.
0002 
0003 // Use, modification and distribution is subject to the Boost Software
0004 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 //  Authors: Douglas Gregor
0008 //           Peter Gottschling
0009 //           Andrew Lumsdaine
0010 #ifndef BOOST_PARALLEL_DISTRIBUTION_HPP
0011 #define BOOST_PARALLEL_DISTRIBUTION_HPP
0012 
0013 #ifndef BOOST_GRAPH_USE_MPI
0014 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
0015 #endif
0016 
0017 #include <cstddef>
0018 #include <vector>
0019 #include <algorithm>
0020 #include <numeric>
0021 #include <boost/assert.hpp>
0022 #include <boost/iterator/counting_iterator.hpp>
0023 #include <boost/random/uniform_int.hpp>
0024 #include <boost/shared_ptr.hpp>
0025 #include <boost/config.hpp>
0026 #include <typeinfo>
0027 
0028 namespace boost { namespace parallel {
0029 
0030 template<typename ProcessGroup, typename SizeType = std::size_t>
0031 class variant_distribution
0032 {
0033 public:
0034   typedef typename ProcessGroup::process_id_type process_id_type;
0035   typedef typename ProcessGroup::process_size_type process_size_type;
0036   typedef SizeType size_type;
0037 
0038 private:
0039   struct basic_distribution
0040   {
0041     virtual ~basic_distribution() {}
0042     virtual size_type block_size(process_id_type, size_type) const = 0;
0043     virtual process_id_type in_process(size_type) const = 0;
0044     virtual size_type local(size_type) const = 0;
0045     virtual size_type global(size_type) const = 0;
0046     virtual size_type global(process_id_type, size_type) const = 0;
0047     virtual void* address() = 0;
0048     virtual const void* address() const = 0;
0049     virtual const std::type_info& type() const = 0;
0050   };
0051 
0052   template<typename Distribution>
0053   struct poly_distribution : public basic_distribution
0054   {
0055     explicit poly_distribution(const Distribution& distribution)
0056       : distribution_(distribution) { }
0057 
0058     virtual size_type block_size(process_id_type id, size_type n) const
0059     { return distribution_.block_size(id, n); }
0060 
0061     virtual process_id_type in_process(size_type i) const
0062     { return distribution_(i); }
0063 
0064     virtual size_type local(size_type i) const
0065     { return distribution_.local(i); }
0066 
0067     virtual size_type global(size_type n) const
0068     { return distribution_.global(n); }
0069 
0070     virtual size_type global(process_id_type id, size_type n) const
0071     { return distribution_.global(id, n); }
0072 
0073     virtual void* address() { return &distribution_; }
0074     virtual const void* address() const { return &distribution_; }
0075     virtual const std::type_info& type() const { return typeid(Distribution); }
0076 
0077   private:
0078     Distribution distribution_;
0079   };
0080 
0081 public:
0082   variant_distribution() { }
0083 
0084   template<typename Distribution>
0085   variant_distribution(const Distribution& distribution)
0086     : distribution_(new poly_distribution<Distribution>(distribution)) { }
0087 
0088   size_type block_size(process_id_type id, size_type n) const
0089   { return distribution_->block_size(id, n); }
0090   
0091   process_id_type operator()(size_type i) const
0092   { return distribution_->in_process(i); }
0093   
0094   size_type local(size_type i) const
0095   { return distribution_->local(i); }
0096   
0097   size_type global(size_type n) const
0098   { return distribution_->global(n); }
0099 
0100   size_type global(process_id_type id, size_type n) const
0101   { return distribution_->global(id, n); }
0102 
0103   operator bool() const { return distribution_; }
0104 
0105   void clear() { distribution_.reset(); }
0106 
0107   template<typename T>
0108   T* as()
0109   {
0110     if (distribution_->type() == typeid(T))
0111       return static_cast<T*>(distribution_->address());
0112     else
0113       return 0;
0114   }
0115 
0116   template<typename T>
0117   const T* as() const
0118   {
0119     if (distribution_->type() == typeid(T))
0120       return static_cast<T*>(distribution_->address());
0121     else
0122       return 0;
0123   }
0124 
0125 private:
0126   shared_ptr<basic_distribution> distribution_;
0127 };
0128 
0129 struct block
0130 {
0131   template<typename LinearProcessGroup>
0132   explicit block(const LinearProcessGroup& pg, std::size_t n) 
0133     : id(process_id(pg)), p(num_processes(pg)), n(n) { }
0134 
0135   // If there are n elements in the distributed data structure, returns the number of elements stored locally.
0136   template<typename SizeType>
0137   SizeType block_size(SizeType n) const
0138   { return (n / p) + ((std::size_t)(n % p) > id? 1 : 0); }
0139 
0140   // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
0141   template<typename SizeType, typename ProcessID>
0142   SizeType block_size(ProcessID id, SizeType n) const
0143   { return (n / p) + ((ProcessID)(n % p) > id? 1 : 0); }
0144 
0145   // Returns the processor on which element with global index i is stored
0146   template<typename SizeType>
0147   SizeType operator()(SizeType i) const
0148   { 
0149     SizeType cutoff_processor = n % p;
0150     SizeType cutoff = cutoff_processor * (n / p + 1);
0151 
0152     if (i < cutoff) return i / (n / p + 1);
0153     else return cutoff_processor + (i - cutoff) / (n / p);
0154   }
0155 
0156   // Find the starting index for processor with the given id
0157   template<typename ID>
0158   std::size_t start(ID id) const
0159   {
0160     std::size_t estimate = id * (n / p + 1);
0161     ID cutoff_processor = n % p;
0162     if (id < cutoff_processor) return estimate;
0163     else return estimate - (id - cutoff_processor);
0164   }
0165 
0166   // Find the local index for the ith global element
0167   template<typename SizeType>
0168   SizeType local(SizeType i) const
0169   { 
0170     SizeType owner = (*this)(i);
0171     return i - start(owner);
0172   }
0173 
0174   // Returns the global index of local element i
0175   template<typename SizeType>
0176   SizeType global(SizeType i) const
0177   { return global(id, i); }
0178 
0179   // Returns the global index of the ith local element on processor id
0180   template<typename ProcessID, typename SizeType>
0181   SizeType global(ProcessID id, SizeType i) const
0182   { return i + start(id); }
0183 
0184  private:
0185   std::size_t id; //< The ID number of this processor
0186   std::size_t p;  //< The number of processors
0187   std::size_t n;  //< The size of the problem space
0188 };
0189 
0190 // Block distribution with arbitrary block sizes
0191 struct uneven_block
0192 {
0193   typedef std::vector<std::size_t>    size_vector;
0194 
0195   template<typename LinearProcessGroup>
0196   explicit uneven_block(const LinearProcessGroup& pg, const std::vector<std::size_t>& local_sizes) 
0197     : id(process_id(pg)), p(num_processes(pg)), local_sizes(local_sizes)
0198   { 
0199     BOOST_ASSERT(local_sizes.size() == p);
0200     local_starts.resize(p + 1);
0201     local_starts[0] = 0;
0202     std::partial_sum(local_sizes.begin(), local_sizes.end(), &local_starts[1]);
0203     n = local_starts[p];
0204   }
0205 
0206   // To do maybe: enter local size in each process and gather in constructor (much handier)
0207   // template<typename LinearProcessGroup>
0208   // explicit uneven_block(const LinearProcessGroup& pg, std::size_t my_local_size) 
0209 
0210   // If there are n elements in the distributed data structure, returns the number of elements stored locally.
0211   template<typename SizeType>
0212   SizeType block_size(SizeType) const
0213   { return local_sizes[id]; }
0214 
0215   // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
0216   template<typename SizeType, typename ProcessID>
0217   SizeType block_size(ProcessID id, SizeType) const
0218   { return local_sizes[id]; }
0219 
0220   // Returns the processor on which element with global index i is stored
0221   template<typename SizeType>
0222   SizeType operator()(SizeType i) const
0223   {
0224     BOOST_ASSERT (i >= (SizeType) 0 && i < (SizeType) n); // check for valid range
0225     size_vector::const_iterator lb = std::lower_bound(local_starts.begin(), local_starts.end(), (std::size_t) i);
0226     return ((SizeType)(*lb) == i ? lb : --lb) - local_starts.begin();
0227   }
0228 
0229   // Find the starting index for processor with the given id
0230   template<typename ID>
0231   std::size_t start(ID id) const 
0232   {
0233     return local_starts[id];
0234   }
0235 
0236   // Find the local index for the ith global element
0237   template<typename SizeType>
0238   SizeType local(SizeType i) const
0239   { 
0240     SizeType owner = (*this)(i);
0241     return i - start(owner);
0242   }
0243 
0244   // Returns the global index of local element i
0245   template<typename SizeType>
0246   SizeType global(SizeType i) const
0247   { return global(id, i); }
0248 
0249   // Returns the global index of the ith local element on processor id
0250   template<typename ProcessID, typename SizeType>
0251   SizeType global(ProcessID id, SizeType i) const
0252   { return i + start(id); }
0253 
0254  private:
0255   std::size_t              id;           //< The ID number of this processor
0256   std::size_t              p;            //< The number of processors
0257   std::size_t              n;            //< The size of the problem space
0258   std::vector<std::size_t> local_sizes;  //< The sizes of all blocks
0259   std::vector<std::size_t> local_starts; //< Lowest global index of each block
0260 };
0261 
0262 
0263 struct oned_block_cyclic
0264 {
0265   template<typename LinearProcessGroup>
0266   explicit oned_block_cyclic(const LinearProcessGroup& pg, std::size_t size)
0267     : id(process_id(pg)), p(num_processes(pg)), size(size) { }
0268       
0269   template<typename SizeType>
0270   SizeType block_size(SizeType n) const
0271   { 
0272     return block_size(id, n);
0273   }
0274 
0275   template<typename SizeType, typename ProcessID>
0276   SizeType block_size(ProcessID id, SizeType n) const
0277   {
0278     SizeType all_blocks = n / size;
0279     SizeType extra_elements = n % size;
0280     SizeType everyone_gets = all_blocks / p;
0281     SizeType extra_blocks = all_blocks % p;
0282     SizeType my_blocks = everyone_gets + (p < extra_blocks? 1 : 0);
0283     SizeType my_elements = my_blocks * size 
0284                          + (p == extra_blocks? extra_elements : 0);
0285     return my_elements;
0286   }
0287 
0288   template<typename SizeType>
0289   SizeType operator()(SizeType i) const
0290   { 
0291     return (i / size) % p;
0292   }
0293 
0294   template<typename SizeType>
0295   SizeType local(SizeType i) const
0296   { 
0297     return ((i / size) / p) * size + i % size;
0298   }
0299 
0300   template<typename SizeType>
0301   SizeType global(SizeType i) const
0302   { return global(id, i); }
0303 
0304   template<typename ProcessID, typename SizeType>
0305   SizeType global(ProcessID id, SizeType i) const
0306   { 
0307     return ((i / size) * p + id) * size + i % size;
0308   }
0309 
0310  private:
0311   std::size_t id;                   //< The ID number of this processor
0312   std::size_t p;                    //< The number of processors
0313   std::size_t size;                 //< Block size
0314 };
0315 
0316 struct twod_block_cyclic
0317 {
0318   template<typename LinearProcessGroup>
0319   explicit twod_block_cyclic(const LinearProcessGroup& pg,
0320                              std::size_t block_rows, std::size_t block_columns,
0321                              std::size_t data_columns_per_row)
0322     : id(process_id(pg)), p(num_processes(pg)), 
0323       block_rows(block_rows), block_columns(block_columns), 
0324       data_columns_per_row(data_columns_per_row)
0325   { }
0326       
0327   template<typename SizeType>
0328   SizeType block_size(SizeType n) const
0329   { 
0330     return block_size(id, n);
0331   }
0332 
0333   template<typename SizeType, typename ProcessID>
0334   SizeType block_size(ProcessID id, SizeType n) const
0335   {
0336     // TBD: This is really lame :)
0337     int result = -1;
0338     while (n > 0) {
0339       --n;
0340       if ((*this)(n) == id && (int)local(n) > result) result = local(n);
0341     }
0342     ++result;
0343 
0344     //    std::cerr << "Block size of id " << id << " is " << result << std::endl;
0345     return result;
0346   }
0347 
0348   template<typename SizeType>
0349   SizeType operator()(SizeType i) const
0350   { 
0351     SizeType result = get_block_num(i) % p;
0352     //    std::cerr << "Item " << i << " goes on processor " << result << std::endl;
0353     return result;
0354   }
0355 
0356   template<typename SizeType>
0357   SizeType local(SizeType i) const
0358   { 
0359     // Compute the start of the block
0360     std::size_t block_num = get_block_num(i);
0361     //    std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
0362 
0363     std::size_t local_block_num = block_num / p;
0364     std::size_t block_start = local_block_num * block_rows * block_columns;
0365 
0366     // Compute the offset into the block 
0367     std::size_t data_row = i / data_columns_per_row;
0368     std::size_t data_col = i % data_columns_per_row;
0369     std::size_t block_offset = (data_row % block_rows) * block_columns 
0370                              + (data_col % block_columns);    
0371 
0372     //    std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
0373     return block_start + block_offset;
0374   }
0375 
0376   template<typename SizeType>
0377   SizeType global(SizeType i) const
0378   { 
0379     // Compute the (global) block in which this element resides
0380     SizeType local_block_num = i / (block_rows * block_columns);
0381     SizeType block_offset = i % (block_rows * block_columns);
0382     SizeType block_num = local_block_num * p + id;
0383 
0384     // Compute the position of the start of the block (globally)
0385     SizeType block_start = block_num * block_rows * block_columns;
0386 
0387     std::cerr << "Block " << block_num << " starts at index " << block_start
0388               << std::endl;
0389 
0390     // Compute the row and column of this block
0391     SizeType block_row = block_num / (data_columns_per_row / block_columns);
0392     SizeType block_col = block_num % (data_columns_per_row / block_columns);
0393 
0394     SizeType row_in_block = block_offset / block_columns;
0395     SizeType col_in_block = block_offset % block_columns;
0396 
0397     std::cerr << "Local index " << i << " is in block at row " << block_row
0398               << ", column " << block_col << ", in-block row " << row_in_block
0399               << ", in-block col " << col_in_block << std::endl;
0400 
0401     SizeType result = block_row * block_rows + block_col * block_columns
0402                     + row_in_block * block_rows + col_in_block;
0403 
0404 
0405     std::cerr << "global(" << i << "@" << id << ") = " << result 
0406               << " =? " << local(result) << std::endl;
0407     BOOST_ASSERT(i == local(result));
0408     return result;
0409   }
0410 
0411  private:
0412   template<typename SizeType>
0413   std::size_t get_block_num(SizeType i) const
0414   {
0415     std::size_t data_row = i / data_columns_per_row;
0416     std::size_t data_col = i % data_columns_per_row;
0417     std::size_t block_row = data_row / block_rows;
0418     std::size_t block_col = data_col / block_columns;
0419     std::size_t blocks_in_row = data_columns_per_row / block_columns;
0420     std::size_t block_num = block_col * blocks_in_row + block_row;
0421     return block_num;
0422   }
0423 
0424   std::size_t id;                   //< The ID number of this processor
0425   std::size_t p;                    //< The number of processors
0426   std::size_t block_rows;           //< The # of rows in each block
0427   std::size_t block_columns;        //< The # of columns in each block
0428   std::size_t data_columns_per_row; //< The # of columns per row of data
0429 };
0430 
0431 class twod_random
0432 {
0433   template<typename RandomNumberGen>
0434   struct random_int
0435   {
0436     explicit random_int(RandomNumberGen& gen) : gen(gen) { }
0437 
0438     template<typename T>
0439     T operator()(T n) const
0440     {
0441       uniform_int<T> distrib(0, n-1);
0442       return distrib(gen);
0443     }
0444 
0445   private:
0446     RandomNumberGen& gen;
0447   };
0448   
0449  public:
0450   template<typename LinearProcessGroup, typename RandomNumberGen>
0451   explicit twod_random(const LinearProcessGroup& pg,
0452                        std::size_t block_rows, std::size_t block_columns,
0453                        std::size_t data_columns_per_row,
0454                        std::size_t n,
0455                        RandomNumberGen& gen)
0456     : id(process_id(pg)), p(num_processes(pg)), 
0457       block_rows(block_rows), block_columns(block_columns), 
0458       data_columns_per_row(data_columns_per_row),
0459       global_to_local(n / (block_rows * block_columns))
0460   { 
0461     std::copy(make_counting_iterator(std::size_t(0)),
0462               make_counting_iterator(global_to_local.size()),
0463               global_to_local.begin());
0464 
0465 #if defined(BOOST_NO_CXX98_RANDOM_SHUFFLE)
0466     std::shuffle(global_to_local.begin(), global_to_local.end(), gen);
0467 #else
0468     random_int<RandomNumberGen> rand(gen);
0469     std::random_shuffle(global_to_local.begin(), global_to_local.end(), rand);
0470 #endif
0471   }
0472       
0473   template<typename SizeType>
0474   SizeType block_size(SizeType n) const
0475   { 
0476     return block_size(id, n);
0477   }
0478 
0479   template<typename SizeType, typename ProcessID>
0480   SizeType block_size(ProcessID id, SizeType n) const
0481   {
0482     // TBD: This is really lame :)
0483     int result = -1;
0484     while (n > 0) {
0485       --n;
0486       if ((*this)(n) == id && (int)local(n) > result) result = local(n);
0487     }
0488     ++result;
0489 
0490     //    std::cerr << "Block size of id " << id << " is " << result << std::endl;
0491     return result;
0492   }
0493 
0494   template<typename SizeType>
0495   SizeType operator()(SizeType i) const
0496   { 
0497     SizeType result = get_block_num(i) % p;
0498     //    std::cerr << "Item " << i << " goes on processor " << result << std::endl;
0499     return result;
0500   }
0501 
0502   template<typename SizeType>
0503   SizeType local(SizeType i) const
0504   { 
0505     // Compute the start of the block
0506     std::size_t block_num = get_block_num(i);
0507     //    std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
0508 
0509     std::size_t local_block_num = block_num / p;
0510     std::size_t block_start = local_block_num * block_rows * block_columns;
0511 
0512     // Compute the offset into the block 
0513     std::size_t data_row = i / data_columns_per_row;
0514     std::size_t data_col = i % data_columns_per_row;
0515     std::size_t block_offset = (data_row % block_rows) * block_columns 
0516                              + (data_col % block_columns);    
0517 
0518     //    std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
0519     return block_start + block_offset;
0520   }
0521 
0522  private:
0523   template<typename SizeType>
0524   std::size_t get_block_num(SizeType i) const
0525   {
0526     std::size_t data_row = i / data_columns_per_row;
0527     std::size_t data_col = i % data_columns_per_row;
0528     std::size_t block_row = data_row / block_rows;
0529     std::size_t block_col = data_col / block_columns;
0530     std::size_t blocks_in_row = data_columns_per_row / block_columns;
0531     std::size_t block_num = block_col * blocks_in_row + block_row;
0532     return global_to_local[block_num];
0533   }
0534 
0535   std::size_t id;                   //< The ID number of this processor
0536   std::size_t p;                    //< The number of processors
0537   std::size_t block_rows;           //< The # of rows in each block
0538   std::size_t block_columns;        //< The # of columns in each block
0539   std::size_t data_columns_per_row; //< The # of columns per row of data
0540   std::vector<std::size_t> global_to_local;
0541 };
0542 
0543 class random_distribution
0544 {
0545   template<typename RandomNumberGen>
0546   struct random_int
0547   {
0548     explicit random_int(RandomNumberGen& gen) : gen(gen) { }
0549 
0550     template<typename T>
0551     T operator()(T n) const
0552     {
0553       uniform_int<T> distrib(0, n-1);
0554       return distrib(gen);
0555     }
0556 
0557   private:
0558     RandomNumberGen& gen;
0559   };
0560   
0561  public:
0562   template<typename LinearProcessGroup, typename RandomNumberGen>
0563   random_distribution(const LinearProcessGroup& pg, RandomNumberGen& gen,
0564                       std::size_t n)
0565     : base(pg, n), local_to_global(n), global_to_local(n)
0566   {
0567     std::copy(make_counting_iterator(std::size_t(0)),
0568               make_counting_iterator(n),
0569               local_to_global.begin());
0570 
0571 #if defined(BOOST_NO_CXX98_RANDOM_SHUFFLE)
0572     std::shuffle(local_to_global.begin(), local_to_global.end(), gen);
0573 #else
0574     random_int<RandomNumberGen> rand(gen);
0575     std::random_shuffle(local_to_global.begin(), local_to_global.end(), rand);
0576 #endif
0577 
0578     for (std::vector<std::size_t>::size_type i = 0; i < n; ++i)
0579       global_to_local[local_to_global[i]] = i;
0580   }
0581 
0582   template<typename SizeType>
0583   SizeType block_size(SizeType n) const
0584   { return base.block_size(n); }
0585 
0586   template<typename SizeType, typename ProcessID>
0587   SizeType block_size(ProcessID id, SizeType n) const
0588   { return base.block_size(id, n); }
0589 
0590   template<typename SizeType>
0591   SizeType operator()(SizeType i) const
0592   {
0593     return base(global_to_local[i]);
0594   }
0595 
0596   template<typename SizeType>
0597   SizeType local(SizeType i) const
0598   { 
0599     return base.local(global_to_local[i]);
0600   }
0601 
0602   template<typename ProcessID, typename SizeType>
0603   SizeType global(ProcessID p, SizeType i) const
0604   { 
0605     return local_to_global[base.global(p, i)];
0606   }
0607 
0608   template<typename SizeType>
0609   SizeType global(SizeType i) const
0610   { 
0611     return local_to_global[base.global(i)];
0612   }
0613 
0614  private:
0615   block base;
0616   std::vector<std::size_t> local_to_global;
0617   std::vector<std::size_t> global_to_local;
0618 };
0619 
0620 } } // end namespace boost::parallel
0621 
0622 #endif // BOOST_PARALLEL_DISTRIBUTION_HPP
0623