fpmas 1.5
distributed_graph.h
Go to the documentation of this file.
1#ifndef FPMAS_DISTRIBUTED_GRAPH_H
2#define FPMAS_DISTRIBUTED_GRAPH_H
3
8#include <set>
9
13#include "fpmas/synchro/synchro.h"
14
15#include "fpmas/graph/graph.h"
16
17#define DIST_GRAPH_PARAMS\
18 typename T,\
19 template<typename> class SyncMode,\
20 template<typename> class DistNodeImpl,\
21 template<typename> class DistEdgeImpl,\
22 template<typename> class TypedMpi,\
23 template<typename> class LocationManagerImpl
24
25#define DIST_GRAPH_PARAMS_SPEC\
26 T,\
27 SyncMode,\
28 DistNodeImpl,\
29 DistEdgeImpl,\
30 TypedMpi,\
31 LocationManagerImpl
32
33namespace fpmas { namespace graph {
34
38 namespace detail {
39
43 template<DIST_GRAPH_PARAMS>
45 public Graph<
46 api::graph::DistributedNode<T>,
47 api::graph::DistributedEdge<T>>,
49 {
50 public:
51 static_assert(
52 std::is_base_of<api::graph::DistributedNode<T>, DistNodeImpl<T>>::value,
53 "DistNodeImpl must implement api::graph::DistributedNode"
54 );
55 static_assert(
56 std::is_base_of<api::graph::DistributedEdge<T>, DistEdgeImpl<T>>::value,
57 "DistEdgeImpl must implement api::graph::DistributedEdge"
58 );
59
60 public:
69
73
75 = delete;
76 DistributedGraph& operator=(
78 = delete;
79
88
89 private:
90 class EraseNodeCallback : public api::utils::Callback<NodeType*> {
91 private:
93 bool enabled = true;
94 public:
95 EraseNodeCallback(DistributedGraph<DIST_GRAPH_PARAMS_SPEC>& graph)
96 : graph(graph) {}
97
98 void disable() {
99 enabled = false;
100 }
101 void call(NodeType* node) {
102 if(enabled) {
103 graph.location_manager.remove(node);
104 graph.unsynchronized_nodes.erase(node);
105 }
106 }
107 };
108
109 api::communication::MpiCommunicator* mpi_communicator;
110 TypedMpi<DistributedId> id_mpi {*mpi_communicator};
111 TypedMpi<std::pair<DistributedId, int>> location_mpi {*mpi_communicator};
112
113 LocationManagerImpl<T> location_manager;
114 SyncMode<T> sync_mode;
115
116 std::vector<SetLocalNodeCallback*> set_local_callbacks;
117 std::vector<SetDistantNodeCallback*> set_distant_callbacks;
118
119 EraseNodeCallback* erase_node_callback;
120
121 NodeType* _buildNode(NodeType*);
122
123 void setLocal(
124 api::graph::DistributedNode<T>* node,
126 );
127 void setDistant(
128 api::graph::DistributedNode<T>* node,
130 );
131
132 void triggerSetLocalCallbacks(
133 const api::graph::SetLocalNodeEvent<T>& event) {
134 for(auto callback : set_local_callbacks)
135 callback->call(event);
136 }
137
138 void triggerSetDistantCallbacks(
139 const api::graph::SetDistantNodeEvent<T>& event) {
140 for(auto callback : set_distant_callbacks)
141 callback->call(event);
142 }
143
144 void clearDistantNodes();
145 void clearNode(NodeType*);
146 void _distribute(api::graph::PartitionMap);
147
148 DistributedId node_id;
149 DistributedId edge_id;
150
151 std::unordered_set<api::graph::DistributedNode<T>*> unsynchronized_nodes;
152
153 public:
160 mpi_communicator(&comm), location_manager(*mpi_communicator, id_mpi, location_mpi),
161 sync_mode(*this, *mpi_communicator),
162 // Graph base takes ownership of the erase_node_callback
163 erase_node_callback(new EraseNodeCallback(*this)),
164 node_id(mpi_communicator->getRank(), 0),
165 edge_id(mpi_communicator->getRank(), 0) {
166 this->addCallOnEraseNode(erase_node_callback);
167 }
168
175 return *mpi_communicator;
176 };
177
182 return *mpi_communicator;
183 };
184
185 DistributedId currentNodeId() const override {return node_id;}
186 void setCurrentNodeId(DistributedId id) override {this->node_id = id;}
187 DistributedId currentEdgeId() const override {return edge_id;}
188 void setCurrentEdgeId(DistributedId id) override {this->edge_id = id;}
189
190 NodeType* importNode(NodeType* node) override;
191 EdgeType* importEdge(EdgeType* edge) override;
192 std::unordered_set<api::graph::DistributedNode<T>*> getUnsyncNodes() const override {
193 return unsynchronized_nodes;
194 }
195
196
205 const SyncMode<T>& getSyncMode() const {return sync_mode;}
206
207 SyncMode<T>& synchronizationMode() override {return sync_mode;}
208
209 const LocationManagerImpl<T>&
210 getLocationManager() const override {return location_manager;}
211 LocationManagerImpl<T>&
212 getLocationManager() override {return location_manager;}
213
214 void balance(api::graph::LoadBalancing<T>& load_balancing) override {
215 balance(load_balancing, api::graph::PARTITION);
216 };
217
219 api::graph::LoadBalancing<T>& load_balancing,
220 api::graph::PartitionMode partition_mode
221 ) override {
222 FPMAS_LOGI(
223 getMpiCommunicator().getRank(), "LB",
224 "Balancing graph (%lu nodes, %lu edges)",
225 this->getNodes().size(), this->getEdges().size());
226
227 sync_mode.getSyncLinker().synchronize();
228 this->_distribute(load_balancing.balance(
229 this->location_manager.getLocalNodes(),
230 partition_mode
231 ));
232
233 // Data synchronization of newly imported DISTANT nodes.
234 // In GhostMode, this import DISTANT nodes data, so that it can
235 // be used directly following the _distribute() operation
236 // without any additional and complete synchronize().
237 this->synchronize(this->unsynchronized_nodes, false);
238
239 FPMAS_LOGI(
240 getMpiCommunicator().getRank(), "LB",
241 "Graph balanced : %lu nodes, %lu edges",
242 this->getNodes().size(), this->getEdges().size());
243 }
244
247 api::graph::PartitionMap fixed_vertices
248 ) override {
249 balance(load_balancing, fixed_vertices, api::graph::PARTITION);
250 };
251
254 api::graph::PartitionMap fixed_vertices,
255 api::graph::PartitionMode partition_mode
256 ) override {
257
258 FPMAS_LOGI(
259 getMpiCommunicator().getRank(), "LB",
260 "Balancing graph (%lu nodes, %lu edges)",
261 this->getNodes().size(), this->getEdges().size());
262
263 sync_mode.getSyncLinker().synchronize();
264 this->_distribute(load_balancing.balance(
265 this->location_manager.getLocalNodes(),
266 fixed_vertices,
267 partition_mode));
268
269 // Same as above
270 this->synchronize(this->unsynchronized_nodes, false);
271
272 FPMAS_LOGI(
273 getMpiCommunicator().getRank(), "LB",
274 "Graph balanced : %lu nodes, %lu edges",
275 this->getNodes().size(), this->getEdges().size());
276 }
277
278 void distribute(api::graph::PartitionMap partition) override;
279
280 void synchronize() override;
282 std::unordered_set<NodeType*> nodes,
283 bool synchronize_links = true
284 ) override;
285
286 NodeType* buildNode(T&& = std::move(T())) override;
287 NodeType* buildNode(const T&) override;
289
290 EdgeType* link(NodeType* const src, NodeType* const tgt, api::graph::LayerId layer) override;
291
293 void removeNode(DistributedId id) override {
294 this->removeNode(this->getNode(id));
295 }
296
297 void unlink(EdgeType*) override;
298 void unlink(DistributedId id) override {
299 this->unlink(this->getEdge(id));
300 }
301
302 void switchLayer(EdgeType* edge, api::graph::LayerId layer_id) override;
303
304 void addCallOnSetLocal(SetLocalNodeCallback* callback) override {
305 set_local_callbacks.push_back(callback);
306 };
307 std::vector<SetLocalNodeCallback*> onSetLocalCallbacks() const override {
308 return set_local_callbacks;
309 }
310
312 set_distant_callbacks.push_back(callback);
313 };
314 std::vector<SetDistantNodeCallback*> onSetDistantCallbacks() const override {
315 return set_distant_callbacks;
316 }
317
319 };
320
321 template<DIST_GRAPH_PARAMS>
323 // Calls base Graph move constructor
324 Graph<api::graph::DistributedNode<T>, api::graph::DistributedEdge<T>>(std::move(graph)),
325 // Moves DistributedGraph specific fields
326 mpi_communicator(graph.mpi_communicator),
327 location_manager(std::move(graph.location_manager)),
328 sync_mode(std::move(graph.sync_mode)),
329 set_local_callbacks(std::move(graph.set_local_callbacks)),
330 set_distant_callbacks(std::move(graph.set_distant_callbacks)),
331 erase_node_callback(graph.erase_node_callback),
332 node_id(std::move(graph.node_id)), edge_id(std::move(graph.edge_id)) {
333 // TODO: Refactor callbacks system
334 // Prevents erase_node_callback->disable() when `graph` is
335 // deleted
336 graph.erase_node_callback = nullptr;
337 }
338
339 template<DIST_GRAPH_PARAMS>
341 // Calls base Graph move assignment
344 // Reassigns MPI communicators
345 //
346 // Notice that TypedMpi instances, location_manager and
347 // sync_mode have been constructed in `graph` using
348 // `graph.mpi_communicator`. Since we reassign
349 // `this->mpi_communicator` to `graph.mpi_communicator`, we can
350 // safely move those member fields from `graph` to `this`
351 // without introducing inconsistencies.
352 this->mpi_communicator = graph.mpi_communicator;
353 this->id_mpi = std::move(graph.id_mpi);
354 this->location_mpi = std::move(graph.location_mpi);
355
356 this->location_manager = std::move(graph.location_manager);
357 this->sync_mode = std::move(graph.sync_mode);
358 // The obsolete `this->erase_node_callback` is
359 // automatically deleted by the base Graph move assignment,
360 // when the "erase_node_callback" list is cleared.
361 this->erase_node_callback = graph.erase_node_callback;
362 // Prevents erase_node_callback->disable() when `graph` is
363 // deleted
364 graph.erase_node_callback = nullptr;
365
366 this->node_id = std::move(graph.node_id);
367 this->edge_id = std::move(graph.edge_id);
368 for(auto callback : set_local_callbacks)
369 delete callback;
370 for(auto callback : set_distant_callbacks)
371 delete callback;
372 this->set_local_callbacks = std::move(graph.set_local_callbacks);
373 this->set_distant_callbacks = std::move(graph.set_distant_callbacks);
374
375 return *this;
376 }
377
378 template<DIST_GRAPH_PARAMS>
382 location_manager.setLocal(node);
383 triggerSetLocalCallbacks({node, context});
384 }
385
386 template<DIST_GRAPH_PARAMS>
387 void DistributedGraph<DIST_GRAPH_PARAMS_SPEC>::setDistant(
390 location_manager.setDistant(node);
391 triggerSetDistantCallbacks({node, context});
392 }
393
394 template<DIST_GRAPH_PARAMS>
396 sync_mode.getSyncLinker().unlink(edge);
397 this->erase(edge);
398 }
399
400 template<DIST_GRAPH_PARAMS>
402 EdgeType* edge, api::graph::LayerId layer_id) {
403 assert(edge->state() == api::graph::LOCAL);
404
405 edge->getSourceNode()->unlinkOut(edge);
406 edge->getTargetNode()->unlinkIn(edge);
407 edge->setLayer(layer_id);
408 edge->getSourceNode()->linkOut(edge);
409 edge->getTargetNode()->linkIn(edge);
410 }
411
412 template<DIST_GRAPH_PARAMS>
415 FPMAS_LOGD(getMpiCommunicator().getRank(), "DIST_GRAPH", "Importing node %s...", FPMAS_C_STR(node->getId()));
416 // The input node must be a temporary dynamically allocated object.
417 // A representation of the node might already be contained in the
418 // graph, if it were already built as a "distant" node.
419
420 if(this->getNodes().count(node->getId())==0) {
421 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Inserting new LOCAL node %s.", FPMAS_C_STR(node->getId()));
422 // The node is not contained in the graph, we need to build a new
423 // one.
424 // But instead of completely building a new node, we can re-use the
425 // temporary input node.
426 this->insert(node);
428 node->setMutex(sync_mode.buildMutex(node));
429 return node;
430 }
431 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Replacing existing DISTANT node %s.", FPMAS_C_STR(node->getId()));
432 // A representation of the node was already contained in the graph.
433 // We just need to update its state.
434
435 // Set local representation as local
436 auto local_node = this->getNode(node->getId());
438 local_node->data(), std::move(node->data())
439 );
440 local_node->setWeight(node->getWeight());
442
443 // Deletes unused temporary input node
444 delete node;
445
446 return local_node;
447 }
448
449 template<DIST_GRAPH_PARAMS>
452 std::unique_ptr<api::graph::TemporaryNode<T>> temp_src
453 = edge->getTempSourceNode();
454 std::unique_ptr<api::graph::TemporaryNode<T>> temp_tgt
455 = edge->getTempTargetNode();
456
457 FPMAS_LOGD(getMpiCommunicator().getRank(), "DIST_GRAPH", "Importing edge %s (from %s to %s)...",
458 FPMAS_C_STR(edge->getId()),
459 FPMAS_C_STR(temp_src->getId()),
460 FPMAS_C_STR(temp_tgt->getId())
461 );
462 // The input edge must be a dynamically allocated object, with temporary
463 // dynamically allocated nodes as source and target.
464 // A representation of the imported edge might already be present in the
465 // graph, for example if it has already been imported as a "distant"
466 // edge with other nodes at other epochs.
467
468 if(this->getEdges().count(edge->getId())==0) {
469 // The edge does not belong to the graph : a new one must be built.
470
471 DistributedId src_id = temp_src->getId();
472 NodeType* src;
473 DistributedId tgt_id = temp_tgt->getId();
474 NodeType* tgt;
475
476 LocationState edgeLocationState = LocationState::LOCAL;
477
478 if(this->getNodes().count(src_id) > 0) {
479 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Linking existing source %s", FPMAS_C_STR(src_id));
480 // The source node is already contained in the graph
481 src = this->getNode(src_id);
482 if(src->state() == LocationState::DISTANT) {
483 // At least src is DISTANT, so the imported edge is
484 // necessarily DISTANT.
485 edgeLocationState = LocationState::DISTANT;
486 }
487 // Deletes the temporary source node
488 temp_src.reset();
489
490 // Links the temporary edge with the src contained in the graph
491 edge->setSourceNode(src);
492 src->linkOut(edge);
493 } else {
494 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Creating DISTANT source %s", FPMAS_C_STR(src_id));
495 // The source node is not contained in the graph : it must be
496 // built as a DISTANT node.
497 edgeLocationState = LocationState::DISTANT;
498
499 // Instead of building a new node, we re-use the temporary
500 // source node.
501 src = temp_src->build();
502 temp_src.reset();
503
504 edge->setSourceNode(src);
505 src->linkOut(edge);
506 this->insert(src);
508 src->setMutex(sync_mode.buildMutex(src));
509 unsynchronized_nodes.insert(src);
510 }
511 if(this->getNodes().count(tgt_id) > 0) {
512 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Linking existing target %s", FPMAS_C_STR(tgt_id));
513 // The target node is already contained in the graph
514 tgt = this->getNode(tgt_id);
515 if(tgt->state() == LocationState::DISTANT) {
516 // At least src is DISTANT, so the imported edge is
517 // necessarily DISTANT.
518 edgeLocationState = LocationState::DISTANT;
519 }
520 // Deletes the temporary target node
521 temp_tgt.reset();
522
523 // Links the temporary edge with the tgt contained in the graph
524 edge->setTargetNode(tgt);
525 tgt->linkIn(edge);
526 } else {
527 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Creating DISTANT target %s", FPMAS_C_STR(tgt_id));
528 // The target node is not contained in the graph : it must be
529 // built as a DISTANT node.
530 edgeLocationState = LocationState::DISTANT;
531
532 // Instead of building a new node, we re-use the temporary
533 // target node.
534 tgt = temp_tgt->build();
535 temp_tgt.reset();
536
537 edge->setTargetNode(tgt);
538 tgt->linkIn(edge);
539 this->insert(tgt);
541 tgt->setMutex(sync_mode.buildMutex(tgt));
542 unsynchronized_nodes.insert(tgt);
543 }
544 // Finally, insert the temporary edge into the graph.
545 edge->setState(edgeLocationState);
546 this->insert(edge);
547 return edge;
548 } // if (graph.count(edge_id) > 0)
549
550 // A representation of the edge is already present in the graph : it is
551 // useless to insert it again. We just need to update its state.
552
553 auto local_edge = this->getEdge(edge->getId());
554 if(local_edge->getSourceNode()->state() == LocationState::LOCAL
555 && local_edge->getTargetNode()->state() == LocationState::LOCAL) {
556 local_edge->setState(LocationState::LOCAL);
557 }
558
559 // Completely deletes temporary items, nothing is re-used
560 // Temporary nodes are automatically deleted
561 delete edge;
562
563 return local_edge;
564 }
565
566 template<DIST_GRAPH_PARAMS>
569 this->insert(node);
571 location_manager.addManagedNode(node->getId(), mpi_communicator->getRank());
572 node->setMutex(sync_mode.buildMutex(node));
573 return node;
574 }
575
576 template<DIST_GRAPH_PARAMS>
579 return _buildNode(new DistNodeImpl<T>(
580 this->node_id++, std::move(data)
581 ));
582 }
583
584 template<DIST_GRAPH_PARAMS>
587 return _buildNode(new DistNodeImpl<T>(
588 this->node_id++, data
589 ));
590 }
591 template<DIST_GRAPH_PARAMS>
594 FPMAS_LOGD(getMpiCommunicator().getRank(),
595 "GRAPH", "Inserting temporary distant node: %s",
596 FPMAS_C_STR(node->getId()));
597 if(this->getNodes().count(node->getId()) == 0) {
598 this->insert(node);
600 node->setMutex(sync_mode.buildMutex(node));
601 unsynchronized_nodes.insert(node);
602 return node;
603 } else {
604 auto id = node->getId();
605 delete node;
606 return this->getNode(id);
607 }
608 }
609
610 template<DIST_GRAPH_PARAMS>
613 sync_mode.getSyncLinker().removeNode(node);
614 }
615
616 template<DIST_GRAPH_PARAMS>
619 NodeType* const src, NodeType* const tgt, api::graph::LayerId layer) {
620 // Builds the new edge
621 auto edge = new DistEdgeImpl<T>(
622 this->edge_id++, layer
623 );
624 edge->setSourceNode(src);
625 src->linkOut(edge);
626 edge->setTargetNode(tgt);
627 tgt->linkIn(edge);
628
629 edge->setState(
633 );
634 sync_mode.getSyncLinker().link(edge);
635
636 // Inserts the edge in the Graph
637 this->insert(edge);
638
639 return edge;
640 }
641
642 template<DIST_GRAPH_PARAMS>
645
646 sync_mode.getSyncLinker().synchronize();
647
648 _distribute(partition);
649
650 sync_mode.getDataSync().synchronize();
651 }
652
653
654 template<DIST_GRAPH_PARAMS>
657 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
658 "Distributing graph...", "");
659 std::string partition_str = "\n";
660 for(auto item : partition) {
661 std::string str = FPMAS_C_STR(item.first);
662 str.append(" : " + std::to_string(item.second) + "\n");
663 partition_str.append(str);
664 }
665 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH", "Partition : %s", partition_str.c_str());
666
667 std::unordered_map<int, communication::DataPack> serial_nodes;
668 std::unordered_map<int, communication::DataPack> serial_edges;
669
670 {
671 // Builds node and edges export maps
672 std::vector<NodeType*> exported_nodes;
673 std::unordered_map<int, std::vector<NodePtrWrapper<T>>> node_export_map;
674 std::unordered_map<int, std::set<DistributedId>> edge_ids_to_export;
675 std::unordered_map<int, std::vector<EdgePtrWrapper<T>>> edge_export_map;
676 for(auto item : partition) {
677 if(item.second != mpi_communicator->getRank()) {
678 auto node = this->getNodes().find(item.first);
679 if(node != this->getNodes().end()
680 && node->second->state() == api::graph::LOCAL) {
681 FPMAS_LOGV(getMpiCommunicator().getRank(), "DIST_GRAPH",
682 "Exporting node %s to %i", FPMAS_C_STR(item.first), item.second);
683 auto node_to_export = node->second;
684 exported_nodes.push_back(node_to_export);
685 node_export_map[item.second].emplace_back(node_to_export);
686 for(auto edge : node_to_export->getIncomingEdges()) {
687 // Insert or replace in the IDs set
688 edge_ids_to_export[item.second].insert(edge->getId());
689 }
690 for(auto edge : node_to_export->getOutgoingEdges()) {
691 // Insert or replace in the IDs set
692 edge_ids_to_export[item.second].insert(edge->getId());
693 }
694 }
695 }
696 }
697 // Ensures that each edge is exported once to each process
698 for(auto list : edge_ids_to_export) {
699 for(auto id : list.second) {
700 edge_export_map[list.first].emplace_back(this->getEdge(id));
701 }
702 }
703
704 // Serializes nodes and edges
705 for(auto& item : node_export_map)
706 serial_nodes.emplace(
707 item.first,
708 io::datapack::ObjectPack(item.second).dump()
709 );
710 for(auto& item : edge_export_map)
711 serial_edges.emplace(
712 item.first,
713 io::datapack::LightObjectPack(item.second).dump()
714 );
715
716 // Once serialized nodes and associated edges can
717 // eventually be cleared. This might save memory for the
718 // next import.
719
720 for(auto node : exported_nodes) {
721 setDistant(node, api::graph::SetDistantNodeEvent<T>::EXPORT_DISTANT);
722 }
723
724 FPMAS_LOGD(getMpiCommunicator().getRank(), "DIST_GRAPH", "Clearing exported nodes...", "");
725 for(auto node : exported_nodes) {
726 clearNode(node);
727 }
728
729 // Clears buffers required for serialization
730 }
731
732 {
733 // Node import
734 std::unordered_map<int, std::vector<NodePtrWrapper<T>>> node_import;
735 for(auto& item : mpi_communicator->allToAll(std::move(serial_nodes), MPI_CHAR))
736 node_import.emplace(
737 item.first, io::datapack::ObjectPack::parse(
738 std::move(item.second)
739 ).get<std::vector<NodePtrWrapper<T>>>()
740 );
741
742 for(auto& import_node_list_from_proc : node_import) {
743 for(auto& imported_node : import_node_list_from_proc.second) {
744 this->importNode(imported_node);
745 }
746 import_node_list_from_proc.second.clear();
747 }
748 // node_import is deleted at the end of this scope
749 }
750
751 {
752 // Edge import
753 std::unordered_map<int, std::vector<EdgePtrWrapper<T>>> edge_import;
754
755 for(auto& item : mpi_communicator->allToAll(std::move(serial_edges), MPI_CHAR))
756 edge_import.emplace(
757 item.first, io::datapack::LightObjectPack::parse(
758 std::move(item.second)
759 ).get<std::vector<EdgePtrWrapper<T>>>()
760 );
761
762 for(auto& import_edge_list_from_proc : edge_import) {
763 for(auto& imported_edge : import_edge_list_from_proc.second) {
764 this->importEdge(imported_edge);
765 }
766 import_edge_list_from_proc.second.clear();
767 }
768 // edge_import is deleted at the end of this scope
769 }
770
771 FPMAS_LOGD(getMpiCommunicator().getRank(), "DIST_GRAPH", "Exported nodes cleared.", "");
772
773
774 location_manager.updateLocations();
775
776 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
777 "End of distribution.", "");
778 }
779
780 template<DIST_GRAPH_PARAMS>
781 void DistributedGraph<DIST_GRAPH_PARAMS_SPEC>
782 ::synchronize() {
783 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
784 "Synchronizing graph...", "");
785
786 sync_mode.getSyncLinker().synchronize();
787
788 clearDistantNodes();
789
790 sync_mode.getDataSync().synchronize();
791
792 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
793 "End of graph synchronization.", "");
794 }
795
796 template<DIST_GRAPH_PARAMS>
798 ::synchronize(std::unordered_set<NodeType*> nodes, bool synchronize_links) {
799 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
800 "Partially synchronizing graph...", "");
801
802 if(synchronize_links) {
803 sync_mode.getSyncLinker().synchronize();
804 clearDistantNodes();
805 }
806
807 sync_mode.getDataSync().synchronize(nodes);
808
809 for(auto node : nodes)
810 unsynchronized_nodes.erase(node);
811
812 FPMAS_LOGI(getMpiCommunicator().getRank(), "DIST_GRAPH",
813 "End of partial graph synchronization.", "");
814 }
815
816 template<DIST_GRAPH_PARAMS>
819 NodeMap distant_nodes = location_manager.getDistantNodes();
820 for(auto node : distant_nodes)
821 clearNode(node.second);
822 }
823
824 template<DIST_GRAPH_PARAMS>
826 ::clearNode(NodeType* node) {
827 DistributedId id = node->getId();
828 FPMAS_LOGD(
829 mpi_communicator->getRank(),
830 "DIST_GRAPH", "Clearing node %s...",
831 FPMAS_C_STR(id)
832 );
833
834 bool eraseNode = true;
835 std::set<EdgeType*> obsoleteEdges;
836 for(auto edge : node->getIncomingEdges()) {
837 if(edge->getSourceNode()->state()==LocationState::LOCAL) {
838 eraseNode = false;
839 } else {
840 obsoleteEdges.insert(edge);
841 }
842 }
843 for(auto edge : node->getOutgoingEdges()) {
844 if(edge->getTargetNode()->state()==LocationState::LOCAL) {
845 eraseNode = false;
846 } else {
847 obsoleteEdges.insert(edge);
848 }
849 }
850 if(eraseNode) {
851 FPMAS_LOGD(
852 mpi_communicator->getRank(),
853 "DIST_GRAPH", "Erasing obsolete node %s.",
854 FPMAS_C_STR(node->getId())
855 );
856 this->erase(node);
857 } else {
858 for(auto edge : obsoleteEdges) {
859 FPMAS_LOGD(
860 mpi_communicator->getRank(),
861 "DIST_GRAPH", "Erasing obsolete edge %s (%p) (from %s to %s)",
862 FPMAS_C_STR(node->getId()), edge,
863 FPMAS_C_STR(edge->getSourceNode()->getId()),
864 FPMAS_C_STR(edge->getTargetNode()->getId())
865 );
866 this->erase(edge);
867 }
868 }
869 FPMAS_LOGD(
870 mpi_communicator->getRank(),
871 "DIST_GRAPH", "Node %s cleared.",
872 FPMAS_C_STR(id)
873 );
874 }
875
876 template<DIST_GRAPH_PARAMS>
877 DistributedGraph<DIST_GRAPH_PARAMS_SPEC>::~DistributedGraph() {
878 //TODO: Refactor callback system
879 if(erase_node_callback != nullptr)
880 // This DistributedGraph has not been moved
881 erase_node_callback->disable();
882 for(auto callback : set_local_callbacks)
883 delete callback;
884 for(auto callback : set_distant_callbacks)
885 delete callback;
886 }
887
888 }
889
895 template<typename T, template<typename> class SyncMode>
899
900}}
901
902namespace nlohmann {
912 template<typename T>
913 class adl_serializer<fpmas::api::graph::DistributedGraph<T>> {
914 private:
916
917 class NullTemporaryNode : public fpmas::api::graph::TemporaryNode<T> {
918 private:
919 DistributedId id;
920 public:
921 NullTemporaryNode(DistributedId id) : id(id) {
922 }
923
924 DistributedId getId() const override {
925 return id;
926 }
927
928 int getLocation() const override {
929 assert(false);
930 return -1;
931 }
932
933 fpmas::api::graph::DistributedNode<T>* build() override {
934 assert(false);
935 return nullptr;
936 }
937 };
938
939 public:
940
949 static void to_json(json& j, const fpmas::api::graph::DistributedGraph<T>& graph) {
950 // Specifies the rank on which the local distributed graph
951 // was built
952 j["rank"] = graph.getMpiCommunicator().getRank();
953 for(auto node : graph.getNodes())
954 j["graph"]["nodes"].push_back({
955 NodePtrWrapper<T>(node.second),
956 node.second->location()
957 });
958 for(auto edge : graph.getEdges()) {
959 j["graph"]["edges"].push_back({
960 {"id", edge.first},
961 {"layer", edge.second->getLayer()},
962 {"weight", edge.second->getWeight()},
963 {"src", edge.second->getSourceNode()->getId()},
964 {"tgt", edge.second->getTargetNode()->getId()}
965 });
966 }
967 j["edge_id"] = graph.currentEdgeId();
968 j["node_id"] = graph.currentNodeId();
969 j["loc_manager"] = graph.getLocationManager();
970 nlohmann::json::json_serializer<fpmas::api::graph::LocationManager<T>, void>
971 ::to_json(j["loc_manager"], graph.getLocationManager());
972 }
973
987 static void from_json(const json& j, fpmas::api::graph::DistributedGraph<T>& graph) {
988 auto j_graph = j["graph"];
989 for(auto j_node : j_graph["nodes"]) {
990 auto location = j_node[1].get<int>();
992 = j_node[0].get<NodePtrWrapper<T>>();
993 if(location == graph.getMpiCommunicator().getRank())
994 node = graph.importNode(node);
995 else
996 node = graph.insertDistant(node);
997
998 node->setLocation(location);
999 }
1000 for(auto j_edge : j_graph["edges"]) {
1001 auto edge = new fpmas::graph::DistributedEdge<T>(
1002 j_edge["id"].get<fpmas::graph::DistributedId>(),
1003 j_edge["layer"].get<fpmas::graph::LayerId>()
1004 );
1005 edge->setWeight(j_edge["weight"].get<float>());
1006 // Temp source and target are never used (expect for
1007 // their ids) since source and target are necessarily
1008 // contained in the graph (see previous step)
1009 auto src_id = j_edge["src"].get<DistributedId>();
1010 edge->setTempSourceNode(
1011 std::unique_ptr<fpmas::api::graph::TemporaryNode<T>>(
1012 new NullTemporaryNode(src_id)
1013 )
1014 );
1015 auto tgt_id = j_edge["tgt"].get<DistributedId>();
1016 edge->setTempTargetNode(
1017 std::unique_ptr<fpmas::api::graph::TemporaryNode<T>>(
1018 new NullTemporaryNode(tgt_id)
1019 )
1020 );
1021 assert(graph.getNodes().count(src_id) > 0);
1022 assert(graph.getNodes().count(tgt_id) > 0);
1023 graph.importEdge(edge);
1024 }
1025 graph.setCurrentNodeId(j.at("node_id").get<DistributedId>());
1026 graph.setCurrentEdgeId(j.at("edge_id").get<DistributedId>());
1027
1028 nlohmann::json::json_serializer<fpmas::api::graph::LocationManager<T>, void>
1029 ::from_json(j["loc_manager"], graph.getLocationManager());
1030 }
1031 };
1032}
1033#endif
Definition: communication.h:251
Definition: distributed_edge.h:91
virtual void setState(LocationState state)=0
virtual std::unique_ptr< TemporaryNode< T > > getTempSourceNode()=0
virtual LocationState state() const =0
virtual std::unique_ptr< TemporaryNode< T > > getTempTargetNode()=0
Definition: distributed_graph.h:169
virtual api::communication::MpiCommunicator & getMpiCommunicator()=0
virtual DistributedEdge< T > * importEdge(DistributedEdge< T > *edge)=0
virtual DistributedId currentEdgeId() const =0
virtual DistributedId currentNodeId() const =0
virtual void setCurrentNodeId(DistributedId id)=0
virtual DistributedNode< T > * insertDistant(DistributedNode< T > *node)=0
virtual DistributedNode< T > * importNode(DistributedNode< T > *node)=0
virtual void setCurrentEdgeId(DistributedId id)=0
virtual LocationManager< T > & getLocationManager()=0
Definition: distributed_id.h:89
Definition: distributed_node.h:28
virtual LocationState state() const =0
virtual void setMutex(synchro::Mutex< T > *mutex)=0
virtual IdType getId() const =0
virtual void setTargetNode(NodeType *const tgt)=0
virtual void setLayer(LayerId layer)=0
virtual void setSourceNode(NodeType *const src)=0
virtual NodeType * getSourceNode() const =0
virtual NodeType * getTargetNode() const =0
Definition: load_balancing.h:47
virtual PartitionMap balance(NodeMap< T > nodes, PartitionMap fixed_vertices)=0
virtual void erase(NodeType *node)=0
virtual const NodeMap & getNodes() const =0
virtual const EdgeMap & getEdges() const =0
std::unordered_map< NodeIdType, DistributedNode< T > *, NodeIdHash > NodeMap
Definition: graph.h:50
Definition: load_balancing.h:92
virtual PartitionMap balance(NodeMap< T > nodes)=0
virtual void unlinkOut(EdgeType *edge)=0
virtual void linkIn(EdgeType *edge)=0
virtual float getWeight() const =0
virtual void unlinkIn(EdgeType *edge)=0
virtual IdType getId() const =0
virtual void linkOut(EdgeType *edge)=0
Definition: distributed_edge.h:47
Definition: callback.h:16
Definition: distributed_edge.h:22
Definition: distributed_node.h:25
Definition: graph.h:21
void addCallOnEraseNode(api::utils::Callback< api::graph::DistributedNode< T > * > *callback) override
Definition: graph.h:72
api::graph::DistributedEdge< T > * getEdge(EdgeIdType) override
Definition: graph.h:246
api::graph::DistributedNode< T > * getNode(NodeIdType) override
Definition: graph.h:228
Definition: location_manager.h:22
Definition: distributed_graph.h:49
DistributedGraph & operator=(DistributedGraph< DIST_GRAPH_PARAMS_SPEC > &&graph)
Definition: distributed_graph.h:340
void balance(api::graph::LoadBalancing< T > &load_balancing) override
Definition: distributed_graph.h:214
LocationManagerImpl< T > & getLocationManager() override
Definition: distributed_graph.h:212
DistributedGraph(api::communication::MpiCommunicator &comm)
Definition: distributed_graph.h:159
void setCurrentEdgeId(DistributedId id) override
Definition: distributed_graph.h:188
void balance(api::graph::FixedVerticesLoadBalancing< T > &load_balancing, api::graph::PartitionMap fixed_vertices) override
Definition: distributed_graph.h:245
NodeType * buildNode(const T &) override
Definition: distributed_graph.h:586
EdgeType * importEdge(EdgeType *edge) override
Definition: distributed_graph.h:451
api::graph::DistributedNode< T > * insertDistant(api::graph::DistributedNode< T > *) override
Definition: distributed_graph.h:592
void addCallOnSetDistant(SetDistantNodeCallback *callback) override
Definition: distributed_graph.h:311
api::communication::MpiCommunicator & getMpiCommunicator() override
Definition: distributed_graph.h:174
NodeType * importNode(NodeType *node) override
Definition: distributed_graph.h:414
void setCurrentNodeId(DistributedId id) override
Definition: distributed_graph.h:186
const api::communication::MpiCommunicator & getMpiCommunicator() const override
Definition: distributed_graph.h:181
void switchLayer(EdgeType *edge, api::graph::LayerId layer_id) override
Definition: distributed_graph.h:401
void balance(api::graph::FixedVerticesLoadBalancing< T > &load_balancing, api::graph::PartitionMap fixed_vertices, api::graph::PartitionMode partition_mode) override
Definition: distributed_graph.h:252
DistributedGraph(DistributedGraph< DIST_GRAPH_PARAMS_SPEC > &&graph)
Definition: distributed_graph.h:322
void removeNode(api::graph::DistributedNode< T > *) override
Definition: distributed_graph.h:611
EdgeType * link(NodeType *const src, NodeType *const tgt, api::graph::LayerId layer) override
Definition: distributed_graph.h:618
void synchronize() override
Definition: distributed_graph.h:782
SyncMode< T > & synchronizationMode() override
Definition: distributed_graph.h:207
std::unordered_set< api::graph::DistributedNode< T > * > getUnsyncNodes() const override
Definition: distributed_graph.h:192
api::graph::DistributedNode< T > NodeType
Definition: distributed_graph.h:54
void removeNode(DistributedId id) override
Definition: distributed_graph.h:293
void addCallOnSetLocal(SetLocalNodeCallback *callback) override
Definition: distributed_graph.h:304
const SyncMode< T > & getSyncMode() const
Definition: distributed_graph.h:205
std::vector< SetLocalNodeCallback * > onSetLocalCallbacks() const override
Definition: distributed_graph.h:307
void unlink(DistributedId id) override
Definition: distributed_graph.h:298
NodeType * buildNode(T &&=std::move(T())) override
Definition: distributed_graph.h:578
void unlink(EdgeType *) override
Definition: distributed_graph.h:395
const LocationManagerImpl< T > & getLocationManager() const override
Definition: distributed_graph.h:210
std::vector< SetDistantNodeCallback * > onSetDistantCallbacks() const override
Definition: distributed_graph.h:314
api::graph::DistributedEdge< T > EdgeType
Definition: distributed_graph.h:68
DistributedId currentNodeId() const override
Definition: distributed_graph.h:185
DistributedId currentEdgeId() const override
Definition: distributed_graph.h:187
void distribute(api::graph::PartitionMap partition) override
Definition: distributed_graph.h:644
void synchronize(std::unordered_set< NodeType * > nodes, bool synchronize_links=true) override
Definition: distributed_graph.h:798
void balance(api::graph::LoadBalancing< T > &load_balancing, api::graph::PartitionMode partition_mode) override
Definition: distributed_graph.h:218
static void to_json(json &j, const fpmas::api::graph::DistributedGraph< T > &graph)
Definition: distributed_graph.h:949
static void from_json(const json &j, fpmas::api::graph::DistributedGraph< T > &graph)
Definition: distributed_graph.h:987
#define FPMAS_C_STR(arg)
Definition: macros.h:24
LocationState
Definition: location_state.h:15
@ DISTANT
Definition: location_state.h:28
@ LOCAL
Definition: location_state.h:21
std::unordered_map< DistributedId, int, api::graph::IdHash< DistributedId > > PartitionMap
Definition: load_balancing.h:19
int LayerId
Definition: edge.h:13
PartitionMode
Definition: load_balancing.h:30
@ PARTITION
Definition: load_balancing.h:35
typename graph::Graph< graph::DistributedNode< T >, graph::DistributedEdge< T > >::NodeMap NodeMap
Definition: load_balancing.h:25
detail::DistributedGraph< T, SyncMode, graph::DistributedNode, graph::DistributedEdge, communication::TypedMpi, graph::LocationManager > DistributedGraph
Definition: distributed_graph.h:898
BasicObjectPack< LightSerializer > LightObjectPack
Definition: datapack.h:1366
BasicObjectPack< Serializer > ObjectPack
Definition: datapack.h:1364
Definition: fpmas.cpp:3
Definition: distributed_graph.h:99
Context
Definition: distributed_graph.h:103
Definition: distributed_graph.h:42
Context
Definition: distributed_graph.h:46
Definition: callback.h:33
Definition: communication.h:590
Definition: synchro.h:16