jumoku 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/Gemfile +0 -1
  2. data/lib/jumoku.rb +2 -3
  3. data/lib/jumoku/builders/extended.rb +15 -23
  4. data/lib/jumoku/builders/raw_directed_tree.rb +15 -0
  5. data/lib/jumoku/builders/raw_undirected_tree.rb +15 -0
  6. data/lib/jumoku/builders/shared.rb +2 -5
  7. data/lib/jumoku/support/ruby_compatibility.rb +19 -0
  8. data/lib/jumoku/version.rb +1 -1
  9. data/spec/arborescence_spec.rb +14 -0
  10. data/spec/behaviors/core_tree.rb +281 -0
  11. data/spec/behaviors/extended.rb +530 -0
  12. data/spec/raw_directed_tree_spec.rb +14 -0
  13. data/spec/raw_undirected_tree_spec.rb +9 -310
  14. data/spec/spec_helper.rb +2 -0
  15. data/spec/tree_spec.rb +8 -535
  16. metadata +21 -86
  17. data/lib/jumoku/tree_api.rb +0 -27
  18. data/vendor/git/plexus/CREDITS.md +0 -31
  19. data/vendor/git/plexus/Gemfile +0 -3
  20. data/vendor/git/plexus/Gemfile.lock +0 -28
  21. data/vendor/git/plexus/LICENSE +0 -37
  22. data/vendor/git/plexus/README.md +0 -208
  23. data/vendor/git/plexus/Rakefile +0 -25
  24. data/vendor/git/plexus/TODO.md +0 -20
  25. data/vendor/git/plexus/VERSION +0 -1
  26. data/vendor/git/plexus/examples/graph_self.rb +0 -56
  27. data/vendor/git/plexus/examples/module_graph.jpg +0 -0
  28. data/vendor/git/plexus/examples/module_graph.rb +0 -14
  29. data/vendor/git/plexus/examples/self_graph.jpg +0 -0
  30. data/vendor/git/plexus/examples/visualize.jpg +0 -0
  31. data/vendor/git/plexus/examples/visualize.rb +0 -10
  32. data/vendor/git/plexus/lib/plexus.rb +0 -90
  33. data/vendor/git/plexus/lib/plexus/adjacency_graph.rb +0 -224
  34. data/vendor/git/plexus/lib/plexus/arc.rb +0 -59
  35. data/vendor/git/plexus/lib/plexus/arc_number.rb +0 -52
  36. data/vendor/git/plexus/lib/plexus/biconnected.rb +0 -84
  37. data/vendor/git/plexus/lib/plexus/chinese_postman.rb +0 -91
  38. data/vendor/git/plexus/lib/plexus/classes/graph_classes.rb +0 -28
  39. data/vendor/git/plexus/lib/plexus/common.rb +0 -63
  40. data/vendor/git/plexus/lib/plexus/comparability.rb +0 -63
  41. data/vendor/git/plexus/lib/plexus/directed_graph.rb +0 -78
  42. data/vendor/git/plexus/lib/plexus/directed_graph/algorithms.rb +0 -95
  43. data/vendor/git/plexus/lib/plexus/directed_graph/distance.rb +0 -167
  44. data/vendor/git/plexus/lib/plexus/dot.rb +0 -94
  45. data/vendor/git/plexus/lib/plexus/edge.rb +0 -36
  46. data/vendor/git/plexus/lib/plexus/ext.rb +0 -79
  47. data/vendor/git/plexus/lib/plexus/graph.rb +0 -626
  48. data/vendor/git/plexus/lib/plexus/graph_api.rb +0 -35
  49. data/vendor/git/plexus/lib/plexus/labels.rb +0 -113
  50. data/vendor/git/plexus/lib/plexus/maximum_flow.rb +0 -77
  51. data/vendor/git/plexus/lib/plexus/ruby_compatibility.rb +0 -17
  52. data/vendor/git/plexus/lib/plexus/search.rb +0 -510
  53. data/vendor/git/plexus/lib/plexus/strong_components.rb +0 -93
  54. data/vendor/git/plexus/lib/plexus/support/support.rb +0 -9
  55. data/vendor/git/plexus/lib/plexus/undirected_graph.rb +0 -56
  56. data/vendor/git/plexus/lib/plexus/undirected_graph/algorithms.rb +0 -90
  57. data/vendor/git/plexus/lib/plexus/version.rb +0 -6
  58. data/vendor/git/plexus/plexus.gemspec +0 -24
  59. data/vendor/git/plexus/spec/biconnected_spec.rb +0 -27
  60. data/vendor/git/plexus/spec/chinese_postman_spec.rb +0 -27
  61. data/vendor/git/plexus/spec/community_spec.rb +0 -44
  62. data/vendor/git/plexus/spec/complement_spec.rb +0 -27
  63. data/vendor/git/plexus/spec/digraph_distance_spec.rb +0 -121
  64. data/vendor/git/plexus/spec/digraph_spec.rb +0 -339
  65. data/vendor/git/plexus/spec/dot_spec.rb +0 -48
  66. data/vendor/git/plexus/spec/edge_spec.rb +0 -158
  67. data/vendor/git/plexus/spec/inspection_spec.rb +0 -38
  68. data/vendor/git/plexus/spec/multi_edge_spec.rb +0 -32
  69. data/vendor/git/plexus/spec/neighborhood_spec.rb +0 -36
  70. data/vendor/git/plexus/spec/properties_spec.rb +0 -146
  71. data/vendor/git/plexus/spec/search_spec.rb +0 -227
  72. data/vendor/git/plexus/spec/spec.opts +0 -4
  73. data/vendor/git/plexus/spec/spec_helper.rb +0 -59
  74. data/vendor/git/plexus/spec/strong_components_spec.rb +0 -61
  75. data/vendor/git/plexus/spec/triangulated_spec.rb +0 -125
  76. data/vendor/git/plexus/spec/undirected_graph_spec.rb +0 -220
  77. data/vendor/git/plexus/vendor/priority-queue/CHANGELOG +0 -33
  78. data/vendor/git/plexus/vendor/priority-queue/Makefile +0 -140
  79. data/vendor/git/plexus/vendor/priority-queue/README +0 -133
  80. data/vendor/git/plexus/vendor/priority-queue/benchmark/dijkstra.rb +0 -171
  81. data/vendor/git/plexus/vendor/priority-queue/compare_comments.rb +0 -49
  82. data/vendor/git/plexus/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  83. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.gp +0 -14
  84. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.png +0 -0
  85. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.gp +0 -15
  86. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.png +0 -0
  87. data/vendor/git/plexus/vendor/priority-queue/doc/results.csv +0 -37
  88. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +0 -2
  89. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +0 -947
  90. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue.rb +0 -14
  91. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +0 -1
  92. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +0 -46
  93. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +0 -526
  94. data/vendor/git/plexus/vendor/priority-queue/priority_queue.so +0 -0
  95. data/vendor/git/plexus/vendor/priority-queue/setup.rb +0 -1551
  96. data/vendor/git/plexus/vendor/priority-queue/test/priority_queue_test.rb +0 -371
  97. data/vendor/git/plexus/vendor/rdot.rb +0 -360
