vSMC
vSMC: Scalable Monte Carlo
backend_mpi.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/mpi/backend_mpi.hpp
3 //----------------------------------------------------------------------------
4 // vSMC: Scalable Monte Carlo
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013,2014, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef VSMC_MPI_BACKEND_MPI_HPP
33 #define VSMC_MPI_BACKEND_MPI_HPP
34 
35 #include <vsmc/internal/common.hpp>
36 #include <vsmc/core/weight_set.hpp>
38 #include <vsmc/mpi/mpi_manager.hpp>
40 
41 #define VSMC_RUNTIME_ASSERT_MPI_BACKEND_MPI_COPY_SIZE_MISMATCH \
42  VSMC_RUNTIME_ASSERT((N == global_size_), \
43  ("**StateMPI::copy** SIZE MISMATCH"))
44 
45 namespace vsmc {
46 
49 template <typename WeightSetBase, typename ID>
50 class WeightSetMPI : public WeightSetBase
51 {
52  public :
53 
54  typedef typename WeightSetBase::size_type size_type;
55  typedef ID mpi_id;
56 
57  explicit WeightSetMPI (size_type N) :
58  WeightSetBase(N), world_(MPICommunicator<ID>::instance().get(),
59  ::boost::mpi::comm_duplicate), resample_size_(0)
60  {
61  ::boost::mpi::all_reduce(world_, N, resample_size_,
62  std::plus<size_type>());
63  this->set_ess(static_cast<double>(resample_size_));
64  }
65 
66  size_type resample_size () const {return resample_size_;}
67 
68  void read_resample_weight (double *first) const
69  {
70  gather_resample_weight();
71  if (world_.rank() == 0) {
72  const std::size_t S = static_cast<std::size_t>(world_.size());
73  for (std::size_t r = 0; r != S; ++r) {
74  first = std::copy(weight_all_[r].begin(), weight_all_[r].end(),
75  first);
76  }
77  }
78  }
79 
80  const double *resample_weight_data () const
81  {
82  resample_weight_.resize(resample_size_);
83  read_resample_weight(&resample_weight_[0]);
84 
85  return world_.rank() == 0 ? &resample_weight_[0] : VSMC_NULLPTR;
86  }
87 
89  const ::boost::mpi::communicator &world () const {return world_;}
90 
91  protected :
92 
94  {
95  const size_type N = static_cast<size_type>(this->size());
96  double *const lwptr = this->mutable_log_weight_data();
97 
98  double lmax_weight = lwptr[0];
99  for (size_type i = 0; i != N; ++i)
100  if (lmax_weight < lwptr[i])
101  lmax_weight = lwptr[i];
102  double gmax_weight = 0;
103  ::boost::mpi::all_reduce(world_, lmax_weight, gmax_weight,
104  ::boost::mpi::maximum<double>());
105  for (size_type i = 0; i != N; ++i)
106  lwptr[i] -= gmax_weight;
107  }
108 
110  {
111  const size_type N = static_cast<size_type>(this->size());
112  double *const wptr = this->mutable_weight_data();
113 
114  double lcoeff = 0;
115  for (size_type i = 0; i != N; ++i)
116  lcoeff += wptr[i];
117  double gcoeff = 0;
118  ::boost::mpi::all_reduce(world_, lcoeff, gcoeff, std::plus<double>());
119  gcoeff = 1 / gcoeff;
120  for (size_type i = 0; i != N; ++i)
121  wptr[i] *= gcoeff;
122 
123  double less = 0;
124  for (size_type i = 0; i != N; ++i)
125  less += wptr[i] * wptr[i];
126  double gess = 0;
127  ::boost::mpi::all_reduce(world_, less, gess, std::plus<double>());
128  gess = 1 / gess;
129  this->set_ess(gess);
130  }
131 
132  double compute_ess (const double *first, bool use_log) const
133  {
134  using std::exp;
135 
136  const size_type N = static_cast<size_type>(this->size());
137  std::vector<double, AlignedAllocator<double> > buffer(N);
138  double *const bptr = &buffer[0];
139 
140  if (use_log) {
141  const double *const lwptr = this->log_weight_data();
142  for (size_type i = 0; i != N; ++i)
143  bptr[i] = lwptr[i] + first[i];
144  double lmax_weight = bptr[0];
145  for (size_type i = 0; i != N; ++i)
146  if (lmax_weight < bptr[i])
147  lmax_weight = bptr[i];
148  double gmax_weight = 0;
149  ::boost::mpi::all_reduce(world_, lmax_weight, gmax_weight,
150  ::boost::mpi::maximum<double>());
151  for (size_type i = 0; i != N; ++i)
152  bptr[i] -= gmax_weight;
153  for (size_type i = 0; i != N; ++i)
154  bptr[i] = exp(bptr[i]);
155  } else {
156  const double *const wptr = this->weight_data();
157  for (size_type i = 0; i != N; ++i)
158  bptr[i] = wptr[i] * first[i];
159  }
160 
161  double lcoeff = 0;
162  for (size_type i = 0; i != N; ++i)
163  lcoeff += bptr[i];
164  double gcoeff = 0;
165  ::boost::mpi::all_reduce(world_, lcoeff, gcoeff, std::plus<double>());
166  gcoeff = 1 / gcoeff;
167  for (size_type i = 0; i != N; ++i)
168  bptr[i] *= gcoeff;
169 
170  double less = 0;
171  for (size_type i = 0; i != N; ++i)
172  less += bptr[i] * bptr[i];
173  double gess = 0;
174  ::boost::mpi::all_reduce(world_, less, gess, std::plus<double>());
175 
176  return 1 / gess;
177  }
178 
179  double compute_cess (const double *first, bool use_log) const
180  {
181  using std::exp;
182 
183  const size_type N = static_cast<size_type>(this->size());
184  const double *bptr = first;
185  const double *const wptr = this->weight_data();
186  std::vector<double, AlignedAllocator<double> > buffer;
187  if (use_log) {
188  buffer.resize(N);
189  double *const cptr = &buffer[0];
190  for (size_type i = 0; i != N; ++i)
191  cptr[i] = exp(first[i]);
192  bptr = cptr;
193  }
194 
195  double labove = 0;
196  double lbelow = 0;
197  for (size_type i = 0; i != N; ++i) {
198  double wb = wptr[i] * bptr[i];
199  labove += wb;
200  lbelow += wb * bptr[i];
201  }
202  double gabove = 0;
203  double gbelow = 0;
204  ::boost::mpi::all_reduce(world_, labove, gabove, std::plus<double>());
205  ::boost::mpi::all_reduce(world_, lbelow, gbelow, std::plus<double>());
206 
207  return gabove * gabove / gbelow;
208  }
209 
210  private :
211 
212  ::boost::mpi::communicator world_;
213  size_type resample_size_;
214  mutable std::vector<double> resample_weight_;
215  mutable std::vector<double> weight_;
216  mutable std::vector<std::vector<double> > weight_all_;
217 
218  void gather_resample_weight () const
219  {
220  weight_.resize(this->size());
221  this->read_weight(&weight_[0]);
222  if (world_.rank() == 0)
223  ::boost::mpi::gather(world_, weight_, weight_all_, 0);
224  else
225  ::boost::mpi::gather(world_, weight_, 0);
226  }
227 }; // class WeightSetMPI
228 
231 template <typename BaseState, typename ID>
232 class StateMPI : public BaseState
233 {
234  public :
235 
239  typedef ID mpi_id;
240 
241  explicit StateMPI (size_type N) :
242  BaseState(N), world_(MPICommunicator<ID>::instance().get(),
243  ::boost::mpi::comm_duplicate),
244  offset_(0), global_size_(0), size_equal_(true),
245  copy_tag_(::boost::mpi::environment::max_tag())
246  {
247  ::boost::mpi::all_gather(world_, N, size_all_);
248  const std::size_t R = static_cast<std::size_t>(world_.rank());
249  const std::size_t S = static_cast<std::size_t>(world_.size());
250  for (std::size_t i = 0; i != R; ++i) {
251  offset_ += size_all_[i];
252  global_size_ += size_all_[i];
253  size_equal_ = size_equal_ && N == size_all_[i];
254  }
255  for (std::size_t i = R; i != S; ++i) {
256  global_size_ += size_all_[i];
257  size_equal_ = size_equal_ && N == size_all_[i];
258  }
259  }
260 
319  template <typename IntType>
320  void copy (size_type N, const IntType *copy_from)
321  {
323 
324  copy_pre_processor_dispatch(has_copy_pre_processor_<BaseState>());
325  copy_from_.resize(N);
326  if (world_.rank() == 0)
327  std::copy(copy_from, copy_from + N, copy_from_.begin());
328  ::boost::mpi::broadcast(world_, copy_from_, 0);
329  copy_this_node(N, &copy_from_[0], copy_recv_, copy_send_);
330  copy_inter_node(copy_recv_, copy_send_);
331  copy_post_processor_dispatch(has_copy_post_processor_<BaseState>());
332  }
333 
335  const ::boost::mpi::communicator &world () const {return world_;}
336 
338  size_type global_size () const {return global_size_;}
339 
342  size_type offset () const {return offset_;}
343 
346  int rank (size_type global_id) const
347  {
348  if (size_equal_)
349  return static_cast<int>(global_id / this->size());
350 
351  std::size_t r = 0;
352  size_type g = size_all_[0];
353  while (g <= global_id) {
354  ++r;
355  g += size_all_[r];
356  }
357 
358  return static_cast<int>(r);
359  }
360 
362  bool is_local (size_type global_id) const
363  {return global_id >= offset_ && global_id < this->size() + offset_;}
364 
367  size_type local_id (size_type global_id) const
368  {
369  if (size_equal_) {
370  return global_id -
371  this->size() * static_cast<size_type>(rank(global_id));
372  }
373 
374  std::size_t r = 0;
375  size_type g = size_all_[0];
376  while (g <= global_id) {
377  ++r;
378  g += size_all_[r];
379  }
380  g -= size_all_[r];
381 
382  return global_id - g;
383  }
384 
387  size_type global_id (size_type local_id) const {return local_id + offset_;}
388 
389  protected :
390 
392  int copy_tag () const {return copy_tag_;}
393 
417  void copy_this_node (size_type N, const size_type *copy_from,
418  std::vector<std::pair<int, size_type> > &copy_recv,
419  std::vector<std::pair<int, size_type> > &copy_send)
420  {
421  using std::advance;
422 
423  const int rank_this = world_.rank();
424 
425  copy_from_this_.resize(this->size());
426  const size_type *first = copy_from + offset_;
427  for (size_type to = 0; to != this->size(); ++to, ++first) {
428  size_type from = *first;
429  copy_from_this_[to] =
430  rank_this == rank(from) ? local_id(from) : to;
431  }
432  BaseState::copy(this->size(), &copy_from_this_[0]);
433 
434  copy_recv.clear();
435  copy_send.clear();
436  for (size_type to = 0; to != N; ++to, ++copy_from) {
437  size_type from = *copy_from;
438  int rank_recv = rank(to);
439  int rank_send = rank(from);
440  size_type id_recv = local_id(to);
441  size_type id_send = local_id(from);
442  if (rank_this == rank_recv && rank_this == rank_send) {
443  continue;
444  } else if (rank_this == rank_recv) {
445  copy_recv.push_back(std::make_pair(rank_send, id_recv));
446  } else if (rank_this == rank_send) {
447  copy_send.push_back(std::make_pair(rank_recv, id_send));
448  }
449  }
450  }
451 
457  const std::vector<std::pair<int, size_type> > &copy_recv,
458  const std::vector<std::pair<int, size_type> > &copy_send)
459  {
460  const int rank_this = world_.rank();
461  for (int r = 0; r != world_.size(); ++r) {
462  if (rank_this == r) {
463  for (std::size_t i = 0; i != copy_recv.size(); ++i) {
464  world_.recv(copy_recv[i].first, copy_tag_, pack_recv_);
465 #if VSMC_HAS_CXX11_RVALUE_REFERENCES
466  this->state_unpack(copy_recv[i].second,
467  cxx11::move(pack_recv_));
468 #else
469  this->state_unpack(copy_recv[i].second,
470  pack_recv_);
471 #endif
472  }
473  } else {
474  for (std::size_t i = 0; i != copy_send.size(); ++i) {
475  if (copy_send[i].first == r) {
476  pack_send_ = this->state_pack(copy_send[i].second);
477  world_.send(copy_send[i].first, copy_tag_, pack_send_);
478  }
479  }
480  }
481  }
482  }
483 
484  private :
485 
486  ::boost::mpi::communicator world_;
487  size_type offset_;
488  size_type global_size_;
489  bool size_equal_;
490  std::vector<size_type> size_all_;
491  int copy_tag_;
492  std::vector<size_type> copy_from_;
493  std::vector<size_type> copy_from_this_;
494  std::vector<std::pair<int, size_type> > copy_recv_;
495  std::vector<std::pair<int, size_type> > copy_send_;
496  typename BaseState::state_pack_type pack_recv_;
497  typename BaseState::state_pack_type pack_send_;
498 
499  VSMC_DEFINE_METHOD_CHECKER(copy_pre_processor, void, ())
500  VSMC_DEFINE_METHOD_CHECKER(copy_post_processor, void, ())
501 
502  void copy_pre_processor_dispatch (cxx11::true_type)
503  {BaseState::copy_pre_processor();}
504 
505  void copy_pre_processor_dispatch (cxx11::false_type) {}
506 
507  void copy_post_processor_dispatch (cxx11::true_type)
508  {BaseState::copy_post_processor();}
509 
510  void copy_post_processor_dispatch (cxx11::false_type) {}
511 }; // class StateMPI
512 
513 } // namespace vsmc
514 
515 #endif // VSMC_MPI_BACKEND_MPI_HPP
double compute_cess(const double *first, bool use_log) const
Definition: adapter.hpp:37
StateMPI(size_type N)
WeightSetBase::size_type size_type
Definition: backend_mpi.hpp:54
#define VSMC_RUNTIME_ASSERT_MPI_BACKEND_MPI_COPY_SIZE_MISMATCH
Definition: backend_mpi.hpp:41
bool is_local(size_type global_id) const
Given a global particle id check if it is on this node
size_type offset() const
The number of particles on nodes with ranks less than the rank of this node.
void copy(size_type N, const IntType *copy_from)
Copy particles.
#define VSMC_DEFINE_METHOD_CHECKER(name, RT, Args)
Definition: traits.hpp:118
size_type resample_size() const
Definition: backend_mpi.hpp:66
integral_constant< bool, false > false_type
WeightSetMPI(size_type N)
Definition: backend_mpi.hpp:57
T & get(Array< T, N > &ary)
Array ADL of get.
Definition: array.hpp:322
remove_reference< T >::type && move(T &&t) noexcept
WeightSetMPI< typename traits::WeightSetTypeTrait< BaseState >::type, ID > weight_set_type
const ::boost::mpi::communicator & world() const
A duplicated MPI communicator for this weight set object.
Definition: backend_mpi.hpp:89
size_type global_id(size_type local_id) const
Transfer a local particle id on this node into a global particle id.
#define VSMC_NULLPTR
nullptr
Definition: defines.hpp:79
Particle::weight_set_type subtype using MPI.
Definition: forward.hpp:71
integral_constant< bool, true > true_type
MPI Communicator.
Definition: forward.hpp:70
void copy_this_node(size_type N, const size_type *copy_from, std::vector< std::pair< int, size_type > > &copy_recv, std::vector< std::pair< int, size_type > > &copy_send)
Perform local copy.
const ::boost::mpi::communicator & world() const
A duplicated MPI communicator for this state value object.
internal::SizeTypeDispatch< T, value >::type type
Definition: traits.hpp:148
int rank(size_type global_id) const
Given a global particle id return the rank of the node it belongs.
size_type global_size() const
The number of particles on all nodes.
int copy_tag() const
The MPI recv/send tag used by copy_inter_node
void copy_inter_node(const std::vector< std::pair< int, size_type > > &copy_recv, const std::vector< std::pair< int, size_type > > &copy_send)
Perform global copy.
double compute_ess(const double *first, bool use_log) const
traits::SizeTypeTrait< BaseState >::type size_type
void normalize_log_weight()
Definition: backend_mpi.hpp:93
size_type local_id(size_type global_id) const
Transfer a global particle id into a local particle id (possibly not on this node, use rank to get the rank of its node)
const double * resample_weight_data() const
Definition: backend_mpi.hpp:80
void read_resample_weight(double *first) const
Definition: backend_mpi.hpp:68