networkx 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +21 -11
  4. data/.github/ISSUE_TEMPLATE.md +15 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  6. data/.github/workflows/ci.yml +17 -0
  7. data/.github/workflows/doc.yml +23 -0
  8. data/.github/workflows/gem-push.yml +45 -0
  9. data/.rspec +0 -1
  10. data/.rubocop.yml +56 -63
  11. data/.yardopts +0 -1
  12. data/README.md +27 -27
  13. data/Rakefile +2 -3
  14. data/lib/networkx/auxillary_functions/cliques.rb +62 -0
  15. data/lib/networkx/auxillary_functions/cycles.rb +114 -0
  16. data/lib/networkx/auxillary_functions/dag.rb +59 -0
  17. data/lib/networkx/auxillary_functions/eccentricity.rb +37 -0
  18. data/lib/networkx/auxillary_functions/mis.rb +23 -0
  19. data/lib/networkx/auxillary_functions/mst.rb +33 -0
  20. data/lib/networkx/auxillary_functions/union_find.rb +104 -0
  21. data/lib/networkx/auxillary_functions/vitality.rb +13 -0
  22. data/lib/networkx/auxillary_functions/wiener.rb +13 -0
  23. data/lib/networkx/converters/to_csv.rb +45 -0
  24. data/lib/networkx/converters/to_json.rb +37 -0
  25. data/lib/networkx/digraph.rb +234 -0
  26. data/lib/networkx/flow/capacityscaling.rb +249 -0
  27. data/lib/networkx/flow/edmondskarp.rb +115 -0
  28. data/lib/networkx/flow/preflowpush.rb +249 -0
  29. data/lib/networkx/flow/shortestaugmentingpath.rb +154 -0
  30. data/lib/networkx/flow/utils.rb +139 -0
  31. data/lib/networkx/graph.rb +448 -0
  32. data/lib/networkx/link_analysis/hits.rb +59 -0
  33. data/lib/networkx/link_analysis/pagerank.rb +89 -0
  34. data/lib/networkx/multidigraph.rb +249 -0
  35. data/lib/networkx/multigraph.rb +199 -0
  36. data/lib/networkx/operators/all.rb +65 -0
  37. data/lib/networkx/operators/binary.rb +222 -0
  38. data/lib/networkx/operators/product.rb +201 -0
  39. data/lib/networkx/operators/unary.rb +17 -0
  40. data/lib/networkx/others/bridges.rb +30 -0
  41. data/lib/networkx/others/generators.rb +237 -0
  42. data/lib/networkx/others/grid_2d_graph.rb +38 -0
  43. data/lib/networkx/others/info.rb +11 -0
  44. data/lib/networkx/others/number_connected_components.rb +17 -0
  45. data/lib/networkx/others/reads.rb +52 -0
  46. data/lib/networkx/shortest_path/astar.rb +73 -0
  47. data/lib/networkx/shortest_path/dense.rb +29 -0
  48. data/lib/networkx/shortest_path/unweighted.rb +136 -0
  49. data/lib/networkx/shortest_path/weighted.rb +417 -0
  50. data/lib/networkx/to_matrix.rb +51 -0
  51. data/lib/networkx/traversals/bfs.rb +110 -0
  52. data/lib/networkx/traversals/dfs.rb +135 -0
  53. data/lib/networkx/traversals/edge_dfs.rb +114 -0
  54. data/lib/networkx/version.rb +1 -1
  55. data/lib/networkx.rb +43 -1
  56. data/networkx.gemspec +14 -12
  57. metadata +118 -62
  58. data/.rspec_formatter.rb +0 -24
  59. data/.travis.yml +0 -18
  60. data/Guardfile +0 -7
