fpmas 1.5
zoltan_load_balancing.h
Go to the documentation of this file.
1#ifndef FPMAS_ZOLTAN_LOAD_BALANCING_H
2#define FPMAS_ZOLTAN_LOAD_BALANCING_H
3
10#include "zoltan_cpp.h"
11
12#include <set>
13
14namespace fpmas { namespace graph {
17
21 namespace zoltan {
26 template<typename T>
27 struct ZoltanData {
31 NodeMap<T> node_map;
32
37 std::set<fpmas::api::graph::DistributedId> distributed_node_ids;
38
44 std::vector<fpmas::api::graph::DistributedNode<T>*> nodes;
45
51 std::vector<std::vector<fpmas::api::graph::DistributedNode<T>*>> target_nodes;
59 std::vector<std::vector<float>> edge_weights;
60 };
61
70 template<
71 typename IdType = FPMAS_ID_TYPE,
72 typename std::enable_if<(sizeof(IdType) <= sizeof(ZOLTAN_ID_TYPE)), bool>::type = true>
73 static constexpr int num_gid_entries() {
74 return 2;
75 }
93 template<
94 typename IdType = FPMAS_ID_TYPE,
95 typename std::enable_if<(sizeof(IdType) > sizeof(ZOLTAN_ID_TYPE)), bool>::type = true>
96 static constexpr int num_gid_entries() {
97 return sizeof(FPMAS_ID_TYPE) / sizeof(ZOLTAN_ID_TYPE) + 1;
98 }
99
106 constexpr int NUM_GID_ENTRIES = num_gid_entries<>();
107
114 void zoltan_config(Zoltan* zoltan);
115
128 DistributedId read_zoltan_id(const ZOLTAN_ID_PTR global_ids);
129
140 void write_zoltan_id(DistributedId id, ZOLTAN_ID_PTR global_ids);
141
158 template<typename T> void obj_size(
159 void* data,
160 int num_gid_entries,
161 int, // num_lid_entries (unused)
162 int num_ids,
163 ZOLTAN_ID_PTR global_ids,
164 ZOLTAN_ID_PTR, // local_ids (unused)
165 int* sizes,
166 int* // ierr (unused)
167 ) {
168 ZoltanData<T>* z_data = (ZoltanData<T>*) data;
169 for(int i = 0; i < num_ids; i++) {
170 int size = 1;
171 DistributedId id = zoltan::read_zoltan_id(&global_ids[i*num_gid_entries]);
172 api::graph::DistributedNode<T>* node = z_data->node_map[id];
173 size += node->getOutgoingEdges().size();
174 size += node->getIncomingEdges().size();
175 sizes[i] = size;
176 }
177 }
178
190 template<typename T> int num_obj(void *data, int*) {
191 return ((ZoltanData<T>*) data)->node_map.size();
192 }
193
206 template<typename T> void obj_list(
207 void *data,
208 int num_gid_entries,
209 int, // num_lid_entries (unused)
210 ZOLTAN_ID_PTR global_ids,
211 ZOLTAN_ID_PTR, // local_ids (unused)
212 int, // wgt_dim, 1 by default
213 float *obj_wgts,
214 int * // ierr (unused)
215 ) {
216 ZoltanData<T>* z_data = (ZoltanData<T>*) data;
217 int i = 0;
218 for(auto n : z_data->node_map) {
219 z_data->nodes.push_back(n.second);
220 zoltan::write_zoltan_id(n.first, &global_ids[i * num_gid_entries]);
221 obj_wgts[i++] = n.second->getWeight();
222 }
223 }
224
235 template<typename T> void num_edges_multi_fn(
236 void *data,
237 int, // num_gid_entries (unused)
238 int, // num_lid_entries (unused)
239 int num_obj,
240 ZOLTAN_ID_PTR, // global_ids (unused)
241 ZOLTAN_ID_PTR, // local_ids (unused)
242 int *num_edges,
243 int * // ierr (unused)
244 ) {
245 struct NodeHandler {
246 static void handle(
247 ZoltanData<T>* z_data, int& count, int i, DistributedId node_id,
248 api::graph::DistributedNode<T>* target_node, float edge_weight) {
249 auto tgt_id = target_node->getId();
250 // Incoming and outgoing edges are considered from all the
251 // nodes, but should be treated once by all processes. (Notice
252 // that the two nodes might be located on distinct processes)
253 //
254 // Considering the relation `rel` below, an edge is considered
255 // only if `rel(node_id, tgt_id)` is true. Moreover, `rel` is
256 // such as `rel(A, B) == true` XOR `rel(B, A) == true`: this
257 // ensures that each edge is treated exactly once from A or B.
258 // The first comparison on id() should ensure that no
259 // preference is given on A or B for each edge. Moreover,
260 // `rel(A, A) == false` so self edges are ignored.
261 if(node_id.id() > tgt_id.id()
262 || (node_id.id() == tgt_id.id() && node_id.rank() > tgt_id.rank())
263 ) {
264 // Considers the neighbor only if the load balancing
265 // algorithm is also applied to it, potentially from an
266 // other process.
267 // The distributed_node_ids list contains the ids of ALL
268 // the nodes that are currently balanced.
269 // It is the responsability of
270 // ZoltanLoadBalancing::balance() to perform communications
271 // required to build this list, before the Zoltan algorithm
272 // is effectively applied.
273 if(z_data->distributed_node_ids.count(tgt_id) > 0) {
274 auto& target_nodes = z_data->target_nodes[i];
275 auto& edge_weights = z_data->edge_weights[i];
276 auto it = std::find(
277 target_nodes.begin(), target_nodes.end(),
278 target_node
279 );
280 if(it == target_nodes.end()) {
281 // No edges between node_id and tgt_id exists:
282 // create a new one
283 count++;
284 target_nodes.push_back(target_node);
285 edge_weights.push_back(edge_weight);
286 } else {
287 // An edge (outgoing or incoming) from node_id to
288 // tgt_id already exists, so the weight of the
289 // current edge is added to the existing one.
290 edge_weights[std::distance(target_nodes.begin(), it)]
291 +=edge_weight;
292 }
293 }
294 }
295 }
296 };
297
298 ZoltanData<T>* z_data = (ZoltanData<T>*) data;
299 // Initializes `num_obj` empty vectors
300 z_data->target_nodes.resize(num_obj);
301 z_data->edge_weights.resize(num_obj);
302 for(int i = 0; i < num_obj; i++) {
303 auto node = z_data->nodes[i];
304 auto node_id = node->getId();
305 int count = 0;
306 for(auto edge : node->getOutgoingEdges())
307 NodeHandler::handle(
308 z_data, count, i, node_id,
309 edge->getTargetNode(), edge->getWeight()
310 );
311 for(auto edge : node->getIncomingEdges())
312 NodeHandler::handle(
313 z_data, count, i, node_id,
314 edge->getSourceNode(), edge->getWeight()
315 );
316
317 num_edges[i] = count;
318 }
319 }
320
336 template<typename T> void edge_list_multi_fn(
337 void *data,
338 int num_gid_entries,
339 int, // num_lid_entries (unused)
340 int num_obj,
341 ZOLTAN_ID_PTR, // global_ids (unused)
342 ZOLTAN_ID_PTR, // local_ids (unused)
343 int *, // num_edges (unused)
344 ZOLTAN_ID_PTR nbor_global_id,
345 int *nbor_procs,
346 int, // wgt_dim (unused, 1 by default)
347 float *ewgts,
348 int * // ierr (unused)
349 ) {
350
351 ZoltanData<T>* z_data = (ZoltanData<T>*) data;
352
353 int neighbor_index = 0;
354 for (int i = 0; i < num_obj; ++i) {
355 for(std::size_t j = 0; j < z_data->target_nodes[i].size(); j++) {
356 auto target = z_data->target_nodes[i][j];
358 target->getId(),
359 &nbor_global_id[neighbor_index * num_gid_entries]
360 );
361
362 nbor_procs[neighbor_index]
363 = target->location();
364
365 ewgts[neighbor_index] = z_data->edge_weights[i][j];
366 neighbor_index++;
367 }
368 }
369 }
370
379 template<typename T> int num_fixed_obj_fn(void* data, int*) {
380 return ((NodeMap<T>*) data)->size();
381 }
382
396 template<typename T> void fixed_obj_list_fn(
397 void *data,
398 int, // num_fixed_obj (unused)
399 int num_gid_entries,
400 ZOLTAN_ID_PTR fixed_gids,
401 int *fixed_parts,
402 int * // ierr (unused)
403 ) {
404 PartitionMap* fixed_nodes = (PartitionMap*) data;
405 int i = 0;
406 for(auto fixed_node : *fixed_nodes) {
407 zoltan::write_zoltan_id(fixed_node.first, &fixed_gids[i * num_gid_entries]);
408 fixed_parts[i] = fixed_node.second;
409 i++;
410 }
411 }
412 }
413
418 template<typename T>
420 private:
421 struct ConcatSet {
422 std::set<DistributedId> operator()(
423 const std::set<DistributedId> s1,
424 const std::set<DistributedId> s2
425 ) {
426 std::set<DistributedId> s;
427 s.insert(s1.begin(), s1.end());
428 s.insert(s2.begin(), s2.end());
429 return s;
430 }
431 };
432
433 //Zoltan instance
434 Zoltan zoltan;
435
436 // Number of edges to export.
437 int export_edges_num;
438
439 // Edge ids to export buffer.
440 ZOLTAN_ID_PTR export_edges_global_ids;
441 // Edge export procs buffer.
442 int* export_edges_procs;
443
444 void setUpZoltan(int lb_period);
445
446 zoltan::ZoltanData<T> zoltan_data;
447 PartitionMap fixed_vertices;
450
451 public:
452
464 : ZoltanLoadBalancing(comm, 100) {
465 }
466
480 : zoltan(comm.getMpiComm()), comm(comm), id_mpi(comm) {
481 setUpZoltan(lb_period);
482 }
483
491 api::graph::PartitionMap fixed_vertices
492 ) override;
493
505 api::graph::PartitionMap fixed_vertices,
506 api::graph::PartitionMode partition_mode
507 ) override;
508
515
527 api::graph::PartitionMode partition_mode) override;
528 };
529
530 template<typename T> PartitionMap
533 api::graph::PartitionMap fixed_vertices
534 ) {
535 return balance(nodes, fixed_vertices, api::graph::PARTITION);
536 }
537
538 template<typename T> PartitionMap
541 api::graph::PartitionMap fixed_vertices,
542 api::graph::PartitionMode partition_mode
543 ) {
544 switch(partition_mode) {
546 this->zoltan.Set_Param("LB_APPROACH", "PARTITION");
547 break;
549 this->zoltan.Set_Param("LB_APPROACH", "REPARTITION");
550 break;
551 }
552
553 for(auto local_node : nodes)
554 zoltan_data.distributed_node_ids.insert(local_node.first);
555
556 // Fetches ids of **all** the nodes that are currently partitionned
557 zoltan_data.distributed_node_ids = communication::all_reduce(
558 id_mpi, zoltan_data.distributed_node_ids, ConcatSet()
559 );
560
561
562 // Moves the temporary node map into `zoltan_data`. This is safe,
563 // since `nodes` is not reused in this scope.
564 zoltan_data.node_map = std::move(nodes);
565
566 this->fixed_vertices = fixed_vertices;
567 int changes;
568 int num_lid_entries;
569 int num_gid_entries;
570
571
572 int num_import;
573 ZOLTAN_ID_PTR import_global_ids;
574 ZOLTAN_ID_PTR import_local_ids;
575 int * import_procs;
576 int * import_to_part;
577
578 int export_node_num;
579 ZOLTAN_ID_PTR export_node_global_ids;
580 ZOLTAN_ID_PTR export_local_ids;
581 int* export_node_procs;
582 int* export_to_part;
583
584 // Computes Zoltan partitioning
585 this->zoltan.LB_Partition(
586 changes,
587 num_gid_entries,
588 num_lid_entries,
589 num_import,
590 import_global_ids,
591 import_local_ids,
592 import_procs,
593 import_to_part,
594 export_node_num,
595 export_node_global_ids,
596 export_local_ids,
597 export_node_procs,
598 export_to_part
599 );
600
601 PartitionMap partition;
602 for(int i = 0; i < export_node_num; i++) {
603 partition[zoltan::read_zoltan_id(&export_node_global_ids[i * num_gid_entries])]
604 = export_node_procs[i];
605 }
606 this->zoltan.LB_Free_Part(
607 &import_global_ids,
608 &import_local_ids,
609 &import_procs,
610 &import_to_part
611 );
612
613 this->zoltan.LB_Free_Part(
614 &export_node_global_ids,
615 &export_local_ids,
616 &export_node_procs,
617 &export_to_part
618 );
619
620 // Clears `zoltan_data`
621 zoltan_data.node_map.clear();
622 zoltan_data.distributed_node_ids.clear();
623 zoltan_data.nodes.clear();
624 zoltan_data.target_nodes.clear();
625 zoltan_data.edge_weights.clear();
626
627 return partition;
628 }
629
630 template<typename T> PartitionMap
632 return this->balance(nodes, api::graph::PARTITION);
633 }
634
635 template<typename T> PartitionMap
638 return this->balance(nodes, {}, partition_mode);
639 }
640
641 /*
642 * Initializes zoltan parameters and zoltan lb query functions.
643 */
644 template<typename T> void ZoltanLoadBalancing<T>::setUpZoltan(int lb_period) {
645 zoltan::zoltan_config(&this->zoltan);
646
647 this->zoltan.Set_Param("PHG_REPART_MULTIPLIER", std::to_string(lb_period));
648
649 // Initializes Zoltan Node Load Balancing functions
650 this->zoltan.Set_Obj_Size_Multi_Fn(zoltan::obj_size<T>, &this->zoltan_data);
651 this->zoltan.Set_Num_Fixed_Obj_Fn(zoltan::num_fixed_obj_fn<T>, &this->fixed_vertices);
652 this->zoltan.Set_Fixed_Obj_List_Fn(zoltan::fixed_obj_list_fn<T>, &this->fixed_vertices);
653 this->zoltan.Set_Num_Obj_Fn(zoltan::num_obj<T>, &this->zoltan_data);
654 this->zoltan.Set_Obj_List_Fn(zoltan::obj_list<T>, &this->zoltan_data);
655 this->zoltan.Set_Num_Edges_Multi_Fn(zoltan::num_edges_multi_fn<T>, &this->zoltan_data);
656 this->zoltan.Set_Edge_List_Multi_Fn(zoltan::edge_list_multi_fn<T>, &this->zoltan_data);
657 }
658
659}}
660#endif
Definition: communication.h:251
Definition: distributed_id.h:89
FPMAS_ID_TYPE id() const
Definition: distributed_id.h:166
int rank() const
Definition: distributed_id.h:157
Definition: distributed_node.h:28
Definition: load_balancing.h:47
Definition: load_balancing.h:92
virtual const std::vector< EdgeType * > getIncomingEdges() const =0
virtual IdType getId() const =0
virtual const std::vector< EdgeType * > getOutgoingEdges() const =0
Definition: communication.h:29
Definition: zoltan_load_balancing.h:419
PartitionMap balance(api::graph::NodeMap< T > nodes, api::graph::PartitionMap fixed_vertices, api::graph::PartitionMode partition_mode) override
Definition: zoltan_load_balancing.h:539
PartitionMap balance(api::graph::NodeMap< T > nodes, api::graph::PartitionMap fixed_vertices) override
Definition: zoltan_load_balancing.h:531
PartitionMap balance(api::graph::NodeMap< T > nodes, api::graph::PartitionMode partition_mode) override
Definition: zoltan_load_balancing.h:636
ZoltanLoadBalancing(communication::MpiCommunicatorBase &comm)
Definition: zoltan_load_balancing.h:463
PartitionMap balance(api::graph::NodeMap< T > nodes) override
Definition: zoltan_load_balancing.h:631
ZoltanLoadBalancing(communication::MpiCommunicatorBase &comm, int lb_period)
Definition: zoltan_load_balancing.h:479
#define FPMAS_ID_TYPE
Definition: distributed_id.h:32
std::unordered_map< DistributedId, int, api::graph::IdHash< DistributedId > > PartitionMap
Definition: load_balancing.h:19
PartitionMode
Definition: load_balancing.h:30
@ REPARTITION
Definition: load_balancing.h:40
@ PARTITION
Definition: load_balancing.h:35
typename graph::Graph< graph::DistributedNode< T >, graph::DistributedEdge< T > >::NodeMap NodeMap
Definition: load_balancing.h:25
T all_reduce(api::communication::TypedMpi< T > &mpi, const T &data, BinaryOp binary_op=BinaryOp())
Definition: communication.h:639
void write_zoltan_id(DistributedId id, ZOLTAN_ID_PTR global_ids)
Definition: zoltan_load_balancing.cpp:34
void obj_size(void *data, int num_gid_entries, int, int num_ids, ZOLTAN_ID_PTR global_ids, ZOLTAN_ID_PTR, int *sizes, int *)
Definition: zoltan_load_balancing.h:158
DistributedId read_zoltan_id(const ZOLTAN_ID_PTR global_ids)
Definition: zoltan_load_balancing.cpp:25
void fixed_obj_list_fn(void *data, int, int num_gid_entries, ZOLTAN_ID_PTR fixed_gids, int *fixed_parts, int *)
Definition: zoltan_load_balancing.h:396
int num_obj(void *data, int *)
Definition: zoltan_load_balancing.h:190
void zoltan_config(Zoltan *zz)
Definition: zoltan_load_balancing.cpp:4
void edge_list_multi_fn(void *data, int num_gid_entries, int, int num_obj, ZOLTAN_ID_PTR, ZOLTAN_ID_PTR, int *, ZOLTAN_ID_PTR nbor_global_id, int *nbor_procs, int, float *ewgts, int *)
Definition: zoltan_load_balancing.h:336
void num_edges_multi_fn(void *data, int, int, int num_obj, ZOLTAN_ID_PTR, ZOLTAN_ID_PTR, int *num_edges, int *)
Definition: zoltan_load_balancing.h:235
void obj_list(void *data, int num_gid_entries, int, ZOLTAN_ID_PTR global_ids, ZOLTAN_ID_PTR, int, float *obj_wgts, int *)
Definition: zoltan_load_balancing.h:206
constexpr int NUM_GID_ENTRIES
Definition: zoltan_load_balancing.h:106
int num_fixed_obj_fn(void *data, int *)
Definition: zoltan_load_balancing.h:379
graph::ZoltanLoadBalancing< AgentPtr > ZoltanLoadBalancing
Definition: model.h:296
Definition: fpmas.cpp:3
Definition: communication.h:590
Definition: zoltan_load_balancing.h:27
std::vector< std::vector< float > > edge_weights
Definition: zoltan_load_balancing.h:59
std::vector< std::vector< fpmas::api::graph::DistributedNode< T > * > > target_nodes
Definition: zoltan_load_balancing.h:51
std::set< fpmas::api::graph::DistributedId > distributed_node_ids
Definition: zoltan_load_balancing.h:37
std::vector< fpmas::api::graph::DistributedNode< T > * > nodes
Definition: zoltan_load_balancing.h:44
NodeMap< T > node_map
Definition: zoltan_load_balancing.h:31