@@ -1,35 +0,0 @@
1
- module Plexus
2
- # This module defines the minimum set of functions required to make a graph that can
3
- # use the algorithms defined by this library.
4
- #
5
- # Each implementation module must implement the following routines:
6
- #
7
- # * directed? # Is the graph directed?
8
- # * add_vertex!(v,l=nil) # Add a vertex to the graph and return the graph. `l` is an optional label.
9
- # * add_edge!(u,v=nil,l=nil) # Add an edge to the graph and return the graph. `u` can be an {Arc} or {Edge}, or `u,v` a {Edge} pair. The last parameter `l` is an optional label.
10
- # * remove_vertex!(v) # Remove a vertex to the graph and return the graph.
11
- # * remove_edge!(u,v=nil) # Remove an edge from the graph and return the graph.
12
- # * vertices # Returns an array of of all vertices.
13
- # * edges # Returns an array of all edges.
14
- # * edge_class # Returns the class used to store edges.
15
- module GraphAPI
16
- # @raise if the API is not completely implemented
17
- def self.included(klass)
18
- @api_methods ||= [:directed?, :add_vertex!, :add_edge!, :remove_vertex!, :remove_edge!, :vertices, :edges, :edge_class]
19
- ruby_18 { @api_methods.each { |m| m.to_s } }
20
-
21
- @api_methods.each do |meth|
22
- raise "Must implement #{meth}" unless klass.instance_methods.include?(meth)
23
- end
24
-
25
- klass.class_eval do
26
- # Is this right?
27
- alias remove_arc! remove_edge!
28
- alias add_arc! add_edge!
29
- alias arcs edges
30
- alias arc_class edge_class
31
- end
32
- end
33
-
34
- end # GraphAPI
35
- end # Plexus
@@ -1,113 +0,0 @@
1
- module Plexus
2
- # This module add support for labels.
3
- #
4
- # The graph labeling process consist in assigning labels, traditionally represented
5
- # by integers, to the edges or vertices, or both, of a graph. Plexus recommands you
6
- # abide by this rule and do use integers as labels.
7
- #
8
- # Some algorithms can make use of labeling (sea {Plexus::Search} for instance).
9
- module Labels
10
-
11
- # Return a label for an edge or vertex.
12
- def [](u)
13
- (u.is_a? Plexus::Arc) ? edge_label(u) : vertex_label(u)
14
- end
15
-
16
- # Set a label for an edge or vertex.
17
- def []=(u, value)
18
- (u.is_a? Plexus::Arc) ? edge_label_set(u, value) : vertex_label_set(u, value)
19
- end
20
-
21
- # Delete a label entirely.
22
- def delete_label(u)
23
- (u.is_a? Plexus::Arc) ? edge_label_delete(u) : vertex_label_delete(u)
24
- end
25
-
26
- # Get the label for an edge.
27
- def vertex_label(v)
28
- vertex_label_dict[v]
29
- end
30
-
31
- # Set the label for an edge.
32
- def vertex_label_set(v, l)
33
- vertex_label_dict[v] = l
34
- self
35
- end
36
-
37
- # Get the label for an edge.
38
- def edge_label(u, v = nil, n = nil)
39
- u = edge_convert(u,v,n)
40
- edge_label_dict[u]
41
- end
42
-
43
- # Set the label for an edge.
44
- def edge_label_set(u, v = nil, l = nil, n = nil)
45
- u.is_a?(Plexus::Arc) ? l = v : u = edge_convert(u, v, n)
46
- edge_label_dict[u] = l
47
- self
48
- end
49
-
50
- # Delete all graph labels.
51
- def clear_all_labels
52
- @vertex_labels = {}
53
- @edge_labels = {}
54
- end
55
-
56
- # Delete an edge label.
57
- def edge_label_delete(u, v = nil, n = nil)
58
- u = edge_convert(u, v, n)
59
- edge_label_dict.delete(u)
60
- end
61
-
62
- # Delete a vertex label.
63
- def vertex_label_delete(v)
64
- vertex_label_dict.delete(v)
65
- end
66
-
67
- protected
68
-
69
- def vertex_label_dict
70
- @vertex_labels ||= {}
71
- end
72
-
73
- def edge_label_dict
74
- @edge_labels ||= {}
75
- end
76
-
77
- # A generic cost function.
78
- #
79
- # It either calls the `weight` function with an edge constructed from the
80
- # two specified nodes, or calls the `[]` operator of the label when given
81
- # a single value.
82
- #
83
- # If no weight value is specified, the label itself is treated as the cost value.
84
- #
85
- # Note: This function will not work for Pseudo or Multi graphs at present.
86
- # FIXME: Remove u,v interface to fix Pseudo Multi graph problems.
87
- def cost(u, v = nil, weight = nil)
88
- u.is_a?(Arc) ? weight = v : u = edge_class[u,v]
89
- case weight
90
- when Proc
91
- weight.call(u)
92
- when nil
93
- self[u]
94
- else
95
- self[u][weight]
96
- end
97
- end
98
- alias property cost # makes sense for property retrieval in general
99
-
100
- # A function to set properties specified by the user.
101
- def property_set(u, name, value)
102
- case name
103
- when Proc
104
- name.call(value)
105
- when nil
106
- self[u] = value
107
- else
108
- self[u][name] = value
109
- end
110
- end
111
-
112
- end # Labels
113
- end # Plexus
@@ -1,77 +0,0 @@
1
- module Plexus
2
- class Network
3
- include DigraphBuilder
4
-
5
- attr_accessor :lower, :upper, :cost, :flow
6
-
7
- def residual(residual_capacity, cost_property, zero = 0)
8
- r = Digraph.new
9
- edges.each do |e1|
10
- [e1,e1.reverse].each do |e|
11
- rij = property(e,self.upper) - property(e,self.flow) if edge? e
12
- rij += property(e.reverse,self.flow) - property(e.reverse,self.lower) if edge? e.reverse
13
- r.add_edge!(e) if rij > zero
14
- r.property_set(e,residual_capacity, rij)
15
- r.property_set(e,cost, cost(e,cost_property))
16
- end
17
- end
18
- r
19
- end
20
-
21
- def maximum_flow() eliminate_lower_bounds.maximum_flow_prime.restore_lower_bounds(self); end
22
-
23
- private
24
-
25
- def eliminate_lower_bounds
26
- no_lower_bounds = Digraph.new(self)
27
- if self.upper.kind_of? Proc then
28
- no_lower_bounds.upper = Proc.new {|e| self.upper.call(e) - property(e,self.lower) }
29
- else
30
- no_lower_bounds.edges.each {|e| no_lower_bounds[e][self.upper] -= property(e,self.lower)}
31
- end
32
- no_lower_bounds
33
- end
34
-
35
- def restore_lower_bounds(src)
36
- src.edges.each do |e|
37
- (src.flow ? src[e][src.flow] : src[e]) = property(e, self.flow) + src.property(e, self.lower)
38
- end
39
- src
40
- end
41
-
42
- def maximum_flow_prime
43
- end
44
- end # Network
45
-
46
- module GraphBuilder
47
- module MaximumFlow
48
- # Maximum flow, it returns an array with the maximum flow and a hash of flow per edge
49
- # Currently a highly inefficient implementation, FIXME, This should use Goldberg and Tarjan's method.
50
- def maximum_flow(s, t, capacity = nil, zero = 0)
51
- flow = Hash.new(zero)
52
- available = Hash.new(zero)
53
- total = zero
54
- edges.each {|e| available[e] = cost(e,capacity)}
55
- adj_positive = Proc.new do |u|
56
- adjacent(u).select {|r| available[edge_class[u,r]] > zero}
57
- end
58
- while (tree = bfs_tree_from_vertex(start))[t]
59
- route = [t]
60
- while route[-1] != s
61
- route << tree[route[route[-1]]]
62
- raise ArgumentError, "No route from #{s} to #{t} possible"
63
- end; route.reverse
64
- amt = route.map {|e| available[e]}.min
65
- route.each do |e|
66
- flow[e] += amt
67
- available[e] -= amt
68
- end
69
- total += amt
70
- end
71
-
72
- [total, flow]
73
- end
74
-
75
- end # MaximumFlow
76
- end # GraphBuilder
77
- end # Plexus
@@ -1,17 +0,0 @@
1
- if RUBY_VERSION < "1.9"
2
- def ruby_18
3
- yield
4
- end
5
-
6
- def ruby_19
7
- false
8
- end
9
- else
10
- def ruby_18
11
- false
12
- end
13
-
14
- def ruby_19
15
- yield
16
- end
17
- end
@@ -1,510 +0,0 @@
1
- module Plexus
2
- # **Search/traversal algorithms.**
3
- #
4
- # This module defines a collection of search/traversal algorithms, in a unified API.
5
- # Read through the doc to get familiar with the calling pattern.
6
- #
7
- # Options are mostly callbacks passed in as a hash. The following are valid,
8
- # anything else is ignored:
9
- #
10
- # * `:enter_vertex` => `Proc` Called upon entry of a vertex.
11
- # * `:exit_vertex` => `Proc` Called upon exit of a vertex.
12
- # * `:root_vertex` => `Proc` Called when a vertex is the root of a tree.
13
- # * `:start_vertex` => `Proc` Called for the first vertex of the search.
14
- # * `:examine_edge` => `Proc` Called when an edge is examined.
15
- # * `:tree_edge` => `Proc` Called when the edge is a member of the tree.
16
- # * `:back_edge` => `Proc` Called when the edge is a back edge.
17
- # * `:forward_edge` => `Proc` Called when the edge is a forward edge.
18
- # * `:adjacent` => `Proc` which, given a vertex, returns adjacent nodes, defaults to adjacent call of graph useful for changing the definition of adjacent in some algorithms.
19
- # * `:start` => vertex Specifies the vertex to start search from.
20
- #
21
- # If a `&block` instead of an option hash is specified, it defines `:enter_vertex`.
22
- #
23
- # Each search algorithm returns the list of vertexes as reached by `enter_vertex`.
24
- # This allows for calls like, `g.bfs.each { |v| ... }`
25
- #
26
- # Can also be called like `bfs_examine_edge { |e| ... }` or
27
- # `dfs_back_edge { |e| ... }` for any of the callbacks.
28
- #
29
- # A full example usage is as follows:
30
- #
31
- # ev = Proc.new { |x| puts "Enter vertex #{x}" }
32
- # xv = Proc.new { |x| puts "Exit vertex #{x}" }
33
- # sv = Proc.new { |x| puts "Start vertex #{x}" }
34
- # ee = Proc.new { |x| puts "Examine Arc #{x}" }
35
- # te = Proc.new { |x| puts "Tree Arc #{x}" }
36
- # be = Proc.new { |x| puts "Back Arc #{x}" }
37
- # fe = Proc.new { |x| puts "Forward Arc #{x}" }
38
- # Digraph[1,2, 2,3, 3,4].dfs({
39
- # :enter_vertex => ev,
40
- # :exit_vertex => xv,
41
- # :start_vertex => sv,
42
- # :examine_edge => ee,
43
- # :tree_edge => te,
44
- # :back_edge => be,
45
- # :forward_edge => fe })
46
- #
47
- # Which outputs:
48
- #
49
- # Start vertex 1
50
- # Enter vertex 1
51
- # Examine Arc (1=2)
52
- # Tree Arc (1=2)
53
- # Enter vertex 2
54
- # Examine Arc (2=3)
55
- # Tree Arc (2=3)
56
- # Enter vertex 3
57
- # Examine Arc (3=4)
58
- # Tree Arc (3=4)
59
- # Enter vertex 4
60
- # Examine Arc (1=4)
61
- # Back Arc (1=4)
62
- # Exit vertex 4
63
- # Exit vertex 3
64
- # Exit vertex 2
65
- # Exit vertex 1
66
- # => [1, 2, 3, 4]
67
- module Search
68
-
69
- # Performs a breadth-first search.
70
- #
71
- # @param [Hash] options
72
- def bfs(options = {}, &block)
73
- plexus_search_helper(:shift, options, &block)
74
- end
75
- alias :bread_first_search :bfs
76
-
77
- # Performs a depth-first search.
78
- #
79
- # @param [Hash] options
80
- def dfs(options = {}, &block)
81
- plexus_search_helper(:pop, options, &block)
82
- end
83
- alias :depth_first_search :dfs
84
-
85
- # Routine which computes a spanning forest for the given search method.
86
- # Returns two values: a hash of predecessors and an array of root nodes.
87
- #
88
- # @param [vertex] start
89
- # @param [Symbol] routine the search method (`:dfs`, `:bfs`)
90
- # @return [Array] predecessors and root nodes
91
- def spanning_forest(start, routine)
92
- predecessor = {}
93
- roots = []
94
- te = Proc.new { |e| predecessor[e.target] = e.source }
95
- rv = Proc.new { |v| roots << v }
96
- send routine, :start => start, :tree_edge => te, :root_vertex => rv
97
- [predecessor, roots]
98
- end
99
-
100
- # Returns the dfs spanning forest for the given start node, see {Search#spanning_forest spanning_forest}.
101
- #
102
- # @param [vertex] start
103
- # @return [Array] predecessors and root nodes
104
- def dfs_spanning_forest(start)
105
- spanning_forest(start, :dfs)
106
- end
107
-
108
- # Returns the bfs spanning forest for the given start node, see {Search#spanning_forest spanning_forest}.
109
- #
110
- # @param [vertex] start
111
- # @return [Array] predecessors and root nodes
112
- def bfs_spanning_forest(start)
113
- spanning_forest(start, :bfs)
114
- end
115
-
116
- # Returns a hash of predecessors in a tree rooted at the start node. If this is a connected graph,
117
- # then it will be a spanning tree containing all vertices. An easier way to tell if it's a
118
- # spanning tree is to use a {Search#spanning_forest spanning_forest} call and check if there is a
119
- # single root node.
120
- #
121
- # @param [vertex] start
122
- # @param [Symbol] routine the search method (`:dfs`, `:bfs`)
123
- # @return [Hash] predecessors vertices
124
- def tree_from_vertex(start, routine)
125
- predecessor = {}
126
- correct_tree = false
127
- te = Proc.new { |e| predecessor[e.target] = e.source if correct_tree }
128
- rv = Proc.new { |v| correct_tree = (v == start) }
129
- send routine, :start => start, :tree_edge => te, :root_vertex => rv
130
- predecessor
131
- end
132
-
133
- # Returns a hash of predecessors for the depth-first search tree rooted at the given node.
134
- #
135
- # @param [vertex] start
136
- # @return [Hash] predecessors vertices
137
- def dfs_tree_from_vertex(start)
138
- tree_from_vertex(start, :dfs)
139
- end
140
-
141
- # Returns a hash of predecessors for the breadth-first search tree rooted at the given node.
142
- #
143
- # @param [Proc] start
144
- # @return [Hash] predecessors vertices
145
- def bfs_tree_from_vertex(start)
146
- tree_from_vertex(start, :bfs)
147
- end
148
-
149
- # An inner class used for greater efficiency in {Search#lexicograph_bfs}.
150
- #
151
- # Original design taken from Golumbic's, *Algorithmic Graph Theory and Perfect Graphs* pg. 87-89.
152
- class LexicographicQueue
153
- # Called with the initial values.
154
- #
155
- # @param [Array] initial vertices values
156
- def initialize(values)
157
- @node = Struct.new(:back, :forward, :data)
158
- @node.class_eval do
159
- def hash
160
- @hash
161
- end
162
- @@cnt = 0
163
- end
164
- @set = {}
165
- @tail = @node.new(nil, nil, Array.new(values))
166
- @tail.instance_eval { @hash = (@@cnt += 1) }
167
- values.each { |a| @set[a] = @tail }
168
- end
169
-
170
- # Pops an entry with the maximum lexical value from the queue.
171
- #
172
- # @return [vertex]
173
- def pop
174
- return nil unless @tail
175
- value = @tail[:data].pop
176
- @tail = @tail[:forward] while @tail and @tail[:data].size == 0
177
- @set.delete(value)
178
- value
179
- end
180
-
181
- # Increase the lexical value of the given values.
182
- #
183
- # @param [Array] vertices values
184
- def add_lexeme(values)
185
- fix = {}
186
-
187
- values.select { |v| @set[v] }.each do |w|
188
- sw = @set[w]
189
- if fix[sw]
190
- s_prime = sw[:back]
191
- else
192
- s_prime = @node.new(sw[:back], sw, [])
193
- s_prime.instance_eval { @hash = (@@cnt += 1) }
194
- @tail = s_prime if @tail == sw
195
- sw[:back][:forward] = s_prime if sw[:back]
196
- sw[:back] = s_prime
197
- fix[sw] = true
198
- end
199
-
200
- s_prime[:data] << w
201
- sw[:data].delete(w)
202
- @set[w] = s_prime
203
- end
204
-
205
- fix.keys.select { |n| n[:data].size == 0 }.each do |e|
206
- e[:forward][:back] = e[:back] if e[:forward]
207
- e[:back][:forward] = e[:forward] if e[:back]
208
- end
209
- end
210
-
211
- end
212
-
213
- # Lexicographic breadth-first search.
214
- #
215
- # The usual queue of vertices is replaced by a queue of *unordered subsets*
216
- # of the vertices, which is sometimes refined but never reordered.
217
- #
218
- # Originally developed by Rose, Tarjan, and Leuker, *Algorithmic
219
- # aspects of vertex elimination on graphs*, SIAM J. Comput. 5, 266-283
220
- # MR53 #12077
221
- #
222
- # Implementation taken from Golumbic's, *Algorithmic Graph Theory and
223
- # Perfect Graphs*, pg. 84-90.
224
- #
225
- # @return [vertex]
226
- def lexicograph_bfs(&block)
227
- lex_q = Plexus::Search::LexicographicQueue.new(vertices)
228
- result = []
229
- num_vertices.times do
230
- v = lex_q.pop
231
- result.unshift(v)
232
- lex_q.add_lexeme(adjacent(v))
233
- end
234
- result.each { |r| block.call(r) } if block
235
- result
236
- end
237
-
238
- # A* Heuristic best first search.
239
- #
240
- # `start` is the starting vertex for the search.
241
- #
242
- # `func` is a `Proc` that when passed a vertex returns the heuristic
243
- # weight of sending the path through that node. It must always
244
- # be equal to or less than the true cost.
245
- #
246
- # `options` are mostly callbacks passed in as a hash, the default block is
247
- # `:discover_vertex` and the weight is assumed to be the label for the {Arc}.
248
- # The following options are valid, anything else is ignored:
249
- #
250
- # * `:weight` => can be a `Proc`, or anything else is accessed using the `[]` for the
251
- # the label or it defaults to using
252
- # the value stored in the label for the {Arc}. If it is a `Proc` it will
253
- # pass the edge to the proc and use the resulting value.
254
- # * `:discover_vertex` => `Proc` invoked when a vertex is first discovered
255
- # and is added to the open list.
256
- # * `:examine_vertex` => `Proc` invoked when a vertex is popped from the
257
- # queue (i.e., it has the lowest cost on the open list).
258
- # * `:examine_edge` => `Proc` invoked on each out-edge of a vertex
259
- # immediately after it is examined.
260
- # * `:edge_relaxed` => `Proc` invoked on edge `(u,v) if d[u] + w(u,v) < d[v]`.
261
- # * `:edge_not_relaxed`=> `Proc` invoked if the edge is not relaxed (see above).
262
- # * `:black_target` => `Proc` invoked when a vertex that is on the closed
263
- # list is "rediscovered" via a more efficient path, and is re-added
264
- # to the open list.
265
- # * `:finish_vertex` => Proc invoked on a vertex when it is added to the
266
- # closed list, which happens after all of its out edges have been
267
- # examined.
268
- #
269
- # Can also be called like `astar_examine_edge {|e| ... }` or
270
- # `astar_edge_relaxed {|e| ... }` for any of the callbacks.
271
- #
272
- # The criteria for expanding a vertex on the open list is that it has the
273
- # lowest `f(v) = g(v) + h(v)` value of all vertices on open.
274
- #
275
- # The time complexity of A* depends on the heuristic. It is exponential
276
- # in the worst case, but is polynomial when the heuristic function h
277
- # meets the following condition: `|h(x) - h*(x)| < O(log h*(x))` where `h*`
278
- # is the optimal heuristic, i.e. the exact cost to get from `x` to the `goal`.
279
- #
280
- # See also: [A* search algorithm](http://en.wikipedia.org/wiki/A*_search_algorithm) on Wikipedia.
281
- #
282
- # @param [vertex] start the starting vertex for the search
283
- # @param [vertex] goal the vertex to reach
284
- # @param [Proc] func heuristic weight computing process
285
- # @param [Hash] options
286
- # @return [Array(vertices), call, nil] an array of nodes in path, or calls block on all nodes,
287
- # upon failure returns `nil`
288
- def astar(start, goal, func, options, &block)
289
- options.instance_eval "def handle_callback(sym,u) self[sym].call(u) if self[sym]; end"
290
-
291
- # Initialize.
292
- d = { start => 0 }
293
- color = { start => :gray } # Open is :gray, closed is :black.
294
- parent = Hash.new { |k| parent[k] = k }
295
- f = { start => func.call(start) }
296
- queue = PriorityQueue.new.push(start, f[start])
297
- block.call(start) if block
298
-
299
- # Process queue.
300
- until queue.empty?
301
- u, dummy = queue.delete_min
302
- options.handle_callback(:examine_vertex, u)
303
-
304
- # Unravel solution if the goal is reached.
305
- if u == goal
306
- solution = [goal]
307
- while u != start
308
- solution << parent[u]
309
- u = parent[u]
310
- end
311
- return solution.reverse
312
- end
313
-
314
- adjacent(u, :type => :edges).each do |e|
315
- v = e.source == u ? e.target : e.source
316
- options.handle_callback(:examine_edge, e)
317
- w = cost(e, options[:weight])
318
- raise ArgumentError unless w
319
-
320
- if d[v].nil? or (w + d[u]) < d[v]
321
- options.handle_callback(:edge_relaxed, e)
322
- d[v] = w + d[u]
323
- f[v] = d[v] + func.call(v)
324
- parent[v] = u
325
-
326
- unless color[v] == :gray
327
- options.handle_callback(:black_target, v) if color[v] == :black
328
- color[v] = :gray
329
- options.handle_callback(:discover_vertex, v)
330
- queue.push v, f[v]
331
- block.call(v) if block
332
- end
333
- else
334
- options.handle_callback(:edge_not_relaxed, e)
335
- end
336
- end # adjacent(u)
337
-
338
- color[u] = :black
339
- options.handle_callback(:finish_vertex, u)
340
- end # queue.empty?
341
-
342
- nil # failure, on fall through
343
- end # astar
344
-
345
- # `best_first` has all the same options as {Search#astar astar}, with `func` set to `h(v) = 0`.
346
- # There is an additional option, `zero`, which should be defined as the zero element
347
- # for the `+` operation performed on the objects used in the computation of cost.
348
- #
349
- # @param [vertex] start the starting vertex for the search
350
- # @param [vertex] goal the vertex to reach
351
- # @param [Proc] func heuristic weight computing process
352
- # @param [Hash] options
353
- # @param [Integer] zero (0)
354
- # @return [Array(vertices), call, nil] an array of nodes in path, or calls block on all nodes,
355
- # upon failure returns `nil`
356
- def best_first(start, goal, options, zero = 0, &block)
357
- func = Proc.new { |v| zero }
358
- astar(start, goal, func, options, &block)
359
- end
360
-
361
- # @private
362
- alias_method :pre_search_method_missing, :method_missing
363
- def method_missing(sym, *args, &block)
364
- m1 = /^dfs_(\w+)$/.match(sym.to_s)
365
- dfs((args[0] || {}).merge({ m1.captures[0].to_sym => block })) if m1
366
- m2 = /^bfs_(\w+)$/.match(sym.to_s)
367
- bfs((args[0] || {}).merge({ m2.captures[0].to_sym => block })) if m2
368
- pre_search_method_missing(sym, *args, &block) unless m1 or m2
369
- end
370
-
371
- private
372
-
373
- # Performs the search using a specific algorithm and a set of options.
374
- #
375
- # @param [Symbol] op the algorithm to be used te perform the search
376
- # @param [Hash] options
377
- # @return [Object] result
378
- def plexus_search_helper(op, options = {}, &block)
379
- return nil if size == 0
380
- result = []
381
-
382
- # Create the options hash handling callbacks.
383
- options = {:enter_vertex => block, :start => to_a[0]}.merge(options)
384
- options.instance_eval "def handle_vertex(sym,u) self[sym].call(u) if self[sym]; end"
385
- options.instance_eval "def handle_edge(sym,e) self[sym].call(e) if self[sym]; end"
386
-
387
- # Create a waiting list, which is a queue or a stack, depending on the op specified.
388
- # The first entry is the start vertex.
389
- waiting = [options[:start]]
390
- waiting.instance_eval "def next; #{op.to_s}; end"
391
-
392
- # Create a color map, all elements set to "unvisited" except for start vertex,
393
- # which will be set to waiting.
394
- color_map = vertices.inject({}) { |a,v| a[v] = :unvisited; a }
395
- color_map.merge!(waiting[0] => :waiting)
396
- options.handle_vertex(:start_vertex, waiting[0])
397
- options.handle_vertex(:root_vertex, waiting[0])
398
-
399
- # Perform the actual search until nothing is "waiting".
400
- until waiting.empty?
401
- # Loop till the search iterator exhausts the waiting list.
402
- visited_edges = {} # This prevents retraversing edges in undirected graphs.
403
- until waiting.empty?
404
- plexus_search_iteration(options, waiting, color_map, visited_edges, result, op == :pop)
405
- end
406
- # Waiting for the list to be exhausted, check if a new root vertex is available.
407
- u = color_map.detect { |key,value| value == :unvisited }
408
- waiting.push(u[0]) if u
409
- options.handle_vertex(:root_vertex, u[0]) if u
410
- end
411
-
412
- result
413
- end
414
-
415
- # Performs a search iteration (step).
416
- #
417
- # @private
418
- def plexus_search_iteration(options, waiting, color_map, visited_edges, result, recursive = false)
419
- # Fetch the next waiting vertex in the list.
420
- #sleep
421
- u = waiting.next
422
- options.handle_vertex(:enter_vertex, u)
423
- result << u
424
-
425
- # Examine all adjacent outgoing edges, but only those not previously traversed.
426
- adj_proc = options[:adjacent] || self.method(:adjacent).to_proc
427
- adj_proc.call(u, :type => :edges, :direction => :out).reject { |w| visited_edges[w] }.each do |e|
428
- e = e.reverse unless directed? or e.source == u # Preserves directionality where required.
429
- v = e.target
430
- options.handle_edge(:examine_edge, e)
431
- visited_edges[e] = true
432
-
433
- case color_map[v]
434
- # If it's unvisited, it goes into the waiting list.
435
- when :unvisited
436
- options.handle_edge(:tree_edge, e)
437
- color_map[v] = :waiting
438
- waiting.push(v)
439
- # If it's recursive (i.e. dfs), then call self.
440
- plexus_search_iteration(options, waiting, color_map, visited_edges, result, true) if recursive
441
- when :waiting
442
- options.handle_edge(:back_edge, e)
443
- else
444
- options.handle_edge(:forward_edge, e)
445
- end
446
- end
447
-
448
- # Done with this vertex!
449
- options.handle_vertex(:exit_vertex, u)
450
- color_map[u] = :visited
451
- end
452
-
453
- public
454
-
455
- # Topological Sort Iterator.
456
- #
457
- # The topological sort algorithm creates a linear ordering of the vertices
458
- # such that if edge (u,v) appears in the graph, then u comes before v in
459
- # the ordering. The graph must be a directed acyclic graph (DAG).
460
- #
461
- # The iterator can also be applied to undirected graph or to a DG graph
462
- # which contains a cycle. In this case, the Iterator does not reach all
463
- # vertices. The implementation of acyclic? and cyclic? uses this fact.
464
- #
465
- # Can be called with a block as a standard ruby iterator, or can
466
- # be used directly as it will return the result as an Array.
467
- #
468
- # @param [vertex] start (nil) the start vertex (nil will fallback on the first
469
- # vertex inserted within the graph)
470
- # @return [Array] a linear representation of the sorted graph
471
- def topsort(start = nil, &block)
472
- result = []
473
- go = true
474
- back = Proc.new { |e| go = false }
475
- push = Proc.new { |v| result.unshift(v) if go }
476
- start ||= vertices[0]
477
- dfs({ :exit_vertex => push, :back_edge => back, :start => start })
478
- result.each { |v| block.call(v) } if block
479
- result
480
- end
481
-
482
- # Does a top sort, but trudges forward if a cycle occurs. Use with caution.
483
- #
484
- # @param [vertex] start (nil) the start vertex (nil will fallback on the first
485
- # vertex inserted within the graph)
486
- # @return [Array] a linear representation of the sorted graph
487
- def sort(start = nil, &block)
488
- result = []
489
- push = Proc.new { |v| result.unshift(v) }
490
- start ||= vertices[0]
491
- dfs({ :exit_vertex => push, :start => start })
492
- result.each { |v| block.call(v) } if block
493
- result
494
- end
495
-
496
- # Returns true if a graph contains no cycles, false otherwise.
497
- #
498
- # @return [Boolean]
499
- def acyclic?
500
- topsort.size == size
501
- end
502
-
503
- # Returns false if a graph contains no cycles, true otherwise.
504
- #
505
- # @return [Boolean]
506
- def cyclic?
507
- not acyclic?
508
- end
509
- end
510
- end