fpmas 1.6
analysis.h
Go to the documentation of this file.
1#ifndef FPMAS_GRAPH_ANALYSIS_H
2#define FPMAS_GRAPH_ANALYSIS_H
3
11#include <set>
12#include <queue>
13
14namespace fpmas { namespace graph {
25 template<typename T> std::size_t node_count(api::graph::DistributedGraph<T>& graph) {
28 int_mpi, graph.getLocationManager().getLocalNodes().size());
29 }
40 template<typename T>
42 std::size_t local_count = 0;
43 for(auto node : graph.getLocationManager().getLocalNodes())
44 local_count += node.second->getOutgoingEdges().size();
45
47 return communication::all_reduce(int_mpi, local_count);
48 }
49
70 template<typename T>
71 std::unordered_map<DistributedId, std::vector<DistributedId>>
75 // MPI instance used to exchange DistributedIds requests
77 graph.getMpiCommunicator());
78 // Builds a request for each \DISTANT node in the graph
79 std::unordered_map<int, std::vector<DistributedId>> requests;
80 for(auto node : graph.getLocationManager().getDistantNodes())
81 requests[node.second->location()].push_back(node.first);
82 requests = requests_mpi.allToAll(requests);
83
84 // MPI instance used to send request results. Results are received
85 // as a vector of pairs {requested node ids, {list of ids of
86 // outgoing neighbors}}.
88 std::unordered_map<int, std::vector<std::pair<DistributedId, std::vector<DistributedId>>>> results;
89 for(auto request : requests) {
90 for(auto node_id : request.second) {
91 // Safely gets neighbors of a LOCAL node
92 auto edges = graph.getNode(node_id)->getOutgoingEdges(layer);
93 // Stores neighbors ids in a list
94 std::vector<DistributedId> neighbors(edges.size());
95 auto it = neighbors.begin();
96 for(auto edge : edges)
97 *it++ = edge->getTargetNode()->getId();
98 // Save the {node_id, {neighbors}} pair, ready to be
99 // transmitted
100 results[request.first].push_back({node_id, neighbors});
101 }
102 }
103 // Exchange results
104 results = results_mpi.allToAll(results);
105
106 // Gathers results from all processes
107 std::unordered_map<DistributedId, std::vector<DistributedId>>
108 distant_nodes_neighbors;
109 for(auto item : results)
110 for(auto neighbors : item.second)
111 distant_nodes_neighbors[neighbors.first] = neighbors.second;
112 // Returns results for all DISTANT nodes on the current process
113 return distant_nodes_neighbors;
114 }
115
141 template<typename T>
145 double c = 0;
146 auto distant_nodes_neighbors = distant_nodes_outgoing_neighbors(graph, layer);
147
148 for(auto node : graph.getLocationManager().getLocalNodes()) {
149 std::size_t n = 0;
150
151 // Neighbors of `node`= the set of incoming AND outgoing
152 // neighbors, without duplicate. The purpose of the algorithm is
153 // then to count edges between those neighbors.
154 std::set<DistributedId> neighbors;
155 for(auto edge : node.second->getIncomingEdges(layer)) {
156 neighbors.insert(edge->getSourceNode()->getId());
157 }
158 for(auto edge : node.second->getOutgoingEdges(layer)) {
159 neighbors.insert(edge->getTargetNode()->getId());
160 }
161 // Do not consider self edges
162 neighbors.erase(node.first);
163
164 for(auto node_id : neighbors) {
165 // The objective is to count edges between neighbor with
166 // others in the neighbors list. Only OUTGOING edges are
167 // considered: since each neighbors is considered, this
168 // guarantees that each edge is counted exactly once.
169 auto neighbor = graph.getNode(node_id);
170 if(neighbor->state() == fpmas::api::graph::LOCAL) {
171 // Edges can be retrieved locally
172 for(auto edge : neighbor->getOutgoingEdges(layer)) {
173 auto id = edge->getTargetNode()->getId();
174 if(
175 // Do not consider edge to the considered node
176 id != node.first
177 // Only edges from a node's neighbor to an other
178 && neighbors.find(id) != neighbors.end()) {
179 ++n;
180 }
181 }
182 } else {
183 // The neighbor is \DISTANT, but its neighbors can be
184 // retrieved with the distant_nodes_neighbors ids map.
185 for(auto id : distant_nodes_neighbors[node_id])
186 if(id != node.first && neighbors.find(id) != neighbors.end()) {
187 ++n;
188 }
189 }
190 }
191 std::size_t k = neighbors.size();
192 if(k > 1)
193 c += (double) n / (k*(k-1));
194 }
197 c = communication::all_reduce(double_mpi, c);
198 std::size_t total_node_count =
199 communication::all_reduce(int_mpi, graph.getLocationManager().getLocalNodes().size());
200 if(total_node_count > 0)
201 return c/total_node_count;
202 else
203 return 0;
204 }
205
206 template<typename T>
207 void __explore_distances(
209 std::queue<std::pair<api::graph::DistributedNode<T>*, float>>& node_queue,
211 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>& distances,
212 std::unordered_map<
213 int,
214 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>
215 >& next_processes) {
216 while(!node_queue.empty()) {
217 auto current_node = node_queue.front();
218 node_queue.pop();
219 if(current_node.first->state() == api::graph::LOCAL) {
220 if(current_node.second < distances[current_node.first->getId()][source]) {
221 distances[current_node.first->getId()][source] = current_node.second;
222 for(auto edge : current_node.first->getOutgoingEdges(layer))
223 node_queue.push({edge->getTargetNode(), current_node.second+1});
224 }
225 } else {
226 // Map with all requests for node from any source
227 auto& current_node_requests = next_processes
228 [current_node.first->location()][current_node.first->getId()];
229 // Request for the current node from the current source
230 auto current_source_request = current_node_requests.find(source);
231 if(current_source_request == current_node_requests.end())
232 // No request from the current source, create new one
233 current_node_requests[source] = current_node.second;
234 else if(current_node.second < current_node_requests[source])
235 // Else a request already exist, but replace it only if it
236 // can produce a shortest path
237 current_node_requests[source] = current_node.second;
238 }
239 }
240 }
241
242
250 template<typename T>
251 std::vector<DistributedId> local_node_ids(
253 std::vector<DistributedId> ids(
254 graph.getLocationManager().getLocalNodes().size());
255 auto it = ids.begin();
256 for(auto& item : graph.getLocationManager().getLocalNodes()) {
257 *it = item.first;
258 ++it;
259 }
260 return ids;
261 }
262
279 template<typename T>
280 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>
284 const std::vector<DistributedId>& source_nodes
285 ) {
287 graph.getMpiCommunicator());
288 // Map local node: (source node: distance)
289 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>
290 distances;
291 std::vector<DistributedId> all_source_nodes = communication::all_reduce(
292 vector_id_mpi, source_nodes, fpmas::utils::Concat());
293 api::graph::NodeMap<T> local_nodes
294 = graph.getLocationManager().getLocalNodes();
295 for(auto node : local_nodes)
296 for(auto id : all_source_nodes)
297 distances[node.first][id] = std::numeric_limits<float>::infinity();
298
299 std::unordered_map<int,
300 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>
301 > next_processes;
303 std::unordered_map<DistributedId, std::unordered_map<DistributedId, float>>>
304 next_mpi(graph.getMpiCommunicator());
305 for(auto source : all_source_nodes) {
306 auto source_node = local_nodes.find(source);
307 if(source_node != local_nodes.end()) {
308 std::queue<std::pair<api::graph::DistributedNode<T>*, float>>
309 node_queue;
310 node_queue.push({source_node->second, 0});
311 __explore_distances(source, node_queue, layer, distances, next_processes);
312 }
313 }
314
316 auto and_fct =
317 [](const bool& b1, const bool& b2) {return b1 && b2;};
318 bool end = communication::all_reduce(
319 bool_mpi, next_processes.size() == 0, and_fct
320 );
321 while(!end) {
322 auto requests = next_mpi.allToAll(next_processes);
323 next_processes.clear();
324 for(auto proc : requests) {
325 for(auto node : proc.second)
326 for(auto source : node.second) {
327 // Re-launch exploration from local node
328 std::queue<std::pair<api::graph::DistributedNode<T>*, float>> node_queue;
329 node_queue.push({graph.getNode(node.first), source.second});
330 __explore_distances(
331 source.first, node_queue, layer,
332 distances, next_processes
333 );
334 }
335 }
337 bool_mpi, next_processes.size() == 0, and_fct
338 );
339 }
340
341 return distances;
342 }
343
371 template<typename T>
375 const std::vector<DistributedId>& source_nodes
376 ) {
377 auto distances = shortest_path_lengths(graph, layer, source_nodes);
378
381 float local_sum = 0;
382 // Number of node pairs between which a path actually exists
383 std::size_t num_pairs = 0;
384 for(auto& source : distances)
385 for(auto& distance : source.second) {
386 if(source.first != distance.first &&
387 distance.second < std::numeric_limits<float>::infinity()) {
388 local_sum+=distance.second;
389 num_pairs++;
390 }
391 }
392 float total_sum = communication::all_reduce(distance_mpi, local_sum);
393 num_pairs = communication::all_reduce(int_mpi, num_pairs);
394 return total_sum / num_pairs;
395 }
396}}
397
398#endif
Definition: distributed_graph.h:169
virtual api::communication::MpiCommunicator & getMpiCommunicator()=0
virtual LocationManager< T > & getLocationManager()=0
Definition: distributed_id.h:89
Definition: distributed_node.h:28
virtual NodeType * getNode(NodeIdType id)=0
std::unordered_map< int, T > allToAll(std::unordered_map< int, T > export_map) override
Definition: communication.h:446
@ LOCAL
Definition: location_state.h:21
int LayerId
Definition: edge.h:13
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
std::unordered_map< DistributedId, std::unordered_map< DistributedId, float > > shortest_path_lengths(fpmas::api::graph::DistributedGraph< T > &graph, fpmas::api::graph::LayerId layer, const std::vector< DistributedId > &source_nodes)
Definition: analysis.h:281
std::size_t edge_count(api::graph::DistributedGraph< T > &graph)
Definition: analysis.h:41
float characteristic_path_length(fpmas::api::graph::DistributedGraph< T > &graph, fpmas::api::graph::LayerId layer, const std::vector< DistributedId > &source_nodes)
Definition: analysis.h:372
double clustering_coefficient(fpmas::api::graph::DistributedGraph< T > &graph, fpmas::api::graph::LayerId layer)
Definition: analysis.h:142
std::unordered_map< DistributedId, std::vector< DistributedId > > distant_nodes_outgoing_neighbors(fpmas::api::graph::DistributedGraph< T > &graph, fpmas::api::graph::LayerId layer)
Definition: analysis.h:72
std::size_t node_count(api::graph::DistributedGraph< T > &graph)
Definition: analysis.h:25
std::vector< DistributedId > local_node_ids(fpmas::api::graph::DistributedGraph< T > &graph)
Definition: analysis.h:251
Definition: fpmas.cpp:3
Definition: communication.h:585
Definition: functional.h:94