Module: NetworkX
- Defined in:
- lib/networkx/others/grid_2d_graph.rb,
lib/networkx/graph.rb,
lib/networkx/digraph.rb,
lib/networkx/version.rb,
lib/networkx/to_matrix.rb,
lib/networkx/flow/utils.rb,
lib/networkx/multigraph.rb,
lib/networkx/others/info.rb,
lib/networkx/multidigraph.rb,
lib/networkx/others/reads.rb,
lib/networkx/operators/all.rb,
lib/networkx/others/bridges.rb,
lib/networkx/traversals/bfs.rb,
lib/networkx/traversals/dfs.rb,
lib/networkx/operators/unary.rb,
lib/networkx/flow/edmondskarp.rb,
lib/networkx/flow/preflowpush.rb,
lib/networkx/operators/binary.rb,
lib/networkx/converters/to_csv.rb,
lib/networkx/operators/product.rb,
lib/networkx/others/generators.rb,
lib/networkx/converters/to_json.rb,
lib/networkx/link_analysis/hits.rb,
lib/networkx/shortest_path/astar.rb,
lib/networkx/shortest_path/dense.rb,
lib/networkx/traversals/edge_dfs.rb,
lib/networkx/flow/capacityscaling.rb,
lib/networkx/link_analysis/pagerank.rb,
lib/networkx/shortest_path/weighted.rb,
lib/networkx/auxillary_functions/dag.rb,
lib/networkx/auxillary_functions/mis.rb,
lib/networkx/auxillary_functions/mst.rb,
lib/networkx/shortest_path/unweighted.rb,
lib/networkx/auxillary_functions/cycles.rb,
lib/networkx/auxillary_functions/wiener.rb,
lib/networkx/auxillary_functions/cliques.rb,
lib/networkx/flow/shortestaugmentingpath.rb,
lib/networkx/auxillary_functions/vitality.rb,
lib/networkx/auxillary_functions/union_find.rb,
lib/networkx/auxillary_functions/eccentricity.rb,
lib/networkx/others/number_connected_components.rb
Overview
Defined Under Namespace
Classes: CurrentEdge, DiGraph, GlobalRelabelThreshold, Graph, Level, MultiDiGraph, MultiGraph, UnionFind
Constant Summary collapse
- VERSION =
'0.4.0'.freeze
Class Method Summary collapse
-
._build_flow_dict(graph, residual) ⇒ Object
Returns the flowdict of the graph.
-
._build_residual_network(graph) ⇒ Object
Returns the residual graph of the given graph.
-
._detect_unboundedness(residual) ⇒ Object
Detects the unboundedness in the residual graph.
-
.activate(node, source, target, levels, residual_nodes) ⇒ Object
Helper function to move a node from inactive set to active set.
-
.all_pairs_dijkstra(graph, cutoff = nil) ⇒ Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>
Finds shortest weighted paths and lengths between all nodes.
-
.all_pairs_dijkstra_path(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Array<Object> }>
Finds shortest weighted paths between all nodes.
-
.all_pairs_dijkstra_path_length(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Numeric }>
Finds shortest weighted path length between all nodes.
-
.all_pairs_shortest_path(graph, cutoff = nil) ⇒ Array<Object, Hash {Object => Array<Object> }>
Computes shortest paths to all nodes from all nodes.
-
.all_pairs_shortest_path_length(graph, cutoff = nil) ⇒ Array<Object, Array<Object, Numeric>>
Computes shortest path values to all nodes from all nodes.
-
.allpairs_bellmanford_path(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Array<Object> }>
Shortest paths between all nodes using Bellman Ford algorithm.
-
.allpairs_bellmanford_path_length(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Numeric }>
Shortest path lengths between all nodes using Bellman Ford algorithm.
-
.ancestors(graph, source) ⇒ Array<Object>
Returns the ancestors of a given node.
-
.arbitrary_element(iterable) ⇒ Object
Helper function to return an arbitrary element from an iterable object.
-
.astar_path(graph, source, target, heuristic = nil) ⇒ Array<Object>
Returns path using astar algorithm between source and target.
-
.astar_path_length(graph, source, target, heuristic = nil) ⇒ Numeric
Returns astar path length b/w source and target.
-
.augment(residual, inf, path) ⇒ Object
Helper function to augment the flow in a residual graph.
-
.authority_matrix(graph) ⇒ Matrix
Computes authority matrix for the graph.
-
.bellmanford_path(graph, source, target) ⇒ Array<Object>
Shortest path from source to target using Bellman Ford algorithm.
-
.bellmanford_path_length(graph, source, target) ⇒ Numeric
Length of shortest path from source to target using Bellman Ford algorithm.
-
.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil) ⇒ Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>
Finds shortest weighted path lengths and predecessors on shortest paths.
-
.bfs_edges(graph, source) ⇒ Object
Returns edges of the graph travelled in breadth first fashion.
-
.bfs_predecessors(graph, source) ⇒ Object
Returns predecessor child pair of the graph travelled in breadth first fashion.
-
.bfs_successors(graph, source) ⇒ Object
Returns parent successor pair of the graph travelled in breadth first fashion.
-
.bidirectional_bfs(residual, source, target) ⇒ Object
Helper function for the bidirectional bfs.
-
.bridges(graph) ⇒ [Object, Object]
Bridges.
-
.build_flow_dict(graph, residual) ⇒ Hash{ Object => Hash{ Object => Numeric }] flowdict containing all the flow values in the edges
Build flow dictionary of a graph from its residual graph.
-
.build_residual_network(graph) ⇒ DiGraph
Builds a residual graph from a constituent graph.
-
.capacity_scaling(graph) ⇒ Array<Numeric, Hash{ Object => Hash{ Object => Numeric } }>
Computes max flow using capacity scaling algorithm.
-
.cartesian_product(g1, g2) ⇒ Graph, ...
Returns the cartesian product of two graphs.
-
.closeness_vitality(graph, node) ⇒ Numeric
Returns the closeness vitality of a node.
-
.complement(graph) ⇒ Graph, ...
Performs the complement operation on the graph.
-
.compose(g1, g2) ⇒ Graph, ...
Performs the composition of two graphs.
-
.compose_all(graphs) ⇒ Graph, ...
Performs the composition of many graphs.
-
.convert_to_distinct_labels(graph, starting_int = -1)) ⇒ Object
Transforms the labels of the nodes of the graphs so that they are disjoint.
-
.cycle?(undirected_graph) ⇒ book
Returns whether the given undirected cycle has cycle.
-
.cycle_basis(graph, root = nil) ⇒ Array<Array<Object>>
Returns all basis cycles in graph.
-
.descendants(graph, source) ⇒ Array<Object>
Returns the descendants of a given node.
-
.detect_unboundedness(r_network, source, target) ⇒ Object
Detects unboundedness in a graph, raises exception when infinite capacity flow is found.
-
.dfs_edges(graph, source, depth_limit = nil) ⇒ Object
Returns edges of the graph travelled in depth first fashion.
-
.dfs_predecessors(graph, source, depth_limit = nil) ⇒ Object
Returns predecessor child pair of the graph travelled in depth first fashion.
-
.dfs_successors(graph, source, depth_limit = nil) ⇒ Object
Returns parent successor pair of the graph travelled in depth first fashion.
-
.dfs_tree(graph, source, depth_limit = nil) ⇒ Object
Returns dfs tree of the graph.
-
.diameter(graph) ⇒ Numeric
Returns the diameter of a graph.
-
.difference(g1, g2) ⇒ Graph, ...
Performs the difference of two graphs.
-
.dijkstra_path(graph, source, target) ⇒ Numeric
Computes shortest path to target from the given node.
-
.dijkstra_path_length(graph, source, target) ⇒ Numeric
Computes shortest path length to target from the given node.
-
.dijkstra_predecessor_distance(graph, source, cutoff = nil) ⇒ <Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predcessor hash and distance hash
Computes weighted shortest path length and predecessors.
-
.directed_edges_cross_edges(g1, g2) ⇒ Object
Returns the product of directed edges with edges.
-
.discharge(u_node, is_phase1, residual_nodes, residual_adj, height, levels, grt, source, target) ⇒ Object
Helper function for discharging a node.
-
.disjoint_union(g1, g2) ⇒ Graph, ...
Performs the disjoint union of two graphs.
-
.disjoint_union_all(graphs) ⇒ Graph, ...
Performs the disjoint union of many graphs.
-
.dist_path_lambda(_graph, _new_weight) ⇒ Object
Helper function to get distances.
- .each_bridge(graph) ⇒ Object
-
.eccentricity(graph, node = nil) ⇒ Array<Numeric>, Numeric
Returns the eccentricity of a particular node or all nodes.
-
.edge_dfs(graph, start, orientation = nil) ⇒ Object
Performs edge dfs on the graph Orientation :ignore, directed edges can be travelled in both fashions Orientation reverse, directed edges can be travelled in reverse fashion Orientation :nil, the graph is not meddled with.
-
.edge_id(graph, edge) ⇒ Object
Helper function of edge_dfs.
-
.edges_cross_nodes(g1, g2) ⇒ Object
Returns the product of edges with edges.
-
.edges_cross_nodes_and_nodes(g1, g2) ⇒ Object
Returns the product of edges with pairs of nodes.
-
.edges_in_array(graph) ⇒ Object
Returns the edges of the graph in an array.
-
.edmondskarp(graph, source, target, residual = nil, cutoff = nil) ⇒ DiGraph
Computes max flow using edmonds karp algorithm.
-
.edmondskarp_core(residual, source, target, cutoff) ⇒ Object
Core helper function for the EdmondsKarp algorithm.
-
.edmondskarp_impl(graph, source, target, residual, cutoff) ⇒ Object
Helper function for the edmondskarp function.
-
.find_cliques(graph) ⇒ Array<Array<Object>>
Returns all cliques in the graph.
-
.find_cycle(graph, node) ⇒ Array<Array<Object>>
Returns the cycle containing the given node.
-
.floyd_warshall(graph) ⇒ Hash{ Object => { Object => { Numeric }}}
Returns the all pair distance between all the nodes.
-
.gap_heuristic(height, levels, residual_nodes) ⇒ Object
Helper function for applying gap heuristic.
-
.generate_unique_node ⇒ Object
Returns a label for unique node.
-
.get_edges(graph) ⇒ Object
Returns the edges of the graph in an array.
-
.get_edges_weights(graph) ⇒ Object
Helper function for the minimum spanning tree.
-
.get_sources(graph) ⇒ Object
Helper function to get sources.
-
.get_weight(graph) ⇒ Object
Helper function to extract weight from a adjecency hash.
-
.global_relabel(from_sink, source, target, residual_nodes, num, levels, residual_pred) ⇒ Object
Helper function for global relabel heuristic.
-
.graph_to_csv(graph, filename = 'graph.csv') ⇒ Object
Saves the graph in a csv file.
-
.graph_to_json(graph) ⇒ JSON
Returns a JSON object of the given graph.
- .grid_2d_graph(m, n, periodic: false, create_using: NetworkX::Graph) ⇒ Object
-
.hash_product(hash1, hash2) ⇒ Object
Returns the hash product of two hashes.
-
.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for bellman ford.
-
.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for single source dijkstra.
-
.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for multisource dijkstra.
-
.help_single_shortest_path(adj, firstlevel, paths, cutoff) ⇒ Object
Helper function for finding single source shortest path.
-
.help_single_shortest_path_length(adj, firstlevel, cutoff) ⇒ Object
Helper function for single source shortest path length.
-
.hits(graph, max_iter = 100, tol = 1e-8, nstart) ⇒ Array<Numeric, Numeric>
Computes hits and authority scores for all the graphs.
-
.hub_matrix(graph) ⇒ Matrix
Computes hub matrix for the graph.
- .info(graph) ⇒ Object
-
.init_product_graph(g1, g2) ⇒ Object
Initializes the product graph.
-
.intersection(g1, g2) ⇒ Graph, ...
Performs the intersection of two graphs.
-
.intersection_all(graphs) ⇒ Graph, ...
Performs the intersection of many graphs.
-
.johnson(graph) ⇒ Array<Object, Hash { Object => Array<Object> }] shortest paths between all pairs of nodes
Returns shortest path between all pairs of nodes.
-
.json_to_graph(json_str) ⇒ Graph, ...
Returns a graph from the json encoded graph.
-
.lexicographic_product(g1, g2) ⇒ Graph, ...
Returns the lexicographic product of two graphs.
-
.maximal_independent_set(graph, nodes) ⇒ Numeric
Returns the maximal independent set of a graph.
-
.minimum_spanning_tree(graph) ⇒ DiGraph, Graph
Returns the minimum spanning tree of a graph.
-
.multisource_dijkstra(graph, sources, target = nil, cutoff = nil) ⇒ Numeric, Array<Object>
Computes shortest paths and path lengths to a target from one of the nodes.
-
.multisource_dijkstra_path(graph, sources, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Computes shortest paths to any from the given nodes.
-
.multisource_dijkstra_path_length(graph, sources, cutoff = nil) ⇒ Hash{ Object => Numeric }
Computes shortest path lengths to any from the given nodes.
-
.negative_edge_cycle(graph) ⇒ Boolean
Finds if there is a negative edge cycle in the graph.
-
.node_product(g1, g2) ⇒ Object
Returns the node product of nodes of two graphs.
-
.nodes_cross_edges(g1, g2) ⇒ Object
Returns the product of directed nodes with edges.
-
.number_connected_components(graph) ⇒ Integer
(also: number_of_connected_components)
The number of connected components on graph.
-
.number_of_bridges(graph) ⇒ Integer
The number of bridges.
-
.number_of_cliques(graph, node) ⇒ Numeric
Returns the number of cliques in a graph containing a node.
-
.out_edges(graph, node) ⇒ Object
Helper function for edge_dfs.
-
.pagerank(graph, alpha: 0.85, personalization: nil, eps: 1e-6, max_iter: 100) ⇒ Hash of Object => Float
Computes pagerank values for the graph.
-
.power(graph, pow) ⇒ Graph, ...
Returns the specified power of the graph.
-
.predecessor(graph, source, cutoff = nil, return_seen = false) ⇒ Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>, Hash{ Object => Array<Object> }
Computes shortest paths to all nodes from all nodes.
-
.preflowpush(graph, source, target, residual = nil, globalrelabel_freq = 1, value_only = false) ⇒ DiGraph
Computes max flow using preflow push algorithm.
-
.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only) ⇒ Object
Helper function to apply the preflow push algorithm.
-
.push(node1, node2, flow, residual_nodes, residual_adj) ⇒ Object
Helper function for augmenting flow.
-
.radius(graph) ⇒ Numeric
Returns the radius of a graph.
-
.relabel(node, num, r_adj, r_nodes) ⇒ Object
Helper function to relable a node to create a permissible edge.
-
.reverse_bfs(src, residual_pred) ⇒ Object
Helper function for reverse bfs.
-
.set_path_lengths_johnson(graph, dist_path, new_weight) ⇒ Object
Helper function to set path lengths for Johnson algorithm.
-
.shortest_augmenting_path(graph, source, target, residual = nil, _value_only = false, two_phase = false, cutoff = nil) ⇒ DiGraph
Computes max flow using shortest augmenting path algorithm.
-
.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff) ⇒ Object
Helper function for running the shortest augmenting path algorithm.
-
.single_source_shortest_path(graph, source, cutoff = nil) ⇒ Array<Object, Array<Object, Array<Object>>>
Computes single source shortest path from a node to every other node.
-
.single_source_shortest_path_length(graph, source, cutoff = nil) ⇒ Array<Object, Numeric>
Computes shortest path values to all nodes from a given node.
- .singlesource_bellmanford(graph, source, target = nil, cutoff = nil) ⇒ Object
-
.singlesource_bellmanford_path(graph, source, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Shortest path from source to all nodes using Bellman Ford algorithm.
-
.singlesource_bellmanford_path_length(graph, source, cutoff = nil) ⇒ Hash{ Object => Numeric }
Shortest path length from source to all nodes using Bellman Ford algorithm.
-
.singlesource_dijkstra(graph, source, target = nil, cutoff = nil) ⇒ Hash{ Object => Array<Object> }, Array<Object>
Computes shortest paths and path distances to all nodes/target from the given node.
-
.singlesource_dijkstra_path(graph, source, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Computes shortest paths to all nodes from the given node.
-
.singlesource_dijkstra_path_length(graph, source, cutoff = nil) ⇒ Hash{ Object => Numeric }
Computes shortest path lengths to all nodes from the given node.
-
.strong_product(g1, g2) ⇒ Graph, ...
Returns the strong product of two graphs.
-
.symmetric_difference(g1, g2) ⇒ Graph, ...
Performs the symmetric difference of two graphs.
-
.tensor_product(g1, g2) ⇒ Graph, ...
Returns the tensor product of two graphs.
- .to_matrix(graph, val, multigraph_weight = 'sum') ⇒ Object
- .to_number_if_possible(str) ⇒ Object
-
.topological_sort(graph) ⇒ Array<Object>
Returns the nodes arranged in the topologically sorted fashion.
-
.undirected_edges_cross_edges(g1, g2) ⇒ Object
Returns the product of undirected edges with edges.
-
.union(g1, g2) ⇒ Graph, ...
Performs the union of two graphs.
-
.union_all(graphs) ⇒ Graph, ...
Performs the union of many graphs.
-
.wiener_index(graph) ⇒ Numeric
Returns the wiener index of the graph.
Instance Method Summary collapse
-
#augment(path, inf, r_adj) ⇒ Object
Helper function for augmenting flow.
Class Method Details
._build_flow_dict(graph, residual) ⇒ Object
Returns the flowdict of the graph
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/networkx/flow/capacityscaling.rb', line 88 def self._build_flow_dict(graph, residual) flow_dict = {} inf = Float::INFINITY if graph.multigraph? graph.nodes(data: true).each_key do |u| flow_dict[u] = {} graph.adj[u].each do |v, uv_edges| flow_dict[u][v] = uv_edges.transform_values do |e| u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity] end end residual.adj[u].each do |v, uv_edges| flow_dict[u][v].merge!(uv_edges.to_h do |_, val| [val[:temp_key][0], val[:flow]] if (val[:flow]).positive? end) end end else graph.nodes(data: true).each_key do |u| flow_dict[u] = graph.adj[u].to_h do |v, e| [v, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]] end merge_dict = {} residual.adj[u].each do |v, uv_edges| uv_edges.each_value { |attrs| merge_dict[v] = attrs[:flow] if (attrs[:flow]).positive? } end flow_dict[u].merge!(merge_dict) end end flow_dict end |
._build_residual_network(graph) ⇒ Object
Returns the residual graph of the given graph
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/networkx/flow/capacityscaling.rb', line 42 def self._build_residual_network(graph) raise ArgumentError, 'Sum of demands should be 0!' unless \ graph.nodes(data: true).values.map { |attr| attr[:demand] || 0 }.inject(0, :+).zero? residual = NetworkX::MultiDiGraph.new(inf: 0) residual.add_nodes(graph.nodes(data: true).map { |u, attr| [u, {excess: (attr[:demand] || 0) * -1, potential: 0}] }) inf = Float::INFINITY edge_list = [] # TODO: Selfloop edges check if graph.multigraph? graph.adj.each do |u, u_edges| u_edges.each do |v, uv_edges| uv_edges.each do |k, attrs| edge_list << [u, v, k, e] if u != v && (attrs[:capacity] || inf).positive? end end end else graph.adj.each do |u, u_edges| u_edges.each do |v, attrs| edge_list << [u, v, 0, attrs] if u != v && (attrs[:capacity] || inf).positive? end end end temp_inf = [residual.nodes(data: true).map do |_u, attrs| attrs[:excess].abs end.inject(0, :+), edge_list.map do |_, _, _, e| (e.has_key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0) end.inject(0, :+) * 2].max inf = temp_inf.zero? ? 1 : temp_inf edge_list.each do |u, v, k, e| r = [e[:capacity] || inf, inf].min w = e[:weight] || 0 residual.add_edge(u, v, temp_key: [k, true], capacity: r, weight: w, flow: 0) residual.add_edge(v, u, temp_key: [k, false], capacity: 0, weight: -w, flow: 0) end residual.graph[:inf] = inf _detect_unboundedness(residual) residual end |
._detect_unboundedness(residual) ⇒ Object
Detects the unboundedness in the residual graph
26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/networkx/flow/capacityscaling.rb', line 26 def self._detect_unboundedness(residual) g = NetworkX::DiGraph.new g.add_nodes(residual.nodes(data: true).keys.zip(residual.nodes(data: true).values)) inf = residual.graph[:inf] residual.nodes(data: true).each do |u, _attr| residual.adj[u].each do |v, uv_attrs| w = inf uv_attrs.each { |_key, edge_attrs| w = [w, edge_attrs[:weight]].min if edge_attrs[:capacity] == inf } g.add_edge(u, v, weight: w) unless w == inf end end raise ArgumentError, 'Negative cost cycle of infinite capacity found!' if negative_edge_cycle(g) end |
.activate(node, source, target, levels, residual_nodes) ⇒ Object
Helper function to move a node from inactive set to active set
119 120 121 122 123 124 125 126 |
# File 'lib/networkx/flow/preflowpush.rb', line 119 def self.activate(node, source, target, levels, residual_nodes) return if node == source || node == target return unless level.inactive.include?(node) level = levels[residual_nodes[node][:height]] level.inactive.delete(node) level.active.add(node) end |
.all_pairs_dijkstra(graph, cutoff = nil) ⇒ Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>
Finds shortest weighted paths and lengths between all nodes
189 190 191 192 193 |
# File 'lib/networkx/shortest_path/weighted.rb', line 189 def self.all_pairs_dijkstra(graph, cutoff = nil) path = [] graph.nodes(data: true).each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] } path end |
.all_pairs_dijkstra_path(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Array<Object> }>
Finds shortest weighted paths between all nodes
213 214 215 216 217 |
# File 'lib/networkx/shortest_path/weighted.rb', line 213 def self.all_pairs_dijkstra_path(graph, cutoff = nil) paths = [] graph.nodes(data: true).each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) } paths end |
.all_pairs_dijkstra_path_length(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Numeric }>
Finds shortest weighted path length between all nodes
201 202 203 204 205 |
# File 'lib/networkx/shortest_path/weighted.rb', line 201 def self.all_pairs_dijkstra_path_length(graph, cutoff = nil) path_lengths = [] graph.nodes(data: true).each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] } path_lengths end |
.all_pairs_shortest_path(graph, cutoff = nil) ⇒ Array<Object, Hash {Object => Array<Object> }>
Computes shortest paths to all nodes from all nodes
94 95 96 97 98 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 94 def self.all_pairs_shortest_path(graph, cutoff = nil) shortest_paths = [] graph.nodes(data: true).each_key { |n| shortest_paths << [n, single_source_shortest_path(graph, n, cutoff)] } shortest_paths end |
.all_pairs_shortest_path_length(graph, cutoff = nil) ⇒ Array<Object, Array<Object, Numeric>>
Computes shortest path values to all nodes from all nodes
46 47 48 49 50 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 46 def self.all_pairs_shortest_path_length(graph, cutoff = nil) shortest_paths = [] graph.nodes(data: true).each_key { |n| shortest_paths << [n, single_source_shortest_path_length(graph, n, cutoff)] } shortest_paths end |
.allpairs_bellmanford_path(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Array<Object> }>
Shortest paths between all nodes using Bellman Ford algorithm
373 374 375 376 377 |
# File 'lib/networkx/shortest_path/weighted.rb', line 373 def self.allpairs_bellmanford_path(graph, cutoff = nil) paths = [] graph.nodes(data: true).each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] } paths end |
.allpairs_bellmanford_path_length(graph, cutoff = nil) ⇒ Array<Object, Hash{ Object => Numeric }>
Shortest path lengths between all nodes using Bellman Ford algorithm
361 362 363 364 365 |
# File 'lib/networkx/shortest_path/weighted.rb', line 361 def self.allpairs_bellmanford_path_length(graph, cutoff = nil) path_lengths = [] graph.nodes(data: true).each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] } path_lengths end |
.ancestors(graph, source) ⇒ Array<Object>
Returns the ancestors of a given node
21 22 23 24 25 26 |
# File 'lib/networkx/auxillary_functions/dag.rb', line 21 def self.ancestors(graph, source) raise ArgumentError, 'Source is not present in the graph!' unless graph.node?(source) anc = single_source_shortest_path_length(graph.reverse, source).map { |u, _| u }.uniq anc - [source] end |
.arbitrary_element(iterable) ⇒ Object
Helper function to return an arbitrary element from an iterable object
3 4 5 6 7 8 9 10 11 |
# File 'lib/networkx/flow/preflowpush.rb', line 3 def self.arbitrary_element(iterable) if iterable.is_a?(Hash) iterable.first[0] elsif iterable.respond_to?(:first) iterable.first elsif iterable.respond_to?(:[]) iterable[0] end end |
.astar_path(graph, source, target, heuristic = nil) ⇒ Array<Object>
Returns path using astar algorithm between source and target
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/networkx/shortest_path/astar.rb', line 11 def self.astar_path(graph, source, target, heuristic = nil) warn 'A* is not implemented for MultiGraph and MultiDiGraph' if graph.is_a?(MultiGraph) || graph.is_a?(MultiDiGraph) raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target) count = ->(i) { i + 1 } i = -1 heuristic ||= (->(_u, _v) { 0 }) heap = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) } heap << [0, count.call(i), source, 0, nil] enqueued, explored = {}, {} until heap.empty? _, _, curnode, dist, parent = heap.pop if curnode == target path = [curnode] node = parent until node.nil? path << node node = explored[node] end path.reverse return path end next if explored.has_key?(curnode) explored[curnode] = parent graph.adj[curnode].each do |u, attrs| next if explored.has_key?(u) ncost = dist + (attrs[:weight] || 1) if enqueued.has_key?(u) qcost, = enqueued[u] next if qcost <= ncost else h = heuristic.call(u, target) enqueued[u] = ncost, h heap << [ncost + h, count.call(i), u, ncost, curnode] end end end raise ArgumentError, 'Target not reachable!' end |
.astar_path_length(graph, source, target, heuristic = nil) ⇒ Numeric
Returns astar path length b/w source and target
65 66 67 68 69 70 71 72 |
# File 'lib/networkx/shortest_path/astar.rb', line 65 def self.astar_path_length(graph, source, target, heuristic = nil) raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target) path = astar_path(graph, source, target, heuristic) path_length = 0 (1..(path.length - 1)).each { |i| path_length += (graph.adj[path[i - 1]][path[i]][:weight] || 1) } path_length end |
.augment(residual, inf, path) ⇒ Object
Helper function to augment the flow in a residual graph
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/networkx/flow/edmondskarp.rb', line 3 def self.augment(residual, inf, path) flow = inf path_first_elem = path.shift u = path_first_elem path.each do |v| flow = [flow, residual.adj[u][v][:capacity] - residual.adj[u][v][:flow]].min u = v end raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf u = path_first_elem path.each do |v| residual.adj[u][v][:flow] += flow residual.adj[v][u][:flow] -= flow u = v end flow end |
.authority_matrix(graph) ⇒ Matrix
Computes authority matrix for the graph
45 46 47 48 |
# File 'lib/networkx/link_analysis/hits.rb', line 45 def self.(graph) matrix, = to_matrix(graph, 0) matrix.transpose * matrix end |
.bellmanford_path(graph, source, target) ⇒ Array<Object>
Shortest path from source to target using Bellman Ford algorithm
326 327 328 329 |
# File 'lib/networkx/shortest_path/weighted.rb', line 326 def self.bellmanford_path(graph, source, target) _, path = singlesource_bellmanford(graph, source, target) path end |
.bellmanford_path_length(graph, source, target) ⇒ Numeric
Length of shortest path from source to target using Bellman Ford algorithm
309 310 311 312 313 314 315 316 317 |
# File 'lib/networkx/shortest_path/weighted.rb', line 309 def self.bellmanford_path_length(graph, source, target) return 0 if source == target weight = get_weight(graph) length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target) raise ArgumentError, 'Node not reachable!' unless length.has_key?(target) length[target] end |
.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil) ⇒ Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>
Finds shortest weighted path lengths and predecessors on shortest paths
277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/networkx/shortest_path/weighted.rb', line 277 def self.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil) raise ArgumentError, 'Node not found!' unless graph.node?(source) weight = get_weight(graph) # TODO: Detection of selfloop edges dist = {source => 0} pred = {source => []} return [pred, dist] if graph.nodes(data: true).length == 1 dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target) [pred, dist] end |
.bfs_edges(graph, source) ⇒ Object
Returns edges of the graph travelled in breadth first fashion
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/networkx/traversals/bfs.rb', line 9 def self.bfs_edges(graph, source) raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source) bfs_edges = [] visited = [source] queue = Queue.new.push([source, graph.neighbours(source)]) until queue.empty? parent, children = queue.pop children.each_key do |child| next if visited.include?(child) bfs_edges << [parent, child] visited << child queue.push([child, graph.neighbours(child)]) end end bfs_edges end |
.bfs_predecessors(graph, source) ⇒ Object
Returns predecessor child pair of the graph travelled in breadth first fashion
52 53 54 55 56 57 |
# File 'lib/networkx/traversals/bfs.rb', line 52 def self.bfs_predecessors(graph, source) bfs_edges = bfs_edges(graph, source) predecessors = {} bfs_edges.each { |u, v| predecessors[v] = u } predecessors end |
.bfs_successors(graph, source) ⇒ Object
Returns parent successor pair of the graph travelled in breadth first fashion
35 36 37 38 39 40 41 42 43 |
# File 'lib/networkx/traversals/bfs.rb', line 35 def self.bfs_successors(graph, source) bfs_edges = bfs_edges(graph, source) successors = {} bfs_edges.each do |u, v| successors[u] = [] if successors[u].nil? successors[u] << v end successors end |
.bidirectional_bfs(residual, source, target) ⇒ Object
Helper function for the bidirectional bfs
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/networkx/flow/edmondskarp.rb', line 23 def self.bidirectional_bfs(residual, source, target) pred, succ = {source => nil}, {target => nil} q_s, q_t = [source], [target] loop do q = [] if q_s.length <= q_t.length q_s.each do |u| residual.adj[u].each do |v, uv_attrs| next unless !pred.include?(v) && (uv_attrs[:flow] < uv_attrs[:capacity]) pred[v] = u return [v, pred, succ] if succ.has_key?(v) q << v end end return [nil, nil, nil] if q.empty? q_s = q else q_t.each do |u| residual.pred[u].each do |v, uv_attrs| next unless !succ.has_key?(v) && uv_attrs[:flow] < uv_attrs[:capacity] succ[v] = u return [v, pred, succ] if pred.has_key?(v) q << v end end return [nil, nil, nil] if q.empty? q_t = q end end end |
.bridges(graph) ⇒ [Object, Object]
Returns bridges.
7 8 9 |
# File 'lib/networkx/others/bridges.rb', line 7 def self.bridges(graph) each_bridge(graph).to_a end |
.build_flow_dict(graph, residual) ⇒ Hash{ Object => Hash{ Object => Numeric }] flowdict containing all the flow values in the edges
Build flow dictionary of a graph from its residual graph
130 131 132 133 134 135 136 137 138 |
# File 'lib/networkx/flow/utils.rb', line 130 def self.build_flow_dict(graph, residual) flow_dict = {} graph.edges.each do |u, u_edges| flow_dict[u] = {} u_edges.each_key { |v| flow_dict[u][v] = 0 } u_edges.each_key { |v| flow_dict[u][v] = residual[u][v][:flow] if (residual[u][v][:flow]).positive? } end flow_dict end |
.build_residual_network(graph) ⇒ DiGraph
Builds a residual graph from a constituent graph
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/networkx/flow/utils.rb', line 61 def self.build_residual_network(graph) raise NotImplementedError, 'MultiGraph and MultiDiGraph not supported!' if graph.multigraph? r_network = NetworkX::DiGraph.new(inf: 0, flow_value: 0) r_network.add_nodes(graph.nodes(data: true).keys) inf = Float::INFINITY edge_list = [] graph.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| edge_list << [u, v, uv_attrs] if (uv_attrs[:capacity] || inf).positive? && u != v end end inf_chk = 3 * edge_list.inject(0) do |result, arr| arr[2].has_key?(:capacity) && arr[2][:capacity] != inf ? (result + arr[2][:capacity]) : result end inf = inf_chk.zero? ? 1 : inf_chk if graph.directed? edge_list.each do |u, v, attrs| r = [attrs[:capacity] || inf, inf].min if r_network.adj[u][v].nil? r_network.add_edge(u, v, capacity: r) r_network.add_edge(v, u, capacity: 0) else r_network[u][v][:capacity] = r end end else edge_list.each do |u, v, attrs| r = [attrs[:capacity] || inf, inf].min r_network.add_edge(u, v, capacity: r) r_network.add_edge(v, u, capacity: r) end end r_network.graph[:inf] = inf r_network end |
.capacity_scaling(graph) ⇒ Array<Numeric, Hash{ Object => Hash{ Object => Numeric } }>
Computes max flow using capacity scaling algorithm
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/networkx/flow/capacityscaling.rb', line 127 def self.capacity_scaling(graph) residual = _build_residual_network(graph) inf = Float::INFINITY flow_cost = 0 # TODO: Account cost of self-loof edges wmax = ([-inf] + residual.adj.each_with_object([]) do |u, arr| u[1].each { |_, key_attrs| key_attrs.each { |_, attrs| arr << attrs[:capacity] } } end).max return flow_cost, _build_flow_dict(graph, residual) if wmax == -inf r_nodes = residual.nodes r_adj = residual.adj delta = 2**Math.log2(wmax).floor while delta >= 1 r_nodes.each do |u, u_attrs| p_u = u_attrs[:potential] r_adj[u].each do |v, uv_edges| uv_edges.each do |_k, e| flow = e[:capacity] next unless (e[:weight] - p_u + r_nodes[v][:potential]).negative? flow = e[:capacity] - e[:flow] next unless flow >= delta e[:flow] += flow r_adj[v][u].each_key do |val| val[:flow] += val[:temp_key][0] == e[:temp_key][0] && val[:temp_key][1] != e[:temp_key][1] ? -flow : 0 end r_nodes[u][:excess] -= flow r_nodes[v][:excess] += flow end end end s_set = Set.new t_set = Set.new residual.nodes(data: true).each do |u, _attrs| excess = r_nodes[u][:excess] if excess >= delta s_set.add(u) elsif excess <= -delta t_set.add(u) end end while !s_set.empty? && !t_set.empty? s = arbitrary_element t = nil d = {} pred = {s => nil} h = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) } h_dict = {s => 0} h << [0, count, s] until h.empty? d_u, _, u = h.pop h_dict.delete(u) d[u] = d_u if t_set.include?(u) t = u break end p_u = r_nodes[u][:potential] r_adj[u].each do |v, uv_edges| next if d.has_key?(v) wmin = inf uv_edges.each_value do |e| next unless e[:capacity] - e[:flow] >= delta w = e[:weight] next unless w < wmin wmin = w end next if wmin == inf d_v = d_u + wmin - p_u + r_nodes[v][:potential] next unless h_dict[v] > d_v h << [d_v, count, v] h_dict[v] = d_v pred[v] = [u, kmin, emin] end end if t.nil? s_set.delete(s) else while u != s v = u u, k, e = pred[v] e[:flow] += delta r_adj[v][u].each_key do |val| val[:flow] += val[:temp_key][0] == k[0] && val[:temp_key][1] != k[1] ? -delta : 0 end end r_nodes[s][:excess] -= delta r_nodes[t][:excess] += delta s_set.delete(s) if r_nodes[s][:excess] < delta t_set.delete(t) if r_nodes[t][:excess] > -delta d_t = d[t] d.each { |node, d_u_node| r_nodes[node][:potential] -= (d_u_node - d_t) } end end delta = (delta / 2).floor end r_nodes.each_value { |attrs| raise ArgumentError, 'No flow satisfying all demands!' if attrs[:excess] != 0 } residual.nodes(data: true).each_key do |node| residual.adj[node].each_value do |uv_edges| uv_edges.each_value do |k_attrs| flow = k_attrs[:flow] flow_cost += (flow * k_attrs[:weight]) end end end [flow_cost, _build_flow_dict(graph, residual)] end |
.cartesian_product(g1, g2) ⇒ Graph, ...
Returns the cartesian product of two graphs
129 130 131 132 133 134 135 |
# File 'lib/networkx/operators/product.rb', line 129 def self.cartesian_product(g1, g2) g = init_product_graph(g1, g2) g.add_nodes(node_product(g1, g2)) g.add_edges(edges_cross_nodes(g1, g2)) g.add_edges(nodes_cross_edges(g1, g2)) g end |
.closeness_vitality(graph, node) ⇒ Numeric
Returns the closeness vitality of a node
8 9 10 11 12 |
# File 'lib/networkx/auxillary_functions/vitality.rb', line 8 def self.closeness_vitality(graph, node) before = wiener_index(graph) after = wiener_index(graph.subgraph(graph.nodes(data: true).keys - [node])) before - after end |
.complement(graph) ⇒ Graph, ...
Performs the complement operation on the graph
7 8 9 10 11 12 13 14 15 16 |
# File 'lib/networkx/operators/unary.rb', line 7 def self.complement(graph) result = Marshal.load(Marshal.dump(graph)) result.clear result.add_nodes(graph.nodes(data: true).map { |u, attrs| [u, attrs] }) graph.adj.each do |u, u_edges| graph.nodes(data: true).each { |v, attrs| result.add_edge(u, v, **attrs) if !u_edges.has_key?(v) && u != v } end result end |
.compose(g1, g2) ⇒ Graph, ...
Performs the composition of two graphs
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/networkx/operators/binary.rb', line 173 def self.compose(g1, g2) result = g1.class.new raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph? result.add_nodes(g1.nodes(data: true).map { |u, attrs| [u, attrs] }) result.add_nodes(g2.nodes(data: true).map { |u, attrs| [u, attrs] }) if g1.multigraph? g1.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, **attrs) } } } g2.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, **attrs) } } } else g1.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, **attrs) } } g2.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, **attrs) } } end result end |
.compose_all(graphs) ⇒ Graph, ...
Performs the composition of many graphs
55 56 57 58 59 60 61 62 63 64 |
# File 'lib/networkx/operators/all.rb', line 55 def self.compose_all(graphs) raise ArgumentError, 'Argument array is empty' if graphs.empty? result = graphs.shift graphs.each do |graph| result = NetworkX.compose(result, graph) end result end |
.convert_to_distinct_labels(graph, starting_int = -1)) ⇒ Object
Transforms the labels of the nodes of the graphs so that they are disjoint.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/networkx/operators/binary.rb', line 27 def self.convert_to_distinct_labels(graph, starting_int = -1) new_graph = graph.class.new idx_dict = graph.nodes(data: true).keys.to_h do |v| starting_int += 1 [v, starting_int] end graph.nodes(data: true).each do |u, attrs| new_graph.add_node(u.to_s + idx_dict[u].to_s, **attrs) end graph.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| if graph.multigraph? uv_attrs.each do |_k, attrs| new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **attrs) end else new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **uv_attrs) end end end new_graph end |
.cycle?(undirected_graph) ⇒ book
Returns whether the given undirected cycle has cycle.
107 108 109 110 111 112 113 |
# File 'lib/networkx/auxillary_functions/cycles.rb', line 107 def self.cycle?(undirected_graph) uf = NetworkX::UnionFind.new undirected_graph.edges.each do |x, y| uf[x] == uf[y] ? (return [x, y]) : uf.unite(x, y) end false end |
.cycle_basis(graph, root = nil) ⇒ Array<Array<Object>>
Returns all basis cycles in graph
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/networkx/auxillary_functions/cycles.rb', line 8 def self.cycle_basis(graph, root = nil) gnodes = graph.nodes(data: true).keys cycles = [] until gnodes.empty? root = gnodes.shift if root.nil? stack = [root] pred = {root => root} used = {root => []} until stack.empty? z = stack.shift zused = used[z] graph.adj[z].each_key do |u| if !used.has_key?(u) pred[u] = z stack << u used[u] = [z] elsif u == z cycles << [z] elsif !zused.include?(u) pn = used[u] cycle = [u, z] p = pred[z] until pn.include?(p) cycle << p p = pred[p] end cycle << p cycles << cycle used[u] << z used[u] = used[u].uniq end end end gnodes -= pred.keys root = nil end cycles end |
.descendants(graph, source) ⇒ Array<Object>
Returns the descendants of a given node
8 9 10 11 12 13 |
# File 'lib/networkx/auxillary_functions/dag.rb', line 8 def self.descendants(graph, source) raise ArgumentError, 'Source is not present in the graph!' unless graph.node?(source) des = single_source_shortest_path_length(graph, source).map { |u, _| u }.uniq des - [source] end |
.detect_unboundedness(r_network, source, target) ⇒ Object
Detects unboundedness in a graph, raises exception when infinite capacity flow is found
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/networkx/flow/utils.rb', line 107 def self.detect_unboundedness(r_network, source, target) q = [source] seen = Set.new([source]) inf = r_network.graph[:inf] until q.empty? u = q.shift r_network.adj[u].each do |v, uv_attrs| next unless uv_attrs[:capacity] == inf && !seen.include?(v) raise ArgumentError, 'Infinite capacity flow!' if v == target seen << v q << v end end end |
.dfs_edges(graph, source, depth_limit = nil) ⇒ Object
Returns edges of the graph travelled in depth first fashion
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/networkx/traversals/dfs.rb', line 10 def self.dfs_edges(graph, source, depth_limit = nil) raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source) depth_limit += 1 if depth_limit depth_limit = graph.nodes(data: true).length if depth_limit.nil? dfs_edges = [] visited = [source] stack = [[-1, source, depth_limit, graph.neighbours(source)]] until stack.empty? earlier_node, parent, depth_now, children = stack.pop dfs_edges << [earlier_node, parent] children.each_key do |child| unless visited.include?(child) visited << child stack.push([parent, child, depth_now - 1, graph.neighbours(child)]) if depth_now > 1 end end end dfs_edges.shift dfs_edges end |
.dfs_predecessors(graph, source, depth_limit = nil) ⇒ Object
Returns predecessor child pair of the graph travelled in depth first fashion
73 74 75 76 77 78 |
# File 'lib/networkx/traversals/dfs.rb', line 73 def self.dfs_predecessors(graph, source, depth_limit = nil) dfs_edges = dfs_edges(graph, source, depth_limit) predecessors = {} dfs_edges.each { |u, v| predecessors[v] = u } predecessors end |
.dfs_successors(graph, source, depth_limit = nil) ⇒ Object
Returns parent successor pair of the graph travelled in depth first fashion
55 56 57 58 59 60 61 62 63 |
# File 'lib/networkx/traversals/dfs.rb', line 55 def self.dfs_successors(graph, source, depth_limit = nil) dfs_edges = dfs_edges(graph, source, depth_limit) successors = {} dfs_edges.each do |u, v| successors[u] = [] if successors[u].nil? successors[u] << v end successors end |
.dfs_tree(graph, source, depth_limit = nil) ⇒ Object
Returns dfs tree of the graph
40 41 42 43 44 45 |
# File 'lib/networkx/traversals/dfs.rb', line 40 def self.dfs_tree(graph, source, depth_limit = nil) t = NetworkX::DiGraph.new t.add_node(source) t.add_edges_from(dfs_edges(graph, source, depth_limit)) t end |
.diameter(graph) ⇒ Numeric
Returns the diameter of a graph
25 26 27 |
# File 'lib/networkx/auxillary_functions/eccentricity.rb', line 25 def self.diameter(graph) eccentricity(graph).values.max end |
.difference(g1, g2) ⇒ Graph, ...
Performs the difference of two graphs
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/networkx/operators/binary.rb', line 93 def self.difference(g1, g2) result = g1.class.new raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph? unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty? raise ArgumentError, 'Node sets must be equal!' end g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) } g1.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| if g1.multigraph? next if u > v && g1.instance_of?(MultiGraph) uv_attrs.each do |k, attrs| result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k) end else result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v) end end end result end |
.dijkstra_path(graph, source, target) ⇒ Numeric
Computes shortest path to target from the given node
163 164 165 166 |
# File 'lib/networkx/shortest_path/weighted.rb', line 163 def self.dijkstra_path(graph, source, target) _, path = singlesource_dijkstra(graph, source, target) path end |
.dijkstra_path_length(graph, source, target) ⇒ Numeric
Computes shortest path length to target from the given node
146 147 148 149 150 151 152 153 154 |
# File 'lib/networkx/shortest_path/weighted.rb', line 146 def self.dijkstra_path_length(graph, source, target) return 0 if source == target weight = get_weight(graph) length = help_dijkstra(graph, source, weight, nil, nil, nil, target) raise KeyError, 'Node not reachable!' unless length.has_key?(target) length[target] end |
.dijkstra_predecessor_distance(graph, source, cutoff = nil) ⇒ <Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predcessor hash and distance hash
Computes weighted shortest path length and predecessors
176 177 178 179 180 |
# File 'lib/networkx/shortest_path/weighted.rb', line 176 def self.dijkstra_predecessor_distance(graph, source, cutoff = nil) weight = get_weight(graph) pred = {source => []} [pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)] end |
.directed_edges_cross_edges(g1, g2) ⇒ Object
Returns the product of directed edges with edges
40 41 42 43 44 45 46 47 48 |
# File 'lib/networkx/operators/product.rb', line 40 def self.directed_edges_cross_edges(g1, g2) result = [] edges_in_array(g1).each do |u, v, c| edges_in_array(g2).each do |x, y, d| result << [[u, x], [v, y], hash_product(c, d)] end end result end |
.discharge(u_node, is_phase1, residual_nodes, residual_adj, height, levels, grt, source, target) ⇒ Object
Helper function for discharging a node
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/networkx/flow/preflowpush.rb', line 135 def self.discharge(u_node, is_phase1, residual_nodes, residual_adj, height, levels, grt, source, target) height_val = residual_nodes[u_node][:height] curr_edge = residual_nodes[u_node][:curr_edge] next_height = height_val levels[height_val].active.delete(u_node) loop do v, attr = curr_edge.get if height_val == residual_nodes[v][:height] + 1 && attr[:flow] < attr[:capacity] flow = [residual_nodes[u_node][:excess], attr[:capacity] - attr[:flow]].min push(u_node, v, flow, residual_nodes, residual_adj) activate(v, source, target, levels, residual_nodes) if residual_nodes[u_node][:excess].zero? levels[height_val].inactive.add(u_node) break end end begin curr_edge.move_to_next rescue StopIteration height_val = relabel(u_node, grt, residual_adj, residual_nodes, source, target, levels) if is_phase1 && height_val >= n - 1 levels[height].active.add(u_node) break end next_height = height_val end end residual_nodes[u_node][:height] = height_val next_height end |
.disjoint_union(g1, g2) ⇒ Graph, ...
Performs the disjoint union of two graphs
225 226 227 228 229 230 231 232 |
# File 'lib/networkx/operators/binary.rb', line 225 def self.disjoint_union(g1, g2) new_g1 = convert_to_distinct_labels(g1) new_g2 = convert_to_distinct_labels(g2) result = union(new_g1, new_g2) result.graph.merge!(g1.graph) result.graph.merge!(g2.graph) result end |
.disjoint_union_all(graphs) ⇒ Graph, ...
Performs the disjoint union of many graphs
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/networkx/operators/all.rb', line 23 def self.disjoint_union_all(graphs) raise ArgumentError, 'Argument array is empty' if graphs.empty? result = graphs.shift graphs.each do |graph| result = NetworkX.disjoint_union(result, graph) end result end |
.dist_path_lambda(_graph, _new_weight) ⇒ Object
Helper function to get distances
385 386 387 388 389 390 391 |
# File 'lib/networkx/shortest_path/weighted.rb', line 385 def self.dist_path_lambda(_graph, _new_weight) lambda do |graph, v, new_weight| paths = {v => [v]} _ = help_dijkstra(graph, v, new_weight, nil, paths) paths end end |
.each_bridge(graph) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/networkx/others/bridges.rb', line 12 def self.each_bridge(graph) return enum_for(:each_bridge, graph) unless block_given? graph.each_edge.with_index do |(s_i, t_i), i| uf = UnionFind.new(1..graph.number_of_nodes) graph.each_edge.with_index do |(s_j, t_j), j| uf.unite(s_j, t_j) if i != j end yield [s_i, t_i] unless uf.same?(s_i, t_i) end end |
.eccentricity(graph, node = nil) ⇒ Array<Numeric>, Numeric
Returns the eccentricity of a particular node or all nodes
8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/networkx/auxillary_functions/eccentricity.rb', line 8 def self.eccentricity(graph, node = nil) e = {} graph.nodes(data: true).each do |u, _| length = single_source_shortest_path_length(graph, u) l = length.length raise ArgumentError, 'Found infinite path length!' unless l == graph.nodes(data: true).length e[u] = length.max_by { |a| a[1] }[1] end node.nil? ? e : e[node] end |
.edge_dfs(graph, start, orientation = nil) ⇒ Object
Performs edge dfs on the graph Orientation :ignore, directed edges can be travelled in both fashions Orientation reverse, directed edges can be travelled in reverse fashion Orientation :nil, the graph is not meddled with
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/networkx/traversals/edge_dfs.rb', line 51 def self.edge_dfs(graph, start, orientation = nil) case orientation when :reverse graph = graph.reverse if graph.instance_of?(::NetworkX::DiGraph) || graph.instance_of?(::NetworkX::MultiDiGraph) when :ignore graph = graph.to_undirected if graph.instance_of?(::NetworkX::DiGraph) graph = graph.to_multigraph if graph.instance_of?(::NetworkX::MultiDiGraph) end visited_edges = [] visited_nodes = [] stack = [start] current_edges = {} e = Enumerator.new do |yield_var| until stack.empty? current = stack.last unless visited_nodes.include?(current) current_edges[current] = out_edges(graph, current) visited_nodes << current end edge = current_edges[current].shift if edge.nil? stack.pop else unless visited_edges.include?(edge_id(graph, edge)) visited_edges << edge_id(graph, edge) stack << edge[1] yield_var.yield edge end end end end e.take(graph.number_of_edges) end |
.edge_id(graph, edge) ⇒ Object
Helper function of edge_dfs
31 32 33 34 35 36 |
# File 'lib/networkx/traversals/edge_dfs.rb', line 31 def self.edge_id(graph, edge) return edge if graph.directed? return Set.new([edge, (edge[0..1].reverse << edge[2])]) if graph.multigraph? Set.new([edge, edge.reverse]) end |
.edges_cross_nodes(g1, g2) ⇒ Object
Returns the product of edges with edges
62 63 64 65 66 67 68 69 70 |
# File 'lib/networkx/operators/product.rb', line 62 def self.edges_cross_nodes(g1, g2) result = [] edges_in_array(g1).each do |u, v, d| g2.nodes(data: true).each_key do |x| result << [[u, x], [v, x], d] end end result end |
.edges_cross_nodes_and_nodes(g1, g2) ⇒ Object
Returns the product of edges with pairs of nodes
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/networkx/operators/product.rb', line 84 def self.edges_cross_nodes_and_nodes(g1, g2) result = [] edges_in_array(g1).each do |u, v, d| g2.nodes(data: true).each_key do |x| g2.nodes(data: true).each_key do |y| result << [[u, x], [v, y], d] end end end result end |
.edges_in_array(graph) ⇒ Object
Returns the edges of the graph in an array
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/networkx/operators/product.rb', line 3 def self.edges_in_array(graph) edge_array = [] if graph.multigraph? graph.adj.each do |u, u_edges| u_edges.each do |v, uv_edges| uv_edges.each do |_k, attrs| edge_array << [u, v, attrs] end end end else graph.adj.each do |u, u_edges| u_edges.each do |v, attrs| edge_array << [u, v, attrs] end end end edge_array end |
.edmondskarp(graph, source, target, residual = nil, cutoff = nil) ⇒ DiGraph
Computes max flow using edmonds karp algorithm
112 113 114 |
# File 'lib/networkx/flow/edmondskarp.rb', line 112 def self.edmondskarp(graph, source, target, residual = nil, cutoff = nil) edmondskarp_impl(graph, source, target, residual, cutoff) end |
.edmondskarp_core(residual, source, target, cutoff) ⇒ Object
Core helper function for the EdmondsKarp algorithm
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/networkx/flow/edmondskarp.rb', line 61 def self.edmondskarp_core(residual, source, target, cutoff) inf = residual.graph[:inf] flow_val = 0 while flow_val < cutoff v, pred, succ = bidirectional_bfs(residual, source, target) break if pred.nil? path = [v] u = v while u != source u = pred[u] path << u end path.reverse! u = v while u != target u = succ[u] path << u end flow_val += augment(residual, inf, path) end flow_val end |
.edmondskarp_impl(graph, source, target, residual, cutoff) ⇒ Object
Helper function for the edmondskarp function
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/networkx/flow/edmondskarp.rb', line 86 def self.edmondskarp_impl(graph, source, target, residual, cutoff) raise ArgumentError, 'Source not in graph!' unless graph.nodes(data: true).has_key?(source) raise ArgumentError, 'Target not in graph!' unless graph.nodes(data: true).has_key?(target) raise ArgumentError, 'Source and target are same node!' if source == target res_graph = residual.nil? ? build_residual_network(graph) : residual.clone res_graph.adj.each do |u, u_edges| u_edges.each do |v, _attrs| res_graph.adj[u][v][:flow] = 0 res_graph.pred[v][u][:flow] = 0 end end cutoff = Float::INFINITY if cutoff.nil? res_graph.graph[:flow_value] = edmondskarp_core(res_graph, source, target, cutoff) res_graph end |
.find_cliques(graph) ⇒ Array<Array<Object>>
Returns all cliques in the graph
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/networkx/auxillary_functions/cliques.rb', line 7 def self.find_cliques(graph) return nil if graph.nodes(data: true).empty? q = [nil] adj = {} graph.nodes(data: true).each_key { |u| adj[u] = [] } graph.adj.each { |u, u_edges| u_edges.each_key { |v| adj[u] << v if u != v } } subg = graph.nodes(data: true).keys cand = graph.nodes(data: true).keys u = subg.max { |n1, n2| (cand & adj[n1]).length <=> (cand & adj[n2]).length } ext_u = cand - adj[u] stack = [] cliques = [] begin loop do if ext_u.empty? q.pop subg, cand, ext_u = stack.pop else q_elem = ext_u.pop cand.delete(q_elem) q[-1] = q_elem adj_q = adj[q_elem] subg_q = subg & adj_q if subg_q.empty? cliques << q[0..(q.length - 1)] else cand_q = cand & adj_q unless cand_q.empty? stack << [subg, cand, ext_u] q << nil subg = subg_q cand = cand_q u = subg.max { |n1, n2| (cand & adj[n1]).length <=> (cand & adj[n2]).length } ext_u = cand - adj[u] end end end end rescue NoMethodError cliques end end |
.find_cycle(graph, node) ⇒ Array<Array<Object>>
Returns the cycle containing the given node
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/networkx/auxillary_functions/cycles.rb', line 53 def self.find_cycle(graph, node) explored = Set.new cycle = [] final_node = nil unless explored.include?(node) edges = [] seen = [node] active_nodes = [node] previous_head = nil edge_dfs(graph, node).each do |edge| tail, head = edge next if explored.include?(head) if !previous_head.nil? && tail != previous_head loop do popped_edge = edges.pop if popped_edge.nil? edges = [] active_nodes = [tail] break else popped_head = popped_edge[1] active_nodes.delete(popped_head) end unless edges.empty? last_head = edges[-1][1] break if tail == last_head end end end edges << edge if active_nodes.include?(head) cycle += edges final_node = head break else seen << head active_nodes << head previous_head = head end end cycle.each_with_index { |edge, i| return cycle[i..(cycle.length - 1)] if final_node == edge[0] } end raise ArgumentError, 'No cycle found!' if cycle.empty? end |
.floyd_warshall(graph) ⇒ Hash{ Object => { Object => { Numeric }}}
Returns the all pair distance between all the nodes
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/networkx/shortest_path/dense.rb', line 8 def self.floyd_warshall(graph) a, index = to_matrix(graph, Float::INFINITY, 'min') nodelen = graph.nodes(data: true).length (0..(nodelen - 1)).each { |i| a[i, i] = 0 } (0..(nodelen - 1)).each do |k| (0..(nodelen - 1)).each do |i| (0..(nodelen - 1)).each do |j| a[i, j] = [a[i, j], a[i, k] + a[k, j]].min end end end as_hash = {} (0..(nodelen - 1)).each do |i| (0..(nodelen - 1)).each do |j| as_hash[index[i]] = {} unless as_hash.has_key?(index[i]) as_hash[index[i]][index[j]] = a[i, j] end end as_hash end |
.gap_heuristic(height, levels, residual_nodes) ⇒ Object
Helper function for applying gap heuristic
168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/networkx/flow/preflowpush.rb', line 168 def self.gap_heuristic(height, levels, residual_nodes) ((height + 1)..(max_height)).each do |idx| level = levels[idx] level.active.each { |u| residual_nodes[u][:height] = n + 1 } level.inactive.each { |u| residual_nodes[u][:height] = n + 1 } levels[n + 1].active.merge!(level.active) level.active.clear levels[n + 1].inactive.merge!(level.inactive) level.inactive.clear end end |
.generate_unique_node ⇒ Object
Returns a label for unique node
3 4 5 |
# File 'lib/networkx/flow/capacityscaling.rb', line 3 def self.generate_unique_node SecureRandom.uuid end |
.get_edges(graph) ⇒ Object
Returns the edges of the graph in an array
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/networkx/operators/binary.rb', line 3 def self.get_edges(graph) edges = [] if graph.is_a?(MultiGraph) graph.adj.each do |u, v_keys| v_keys.each do |v, key_attrs| next if u > v key_attrs.each do |_key, attributes| edges << [u, v, attributes] end end end else graph.adj.each do |u, u_attrs| u_attrs.each do |v, uv_attrs| edges << [u, v, uv_attrs] end end end edges end |
.get_edges_weights(graph) ⇒ Object
Helper function for the minimum spanning tree
4 5 6 7 8 9 10 11 12 |
# File 'lib/networkx/auxillary_functions/mst.rb', line 4 def self.get_edges_weights(graph) edges = [] graph.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| edges << [[u, v], uv_attrs[:weight] || Float::INFINITY] end end edges end |
.get_sources(graph) ⇒ Object
Helper function to get sources
380 381 382 |
# File 'lib/networkx/shortest_path/weighted.rb', line 380 def self.get_sources(graph) graph.nodes(data: true).collect { |k, _v| k } end |
.get_weight(graph) ⇒ Object
Helper function to extract weight from a adjecency hash
3 4 5 6 7 8 9 |
# File 'lib/networkx/shortest_path/weighted.rb', line 3 def self.get_weight(graph) lambda do |_, _, attrs| return attrs[:weight] || 1 unless graph.multigraph? attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max end end |
.global_relabel(from_sink, source, target, residual_nodes, num, levels, residual_pred) ⇒ Object
Helper function for global relabel heuristic
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/networkx/flow/preflowpush.rb', line 181 def self.global_relabel(from_sink, source, target, residual_nodes, num, levels, residual_pred) src = from_sink ? target : source heights = reverse_bfs(src, residual_pred) heights.delete(target) unless from_sink max_height = heights.values.max if from_sink residual_nodes.each { |u, attr| heights[u] = num + 1 if !heights.has_key?(u) && attr[:height] < num } else heights.each_key { |u| heights[u] += num } max_height += num end heights.delete(src) heights.each do |u, new_height| old_height = residual_nodes[u][:height] next unless new_height != old_height if levels[old_height].active.include?(u) levels[old_height].active.delete(u) levels[new_height].active.add(u) else levels[old_height].inactive.delete(u) levels[new_height].inactive.add(u) end residual_nodes[u][:height] = new_height end max_height end |
.graph_to_csv(graph, filename = 'graph.csv') ⇒ Object
Saves the graph in a csv file
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/networkx/converters/to_csv.rb', line 6 def self.graph_to_csv(graph, filename = 'graph.csv') CSV.open(filename, 'wb') do |csv| csv << [graph.class.name] csv << ['graph_values'] csv << graph.graph.keys csv << graph.graph.values csv << ['graph_nodes'] graph.nodes(data: true).each do |u, attrs| node_attrs = [u] attrs.each do |k, v| node_attrs << k node_attrs << v end csv << node_attrs end csv << ['graph_edges'] graph.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| if graph.multigraph? uv_attrs.each do |key, attrs| node_attrs = [u, v, key] attrs.each do |k, k_attrs| node_attrs << k node_attrs << k_attrs end csv << node_attrs end else node_attrs = [u, v] uv_attrs.each do |k, vals| node_attrs << k node_attrs << vals end csv << node_attrs end end end end end |
.graph_to_json(graph) ⇒ JSON
Returns a JSON object of the given graph
7 8 9 10 11 12 13 14 |
# File 'lib/networkx/converters/to_json.rb', line 7 def self.graph_to_json(graph) json_hash = {} json_hash[:class] = graph.class.name json_hash[:graph] = graph.graph json_hash[:nodes] = graph.nodes json_hash[:adj] = graph.adj json_hash.to_json end |
.grid_2d_graph(m, n, periodic: false, create_using: NetworkX::Graph) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/networkx/others/grid_2d_graph.rb', line 12 def self.grid_2d_graph(m, n, periodic: false, create_using: NetworkX::Graph) warn('sorry, periodic is not done yet') if periodic m.is_a?(Integer) or raise(ArgumentError, "[NetworkX] argument m: Integer, not #{m.class}") n.is_a?(Integer) or raise(ArgumentError, "[NetworkX] argument n: Integer, not #{n.class}") create_using.is_a?(Class) \ or raise(ArgumentError, "[NetworkX] argument create_using: `Graph` class or children, not #{create_using.class}") g = create_using.new a = [] m.times { |i| n.times { |j| a << [i, j] } } g.add_nodes_from(a) e1 = [] (m - 1).times { |i| n.times { |j| e1 << [[i, j], [i + 1, j]] } } g.add_edges_from(e1) e2 = [] m.times { |i| (n - 1).times { |j| e2 << [[i, j], [i, j + 1]] } } g.add_edges_from(e2) g.add_edges_from(g.edges.map { |i, j| [j, i] }) if g.directed? g end |
.hash_product(hash1, hash2) ⇒ Object
Returns the hash product of two hashes
24 25 26 |
# File 'lib/networkx/operators/product.rb', line 24 def self.hash_product(hash1, hash2) (hash1.keys | hash2.keys).to_h { |n| [n, [hash1[n], hash2[n]]] } end |
.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for bellman ford
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/networkx/shortest_path/weighted.rb', line 220 def self.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil) pred = sources.product([[]]).to_h if pred.nil? dist = sources.product([0]).to_h if dist.nil? inf, n, count, q, in_q = Float::INFINITY, graph.nodes(data: true).length, {}, sources.clone, Set.new(sources) until q.empty? u = q.shift in_q.delete(u) skip = false pred[u].each { |k| skip = true if in_q.include?(k) } next if skip dist_u = dist[u] graph.adj[u].each do |v, e| dist_v = dist_u + weight.call(u, v, e) next if !cutoff.nil? && dist_v > cutoff next if !target.nil? && dist_v > (dist[target] || inf) if dist_v < (dist[v] || inf) unless in_q.include?(v) q << v in_q.add(v) count_v = (count[v] || 0) + 1 raise ArgumentError, 'Negative edge cycle detected!' if count_v == n count[v] = count_v end dist[v] = dist_v pred[v] = [u] elsif dist.has_key?(v) && dist_v == dist[v] pred[v] << u end end end unless paths.nil? dsts = pred dsts.each_key do |dst| path, cur = [dst], dst until pred[cur][0].nil? cur = pred[cur][0] path << cur end path.reverse paths[dst] = path end end dist end |
.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for single source dijkstra
53 54 55 |
# File 'lib/networkx/shortest_path/weighted.rb', line 53 def self.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil) help_multisource_dijkstra(graph, [source], weight, pred, paths, cutoff, target) end |
.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil) ⇒ Object
Helper function for multisource dijkstra
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/networkx/shortest_path/weighted.rb', line 12 def self.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil) count = ->(i) { i + 1 } i = -1 dist = {} seen = {} fringe = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) } sources.each do |s| seen[s] = 0 fringe << [0, count.call(i), s] end until fringe.empty? d, _, v = fringe.pop next if dist.has_key?(v) dist[v] = d break if v == target graph.adj[v].each do |u, attrs| cost = weight.call(v, u, attrs) next if cost.nil? vu_dist = dist[v] + cost next if !cutoff.nil? && vu_dist > cutoff if dist.has_key?(u) raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u] elsif !seen.has_key?(u) || vu_dist < seen[u] seen[u] = vu_dist fringe << [vu_dist, count.call(i), u] paths[u] = paths[v] + [u] unless paths.nil? pred[u] = [v] unless pred.nil? elsif vu_dist == seen[u] pred[u] << v unless pred.nil? end end end dist end |
.help_single_shortest_path(adj, firstlevel, paths, cutoff) ⇒ Object
Helper function for finding single source shortest path
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 53 def self.help_single_shortest_path(adj, firstlevel, paths, cutoff) level = 0 nextlevel = firstlevel while !nextlevel.empty? && cutoff > level thislevel = nextlevel nextlevel = {} thislevel.each_key do |u| adj[u].each_key do |v| unless paths.has_key?(v) paths[v] = paths[u] + [v] nextlevel[v] = 1 end end end level += 1 end paths end |
.help_single_shortest_path_length(adj, firstlevel, cutoff) ⇒ Object
Helper function for single source shortest path length
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 3 def self.help_single_shortest_path_length(adj, firstlevel, cutoff) Enumerator.new do |e| seen = {} level = 0 nextlevel = firstlevel while !nextlevel.empty? && cutoff >= level thislevel = nextlevel nextlevel = {} thislevel.each do |u, _attrs| next if seen.has_key?(u) seen[u] = level nextlevel.merge!(adj[u]) e.yield [u, level] end level += 1 end seen.clear end end |
.hits(graph, max_iter = 100, tol = 1e-8, nstart) ⇒ Array<Numeric, Numeric>
Computes hits and authority scores for all the graphs
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/networkx/link_analysis/hits.rb', line 10 def self.hits(graph, max_iter = 100, tol = 1e-8, nstart) return [{}, {}] if graph.nodes(data: true).empty? h = nstart sum = h.values.sum h.each_key { |k| h[k] /= (sum * 1.0) } i = 0 a = {} loop do hlast = Marshal.load(Marshal.dump(h)) h, a = {}, {} hlast.each do |k, _v| h[k] = 0 a[k] = 0 end h.each_key { |k| graph.adj[k].each { |nbr, attrs| a[k] += hlast[nbr] * (attrs[:weight] || 1) } } h.each_key { |k| graph.adj[k].each { |nbr, attrs| h[k] += a[nbr] * (attrs[:weight] || 1) } } smax = h.values.max h.each_key { |k| h[k] /= smax } smax = a.values.max a.each_key { |k| a[k] /= smax } break if h.keys.map { |k| (h[k] - hlast[k]).abs }.sum < tol raise ArgumentError, 'Power Iteration failed to converge!' if i > max_iter i += 1 end [h, a] end |
.hub_matrix(graph) ⇒ Matrix
Computes hub matrix for the graph
55 56 57 58 |
# File 'lib/networkx/link_analysis/hits.rb', line 55 def self.hub_matrix(graph) matrix, = to_matrix(graph, 0) matrix * matrix.transpose end |
.info(graph) ⇒ Object
4 5 6 7 8 9 10 |
# File 'lib/networkx/others/info.rb', line 4 def self.info(graph) info = '' info << "Type: #{graph.class}\n" info << "Number of nodes: #{graph.number_of_nodes}\n" info << "Number of edges: #{graph.number_of_edges}\n" info end |
.init_product_graph(g1, g2) ⇒ Object
Initializes the product graph
97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/networkx/operators/product.rb', line 97 def self.init_product_graph(g1, g2) raise ArgumentError, 'Arguments must be both directed or undirected!' unless g1.directed? == g2.directed? g = if g1.multigraph? || g2.multigraph? NetworkX::MultiGraph.new else NetworkX::Graph.new end g = g.to_directed if g.directed? g end |
.intersection(g1, g2) ⇒ Graph, ...
Performs the intersection of two graphs
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/networkx/operators/binary.rb', line 59 def self.intersection(g1, g2) result = g1.class.new raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph? unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty? raise ArgumentError, 'Node sets must be equal!' end g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) } g1, g2 = g2, g1 if g1.number_of_edges > g2.number_of_edges g1.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| if g1.multigraph? next if u > v && g1.instance_of?(MultiGraph) uv_attrs.each do |k, attrs| result.add_edge(u, v, **attrs) if g2.edge?(u, v, k) end elsif g2.edge?(u, v) result.add_edge(u, v, **uv_attrs) end end end result end |
.intersection_all(graphs) ⇒ Graph, ...
Performs the intersection of many graphs
39 40 41 42 43 44 45 46 47 48 |
# File 'lib/networkx/operators/all.rb', line 39 def self.intersection_all(graphs) raise ArgumentError, 'Argument array is empty' if graphs.empty? result = graphs.shift graphs.each do |graph| result = NetworkX.intersection(result, graph) end result end |
.johnson(graph) ⇒ Array<Object, Hash { Object => Array<Object> }] shortest paths between all pairs of nodes
Returns shortest path between all pairs of nodes
405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/networkx/shortest_path/weighted.rb', line 405 def self.johnson(graph) dist, pred = {}, {} sources = get_sources(graph) graph.nodes(data: true).each_key do |n| dist[n], pred[n] = 0, [] end weight = get_weight(graph) dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist) new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] } dist_path = dist_path_lambda(graph, new_weight) set_path_lengths_johnson(graph, dist_path, new_weight) end |
.json_to_graph(json_str) ⇒ Graph, ...
Returns a graph from the json encoded graph
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/networkx/converters/to_json.rb', line 21 def self.json_to_graph(json_str) graph_hash = JSON.parse(json_str) case json_str['class'] when 'NetworkX::Graph' graph = NetworkX::Graph.new(graph_hash.graph) when 'NetworkX::MultiGraph' graph = NetworkX::MultiGraph.new(graph_hash.graph) when 'NetworkX::DiGraph' graph = NetworkX::DiGraph.new(graph_hash.graph) when 'NetworkX::MultiDiGraph' graph = NetworkX::MultiDiGraph.new(graph_hash.graph) end graph.adj = graph_hash['adj'] graph.nodes = graph_hash['nodes'] graph end |
.lexicographic_product(g1, g2) ⇒ Graph, ...
Returns the lexicographic product of two graphs
143 144 145 146 147 148 149 |
# File 'lib/networkx/operators/product.rb', line 143 def self.lexicographic_product(g1, g2) g = init_product_graph(g1, g2) g.add_nodes(node_product(g1, g2)) g.add_edges(edges_cross_nodes_and_nodes(g1, g2)) g.add_edges(nodes_cross_edges(g1, g2)) g end |
.maximal_independent_set(graph, nodes) ⇒ Numeric
Returns the maximal independent set of a graph
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/networkx/auxillary_functions/mis.rb', line 8 def self.maximal_independent_set(graph, nodes) if (graph.nodes(data: true).keys - nodes).empty? raise 'The array containing the nodes should be a subset of the graph!' end neighbours = [] nodes.each { |u| graph.adj[u].each { |v, _| neighbours |= [v] } } raise 'Nodes is not an independent set of graph!' if (neighbours - nodes).empty? available_nodes = graph.nodes(data: true).keys - (neighbours | nodes) until available_nodes.empty? node = available_nodes.sample nodes << node available_nodes -= (graph.adj[node].keys + [node]) end nodes end |
.minimum_spanning_tree(graph) ⇒ DiGraph, Graph
Returns the minimum spanning tree of a graph
19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/networkx/auxillary_functions/mst.rb', line 19 def self.minimum_spanning_tree(graph) mst = Marshal.load(Marshal.dump(graph)) mst.clear edges = get_edges_weights(graph).sort_by { |a| a[1] } union_find = UnionFind.new(graph.nodes(data: true).keys) while edges.any? && mst.nodes(data: true).length <= graph.nodes(data: true).length edge = edges.shift unless union_find.connected?(edge[0][0], edge[0][1]) union_find.union(edge[0][0], edge[0][1]) mst.add_edge(edge[0][0], edge[0][1], **graph.adj[edge[0][0]][edge[0][1]]) end end mst end |
.multisource_dijkstra(graph, sources, target = nil, cutoff = nil) ⇒ Numeric, Array<Object>
Computes shortest paths and path lengths to a target from one of the nodes
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/networkx/shortest_path/weighted.rb', line 65 def self.multisource_dijkstra(graph, sources, target = nil, cutoff = nil) raise ValueError, 'Sources cannot be empty' if sources.empty? return [0, [target]] if sources.include?(target) paths = {} weight = get_weight(graph) sources.each { |source| paths[source] = [source] } dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target) return [dist, paths] if target.nil? raise KeyError, "No path to #{target}!" unless dist.has_key?(target) [dist[target], paths[target]] end |
.multisource_dijkstra_path(graph, sources, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Computes shortest paths to any from the given nodes
100 101 102 103 |
# File 'lib/networkx/shortest_path/weighted.rb', line 100 def self.multisource_dijkstra_path(graph, sources, cutoff = nil) _, path = multisource_dijkstra(graph, sources, nil, cutoff) path end |
.multisource_dijkstra_path_length(graph, sources, cutoff = nil) ⇒ Hash{ Object => Numeric }
Computes shortest path lengths to any from the given nodes
86 87 88 89 90 91 |
# File 'lib/networkx/shortest_path/weighted.rb', line 86 def self.multisource_dijkstra_path_length(graph, sources, cutoff = nil) raise ValueError, 'Sources cannot be empty' if sources.empty? weight = get_weight(graph) help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff) end |
.negative_edge_cycle(graph) ⇒ Boolean
Finds if there is a negative edge cycle in the graph
12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/networkx/flow/capacityscaling.rb', line 12 def self.negative_edge_cycle(graph) newnode = generate_unique_node graph.add_edges(graph.nodes(data: true).keys.map { |n| [newnode, n] }) begin bellmanford_predecesor_distance(graph, newnode) rescue ArgumentError return true ensure graph.remove_node(newnode) end false end |
.node_product(g1, g2) ⇒ Object
Returns the node product of nodes of two graphs
29 30 31 32 33 34 35 36 37 |
# File 'lib/networkx/operators/product.rb', line 29 def self.node_product(g1, g2) n_product = [] g1.nodes(data: true).each do |k1, attrs1| g2.nodes(data: true).each do |k2, attrs2| n_product << [[k1, k2], hash_product(attrs1, attrs2)] end end n_product end |
.nodes_cross_edges(g1, g2) ⇒ Object
Returns the product of directed nodes with edges
73 74 75 76 77 78 79 80 81 |
# File 'lib/networkx/operators/product.rb', line 73 def self.nodes_cross_edges(g1, g2) result = [] g1.nodes(data: true).each_key do |x| edges_in_array(g2).each do |u, v, d| result << [[x, u], [x, v], d] end end result end |
.number_connected_components(graph) ⇒ Integer Also known as: number_of_connected_components
Returns the number of connected components on graph.
8 9 10 11 12 |
# File 'lib/networkx/others/number_connected_components.rb', line 8 def self.number_connected_components(graph) uf = NetworkX::UnionFind.new(graph.nodes(data: false)) graph.each_edge { |x, y| uf.unite(x, y) } uf.groups.size end |
.number_of_bridges(graph) ⇒ Integer
Returns the number of bridges.
27 28 29 |
# File 'lib/networkx/others/bridges.rb', line 27 def self.number_of_bridges(graph) bridges(graph).size end |
.number_of_cliques(graph, node) ⇒ Numeric
Returns the number of cliques in a graph containing a node
58 59 60 61 |
# File 'lib/networkx/auxillary_functions/cliques.rb', line 58 def self.number_of_cliques(graph, node) cliques = find_cliques(graph) cliques.count { |c| c.include?(node) } end |
.out_edges(graph, node) ⇒ Object
Helper function for edge_dfs
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/networkx/traversals/edge_dfs.rb', line 6 def self.out_edges(graph, node) edges = [] visited = {} case graph.class.name when 'NetworkX::Graph', 'NetworkX::DiGraph' graph.adj[node].each do |v, _| if graph.instance_of?(::NetworkX::DiGraph) || visited[[v, node]].nil? visited[[node, v]] = true edges << [node, v] end end else graph.adj[node].each do |v, uv_keys| uv_keys.each_key do |k| if graph.instance_of?(::NetworkX::MultiDiGraph) || visited[[v, node, k]].nil? visited[[node, v, k]] = true edges << [node, v, k] end end end end edges end |
.pagerank(graph, alpha: 0.85, personalization: nil, eps: 1e-6, max_iter: 100) ⇒ Hash of Object => Float
Computes pagerank values for the graph
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/networkx/link_analysis/pagerank.rb', line 10 def self.pagerank(graph, alpha: 0.85, personalization: nil, eps: 1e-6, max_iter: 100) n = graph.number_of_nodes matrix, index_to_node = NetworkX.to_matrix(graph, 0) index_from_node = index_to_node.invert probabilities = Array.new(n) do |i| total = matrix.row(i).sum (matrix.row(i) / total.to_f).to_a end curr = personalization unless curr curr = Array.new(n) graph.each_node{|node| curr[index_from_node[node]] = 1.0 / n } end max_iter.times do prev = curr.clone n.times do |i| ip = 0.0 n.times do |j| ip += probabilities[j][i] * prev[j] end curr[i] = (alpha * ip) + ((1.0 - alpha) / n * 1.0) end err = (0...n).map{|i| (prev[i] - curr[i]).abs }.sum return (0...n).map{|i| [index_to_node[i], curr[i]] }.sort.to_h if err < eps end warn "pagerank() failed within #{max_iter} iterations. Please inclease max_iter: or loosen eps:" (0...n).map{|i| [index_to_node[i], curr[i]] }.sort.to_h end |
.power(graph, pow) ⇒ Graph, ...
Returns the specified power of the graph
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/networkx/operators/product.rb', line 173 def self.power(graph, pow) raise ArgumentError, 'Power must be a positive quantity!' if pow <= 0 result = NetworkX::Graph.new result.add_nodes(graph.nodes(data: true).map { |n, attrs| [n, attrs] }) graph.nodes(data: true).each do |n, _attrs| seen = {} level = 1 next_level = graph.adj[n] until next_level.empty? this_level = next_level next_level = {} this_level.each do |v, _attrs| next if v == n unless seen.has_key?(v) seen[v] = level next_level.merge!(graph.adj[v]) end end break if pow <= level level += 1 end result.add_edges(seen.map { |v, _| [n, v] }) end result end |
.predecessor(graph, source, cutoff = nil, return_seen = false) ⇒ Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>, Hash{ Object => Array<Object> }
Computes shortest paths to all nodes from all nodes
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 110 def self.predecessor(graph, source, cutoff = nil, return_seen = false) raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source) level = 0 nextlevel = [source] seen = {source => level} pred = {source => []} until nextlevel.empty? level += 1 thislevel = nextlevel nextlevel = [] thislevel.each do |u| graph.adj[u].each_key do |v| if !seen.has_key?(v) pred[v] = [u] seen[v] = level nextlevel << v elsif seen[v] == level pred[v] << u end end end break if cutoff.nil? && cutoff <= level end return_seen ? [pred, seen] : pred end |
.preflowpush(graph, source, target, residual = nil, globalrelabel_freq = 1, value_only = false) ⇒ DiGraph
Computes max flow using preflow push algorithm
246 247 248 |
# File 'lib/networkx/flow/preflowpush.rb', line 246 def self.preflowpush(graph, source, target, residual = nil, globalrelabel_freq = 1, value_only = false) preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only) end |
.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only) ⇒ Object
Helper function to apply the preflow push algorithm
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/networkx/flow/preflowpush.rb', line 14 def self.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only) raise ArgumentError, 'Source not in graph!' unless graph.nodes(data: true).has_key?(source) raise ArgumentError, 'Target not in graph!' unless graph.nodes(data: true).has_key?(target) raise ArgumentError, 'Source and Target are same!' if source == target globalrelabel_freq = 0 if globalrelabel_freq.nil? raise ArgumentError, 'Global Relabel Freq must be nonnegative!' if globalrelabel_freq.negative? r_network = residual.nil? ? build_residual_network(graph) : residual detect_unboundedness(r_network, source, target) residual_nodes = r_network.nodes residual_adj = r_network.adj residual_pred = r_network.pred residual_nodes.each do |u, u_attrs| u_attrs[:excess] = 0 residual_adj[u].each { |_v, attrs| attrs[:flow] = 0 } end heights = reverse_bfs(target, residual_pred) unless heights.has_key?(source) r_network.graph[:flow_value] = 0 return r_network end n = r_network.nodes(data: true).length max_height = heights.map { |u, h| u == source ? -1 : h }.max heights[source] = n grt = GlobalRelabelThreshold.new(n, r_network.size, globalrelabel_freq) residual_nodes.each do |u, u_attrs| u_attrs[:height] = heights.has_key?(u) ? heights[u] : (n + 1) u_attrs[:curr_edge] = CurrentEdge.new(residual_adj[u]) end residual_adj[source].each do |u, attr| flow = attr[:capacity] push(source, u, flow, residual_nodes, residual_adj) if flow.positive? end levels = (0..((2 * n) - 1)).map { |_| Level.new } residual_nodes.each do |u, attr| if u != source && u != target level = levels[attr[:height]] (residual_nodes[u][:excess]).positive? ? level.active.add(u) : level.inactive.add(u) end end height = max_height while height.positive? loop do level = levels[height] if level.active.empty? height -= 1 break end old_height = height old_level = level u = arbitrary_element(level.active) height = discharge(u, true, residual_nodes, residual_adj, height, levels, grt, source, target) if grt.reached? height = global_relabel(true, source, target, residual_nodes, n, levels, residual_pred) max_height = height grt.clear_work elsif old_level.active.empty? && old_level.inactive.empty? gap_heuristic(old_height, levels, residual_nodes) height = old_height - 1 max_height = height else max_height = [max_height, height].max end end end if value_only r_network.graph[:flow_value] = residual_nodes[target][:excess] return r_network end height = global_relabel(false, source, target, residual_nodes, n, levels, residual_pred) grt.clear_work while height > n loop do level = levels[height] if level.active.empty? height -= 1 break end u = arbitrary_element(level.active) height = discharge(u, false, residual_nodes, residual_adj, height, levels, grt, source, target) if grt.reached? height = global_relabel(false, source, target, residual_nodes, n, levels, residual_pred) grt.clear_work end end end r_network.graph[:flow_value] = residual_nodes[target][:excess] r_network end |
.push(node1, node2, flow, residual_nodes, residual_adj) ⇒ Object
Helper function for augmenting flow
210 211 212 213 214 215 |
# File 'lib/networkx/flow/preflowpush.rb', line 210 def self.push(node1, node2, flow, residual_nodes, residual_adj) residual_adj[node1][node2][:flow] += flow residual_adj[node2][node1][:flow] -= flow residual_nodes[node1][:excess] -= flow residual_nodes[node2][:excess] += flow end |
.radius(graph) ⇒ Numeric
Returns the radius of a graph
34 35 36 |
# File 'lib/networkx/auxillary_functions/eccentricity.rb', line 34 def self.radius(graph) eccentricity(graph).values.min end |
.relabel(node, num, r_adj, r_nodes) ⇒ Object
Helper function to relable a node to create a permissible edge
129 130 131 132 |
# File 'lib/networkx/flow/preflowpush.rb', line 129 def self.relabel(u_node, grt, r_adj, _r_nodes, _source, _target, _levels) grt.add_work(r_adj[u_node].length) r_adj[u_node].map { |v, attr| attr[:flow] < (attr[:capacity] + 1) ? _nodes[v][:height] : Float::INFINITY }.min end |
.reverse_bfs(src, residual_pred) ⇒ Object
Helper function for reverse bfs
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/networkx/flow/preflowpush.rb', line 218 def self.reverse_bfs(src, residual_pred) heights = {src => 0} q = [[src, 0]] until q.empty? u, height = q.shift height += 1 residual_pred[u].each do |v, attr| if !heights.has_key?(v) && attr[:flow] < attr[:capacity] heights[v] = height q << [v, height] end end end heights end |
.set_path_lengths_johnson(graph, dist_path, new_weight) ⇒ Object
Helper function to set path lengths for Johnson algorithm
394 395 396 397 398 |
# File 'lib/networkx/shortest_path/weighted.rb', line 394 def self.set_path_lengths_johnson(graph, dist_path, new_weight) path_lengths = [] graph.nodes(data: true).each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] } path_lengths end |
.shortest_augmenting_path(graph, source, target, residual = nil, _value_only = false, two_phase = false, cutoff = nil) ⇒ DiGraph
Computes max flow using shortest augmenting path algorithm
149 150 151 152 153 |
# File 'lib/networkx/flow/shortestaugmentingpath.rb', line 149 def self.shortest_augmenting_path(graph, source, target, residual = nil, \ _value_only = false, two_phase = false, cutoff = nil) shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff) end |
.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff) ⇒ Object
Helper function for running the shortest augmenting path algorithm
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/networkx/flow/shortestaugmentingpath.rb', line 3 def self.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff) raise ArgumentError, 'Source is not in the graph!' unless graph.nodes(data: true).has_key?(source) raise ArgumentError, 'Target is not in the graph!' unless graph.nodes(data: true).has_key?(target) raise ArgumentError, 'Source and Target are the same!' if source == target residual = residual.nil? ? build_residual_network(graph) : residual r_nodes = residual.nodes r_pred = residual.pred r_adj = residual.adj r_adj.each_value do |u_edges| u_edges.each_value do |attrs| attrs[:flow] = 0 end end heights = {target => 0} q = [[target, 0]] until q.empty? u, height = q.shift height += 1 r_pred[u].each do |v, attrs| if !heights.has_key?(v) && attrs[:flow] < attrs[:capacity] heights[v] = height q << [v, height] end end end unless heights.has_key?(source) residual.graph[:flow_value] = 0 return residual end n = graph.nodes(data: true).length m = residual.size / 2 r_nodes.each do |node, attrs| attrs[:height] = heights.has_key?(node) ? heights[node] : n attrs[:curr_edge] = CurrentEdge.new(r_adj[node]) end counts = [0] * (2 * n - 1) r_nodes.each_value { |attrs| counts[attrs[:height]] += 1 } inf = graph.graph[:inf] cutoff = Float::INFINITY if cutoff.nil? flow_value = 0 path = [source] u = source d = two_phase ? n : [m**0.5, 2 * (n**(2./ 3))].min.floor done = r_nodes[source][:height] >= d until done height = r_nodes[u][:height] curr_edge = r_nodes[u][:curr_edge] loop do v, attr = curr_edge.get if height == r_nodes[v][:height] + 1 && attr[:flow] < attr[:capacity] path << v u = v break end begin curr_edge.move_to_next rescue StopIteration if counts[height].zero? residual.graph[:flow_value] = flow_value return residual end height = relabel(u, n, r_adj, r_nodes) if u == source && height >= d if two_phase done = true break else residual.graph[:flow_value] = flow_value return residual end end counts[height] += 1 r_nodes[u][:height] = height unless u == source path.pop u = path[-1] break end end end next unless u == target flow_value += augment(path, inf, r_adj) if flow_value >= cutoff residual.graph[:flow_value] = flow_value return residual end end flow_value += edmondskarp_core(residual, source, target, cutoff - flow_value) residual.graph[:flow_value] = flow_value residual end |
.single_source_shortest_path(graph, source, cutoff = nil) ⇒ Array<Object, Array<Object, Array<Object>>>
Computes single source shortest path from a node to every other node
79 80 81 82 83 84 85 86 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 79 def self.single_source_shortest_path(graph, source, cutoff = nil) raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source) cutoff = Float::INFINITY if cutoff.nil? nextlevel = {source => 1} paths = {source => [source]} help_single_shortest_path(graph.adj, nextlevel, paths, cutoff) end |
.single_source_shortest_path_length(graph, source, cutoff = nil) ⇒ Array<Object, Numeric>
Computes shortest path values to all nodes from a given node
32 33 34 35 36 37 38 |
# File 'lib/networkx/shortest_path/unweighted.rb', line 32 def self.single_source_shortest_path_length(graph, source, cutoff = nil) raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source) cutoff = Float::INFINITY if cutoff.nil? nextlevel = {source => 1} help_single_shortest_path_length(graph.adj, nextlevel, cutoff).take(graph.nodes(data: true).length) end |
.singlesource_bellmanford(graph, source, target = nil, cutoff = nil) ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/networkx/shortest_path/weighted.rb', line 290 def self.singlesource_bellmanford(graph, source, target = nil, cutoff = nil) return [0, [source]] if source == target weight = get_weight(graph) paths = {source => [source]} dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target) return [dist, paths] if target.nil? raise ArgumentError, 'Node not reachable!' unless dist.has_key?(target) [dist[target], paths[target]] end |
.singlesource_bellmanford_path(graph, source, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Shortest path from source to all nodes using Bellman Ford algorithm
338 339 340 341 |
# File 'lib/networkx/shortest_path/weighted.rb', line 338 def self.singlesource_bellmanford_path(graph, source, cutoff = nil) _, path = singlesource_bellmanford(graph, source, cutoff) path end |
.singlesource_bellmanford_path_length(graph, source, cutoff = nil) ⇒ Hash{ Object => Numeric }
Shortest path length from source to all nodes using Bellman Ford algorithm
350 351 352 353 |
# File 'lib/networkx/shortest_path/weighted.rb', line 350 def self.singlesource_bellmanford_path_length(graph, source, cutoff = nil) weight = get_weight(graph) help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff) end |
.singlesource_dijkstra(graph, source, target = nil, cutoff = nil) ⇒ Hash{ Object => Array<Object> }, Array<Object>
Computes shortest paths and path distances to all nodes/target from the given node
113 114 115 |
# File 'lib/networkx/shortest_path/weighted.rb', line 113 def self.singlesource_dijkstra(graph, source, target = nil, cutoff = nil) multisource_dijkstra(graph, [source], target, cutoff) end |
.singlesource_dijkstra_path(graph, source, cutoff = nil) ⇒ Hash{ Object => Array<Object> }
Computes shortest paths to all nodes from the given node
135 136 137 |
# File 'lib/networkx/shortest_path/weighted.rb', line 135 def self.singlesource_dijkstra_path(graph, source, cutoff = nil) multisource_dijkstra_path(graph, [source], cutoff) end |
.singlesource_dijkstra_path_length(graph, source, cutoff = nil) ⇒ Hash{ Object => Numeric }
Computes shortest path lengths to all nodes from the given node
124 125 126 |
# File 'lib/networkx/shortest_path/weighted.rb', line 124 def self.singlesource_dijkstra_path_length(graph, source, cutoff = nil) multisource_dijkstra_path_length(graph, [source], cutoff) end |
.strong_product(g1, g2) ⇒ Graph, ...
Returns the strong product of two graphs
157 158 159 160 161 162 163 164 165 |
# File 'lib/networkx/operators/product.rb', line 157 def self.strong_product(g1, g2) g = init_product_graph(g1, g2) g.add_nodes(node_product(g1, g2)) g.add_edges(nodes_cross_edges(g1, g2)) g.add_edges(edges_cross_nodes(g1, g2)) g.add_edges(directed_edges_cross_edges(g1, g2)) g.add_edges(undirected_edges_cross_edges(g1, g2)) unless g.directed? g end |
.symmetric_difference(g1, g2) ⇒ Graph, ...
Performs the symmetric difference of two graphs
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/networkx/operators/binary.rb', line 126 def self.symmetric_difference(g1, g2) result = g1.class.new raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph? unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty? raise ArgumentError, 'Node sets must be equal!' end g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) } g1.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| if g1.multigraph? next if u > v && g1.instance_of?(MultiGraph) uv_attrs.each do |k, attrs| result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k) end else result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v) end end end g2.adj.each do |u, u_edges| u_edges.each do |v, uv_attrs| next if u > v && g1.instance_of?(MultiGraph) if g2.multigraph? uv_attrs.each do |k, attrs| result.add_edge(u, v, **attrs) unless g1.edge?(u, v, k) end else result.add_edge(u, v, **uv_attrs) unless g1.edge?(u, v) end end end result end |
.tensor_product(g1, g2) ⇒ Graph, ...
Returns the tensor product of two graphs
115 116 117 118 119 120 121 |
# File 'lib/networkx/operators/product.rb', line 115 def self.tensor_product(g1, g2) g = init_product_graph(g1, g2) g.add_nodes(node_product(g1, g2)) g.add_edges(directed_edges_cross_edges(g1, g2)) g.add_edges(undirected_edges_cross_edges(g1, g2)) unless g.directed? g end |
.to_matrix(graph, val, multigraph_weight = 'sum') ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/networkx/to_matrix.rb', line 2 def self.to_matrix(graph, val, multigraph_weight = 'sum') is_undirected = !graph.directed? is_multigraph = graph.multigraph? nodelen = graph.nodes(data: true).length m = Matrix.build(nodelen) { val } index = {} inv_index = {} ind = 0 graph.nodes(data: true).each do |u, _| index[u] = ind inv_index[ind] = u ind += 1 end if is_multigraph graph.adj.each do |u, edge_hash| edge_hash.each do |v, keys| all_weights = [] keys.each do |_key, attrs| all_weights << attrs[:weight] end edge_attr = 0 case multigraph_weight when 'sum' edge_attr = all_weights.inject(0, :+) when 'max' edge_attr = all_weights.max when 'min' edge_attr = all_weights.min end m[index[u], index[v]] = edge_attr m[index[v], index[u]] = edge_attr || 1 if is_undirected end end else graph.adj.each do |u, edge_hash| edge_hash.each do |v, attrs| m[index[u], index[v]] = (attrs[:weight] || 1) m[index[v], index[u]] = (attrs[:weight] || 1) if is_undirected end end end [m, inv_index] end |
.to_number_if_possible(str) ⇒ Object
42 43 44 45 46 47 48 49 50 51 |
# File 'lib/networkx/others/reads.rb', line 42 def self.to_number_if_possible(str) case str when /^[+-]?[0-9]+$/ str.to_i when /^([+-]?\d*\.\d*)|(\d*[eE][+-]?\d+)$/ str.to_f else str end end |
.topological_sort(graph) ⇒ Array<Object>
Returns the nodes arranged in the topologically sorted fashion
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/networkx/auxillary_functions/dag.rb', line 33 def self.topological_sort(graph) raise ArgumentError, 'Topological Sort not defined on undirected graphs!' unless graph.directed? nodes = [] indegree_map = graph.nodes(data: true).each_key.map do |u| [u, graph.in_degree(u)] if graph.in_degree(u).positive? end.compact.to_h zero_indegree = graph.nodes(data: true).each_key.select { |u| graph.in_degree(u).zero? } until zero_indegree.empty? node = zero_indegree.shift raise ArgumentError, 'Graph changed during iteration!' unless graph.nodes(data: true).has_key?(node) graph.adj[node].each_key do |child| indegree_map[child] -= 1 if indegree_map[child].zero? zero_indegree << child indegree_map.delete(child) end end nodes << node end raise ArgumentError, 'Graph contains cycle or graph changed during iteration!' unless indegree_map.empty? nodes end |
.undirected_edges_cross_edges(g1, g2) ⇒ Object
Returns the product of undirected edges with edges
51 52 53 54 55 56 57 58 59 |
# File 'lib/networkx/operators/product.rb', line 51 def self.undirected_edges_cross_edges(g1, g2) result = [] edges_in_array(g1).each do |u, v, c| edges_in_array(g2).each do |x, y, d| result << [[v, x], [u, y], hash_product(c, d)] end end result end |
.union(g1, g2) ⇒ Graph, ...
Performs the union of two graphs
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/networkx/operators/binary.rb', line 197 def self.union(g1, g2) raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph? new_graph = g1.class.new new_graph.graph.merge!(g1.graph) new_graph.graph.merge!(g2.graph) unless (g1.nodes(data: true).keys & g2.nodes(data: true).keys).empty? raise ArgumentError, 'Graphs must be disjoint!' end g1_edges = get_edges(g1) g2_edges = get_edges(g2) new_graph.add_nodes(g1.nodes(data: true).keys) new_graph.add_edges(g1_edges) new_graph.add_nodes(g2.nodes(data: true).keys) new_graph.add_edges(g2_edges) new_graph end |
.union_all(graphs) ⇒ Graph, ...
Performs the union of many graphs
7 8 9 10 11 12 13 14 15 16 |
# File 'lib/networkx/operators/all.rb', line 7 def self.union_all(graphs) raise ArgumentError, 'Argument array is empty' if graphs.empty? result = graphs.shift graphs.each do |graph| result = NetworkX.union(result, graph) end result end |
.wiener_index(graph) ⇒ Numeric
Returns the wiener index of the graph
7 8 9 10 11 12 |
# File 'lib/networkx/auxillary_functions/wiener.rb', line 7 def self.wiener_index(graph) total = all_pairs_shortest_path_length(graph) wiener_ind = 0 total.to_h.each { |_, distances| distances.to_h.each { |_, val| wiener_ind += val } } graph.directed? ? wiener_ind : wiener_ind / 2 end |
Instance Method Details
#augment(path, inf, r_adj) ⇒ Object
Helper function for augmenting flow
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/networkx/flow/shortestaugmentingpath.rb', line 108 def augment(path, inf, r_adj) flow = inf temp_path = path.clone u = temp_path.shift temp_path.each do |v| attr = r_adj[u][v] flow = [flow, attr[:capacity] - attr[:flow]].min u = v end raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf temp_path = path.clone u = temp_path.shift temp_path.each do |v| r_adj[u][v][:flow] += flow r_adj[v][u][:flow] -= flow u = v end flow end |