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