1 // Copyright (C) 2004-2008 The Trustees of Indiana University.
2 
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 //  Authors: Douglas Gregor
8 //           Andrew Lumsdaine
9 #include <boost/graph/use_mpi.hpp>
10 #include <boost/config.hpp>
11 #include <boost/throw_exception.hpp>
12 #include <boost/graph/distributed/mpi_process_group.hpp>
13 #include <boost/property_map/property_map.hpp>
14 #include <boost/test/minimal.hpp>
15 #include <vector>
16 #include <string>
17 #include <boost/serialization/vector.hpp>
18 #include <boost/serialization/string.hpp>
19 #include <boost/serialization/utility.hpp>
20 #include <boost/lexical_cast.hpp>
21 #include <boost/graph/parallel/basic_reduce.hpp>
22 
23 #ifdef BOOST_NO_EXCEPTIONS
24 void
throw_exception(std::exception const & ex)25 boost::throw_exception(std::exception const& ex)
26 {
27     std::cout << ex.what() << std::endl;
28     abort();
29 }
30 #endif
31 
32 using namespace boost;
33 using boost::graph::distributed::mpi_process_group;
34 
35 enum color_t { red, blue };
36 
37 struct remote_key
38 {
remote_keyremote_key39   remote_key(int p = -1, std::size_t l = 0) : processor(p), local_key(l) {}
40 
41   int processor;
42   std::size_t local_key;
43 
44   template<typename Archiver>
serializeremote_key45   void serialize(Archiver& ar, const unsigned int /*version*/)
46   {
47     ar & processor & local_key;
48   }
49 };
50 
51 namespace boost { namespace mpi {
52     template<> struct is_mpi_datatype<remote_key> : mpl::true_ { };
53 } }
54 BOOST_IS_BITWISE_SERIALIZABLE(remote_key)
55 BOOST_CLASS_IMPLEMENTATION(remote_key,object_serializable)
56 BOOST_CLASS_TRACKING(remote_key,track_never)
57 
58 namespace boost {
59 
60 template<>
61 struct hash<remote_key>
62 {
operator ()boost::hash63   std::size_t operator()(const remote_key& key) const
64   {
65     std::size_t hash = hash_value(key.processor);
66     hash_combine(hash, key.local_key);
67     return hash;
68   }
69 };
70 }
71 
operator ==(const remote_key & x,const remote_key & y)72 inline bool operator==(const remote_key& x, const remote_key& y)
73 { return x.processor == y.processor && x.local_key == y.local_key; }
74 
75 struct remote_key_to_global
76 {
77   typedef readable_property_map_tag category;
78   typedef remote_key key_type;
79   typedef std::pair<int, std::size_t> value_type;
80   typedef value_type reference;
81 };
82 
83 inline std::pair<int, std::size_t>
get(remote_key_to_global,const remote_key & key)84 get(remote_key_to_global, const remote_key& key)
85 {
86   return std::make_pair(key.processor, key.local_key);
87 }
88 
89 template<typename T>
90 struct my_reduce : boost::parallel::basic_reduce<T> {
91   BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
92 };
93 
colored_test()94 void colored_test()
95 {
96   mpi_process_group pg;
97   const int n = 500;
98 
99   color_t my_start_color = process_id(pg) % 2 == 0? ::red : ::blue;
100   int next_processor = (process_id(pg) + 1) % num_processes(pg);
101   color_t next_start_color = next_processor % 2 == 0? ::red : ::blue;
102 
103   // Initial color map: even-numbered processes are all red,
104   // odd-numbered processes are all blue.
105   std::vector<color_t> color_vec(n, my_start_color);
106 
107   typedef iterator_property_map<std::vector<color_t>::iterator,
108                                 identity_property_map> LocalPropertyMap;
109   LocalPropertyMap local_colors(color_vec.begin(), identity_property_map());
110 
111   synchronize(pg);
112 
113   // Create the distributed property map
114   typedef boost::parallel::distributed_property_map<mpi_process_group,
115                                                     remote_key_to_global,
116                                                     LocalPropertyMap> ColorMap;
117   ColorMap colors(pg, remote_key_to_global(), local_colors);
118   colors.set_reduce(my_reduce<color_t>());
119 
120   if (process_id(pg) == 0) std::cerr << "Checking local colors...";
121   // check local processor colors
122   for (int i = 0; i < n; ++i) {
123     remote_key k(process_id(pg), i);
124     BOOST_CHECK(get(colors, k) == my_start_color);
125   }
126 
127   colors.set_consistency_model(boost::parallel::cm_bidirectional);
128   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's default colors...";
129   // check next processor's colors
130   for (int i = 0; i < n; ++i) {
131     remote_key k(next_processor, i);
132     BOOST_CHECK(get(colors, k) == color_t());
133   }
134 
135   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
136   synchronize(pg);
137 
138   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's colors...";
139   // check next processor's colors
140   for (int i = 0; i < n; ++i) {
141     remote_key k(next_processor, i);
142     BOOST_CHECK(get(colors, k) == next_start_color);
143   }
144 
145   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
146   synchronize(pg);
147 
148   if (process_id(pg) == 0) std::cerr << "OK.\nChanging next processor's colors...";
149   // change the next processor's colors
150   color_t next_finish_color = next_processor % 2 == 0? ::blue : ::red;
151   for (int i = 0; i < n; ++i) {
152     remote_key k(next_processor, i);
153     put(colors, k, next_finish_color);
154   }
155 
156   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
157   synchronize(pg);
158 
159   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed colors...";
160   // check our own colors
161   color_t my_finish_color = process_id(pg) % 2 == 0? ::blue : ::red;
162   for (int i = 0; i < n; ++i) {
163     remote_key k(process_id(pg), i);
164     BOOST_CHECK(get(colors, k) == my_finish_color);
165   }
166 
167   // check our neighbor's colors
168   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed colors on neighbor...";
169   for (int i = 0; i < n; ++i) {
170     remote_key k(next_processor, i);
171     BOOST_CHECK(get(colors, k) == next_finish_color);
172   }
173 
174   synchronize(pg);
175 
176   if (process_id(pg) == 0) std::cerr << "OK.\n";
177 }
178 
bool_test()179 void bool_test()
180 {
181   mpi_process_group pg;
182   const int n = 500;
183 
184   bool my_start_value = process_id(pg) % 2;
185   int next_processor = (process_id(pg) + 1) % num_processes(pg);
186   bool next_start_value = ((process_id(pg) + 1) % num_processes(pg)) % 2;
187 
188   // Initial color map: even-numbered processes are false,
189   // odd-numbered processes are true
190   std::vector<bool> bool_vec(n, my_start_value);
191 
192   typedef iterator_property_map<std::vector<bool>::iterator,
193                                 identity_property_map> LocalPropertyMap;
194   LocalPropertyMap local_values(bool_vec.begin(), identity_property_map());
195 
196   synchronize(pg);
197 
198   // Create the distributed property map
199   typedef boost::parallel::distributed_property_map<mpi_process_group,
200                                                     remote_key_to_global,
201                                                     LocalPropertyMap> ValueMap;
202   ValueMap values(pg, remote_key_to_global(), local_values);
203   values.set_reduce(my_reduce<bool>());
204 
205   if (process_id(pg) == 0) std::cerr << "Checking local values...";
206   // check local processor values
207   for (int i = 0; i < n; ++i) {
208     remote_key k(process_id(pg), i);
209     BOOST_CHECK(get(values, k) == my_start_value);
210   }
211 
212   values.set_consistency_model(boost::parallel::cm_bidirectional);
213   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's default values...";
214   // check next processor's values
215   for (int i = 0; i < n; ++i) {
216     remote_key k(next_processor, i);
217     BOOST_CHECK(get(values, k) == false);
218   }
219 
220   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
221   synchronize(pg);
222 
223   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's values...";
224   // check next processor's values
225   for (int i = 0; i < n; ++i) {
226     remote_key k(next_processor, i);
227     BOOST_CHECK(get(values, k) == next_start_value);
228   }
229 
230   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
231   synchronize(pg);
232 
233   if (process_id(pg) == 0) std::cerr << "OK.\nChanging next processor's values...";
234   // change the next processor's values
235   bool next_finish_value = next_processor % 2 == 0;
236   for (int i = 0; i < n; ++i) {
237     remote_key k(next_processor, i);
238     put(values, k, next_finish_value);
239   }
240 
241   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
242   synchronize(pg);
243 
244   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed values...";
245   // check our own values
246   bool my_finish_value = process_id(pg) % 2 == 0;
247   for (int i = 0; i < n; ++i) {
248     remote_key k(process_id(pg), i);
249     BOOST_CHECK(get(values, k) == my_finish_value);
250   }
251 
252   // check our neighbor's values
253   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed values on neighbor...";
254   for (int i = 0; i < n; ++i) {
255     remote_key k(next_processor, i);
256     BOOST_CHECK(get(values, k) == next_finish_value);
257   }
258 
259   synchronize(pg);
260 
261   if (process_id(pg) == 0) std::cerr << "OK.\n";
262 }
263 
string_test()264 void string_test()
265 {
266   mpi_process_group pg;
267   const int n = 500;
268 
269   std::string my_start_string = lexical_cast<std::string>(process_id(pg));
270   int next_processor = (process_id(pg) + 1) % num_processes(pg);
271   std::string next_start_string = lexical_cast<std::string>(next_processor);
272 
273   // Initial color map: even-numbered processes are false,
274   // odd-numbered processes are true
275   std::vector<std::string> string_vec(n, my_start_string);
276 
277   typedef iterator_property_map<std::vector<std::string>::iterator,
278                                 identity_property_map> LocalPropertyMap;
279   LocalPropertyMap local_strings(string_vec.begin(), identity_property_map());
280 
281   synchronize(pg);
282 
283   // Create the distributed property map
284   typedef boost::parallel::distributed_property_map<mpi_process_group,
285                                                     remote_key_to_global,
286                                                     LocalPropertyMap> StringMap;
287   StringMap strings(pg, remote_key_to_global(), local_strings);
288   strings.set_reduce(my_reduce<std::string>());
289 
290   if (process_id(pg) == 0) std::cerr << "Checking local strings...";
291   // check local processor strings
292   for (int i = 0; i < n; ++i) {
293     remote_key k(process_id(pg), i);
294     BOOST_CHECK(get(strings, k) == my_start_string);
295   }
296 
297   strings.set_consistency_model(boost::parallel::cm_bidirectional);
298   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's default strings...";
299   // check next processor's strings
300   for (int i = 0; i < n; ++i) {
301     remote_key k(next_processor, i);
302     BOOST_CHECK(get(strings, k) == (num_processes(pg) == 1 ? my_start_string : std::string()));
303   }
304 
305   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
306   synchronize(pg);
307 
308   if (process_id(pg) == 0) std::cerr << "OK.\nChecking next processor's strings...";
309   // check next processor's strings
310   for (int i = 0; i < n; ++i) {
311     remote_key k(next_processor, i);
312     BOOST_CHECK(get(strings, k) == next_start_string);
313   }
314 
315   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
316   synchronize(pg);
317 
318   if (process_id(pg) == 0) std::cerr << "OK.\nChanging next processor's strings...";
319   // change the next processor's strings
320   std::string next_finish_string = next_start_string + next_start_string;
321   for (int i = 0; i < n; ++i) {
322     remote_key k(next_processor, i);
323     put(strings, k, next_finish_string);
324   }
325 
326   if (process_id(pg) == 0) std::cerr << "OK.\nSynchronizing...";
327   synchronize(pg);
328 
329   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed strings...";
330   // check our own strings
331   std::string my_finish_string = my_start_string + my_start_string;
332   for (int i = 0; i < n; ++i) {
333     remote_key k(process_id(pg), i);
334     BOOST_CHECK(get(strings, k) == my_finish_string);
335   }
336 
337   // check our neighbor's strings
338   if (process_id(pg) == 0) std::cerr << "OK.\nChecking changed strings on neighbor...";
339   for (int i = 0; i < n; ++i) {
340     remote_key k(next_processor, i);
341     BOOST_CHECK(get(strings, k) == next_finish_string);
342   }
343 
344   synchronize(pg);
345 
346   if (process_id(pg) == 0) std::cerr << "OK.\n";
347 }
348 
test_main(int argc,char ** argv)349 int test_main(int argc, char** argv)
350 {
351   boost::mpi::environment env(argc, argv);
352   colored_test();
353   bool_test();
354   string_test();
355   return 0;
356 }
357