gratr 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Grater.xcf +0 -0
  2. data/README +328 -0
  3. data/Rakefile +220 -0
  4. data/examples/graph_self.rb +54 -0
  5. data/examples/module_graph.jpg +0 -0
  6. data/examples/module_graph.rb +12 -0
  7. data/examples/self_graph.jpg +0 -0
  8. data/examples/visualize.jpg +0 -0
  9. data/examples/visualize.rb +8 -0
  10. data/install.rb +49 -0
  11. data/lib/gratr.rb +33 -0
  12. data/lib/gratr/adjacency_graph.rb +230 -0
  13. data/lib/gratr/base.rb +34 -0
  14. data/lib/gratr/biconnected.rb +116 -0
  15. data/lib/gratr/chinese_postman.rb +123 -0
  16. data/lib/gratr/common.rb +73 -0
  17. data/lib/gratr/comparability.rb +92 -0
  18. data/lib/gratr/digraph.rb +113 -0
  19. data/lib/gratr/digraph_distance.rb +185 -0
  20. data/lib/gratr/dot.rb +90 -0
  21. data/lib/gratr/edge.rb +145 -0
  22. data/lib/gratr/graph.rb +315 -0
  23. data/lib/gratr/graph_api.rb +82 -0
  24. data/lib/gratr/import.rb +44 -0
  25. data/lib/gratr/labels.rb +103 -0
  26. data/lib/gratr/maximum_flow.rb +107 -0
  27. data/lib/gratr/rdot.rb +326 -0
  28. data/lib/gratr/search.rb +409 -0
  29. data/lib/gratr/strong_components.rb +127 -0
  30. data/lib/gratr/undirected_graph.rb +153 -0
  31. data/tests/TestBiconnected.rb +53 -0
  32. data/tests/TestChinesePostman.rb +53 -0
  33. data/tests/TestComplement.rb +54 -0
  34. data/tests/TestDigraph.rb +333 -0
  35. data/tests/TestDigraphDistance.rb +138 -0
  36. data/tests/TestEdge.rb +171 -0
  37. data/tests/TestInspection.rb +57 -0
  38. data/tests/TestMultiEdge.rb +57 -0
  39. data/tests/TestNeighborhood.rb +64 -0
  40. data/tests/TestProperties.rb +160 -0
  41. data/tests/TestSearch.rb +257 -0
  42. data/tests/TestStrongComponents.rb +85 -0
  43. data/tests/TestTriagulated.rb +137 -0
  44. data/tests/TestUndirectedGraph.rb +219 -0
  45. metadata +92 -0
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/ruby -I../lib
2
+
3
+ require 'gratr/import'
4
+ require 'gratr/dot'
5
+
6
+ # This program gives an example of dynamic analysis of a program's call stack,
7
+ # that exports to dot and creates a jpg visualization of the call diagram.
8
+
9
+ class GraphSelf
10
+
11
+ # Setup some data to call Dijkstra's Algorithm
12
+ def initialize
13
+ @d = Digraph[ [:a,:b] => 9, [:a,:e] => 3,
14
+ [:b,:c] => 2, [:b,:e] => 6,
15
+ [:c,:d] => 1,
16
+ [:d,:c] => 2,
17
+ [:e,:b] => 2, [:e,:f] => 1,
18
+ [:f,:c] => 2, [:f,:d] => 7, [:f,:e] => 2 ]
19
+
20
+ @call_stack = []
21
+ @call_graph = Digraph.new
22
+ end
23
+
24
+ # Get the call graph variable
25
+ def call_graph() @call_graph; end
26
+
27
+ # Turn capturing of call graph on
28
+ def capture_func
29
+ Proc.new do |event, f, l, id, b, klass|
30
+ # Only interested in the GRATR library itself
31
+ if ( klass.to_s =~ /GRATR/ )
32
+ case event.to_s
33
+ when /call/
34
+ method = "#{klass.to_s.split('::')[1]}.#{id}" # Removes GRATR::
35
+ @call_graph.add_edge!(@call_stack[-1],method) if @call_stack[-1]
36
+ @call_stack.push(method)
37
+ when /return/ : @call_stack.pop
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ # Run a capture of the call graph for Dijkstra's algorithm
44
+ def run
45
+ set_trace_func capture_func
46
+ @d.dijkstras_algorithm(:a)
47
+ set_trace_func nil
48
+ self
49
+ end
50
+
51
+ end
52
+
53
+ # Run a capture and generate the resulting jpg file
54
+ GraphSelf.new.run.call_graph.write_to_graphic_file('jpg','self_graph')
Binary file
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/ruby -I../lib
2
+
3
+ require 'gratr/import'
4
+ require 'gratr/dot'
5
+
6
+ module_graph=Digraph.new
7
+ ObjectSpace.each_object(Module) do |m|
8
+ m.ancestors.each {|a| module_graph.add_edge!(m,a) if m != a}
9
+ end
10
+
11
+ gv = module_graph.vertices.select {|v| v.to_s.match(/GRATR/)}
12
+ module_graph.induced_subgraph(gv).write_to_graphic_file('jpg','module_graph')
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby -I../lib
2
+
3
+ require 'gratr/import'
4
+ require 'gratr/dot'
5
+
6
+ dg = Digraph[1,2, 2,3, 2,4, 4,5, 6,4, 1,6]
7
+
8
+ dg.write_to_graphic_file('jpg','visualize')
@@ -0,0 +1,49 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ require 'rbconfig'
5
+ require 'ftools'
6
+ require 'find'
7
+
8
+ SRC_BASE = 'lib'
9
+ SRC = 'gratr'
10
+
11
+
12
+ INSTDIR = File.join Config::CONFIG['sitedir']
13
+ DESTDIR = File.join INSTDIR, SRC
14
+
15
+ opts = GetoptLong.new( [ "--uninstall", "-u", GetoptLong::NO_ARGUMENT ] )
16
+
17
+ def install
18
+ begin
19
+ File.makedirs( DESTDIR )
20
+ pwd = Dir.pwd
21
+ Dir.chdir(SRC_BASE)
22
+ Dir['*.rb'].each do |file|
23
+ dst = File.join( INSTDIR, file )
24
+ File.install(file, dst, 0644, true)
25
+ end
26
+ Find.find(SRC) do |file|
27
+ dst = File.join( INSTDIR, file )
28
+ File.install(file, dst, 0644, true) if file =~ /.rb$/
29
+ end
30
+ Dir.chdir(pwd)
31
+ rescue
32
+ puts $!
33
+ end
34
+ end
35
+
36
+ def uninstall
37
+ begin
38
+ puts "Deleting:"
39
+ Find.find(DESTDIR) { |file| File.rm_f file,true }
40
+ Dir.delete DESTDIR
41
+ rescue
42
+ end
43
+ end
44
+
45
+ if (opt = opts.get) and opt[0] =~ /^-?-u/
46
+ uninstall
47
+ else
48
+ install
49
+ end
@@ -0,0 +1,33 @@
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'
@@ -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.2"
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