Parallel numerical verification of the σ_odd problem  October 6, 2018
mpi.cpp
Go to the documentation of this file.
1 /* -*- coding: latin-1 -*- */
2 /** \file mpi/mpi/mpi.cpp (January 17, 2018)
3  *
4  * GPLv3 --- Copyright (C) 2017, 2018 Olivier Pirson
5  * http://www.opimedia.be/
6  */
7 
8 // \cond
9 #include <cassert>
10 
11 #include <algorithm>
12 #include <set>
13 #include <vector>
14 // \endcond
15 
16 #include "../../sequential/sequential/sequential.hpp"
17 
18 #include "mpi.hpp"
19 
20 
21 namespace mpi {
22 
23  /* ***********
24  * Functions *
25  *************/
26 
27  std::set<nat_type>
29  const std::set<nat_type> &previous_bad_table,
30  bool print_bad,
31  unsigned int range_size,
32  unsigned int master_range_size) {
33  assert(sigmaodd::is_odd(first_n));
34  assert(3 <= first_n);
35  assert(first_n <= last_n);
36  assert(last_n <= sigmaodd::MAX_POSSIBLE_N);
37  assert(sigmaodd::is_even(range_size));
38  assert(sigmaodd::is_even(master_range_size));
39 #ifdef PRIME16
40  assert(n < 65536);
41 #endif
42 
43  const unsigned int nb_process = mpi_nb_process();
44 
45  const nat_type bad_first_n = (previous_bad_table.empty()
46  ? first_n
47  : 0);
48 
49  std::set<nat_type> bad_table(previous_bad_table);
50 
51  std::set<nat_type>* bad_tables = new std::set<nat_type>[nb_process]();
52  nat_type* first_ns = new nat_type[nb_process];
53 
54  // Starts empty slave process to simplify following code (all slave process are used)
55  for (unsigned int i = 1; i < nb_process; ++i) {
56  send_first_last_bad_bad_to(1, 0, 0, 0, i); // active the slave
57  first_ns[i] = first_n;
58  }
59 
60  for (nat_type n = first_n; n <= last_n; ) {
61  // Start each slave process on a range
62  first_ns[0] = n;
63 
64  // Update the last known possible bad number
65  nat_type bad_last_n = sequential::sequential_min_array(first_ns, nb_process) - 1;
66 
67  n += master_range_size; // reserve range for master process
68 
69  for (unsigned int i = 1; (i < nb_process) && (n <= last_n); ++i) {
70  if (is_finished_from(i)) { // if slave process i is free
72 
73  const std::set<nat_type> new_bad_table = wait_bad_table_from(i);
74 
75  first_ns[i] = n;
76 
77  // Update the last known possible bad number
78  bad_last_n = sequential::sequential_min_array(first_ns, nb_process) - 1;
79 
80  // Update bad_table
81  bad_table.insert(new_bad_table.cbegin(), new_bad_table.cend());
82  if (print_bad) {
84  }
85 
86  // Start this slave process on its range
87  send_first_last_bad_bad_to(first_ns[i], std::min(n + range_size - 1, last_n),
88  bad_first_n, bad_last_n,
89  i);
90 
91  // Send new bad numbers
92  std::set<nat_type> diff;
93 
94  std::set_difference(bad_table.cbegin(), bad_table.cend(),
95  bad_tables[i].cbegin(),
96  bad_tables[i].cend(),
97  std::inserter(diff, diff.begin()));
98  bad_tables[i].insert(diff.cbegin(), diff.cend());
99  send_bad_table_to(diff, i);
100 
101  // Next range
102  n += range_size;
103  }
104  }
105 
106  // Run on master process
107  {
108  const std::set<nat_type> new_bad_table
110  (first_ns[0],
111  std::min(first_ns[0] + master_range_size - 1, last_n),
112  bad_table,
113  bad_first_n, bad_last_n,
114  false);
115 
116  // Update bad_table
117  bad_table.insert(new_bad_table.cbegin(), new_bad_table.cend());
118  if (print_bad) {
120  }
121  }
122  }
123 
124  // Wait end of each slave process
125  for (unsigned int i = 1; i < nb_process; ++i) {
127 
128  const std::set<nat_type> new_bad_table = wait_bad_table_from(i);
129 
130  send_first_last_bad_bad_to(0, 0, 0, 0, i); // finish the slave
131 
132  // Update bad_table
133  bad_table.insert(new_bad_table.cbegin(), new_bad_table.cend());
134  if (print_bad) {
136  }
137  }
138 
139  delete[] bad_tables;
140  delete[] first_ns;
141 
142  return bad_table;
143  }
144 
145 
146  std::set<nat_type>
148  const std::set<nat_type> &previous_bad_table,
149  bool print_bad) {
150  assert(sigmaodd::is_odd(first_n));
151  assert(3 <= first_n);
152  assert(first_n <= last_n);
153  assert(last_n <= sigmaodd::MAX_POSSIBLE_N);
154 #ifdef PRIME16
155  assert(n < 65536);
156 #endif
157 
158  const unsigned int nb_process = mpi_nb_process();
159 
160  const nat_type bad_first_n = (previous_bad_table.empty()
161  ? first_n
162  : 0);
163 
164  std::set<nat_type> bad_table(previous_bad_table);
165 
166  std::set<nat_type>* bad_tables = new std::set<nat_type>[nb_process]();
167  bool* is_lowers = new bool[nb_process];
168  nat_type* ns = new nat_type[nb_process];
169 
170  for (nat_type n = first_n; n <= last_n; ) {
171  unsigned int nb_process_required = 0;
172 
173  // Assign one number for each process
174  // and start each slave process
175  for ( ; (nb_process_required < nb_process) && (n <= last_n); n += 2) {
177  ns[nb_process_required] = n;
178 
179  if (nb_process_required > 0) { // if not master process
180  send_n_bad_bad_to(ns[nb_process_required],
181  bad_first_n, ns[0] - 1,
182  nb_process_required);
183 
184  // Send new bad numbers
185  std::set<nat_type> diff;
186 
187  std::set_difference(bad_table.cbegin(), bad_table.cend(),
188  bad_tables[nb_process_required].cbegin(),
189  bad_tables[nb_process_required].cend(),
190  std::inserter(diff, diff.begin()));
191  bad_tables[nb_process_required].insert(diff.cbegin(), diff.cend());
192  send_bad_table_to(diff, nb_process_required);
193  }
194 
195  ++nb_process_required;
196  }
197  }
198 
199  // Run on master process
200  is_lowers[0]
202  bad_table,
203  bad_first_n, ns[0] - 1);
204 
205  // Wait end of each slave process
206  for (unsigned int i = 1; i < nb_process_required; ++i) {
207  is_lowers[i] = wait_bool_from(i);
208  }
209 
210  // Update bad_table
211  for (unsigned int i = 0; i < nb_process_required; ++i) {
212  if (!is_lowers[i]) { // new bad number identified
213  bad_table.insert(ns[i]);
214  if (print_bad) {
215  std::cout << ns[i] << std::endl;
216  }
217  }
218  }
219  if (print_bad) {
220  std::cout.flush();
221  }
222  }
223 
224  for (unsigned int i = 1; i < nb_process; ++i) {
225  send_n_bad_bad_to(0, 0, 0, i); // finish the slave
226  }
227 
228  delete[] bad_tables;
229  delete[] is_lowers;
230  delete[] ns;
231 
232  return bad_table;
233  }
234 
235 
236  bool
238  static std::set<nat_type> static_bad_table; // accumulate all bad numbers required
239 
240  // Wait n, first_n and last_n
242 
243  if (nfl.n == 0) { // finish
244  return false;
245  }
246 
247  // Wait new bad numbers (since previous call)
248  const std::set<nat_type> bad_table = wait_bad_table_from(0);
249 
250  static_bad_table.insert(bad_table.cbegin(), bad_table.cend());
251 
252 
253  // Compute n
256  static_bad_table,
257  nfl.bad_first_n, nfl.bad_last_n));
258 
259  // Continue
260  return true;
261  }
262 
263 
264  bool
266  static std::set<nat_type> static_bad_table; // accumulate all bad numbers required
267 
269 
270  if (flb.first_n == 0) { // finish
271  return false;
272  }
273 
274  std::set<nat_type> new_bad_table; // bad numbers will be found during this call
275 
276  if (flb.first_n != 1) { // real value to compute (1 is to active the slave)
277  // Wait new bad numbers (since previous call)
278  const std::set<nat_type> bad_table = wait_bad_table_from(0);
279 
280  static_bad_table.insert(bad_table.cbegin(), bad_table.cend());
281 
282 
283  // Compute range
284  new_bad_table
286  static_bad_table,
287  flb.bad_first_n, flb.bad_last_n,
288  false);
289  }
290 
292  send_bad_table_to(new_bad_table, 0);
293 
294  // Continue
295  return true;
296  }
297 
298 
299  void
301  int subversion;
302  int version;
303  char version_str[MPI_MAX_LIBRARY_VERSION_STRING];
304  int version_str_len;
305 
306  MPI_Get_version(&version, &subversion);
307  MPI_Get_library_version(version_str, &version_str_len);
308 
309  std::cout << "Open MPI standard version: " << version << "." << subversion
310  << "\tLibrary version: " << version_str << std::endl;
311  }
312 
313 
314  void
315  send_bad_table_to(const std::set<nat_type> &bad_table, rank_type to_rank) {
316  assert(sizeof(nat_type) == 8);
317 
318  const uint32_t size = static_cast<uint32_t>(bad_table.size());
319 
320  send_size_to(size, to_rank);
321 
322  const std::vector<nat_type> bad_table_vector(bad_table.cbegin(), bad_table.cend());
323 
324 #pragma GCC diagnostic push
325 #pragma GCC diagnostic ignored "-Wold-style-cast"
326  MPI_Send(bad_table_vector.data(), size, MPI_UINT64_T, to_rank, 0, MPI_COMM_WORLD);
327 #pragma GCC diagnostic pop
328  }
329 
330 
331  std::set<nat_type>
333  assert(sizeof(nat_type) == 8);
334 
335  const unsigned int size = wait_size_from(from_rank);
336 
337  std::vector<nat_type> bad_table_vector(size);
338 
339 #pragma GCC diagnostic push
340 #pragma GCC diagnostic ignored "-Wold-style-cast"
341  MPI_Recv(bad_table_vector.data(), size, MPI_UINT64_T, from_rank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
342 #pragma GCC diagnostic pop
343 
344  const std::set<nat_type> bad_table(bad_table_vector.cbegin(), bad_table_vector.cend());
345 
346  return bad_table;
347  }
348 
349 } // namespace mpi
std::vector< nat_type > sequential_print_in_order(const std::set< nat_type > &ns)
Print number from ns, in increasing order and return a list of these number in the same order...
Definition: sequential.cpp:480
std::set< nat_type > sequential_check_gentle_varsigma_odd(nat_type first_n, nat_type last_n, bool print_bad)
Check in the order all odd gentle numbers between first_n and last_n, and if print_bad then print all...
Definition: sequential.cpp:29
void send_size_to(unsigned int size, rank_type to_rank)
Send a size to a process.
bool mpi_slave_wait_compute_n()
Computation of one n by a slave, for the mpi_check_gentle_varsigma_odd__one_by_one() master function...
Definition: mpi.cpp:237
unsigned int wait_size_from(rank_type from_rank)
Wait and receive a size from a process.
Structure for n, first_n and last_n values.
Definition: mpi.hpp:61
void send_bool_to_master(bool b)
Send a boolean value to the master.
Definition: mpi__inline.hpp:92
constexpr bool is_odd(nat_type n)
Return true iff n is odd.
first_last_bad_bad_type wait_first_last_bad_bad_from_master()
Wait and receive values first_n, last_n and bad_last_n from the master.
constexpr bool is_even(nat_type n)
Return true iff n is even.
nat_type bad_first_n
Definition: mpi.hpp:64
bool sequential_is_varsigma_odd_lower(nat_type n, const std::set< nat_type > &bad_table, nat_type bad_first_n)
Return true iff varsigma_odd(n) < n.
Definition: sequential.cpp:202
void print_mpi_versions()
Print the Open MPI version and the library version.
Definition: mpi.cpp:300
Definition: mpi.cpp:21
void send_finished_to_master()
Send a finished empty message to the master.
sigmaodd::nat_type nat_type
Definition: mpi.hpp:31
std::set< nat_type > wait_bad_table_from(rank_type from_rank)
Wait and receive bad values from a process.
Definition: mpi.cpp:332
bool wait_bool_from(rank_type from_rank)
Wait and receive a boolean value from a process.
unsigned int mpi_nb_process()
Return the number of process.
Definition: mpi__inline.hpp:52
constexpr nat_type MAX_POSSIBLE_N
Lower bound of the bigger number such that it is possible to compute the result of the sigma function...
Definition: helper.hpp:54
constexpr nat_type sequential_min_array(const nat_type ns[], size_t size)
Return the minimum of the first size values of ns.
unsigned int rank_type
Type of MPI rank.
Definition: mpi.hpp:43
bool is_finished_from(rank_type from_rank)
Return true iff the process send an empty finished message.
Definition: mpi__inline.hpp:38
void send_bad_table_to(const std::set< nat_type > &bad_table, rank_type to_rank)
Send bad values to a process.
Definition: mpi.cpp:315
Structure for first_n, last_n, bad_first_n and bad_last_n values.
Definition: mpi.hpp:49
std::set< nat_type > mpi_check_gentle_varsigma_odd__one_by_one(nat_type first_n, nat_type last_n, const std::set< nat_type > &previous_bad_table, bool print_bad)
Definition: mpi.cpp:147
constexpr bool is_first_mersenne_prime_unitary_divide_or_square(nat_type n)
Return true iff is_first_mersenne_prime_unitary_divide(n) or is_square(n).
void send_first_last_bad_bad_to(nat_type first_n, nat_type last_n, nat_type bad_first_n, nat_type bad_last_n, rank_type to_rank)
Send first_n, last_n, bad_first and bad_last_n to a process.
std::set< nat_type > mpi_check_gentle_varsigma_odd__dynamic(nat_type first_n, nat_type last_n, const std::set< nat_type > &previous_bad_table, bool print_bad, unsigned int range_size, unsigned int master_range_size)
Check in the order all odd gentle numbers between first_n and last_n, and if print_bad then print all...
Definition: mpi.cpp:28
nat_type bad_last_n
Definition: mpi.hpp:65
Implementation of the message-passing (MPI) algorithms presented in the report.
bool mpi_slave_wait_compute_range()
Computation of one range by a slave, for the mpi_check_gentle_varsigma_odd__dynamic() master function...
Definition: mpi.cpp:265
n_bad_bad_type wait_n_bad_bad_from_master()
Wait and receive values n, first_n and last_n from the master.
void wait_finished_from(rank_type from_rank)
Wait and received a finished empty message from a process.
void send_n_bad_bad_to(nat_type n, nat_type first_n, nat_type last_n, rank_type to_rank)
Send n, first_n and last_n to a process.