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