fpmas 1.6
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, float imbalance_tol);
445
446 zoltan::ZoltanData<T> zoltan_data;
447 PartitionMap fixed_vertices;
450
451 public:
452
464 : ZoltanLoadBalancing(comm, 10) {
465 }
466
484 : zoltan(comm.getMpiComm()), comm(comm), id_mpi(comm) {
485 setUpZoltan(lb_period, 1.1f);
486 }
501 ZoltanLoadBalancing(communication::MpiCommunicatorBase& comm, int lb_period, float imbalance_tol)
502 : zoltan(comm.getMpiComm()), comm(comm), id_mpi(comm) {
503 setUpZoltan(lb_period, imbalance_tol);
504 }
505
511 PartitionMap balance(
513 api::graph::PartitionMap fixed_vertices
514 ) override;
515
525 PartitionMap balance(
527 api::graph::PartitionMap fixed_vertices,
528 api::graph::PartitionMode partition_mode
529 ) override;
530
536 PartitionMap balance(api::graph::NodeMap<T> nodes) override;
537
547 PartitionMap balance(
549 api::graph::PartitionMode partition_mode) override;
550 };
551
552 template<typename T> PartitionMap
555 api::graph::PartitionMap fixed_vertices
556 ) {
557 return balance(nodes, fixed_vertices, api::graph::PARTITION);
558 }
559
560 template<typename T> PartitionMap
563 api::graph::PartitionMap fixed_vertices,
564 api::graph::PartitionMode partition_mode
565 ) {
566 switch(partition_mode) {
568 this->zoltan.Set_Param("LB_APPROACH", "PARTITION");
569 break;
571 this->zoltan.Set_Param("LB_APPROACH", "REPARTITION");
572 break;
573 }
574
575 for(auto local_node : nodes)
576 zoltan_data.distributed_node_ids.insert(local_node.first);
577
578 // Fetches ids of **all** the nodes that are currently partitionned
579 zoltan_data.distributed_node_ids = communication::all_reduce(
580 id_mpi, zoltan_data.distributed_node_ids, ConcatSet()
581 );
582
583
584 // Moves the temporary node map into `zoltan_data`. This is safe,
585 // since `nodes` is not reused in this scope.
586 zoltan_data.node_map = std::move(nodes);
587
588 this->fixed_vertices = fixed_vertices;
589 int changes;
590 int num_lid_entries;
591 int num_gid_entries;
592
593
594 int num_import;
595 ZOLTAN_ID_PTR import_global_ids;
596 ZOLTAN_ID_PTR import_local_ids;
597 int * import_procs;
598 int * import_to_part;
599
600 int export_node_num;
601 ZOLTAN_ID_PTR export_node_global_ids;
602 ZOLTAN_ID_PTR export_local_ids;
603 int* export_node_procs;
604 int* export_to_part;
605
606 // Computes Zoltan partitioning
607 this->zoltan.LB_Partition(
608 changes,
609 num_gid_entries,
610 num_lid_entries,
611 num_import,
612 import_global_ids,
613 import_local_ids,
614 import_procs,
615 import_to_part,
616 export_node_num,
617 export_node_global_ids,
618 export_local_ids,
619 export_node_procs,
620 export_to_part
621 );
622
623 PartitionMap partition;
624 for(int i = 0; i < export_node_num; i++) {
625 partition[zoltan::read_zoltan_id(&export_node_global_ids[i * num_gid_entries])]
626 = export_node_procs[i];
627 }
628 this->zoltan.LB_Free_Part(
629 &import_global_ids,
630 &import_local_ids,
631 &import_procs,
632 &import_to_part
633 );
634
635 this->zoltan.LB_Free_Part(
636 &export_node_global_ids,
637 &export_local_ids,
638 &export_node_procs,
639 &export_to_part
640 );
641
642 // Clears `zoltan_data`
643 zoltan_data.node_map.clear();
644 zoltan_data.distributed_node_ids.clear();
645 zoltan_data.nodes.clear();
646 zoltan_data.target_nodes.clear();
647 zoltan_data.edge_weights.clear();
648
649 return partition;
650 }
651
652 template<typename T> PartitionMap
654 return this->balance(nodes, api::graph::PARTITION);
655 }
656
657 template<typename T> PartitionMap
660 return this->balance(nodes, {}, partition_mode);
661 }
662
663 /*
664 * Initializes zoltan parameters and zoltan lb query functions.
665 */
666 template<typename T> void ZoltanLoadBalancing<T>::setUpZoltan(
667 int lb_period, float imbalance_tol) {
668 zoltan::zoltan_config(&this->zoltan);
669
670 this->zoltan.Set_Param("PHG_REPART_MULTIPLIER", std::to_string(10*lb_period));
671 this->zoltan.Set_Param("IMBALANCE_TOL", std::to_string(imbalance_tol));
672
673 // Initializes Zoltan Node Load Balancing functions
674 this->zoltan.Set_Obj_Size_Multi_Fn(zoltan::obj_size<T>, &this->zoltan_data);
675 this->zoltan.Set_Num_Fixed_Obj_Fn(zoltan::num_fixed_obj_fn<T>, &this->fixed_vertices);
676 this->zoltan.Set_Fixed_Obj_List_Fn(zoltan::fixed_obj_list_fn<T>, &this->fixed_vertices);
677 this->zoltan.Set_Num_Obj_Fn(zoltan::num_obj<T>, &this->zoltan_data);
678 this->zoltan.Set_Obj_List_Fn(zoltan::obj_list<T>, &this->zoltan_data);
679 this->zoltan.Set_Num_Edges_Multi_Fn(zoltan::num_edges_multi_fn<T>, &this->zoltan_data);
680 this->zoltan.Set_Edge_List_Multi_Fn(zoltan::edge_list_multi_fn<T>, &this->zoltan_data);
681 }
682
683}}
684#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
ZoltanLoadBalancing(communication::MpiCommunicatorBase &comm, int lb_period, float imbalance_tol)
Definition: zoltan_load_balancing.h:501
PartitionMap balance(api::graph::NodeMap< T > nodes, api::graph::PartitionMap fixed_vertices) override
Definition: zoltan_load_balancing.h:553
ZoltanLoadBalancing(communication::MpiCommunicatorBase &comm)
Definition: zoltan_load_balancing.h:463
ZoltanLoadBalancing(communication::MpiCommunicatorBase &comm, int lb_period)
Definition: zoltan_load_balancing.h:483
#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:634
void write_zoltan_id(DistributedId id, ZOLTAN_ID_PTR global_ids)
Definition: zoltan_load_balancing.cpp:31
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:22
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:585
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