gratr19 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/README +335 -0
  2. data/examples/graph_self.rb +54 -0
  3. data/examples/module_graph.jpg +0 -0
  4. data/examples/module_graph.rb +12 -0
  5. data/examples/self_graph.jpg +0 -0
  6. data/examples/visualize.jpg +0 -0
  7. data/examples/visualize.rb +8 -0
  8. data/install.rb +49 -0
  9. data/lib/gratr.rb +42 -0
  10. data/lib/gratr/adjacency_graph.rb +230 -0
  11. data/lib/gratr/base.rb +34 -0
  12. data/lib/gratr/biconnected.rb +116 -0
  13. data/lib/gratr/chinese_postman.rb +123 -0
  14. data/lib/gratr/common.rb +74 -0
  15. data/lib/gratr/comparability.rb +92 -0
  16. data/lib/gratr/digraph.rb +115 -0
  17. data/lib/gratr/digraph_distance.rb +185 -0
  18. data/lib/gratr/dot.rb +90 -0
  19. data/lib/gratr/edge.rb +145 -0
  20. data/lib/gratr/graph.rb +314 -0
  21. data/lib/gratr/graph_api.rb +82 -0
  22. data/lib/gratr/import.rb +44 -0
  23. data/lib/gratr/labels.rb +103 -0
  24. data/lib/gratr/maximum_flow.rb +107 -0
  25. data/lib/gratr/rdot.rb +332 -0
  26. data/lib/gratr/search.rb +422 -0
  27. data/lib/gratr/strong_components.rb +127 -0
  28. data/lib/gratr/undirected_graph.rb +153 -0
  29. data/lib/gratr/version.rb +6 -0
  30. data/lib/priority-queue/benchmark/dijkstra.rb +171 -0
  31. data/lib/priority-queue/compare_comments.rb +49 -0
  32. data/lib/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  33. data/lib/priority-queue/lib/priority_queue.rb +14 -0
  34. data/lib/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  35. data/lib/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  36. data/lib/priority-queue/lib/priority_queue/ruby_priority_queue.rb +525 -0
  37. data/lib/priority-queue/setup.rb +1551 -0
  38. data/lib/priority-queue/test/priority_queue_test.rb +371 -0
  39. data/tests/TestBiconnected.rb +53 -0
  40. data/tests/TestChinesePostman.rb +53 -0
  41. data/tests/TestComplement.rb +54 -0
  42. data/tests/TestDigraph.rb +333 -0
  43. data/tests/TestDigraphDistance.rb +138 -0
  44. data/tests/TestDot.rb +75 -0
  45. data/tests/TestEdge.rb +171 -0
  46. data/tests/TestInspection.rb +57 -0
  47. data/tests/TestMultiEdge.rb +57 -0
  48. data/tests/TestNeighborhood.rb +64 -0
  49. data/tests/TestProperties.rb +160 -0
  50. data/tests/TestSearch.rb +277 -0
  51. data/tests/TestStrongComponents.rb +85 -0
  52. data/tests/TestTriagulated.rb +137 -0
  53. data/tests/TestUndirectedGraph.rb +219 -0
  54. metadata +152 -0
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ # Copyright (c) 2002,2004,2005 by Horst Duchene
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice(s),
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ #++
28
+
29
+
30
+ require 'gratr/base'
31
+ require 'gratr/digraph'
32
+ require 'gratr/undirected_graph'
33
+ require 'gratr/common'
34
+
35
+ # Load priority queue classes
36
+ begin
37
+ # Use installed Gem
38
+ require 'priority_queue'
39
+ rescue LoadError # Use local copy
40
+ require 'priority-queue/lib/priority_queue/ruby_priority_queue'
41
+ PriorityQueue = RubyPriorityQueue
42
+ end
@@ -0,0 +1,230 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ # Copyright (c) 2002,2004,2005 by Horst Duchene
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice(s),
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ #++
28
+
29
+
30
+ require 'gratr/edge'
31
+ require 'gratr/graph'
32
+ require 'set'
33
+
34
+ module GRATR
35
+
36
+ # This provides the basic routines needed to implement the Digraph, UndirectedGraph,
37
+ # PseudoGraph, DirectedPseudoGraph, MultiGraph and DirectedPseudoGraph class.
38
+ module AdjacencyGraph
39
+
40
+ include Graph
41
+
42
+ class ArrayWithAdd < Array # :nodoc:
43
+ alias add push
44
+ end
45
+
46
+ # Initialization parameters can include an Array of edges to add, Graphs to
47
+ # copy (will merge if multiple)
48
+ # :parallel_edges denotes that duplicate edges are allowed
49
+ # :loops denotes that loops are allowed
50
+ def initialize(*params)
51
+ @vertex_dict = Hash.new
52
+ raise ArgumentError if params.any? do |p|
53
+ !(p.kind_of? GRATR::Graph or
54
+ p.kind_of? Array or
55
+ p == :parallel_edges or
56
+ p == :loops)
57
+ end
58
+ clear_all_labels
59
+
60
+ # Basic configuration of adjacency
61
+ @allow_loops = params.any? {|p| p == :loops}
62
+ @parallel_edges = params.any? {|p| p == :parallel_edges}
63
+ @edgelist_class = @parallel_edges ? ArrayWithAdd : Set
64
+ if @parallel_edges
65
+ @edge_number = Hash.new
66
+ @next_edge_number = 0
67
+ end
68
+
69
+ # Copy any given graph into this graph
70
+ params.select {|p| p.kind_of? GRATR::Graph}.each do |g|
71
+ g.edges.each do |e|
72
+ add_edge!(e)
73
+ edge_label_set(e, edge_label(e)) if edge_label(e)
74
+ end
75
+ g.vertices.each do |v|
76
+ vertex_label_set(v, vertex_label(v)) if vertex_label(v)
77
+ end
78
+ end
79
+
80
+ # Add all array edges specified
81
+ params.select {|p| p.kind_of? Array}.each do |a|
82
+ 0.step(a.size-1, 2) {|i| add_edge!(a[i], a[i+1])}
83
+ end
84
+
85
+ end
86
+
87
+ # Returns true if v is a vertex of this Graph
88
+ # An O(1) implementation of vertex?
89
+ def vertex?(v) @vertex_dict.has_key?(v); end
90
+
91
+ # Returns true if [u,v] or u is an Arc
92
+ # An O(1) implementation
93
+ def edge?(u, v=nil)
94
+ u, v = u.source, u.target if u.kind_of? GRATR::Arc
95
+ vertex?(u) and @vertex_dict[u].include?(v)
96
+ end
97
+
98
+ # Adds a vertex to the graph with an optional label
99
+ def add_vertex!(vertex, label=nil)
100
+ @vertex_dict[vertex] ||= @edgelist_class.new
101
+ self[vertex] = label if label
102
+ self
103
+ end
104
+
105
+ # Adds an edge to the graph
106
+ # Can be called in two basic ways, label is optional
107
+ # * add_edge!(Arc[source,target], "Label")
108
+ # * add_edge!(source,target, "Label")
109
+ def add_edge!(u, v=nil, l=nil, n=nil)
110
+ n = u.number if u.class.include? ArcNumber and n.nil?
111
+ u, v, l = u.source, u.target, u.label if u.kind_of? GRATR::Arc
112
+ return self if not @allow_loops and u == v
113
+ n = (@next_edge_number+=1) unless n if @parallel_edges
114
+ add_vertex!(u); add_vertex!(v)
115
+ @vertex_dict[u].add(v)
116
+ (@edge_number[u] ||= @edgelist_class.new).add(n) if @parallel_edges
117
+ unless directed?
118
+ @vertex_dict[v].add(u)
119
+ (@edge_number[v] ||= @edgelist_class.new).add(n) if @parallel_edges
120
+ end
121
+ self[n ? edge_class[u,v,n] : edge_class[u,v]] = l if l
122
+ self
123
+ end
124
+
125
+ # Removes a given vertex from the graph
126
+ def remove_vertex!(v)
127
+ # FIXME This is broken for multi graphs
128
+ @vertex_dict.delete(v)
129
+ @vertex_dict.each_value { |adjList| adjList.delete(v) }
130
+ @vertex_dict.keys.each do |u|
131
+ delete_label(edge_class[u,v])
132
+ delete_label(edge_class[v,u])
133
+ end
134
+ delete_label(v)
135
+ self
136
+ end
137
+
138
+ # Removes an edge from the graph, can be called with source and target or with
139
+ # and object of GRATR::Arc derivation
140
+ def remove_edge!(u, v=nil)
141
+ unless u.kind_of? GRATR::Arc
142
+ raise ArgumentError if @parallel_edges
143
+ u = edge_class[u,v]
144
+ end
145
+ raise ArgumentError if @parallel_edges and (u.number || 0) == 0
146
+ return self unless @vertex_dict[u.source] # It doesn't exist
147
+ delete_label(u) # Get rid of label
148
+ if @parallel_edges
149
+ index = @edge_number[u.source].index(u.number)
150
+ raise NoArcError unless index
151
+ @vertex_dict[u.source].delete_at(index)
152
+ @edge_number[u.source].delete_at(index)
153
+ else
154
+ @vertex_dict[u.source].delete(u.target)
155
+ end
156
+ self
157
+ end
158
+
159
+ # Returns an array of vertices that the graph has
160
+ def vertices() @vertex_dict.keys; end
161
+
162
+ # Returns an array of edges, most likely of class Arc or Edge depending
163
+ # upon the type of graph
164
+ def edges
165
+ @vertex_dict.keys.inject(Set.new) do |a,v|
166
+ if @parallel_edges and @edge_number[v]
167
+ @vertex_dict[v].zip(@edge_number[v]).each do |w|
168
+ s,t,n = v,w[0],w[1]
169
+ a.add( edge_class[ s,t,n, edge_label(s,t,n) ] )
170
+ end
171
+ else
172
+ @vertex_dict[v].each do |w|
173
+ a.add(edge_class[v,w,edge_label(v,w)])
174
+ end
175
+ end; a
176
+ end.to_a
177
+ end
178
+
179
+ alias graph_adjacent adjacent
180
+ def adjacent(x, options={})
181
+ options[:direction] ||= :out
182
+ if !x.kind_of?(GRATR::Arc) and (options[:direction] == :out || !directed?)
183
+ if options[:type] == :edges
184
+ i = -1
185
+ @parallel_edges ?
186
+ @vertex_dict[x].map {|v| e=edge_class[x,v,@edge_number[x][i+=1]]; e.label = self[e]; e} :
187
+ @vertex_dict[x].map {|v| e=edge_class[x,v]; e.label = self[e]; e}
188
+ else
189
+ @vertex_dict[x].to_a
190
+ end
191
+ else
192
+ graph_adjacent(x,options)
193
+ end
194
+ end
195
+
196
+
197
+ public
198
+
199
+ def self.included(cl)
200
+ # Shortcut for creating a Graph
201
+ #
202
+ # Example: GRATR::Digraph[1,2, 2,3, 2,4, 4,5].edges.to_a.to_s =>
203
+ # "(1-2)(2-3)(2-4)(4-5)"
204
+ #
205
+ # Or as a Hash for specifying lables
206
+ # GRATR::Digraph[ [:a,:b] => 3, [:b,:c] => 4 ] (Note: Do not use for Multi or Pseudo graphs)
207
+ def cl.[] (*a)
208
+ result = new
209
+ if a.size == 1 and a[0].kind_of? Hash
210
+ # Convert to edge class
211
+ a[0].each do |k,v|
212
+ if result.edge_class.include? GRATR::ArcNumber
213
+ result.add_edge!(result.edge_class[k[0],k[1],nil,v])
214
+ else
215
+ result.add_edge!(result.edge_class[k[0],k[1],v])
216
+ end
217
+ end
218
+ elsif a[0].kind_of? GRATR::Arc
219
+ a.each{|e| result.add_edge!(e); result[e] = e.label}
220
+ elsif a.size % 2 == 0
221
+ 0.step(a.size-1, 2) {|i| result.add_edge!(a[i], a[i+1])}
222
+ else
223
+ raise ArgumentError
224
+ end
225
+ result
226
+ end
227
+ end
228
+
229
+ end # Adjacency Graph
230
+ end # GRATR
@@ -0,0 +1,34 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ # Copyright (c) 2002,2004,2005 by Horst Duchene
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice(s),
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ #++
28
+
29
+ GRATR_VERSION = "0.4.3"
30
+
31
+ module GRATR
32
+ class NoVertexError < IndexError; end
33
+ class NoArcError < IndexError; end
34
+ end
@@ -0,0 +1,116 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice(s),
8
+ # this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
13
+ # may be used to endorse or promote products derived from this software
14
+ # without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #++
27
+
28
+
29
+ require 'set'
30
+
31
+ module GRATR
32
+ module Graph
33
+ # Biconnected is a module for adding the biconnected algorithm to
34
+ # UndirectedGraphs
35
+ module Biconnected
36
+
37
+ # biconnected computes the biconnected subgraphs
38
+ # of a graph using Tarjan's algorithm based on DFS. See: Robert E. Tarjan
39
+ # _Depth_First_Search_and_Linear_Graph_Algorithms_. SIAM Journal on
40
+ # Computing, 1(2):146-160, 1972
41
+ #
42
+ # The output of the algorithm is a pair, the first value is an
43
+ # array of biconnected subgraphs. The second is the set of
44
+ # articulation vertices.
45
+ #
46
+ # A connected graph is biconnected if the removal of any single vertex
47
+ # (and all edges incident on that vertex) cannot disconnect the graph.
48
+ # More generally, the biconnected components of a graph are the maximal
49
+ # subsets of vertices such that the removal of a vertex from a particular
50
+ # component will not disconnect the component. Unlike connected components,
51
+ # vertices may belong to multiple biconnected components: those vertices
52
+ # that belong to more than one biconnected component are called articulation
53
+ # points or, equivalently, cut vertices. Articulation points are vertices
54
+ # whose removal would increase the number of connected components in the graph.
55
+ # Thus, a graph without articulation points is biconnected.
56
+ def biconnected
57
+ dfs_num = 0
58
+ number = {}; predecessor = {}; low_point = {}
59
+ stack = []; result = []; articulation= []
60
+
61
+ root_vertex = Proc.new {|v| predecessor[v]=v }
62
+ enter_vertex = Proc.new {|u| number[u]=low_point[u]=(dfs_num+=1) }
63
+ tree_edge = Proc.new do |e|
64
+ stack.push(e)
65
+ predecessor[e.target] = e.source
66
+ end
67
+ back_edge = Proc.new do |e|
68
+ if e.target != predecessor[e.source]
69
+ stack.push(e)
70
+ low_point[e.source] = [low_point[e.source], number[e.target]].min
71
+ end
72
+ end
73
+ exit_vertex = Proc.new do |u|
74
+ parent = predecessor[u]
75
+ is_articulation_point = false
76
+ if number[parent] > number[u]
77
+ parent = predecessor[parent]
78
+ is_articulation_point = true
79
+ end
80
+ if parent == u
81
+ is_articulation_point = false if (number[u] + 1) == number[predecessor[u]]
82
+ else
83
+ low_point[parent] = [low_point[parent], low_point[u]].min
84
+ if low_point[u] >= number[parent]
85
+ if number[parent] > number[predecessor[parent]]
86
+ predecessor[u] = predecessor[parent]
87
+ predecessor[parent] = u
88
+ end
89
+ result << (component = self.class.new)
90
+ while number[stack[-1].source] >= number[u]
91
+ component.add_edge!(stack.pop)
92
+ end
93
+ component.add_edge!(stack.pop)
94
+ if stack.empty?
95
+ predecessor[u] = parent
96
+ predecessor[parent] = u
97
+ end
98
+ end
99
+ end
100
+ articulation << u if is_articulation_point
101
+ end
102
+
103
+ # Execute depth first search
104
+ dfs({:root_vertex => root_vertex,
105
+ :enter_vertex => enter_vertex,
106
+ :tree_edge => tree_edge,
107
+ :back_edge => back_edge,
108
+ :exit_vertex => exit_vertex})
109
+
110
+ [result, articulation]
111
+ end # biconnected
112
+
113
+ end # Biconnected
114
+
115
+ end # Graph
116
+ end # GRATR
@@ -0,0 +1,123 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice(s),
8
+ # this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
13
+ # may be used to endorse or promote products derived from this software
14
+ # without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #++
27
+
28
+
29
+ require 'gratr/digraph_distance'
30
+
31
+ module GRATR
32
+ module Graph
33
+ module ChinesePostman
34
+
35
+ # Returns the shortest walk that traverses all arcs at least
36
+ # once, returning to the specified start node.
37
+ def closed_chinese_postman_tour(start, weight=nil, zero=0)
38
+ cost, path, delta = floyd_warshall(weight, zero)
39
+ return nil unless cp_valid_least_cost? cost, zero
40
+ positive, negative = cp_unbalanced(delta)
41
+ f = cp_find_feasible(delta, positive, negative, zero)
42
+ while cp_improve(f, positive, negative, cost, zero); end
43
+ cp_euler_circuit(start, f, path)
44
+ end
45
+
46
+ private
47
+
48
+ def cp_euler_circuit(start, f, path) # :nodoc:
49
+ circuit = [u=v=start]
50
+ bridge_taken = Hash.new {|h,k| h[k] = Hash.new}
51
+ until v.nil?
52
+ if v=f[u].keys.detect {|k| f[u][k] > 0}
53
+ f[u][v] -= 1
54
+ circuit << (u = path[u][v]) while u != v
55
+ else
56
+ unless bridge_taken[u][bridge = path[u][start]]
57
+ v = vertices.detect {|v1| v1 != bridge && edge?(u,v1) && !bridge_taken[u][v1]} || bridge
58
+ bridge_taken[u][v] = true
59
+ circuit << v
60
+ end
61
+ end
62
+ u=v
63
+ end; circuit
64
+ end
65
+
66
+ def cp_cancel_cycle(cost, path, f, start, zero) # :nodoc:
67
+ u = start; k = nil
68
+ begin
69
+ v = path[u][start]
70
+ k = f[v][u] if cost[u][v] < zero and (k.nil? || k > f[v][u])
71
+ end until (u=v) != start
72
+ u = start
73
+ begin
74
+ v = path[u][start]
75
+ cost[u][v] < zero ? f[v][u] -= k : f[u][v] += k
76
+ end until (u=v) != start
77
+ true # This routine always returns true to make cp_improve easier
78
+ end
79
+
80
+ def cp_improve(f, positive, negative, cost, zero) # :nodoc:
81
+ residual = self.class.new
82
+ negative.each do |u|
83
+ positive.each do |v|
84
+ residual.add_edge!(u,v,cost[u][v])
85
+ residual.add_edge!(v,u,-cost[u][v]) if f[u][v] != 0
86
+ end
87
+ end
88
+ r_cost, r_path, r_delta = residual.floyd_warshall(nil, zero)
89
+ i = residual.vertices.detect {|v| r_cost[v][v] and r_cost[v][v] < zero}
90
+ i ? cp_cancel_cycle(r_cost, r_path, f, i) : false
91
+ end
92
+
93
+ def cp_find_feasible(delta, positive, negative, zero) # :nodoc:
94
+ f = Hash.new {|h,k| h[k] = Hash.new}
95
+ negative.each do |i|
96
+ positive.each do |j|
97
+ f[i][j] = -delta[i] < delta[j] ? -delta[i] : delta[j]
98
+ delta[i] += f[i][j]
99
+ delta[j] -= f[i][j]
100
+ end
101
+ end; f
102
+ end
103
+
104
+ def cp_valid_least_cost?(c, zero) # :nodoc:
105
+ vertices.each do |i|
106
+ vertices.each do |j|
107
+ return false unless c[i][j] and c[i][j] >= zero
108
+ end
109
+ end; true
110
+ end
111
+
112
+ def cp_unbalanced(delta) # :nodoc:
113
+ negative = []; positive = []
114
+ vertices.each do |v|
115
+ negative << v if delta[v] < 0
116
+ positive << v if delta[v] > 0
117
+ end; [positive, negative]
118
+ end
119
+
120
+ end # Chinese Postman
121
+ end # Graph
122
+ end # GRATR
123
+