@@ -0,0 +1,154 @@
1
+ module NetworkX
2
+ # Helper function for running the shortest augmenting path algorithm
3
+ def self.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
4
+ raise ArgumentError, 'Source is not in the graph!' unless graph.nodes.has_key?(source)
5
+ raise ArgumentError, 'Target is not in the graph!' unless graph.nodes.has_key?(target)
6
+ raise ArgumentError, 'Source and Target are the same!' if source == target
7
+
8
+ residual = residual.nil? ? build_residual_network(graph) : residual
9
+ r_nodes = residual.nodes
10
+ r_pred = residual.pred
11
+ r_adj = residual.adj
12
+
13
+ r_adj.each_value do |u_edges|
14
+ u_edges.each_value do |attrs|
15
+ attrs[:flow] = 0
16
+ end
17
+ end
18
+
19
+ heights = {target => 0}
20
+ q = [[target, 0]]
21
+
22
+ until q.empty?
23
+ u, height = q.shift
24
+ height += 1
25
+ r_pred[u].each do |v, attrs|
26
+ if !heights.has_key?(v) && attrs[:flow] < attrs[:capacity]
27
+ heights[v] = height
28
+ q << [v, height]
29
+ end
30
+ end
31
+ end
32
+
33
+ unless heights.has_key?(source)
34
+ residual.graph[:flow_value] = 0
35
+ return residual
36
+ end
37
+
38
+ n = graph.nodes.length
39
+ m = residual.size / 2
40
+
41
+ r_nodes.each do |node, attrs|
42
+ attrs[:height] = heights.has_key?(node) ? heights[node] : n
43
+ attrs[:curr_edge] = CurrentEdge.new(r_adj[node])
44
+ end
45
+
46
+ counts = [0] * (2 * n - 1)
47
+ r_nodes.each_value { |attrs| counts[attrs[:height]] += 1 }
48
+ inf = graph.graph[:inf]
49
+
50
+ cutoff = Float::INFINITY if cutoff.nil?
51
+ flow_value = 0
52
+ path = [source]
53
+ u = source
54
+ d = two_phase ? n : [m**0.5, 2 * (n**(2./ 3))].min.floor
55
+ done = r_nodes[source][:height] >= d
56
+
57
+ until done
58
+ height = r_nodes[u][:height]
59
+ curr_edge = r_nodes[u][:curr_edge]
60
+
61
+ loop do
62
+ v, attr = curr_edge.get
63
+ if height == r_nodes[v][:height] + 1 && attr[:flow] < attr[:capacity]
64
+ path << v
65
+ u = v
66
+ break
67
+ end
68
+ begin
69
+ curr_edge.move_to_next
70
+ rescue StopIteration
71
+ if counts[height].zero?
72
+ residual.graph[:flow_value] = flow_value
73
+ return residual
74
+ end
75
+ height = relabel(u, n, r_adj, r_nodes)
76
+ if u == source && height >= d
77
+ if two_phase
78
+ done = true
79
+ break
80
+ else
81
+ residual.graph[:flow_value] = flow_value
82
+ return residual
83
+ end
84
+ end
85
+ counts[height] += 1
86
+ r_nodes[u][:height] = height
87
+ unless u == source
88
+ path.pop
89
+ u = path[-1]
90
+ break
91
+ end
92
+ end
93
+ end
94
+ next unless u == target
95
+
96
+ flow_value += augment(path, inf, r_adj)
97
+ if flow_value >= cutoff
98
+ residual.graph[:flow_value] = flow_value
99
+ return residual
100
+ end
101
+ end
102
+ flow_value += edmondskarp_core(residual, source, target, cutoff - flow_value)
103
+ residual.graph[:flow_value] = flow_value
104
+ residual
105
+ end
106
+
107
+ # Helper function for augmenting flow
108
+ def augment(path, inf, r_adj)
109
+ flow = inf
110
+ temp_path = path.clone
111
+ u = temp_path.shift
112
+ temp_path.each do |v|
113
+ attr = r_adj[u][v]
114
+ flow = [flow, attr[:capacity] - attr[:flow]].min
115
+ u = v
116
+ end
117
+ raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf
118
+
119
+ temp_path = path.clone
120
+ u = temp_path.shift
121
+ temp_path.each do |v|
122
+ r_adj[u][v][:flow] += flow
123
+ r_adj[v][u][:flow] -= flow
124
+ u = v
125
+ end
126
+ flow
127
+ end
128
+
129
+ # Helper function to relable a node to create a permissible edge
130
+ def self.relabel(node, num, r_adj, r_nodes)
131
+ height = num - 1
132
+ r_adj[node].each do |v, attrs|
133
+ height = [height, r_nodes[v][:height]].min if attrs[:flow] < attrs[:capacity]
134
+ end
135
+ height + 1
136
+ end
137
+
138
+ # Computes max flow using shortest augmenting path algorithm
139
+ #
140
+ # @param graph [DiGraph] a graph
141
+ # @param source [Object] source node
142
+ # @param target [Object] target node
143
+ # @param residual [DiGraph, nil] residual graph
144
+ # @param _value_only [Boolean] if true, compute only the maximum flow value
145
+ # @param two_phase [Boolean] if true, two phase variant is used
146
+ # @param cutoff [Numeric] cutoff value for the algorithm
147
+ #
148
+ # @return [DiGraph] a residual graph containing the flow values
149
+ def self.shortest_augmenting_path(graph, source, target, residual = nil, \
150
+ _value_only = false, two_phase = false, cutoff = nil)
151
+
152
+ shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
153
+ end
154
+ end
@@ -0,0 +1,139 @@
1
+ module NetworkX
2
+ # Helper class for preflow push algorithm
3
+ class CurrentEdge
4
+ attr_reader :curr, :edges
5
+
6
+ def initialize(edges)
7
+ @edges = edges
8
+ @index = {}
9
+ @n = edges.length
10
+ @curr = 0
11
+ edges.each_with_index { |(key, _value), idx| @index[idx] = key }
12
+ end
13
+
14
+ def get
15
+ [@index[@curr], @edges[@index[@curr]]]
16
+ end
17
+
18
+ def move_to_next
19
+ @temp = @curr
20
+ @curr = (@curr + 1) % @n
21
+ raise StopIteration if @temp == @n - 1
22
+ end
23
+ end
24
+
25
+ # Helper class for preflow push algorithm
26
+ class Level
27
+ attr_reader :inactive, :active
28
+
29
+ def initialize
30
+ @inactive = Set.new
31
+ @active = Set.new
32
+ end
33
+ end
34
+
35
+ # Helper class for preflow push algorithm
36
+ class GlobalRelabelThreshold
37
+ def initialize(num1, num2, freq)
38
+ freq = freq.nil? ? Float::INFINITY : freq
39
+ @threshold = (num1 + num2) / freq
40
+ @work = 0
41
+ end
42
+
43
+ def add_work(work)
44
+ @work += work
45
+ end
46
+
47
+ def reached?
48
+ @work >= @threshold
49
+ end
50
+
51
+ def clear_work
52
+ @work = 0
53
+ end
54
+ end
55
+
56
+ # Builds a residual graph from a constituent graph
57
+ #
58
+ # @param graph [DiGraph] a graph
59
+ #
60
+ # @return [DiGraph] residual graph
61
+ def self.build_residual_network(graph)
62
+ raise NotImplementedError, 'MultiGraph and MultiDiGraph not supported!' if graph.multigraph?
63
+
64
+ r_network = NetworkX::DiGraph.new(inf: 0, flow_value: 0)
65
+ r_network.add_nodes(graph.nodes.keys)
66
+ inf = Float::INFINITY
67
+ edge_list = []
68
+
69
+ graph.adj.each do |u, u_edges|
70
+ u_edges.each do |v, uv_attrs|
71
+ edge_list << [u, v, uv_attrs] if (uv_attrs[:capacity] || inf).positive? && u != v
72
+ end
73
+ end
74
+
75
+ inf_chk = 3 * edge_list.inject(0) do |result, arr|
76
+ arr[2].has_key?(:capacity) && arr[2][:capacity] != inf ? (result + arr[2][:capacity]) : result
77
+ end
78
+ inf = inf_chk.zero? ? 1 : inf_chk
79
+
80
+ if graph.directed?
81
+ edge_list.each do |u, v, attrs|
82
+ r = [attrs[:capacity] || inf, inf].min
83
+ if r_network.adj[u][v].nil?
84
+ r_network.add_edge(u, v, capacity: r)
85
+ r_network.add_edge(v, u, capacity: 0)
86
+ else
87
+ r_network[u][v][:capacity] = r
88
+ end
89
+ end
90
+ else
91
+ edge_list.each do |u, v, attrs|
92
+ r = [attrs[:capacity] || inf, inf].min
93
+ r_network.add_edge(u, v, capacity: r)
94
+ r_network.add_edge(v, u, capacity: r)
95
+ end
96
+ end
97
+ r_network.graph[:inf] = inf
98
+ r_network
99
+ end
100
+
101
+ # Detects unboundedness in a graph, raises exception when
102
+ # infinite capacity flow is found
103
+ #
104
+ # @param r_network [DiGraph] a residual graph
105
+ # @param source [Object] source node
106
+ # @param target [Object] target node
107
+ def self.detect_unboundedness(r_network, source, target)
108
+ q = [source]
109
+ seen = Set.new([source])
110
+ inf = r_network.graph[:inf]
111
+ until q.empty?
112
+ u = q.shift
113
+ r_network.adj[u].each do |v, uv_attrs|
114
+ next unless uv_attrs[:capacity] == inf && !seen.include?(v)
115
+ raise ArgumentError, 'Infinite capacity flow!' if v == target
116
+
117
+ seen << v
118
+ q << v
119
+ end
120
+ end
121
+ end
122
+
123
+ # Build flow dictionary of a graph from its residual graph
124
+ #
125
+ # @param graph [DiGraph] a graph
126
+ # @param residual [DiGraph] residual graph
127
+ #
128
+ # @return [Hash{ Object => Hash{ Object => Numeric }] flowdict containing all
129
+ # the flow values in the edges
130
+ def self.build_flow_dict(graph, residual)
131
+ flow_dict = {}
132
+ graph.edges.each do |u, u_edges|
133
+ flow_dict[u] = {}
134
+ u_edges.each_key { |v| flow_dict[u][v] = 0 }
135
+ u_edges.each_key { |v| flow_dict[u][v] = residual[u][v][:flow] if (residual[u][v][:flow]).positive? }
136
+ end
137
+ flow_dict
138
+ end
139
+ end