networkx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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