rgl 0.4.0 → 0.5.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 (72) hide show
  1. data/ChangeLog +19 -10
  2. data/Gemfile +3 -0
  3. data/{README → README.rdoc} +70 -98
  4. data/Rakefile +44 -150
  5. data/examples/canvas.rb +63 -64
  6. data/examples/examples.rb +42 -42
  7. data/examples/graph.dot +46 -0
  8. data/examples/images/example.jpg +0 -0
  9. data/examples/images/module_graph.jpg +0 -0
  10. data/examples/images/rgl_modules.png +0 -0
  11. data/examples/{insel-der-tausend-gefahren.rb → insel_der_tausend_gefahren.rb} +18 -19
  12. data/examples/north.rb +2 -2
  13. data/examples/north2.rb +11 -11
  14. data/examples/rdep-rgl.rb +218 -222
  15. data/lib/rgl/adjacency.rb +78 -74
  16. data/lib/rgl/base.rb +160 -78
  17. data/lib/rgl/bellman_ford.rb +115 -0
  18. data/lib/rgl/bidirectional.rb +17 -10
  19. data/lib/rgl/bipartite.rb +87 -0
  20. data/lib/rgl/condensation.rb +13 -4
  21. data/lib/rgl/connected_components.rb +38 -30
  22. data/lib/rgl/dijkstra.rb +158 -0
  23. data/lib/rgl/dijkstra_visitor.rb +42 -0
  24. data/lib/rgl/dot.rb +40 -32
  25. data/lib/rgl/edge_properties_map.rb +55 -0
  26. data/lib/rgl/edmonds_karp.rb +136 -0
  27. data/lib/rgl/enumerable_ext.rb +4 -1
  28. data/lib/rgl/graph_iterator.rb +15 -0
  29. data/lib/rgl/graph_visitor.rb +138 -0
  30. data/lib/rgl/graph_wrapper.rb +15 -0
  31. data/lib/rgl/graphxml.rb +20 -10
  32. data/lib/rgl/implicit.rb +68 -66
  33. data/lib/rgl/mutable.rb +37 -31
  34. data/lib/rgl/path_builder.rb +40 -0
  35. data/lib/rgl/prim.rb +52 -0
  36. data/lib/rgl/rdot.rb +411 -374
  37. data/lib/rgl/topsort.rb +23 -16
  38. data/lib/rgl/transitivity.rb +29 -27
  39. data/lib/rgl/traversal.rb +67 -205
  40. data/rakelib/dep_graph.rake +4 -3
  41. data/test/bellman_ford_test.rb +187 -0
  42. data/test/bipartite_test.rb +47 -0
  43. data/test/components_test.rb +80 -0
  44. data/test/cycles_test.rb +60 -0
  45. data/test/dijkstra_test.rb +148 -0
  46. data/test/directed_graph_test.rb +118 -0
  47. data/test/dot_test.rb +26 -0
  48. data/test/edge_properties_map_test.rb +63 -0
  49. data/test/edge_test.rb +35 -0
  50. data/test/edmonds_karp_test.rb +105 -0
  51. data/{tests/TestGraph.rb → test/graph_test.rb} +6 -6
  52. data/test/graph_xml_test.rb +57 -0
  53. data/test/implicit_test.rb +53 -0
  54. data/test/prim_test.rb +98 -0
  55. data/{tests/TestRdot.rb → test/rdot_test.rb} +309 -308
  56. data/{tests → test}/test_helper.rb +4 -1
  57. data/{tests/TestTransitivity.rb → test/transitivity_test.rb} +43 -43
  58. data/test/traversal_test.rb +221 -0
  59. data/test/undirected_graph_test.rb +103 -0
  60. metadata +226 -145
  61. data/examples/example.jpg +0 -0
  62. data/examples/module_graph.jpg +0 -0
  63. data/install.rb +0 -49
  64. data/tests/TestComponents.rb +0 -65
  65. data/tests/TestCycles.rb +0 -61
  66. data/tests/TestDirectedGraph.rb +0 -125
  67. data/tests/TestDot.rb +0 -18
  68. data/tests/TestEdge.rb +0 -34
  69. data/tests/TestGraphXML.rb +0 -57
  70. data/tests/TestImplicit.rb +0 -52
  71. data/tests/TestTraversal.rb +0 -220
  72. data/tests/TestUnDirectedGraph.rb +0 -102
@@ -0,0 +1,115 @@
1
+ require 'rgl/dijkstra_visitor'
2
+ require 'rgl/edge_properties_map'
3
+ require 'rgl/path_builder'
4
+
5
+ require 'delegate'
6
+ require 'algorithms'
7
+
8
+ module RGL
9
+
10
+ # Bellman-Ford shortest paths algorithm has the following event points:
11
+ #
12
+ # * examine_edge
13
+ # * edge_relaxed
14
+ # * edge_not_relaxed
15
+ # * edge_minimized
16
+ # * edge_not_minimized
17
+ #
18
+ class BellmanFordVisitor < DijkstraVisitor
19
+
20
+ def_event_handlers :edge_minimized, :edge_not_minimized
21
+
22
+ def initialize(graph)
23
+ super(graph)
24
+
25
+ # by default, through an exception if a negative-weight cycle is detected
26
+ @edge_not_minimized_event_handler = lambda do |u, v|
27
+ raise ArgumentError.new("there is a negative-weight cycle including edge (#{u}, #{v})")
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ class BellmanFordAlgorithm
34
+
35
+ # Initializes Bellman-Ford algorithm for a _graph_ with provided edges weights map.
36
+ #
37
+ def initialize(graph, edge_weights_map, visitor)
38
+ @graph = graph
39
+ @edge_weights_map = EdgePropertiesMap.new(edge_weights_map, @graph.directed?)
40
+ @visitor = visitor
41
+ end
42
+
43
+ # Finds the shortest path form the _source_ to every other vertex of the graph.
44
+ #
45
+ # Returns the shortest paths map that contains the shortest path (if it exists) from the source to any vertex of the
46
+ # graph.
47
+ #
48
+ def shortest_paths(source)
49
+ init(source)
50
+ relax_edges
51
+ PathBuilder.new(source, @visitor.parents_map).paths(@graph.vertices)
52
+ end
53
+
54
+ private
55
+
56
+ def init(source)
57
+ @visitor.set_source(source)
58
+ end
59
+
60
+ def relax_edges
61
+ (@graph.size - 1).times do
62
+ @graph.each_edge do |u, v|
63
+ relax_edge(u, v)
64
+ relax_edge(v, u) unless @graph.directed?
65
+ end
66
+ end
67
+
68
+ @graph.each_edge do |u, v|
69
+ if @visitor.distance_map[u] + @edge_weights_map.edge_property(u, v) < @visitor.distance_map[v]
70
+ @visitor.handle_edge_not_minimized(u, v)
71
+ else
72
+ @visitor.handle_edge_minimized(u, v)
73
+ end
74
+ end
75
+ end
76
+
77
+ def relax_edge(u, v)
78
+ @visitor.handle_examine_edge(u, v)
79
+
80
+ new_v_distance = @visitor.distance_map[u] + @edge_weights_map.edge_property(u, v)
81
+
82
+ if new_v_distance < @visitor.distance_map[v]
83
+ @visitor.distance_map[v] = new_v_distance
84
+ @visitor.parents_map[v] = u
85
+
86
+ @visitor.handle_edge_relaxed(u, v)
87
+ else
88
+ @visitor.handle_edge_not_relaxed(u, v)
89
+ end
90
+ end
91
+
92
+ end # class BellmanFordAlgorithm
93
+
94
+ module Graph
95
+
96
+ # Finds the shortest paths from the _source_ to each vertex of the graph.
97
+ #
98
+ # Returns a Hash that maps each vertex of the graph to an Array of vertices that represents the shortest path
99
+ # from the _source_ to the vertex. If the path doesn't exist, the corresponding hash value is nil. For the _source_
100
+ # vertex returned hash contains a trivial one-vertex path - [source].
101
+ #
102
+ # Unlike Dijkstra algorithm, Bellman-Ford shortest paths algorithm works with negative edge weights.
103
+ #
104
+ # Raises ArgumentError if an edge weight is undefined.
105
+ #
106
+ # Raises ArgumentError or the graph has negative-weight cycles. This behavior can be overridden my a custom handler
107
+ # for visitor's _edge_not_minimized_ event.
108
+ #
109
+ def bellman_ford_shortest_paths(edge_weights_map, source, visitor = BellmanFordVisitor.new(self))
110
+ BellmanFordAlgorithm.new(self, edge_weights_map, visitor).shortest_paths(source)
111
+ end
112
+
113
+ end # module Graph
114
+
115
+ end # module RGL
@@ -3,38 +3,45 @@ require 'rgl/base'
3
3
  module RGL
4
4
 
5
5
  # BGL defines the concept BidirectionalGraph as follows:
6
- #
6
+ #
7
7
  # The BidirectionalGraph concept refines IncidenceGraph and adds the
8
- # requirement for efficient access to the in-edges of each vertex. This
8
+ # requirement for efficient access to the in-edges of each vertex. This
9
9
  # concept is separated from IncidenceGraph because, for directed graphs,
10
10
  # efficient access to in-edges typically requires more storage space,
11
- # and many algorithms do not require access to in-edges. For undirected
11
+ # and many algorithms do not require access to in-edges. For undirected
12
12
  # graphs, this is not an issue; because the in_edges() and out_edges()
13
13
  # functions are the same, they both return the edges incident to the vertex.
14
+ #
14
15
  module BidirectionalGraph
16
+
15
17
  include Graph
16
18
 
17
19
  # Iterator providing access to the in-edges (for directed graphs) or incident
18
20
  # edges (for undirected graphs) of vertex _v_. For both directed and
19
21
  # undirected graphs, the target of an out-edge is required to be vertex _v_
20
22
  # and the source is required to be a vertex that is adjacent to _v_.
21
- def each_in_neighbor (v)
23
+ #
24
+ def each_in_neighbor(v)
22
25
  raise NotImplementedError
23
26
  yield u
24
27
  end
25
-
28
+
26
29
  # Returns the number of in-edges (for directed graphs) or the number of
27
30
  # incident edges (for undirected graphs) of vertex _v_.
28
- def in_degree (v)
29
- r = 0;
30
- each_in_neighbor(v) { |u| r += 1}
31
+ #
32
+ def in_degree(v)
33
+ r = 0
34
+ each_in_neighbor(v) { |u| r += 1 }
31
35
  r
32
36
  end
33
37
 
34
- # Returns the number of in-edges plus out-edges (for directed graphs) or the
38
+ # Returns the number of in-edges plus out-edges (for directed graphs) or the
35
39
  # number of incident edges (for undirected graphs) of vertex _v_.
36
- def degree (v)
40
+ #
41
+ def degree(v)
37
42
  in_degree(v) + out_degree(v)
38
43
  end
44
+
39
45
  end
46
+
40
47
  end
@@ -0,0 +1,87 @@
1
+ require 'rgl/base'
2
+ require 'rgl/traversal'
3
+
4
+ module RGL
5
+
6
+ module Graph
7
+
8
+ # Separates graph's vertices into two disjoint sets so that every edge of the graph connects vertices from different
9
+ # sets. If it's possible, the graph is bipartite.
10
+ #
11
+ # Returns an array of two disjoint vertices sets (represented as arrays) if the graph is bipartite. Otherwise,
12
+ # returns nil.
13
+ #
14
+ def bipartite_sets
15
+ raise NotUndirectedError.new('bipartite sets can only be found for an undirected graph') if directed?
16
+
17
+ bfs = BipartiteBFSIterator.new(self)
18
+
19
+ # if necessary, we start BFS from each vertex to make sure
20
+ # that all connected components of the graph are processed
21
+ each_vertex do |u|
22
+ next if bfs.finished_vertex?(u)
23
+
24
+ bfs.reset_start(u)
25
+ bfs.move_forward_until { @found_odd_cycle }
26
+
27
+ return if bfs.found_odd_cycle
28
+ end
29
+
30
+ bfs.bipartite_sets_map.inject([[], []]) do |sets, (vertex, set)|
31
+ sets[set] << vertex
32
+ sets
33
+ end
34
+ end
35
+
36
+ # Returns true if the graph is bipartite. Otherwise returns false.
37
+ #
38
+ def bipartite?
39
+ !bipartite_sets.nil?
40
+ end
41
+
42
+ end # module Graph
43
+
44
+ class BipartiteBFSIterator < BFSIterator
45
+
46
+ attr_reader :bipartite_sets_map, :found_odd_cycle
47
+
48
+ def reset
49
+ super
50
+
51
+ @bipartite_sets_map = {}
52
+ @found_odd_cycle = false
53
+ end
54
+
55
+ def set_to_begin
56
+ super
57
+
58
+ @bipartite_sets_map[@start_vertex] = 0
59
+ end
60
+
61
+ def reset_start(new_start)
62
+ @start_vertex = new_start
63
+ set_to_begin
64
+ end
65
+
66
+ def handle_tree_edge(u, v)
67
+ @bipartite_sets_map[v] = (@bipartite_sets_map[u] + 1) % 2 unless u.nil? # put v into the other set
68
+ end
69
+
70
+ def handle_back_edge(u, v)
71
+ verify_odd_cycle(u, v)
72
+ end
73
+
74
+ def handle_forward_edge(u, v)
75
+ verify_odd_cycle(u, v)
76
+ end
77
+
78
+ private
79
+
80
+ def verify_odd_cycle(u, v)
81
+ u_set = @bipartite_sets_map[u]
82
+ @found_odd_cycle = true if u_set && u_set == @bipartite_sets_map[v]
83
+ end
84
+
85
+ end # class BipartiteBFSIterator
86
+
87
+ end # module RGL
@@ -1,23 +1,28 @@
1
1
  require 'rgl/base'
2
+ require 'rgl/connected_components'
2
3
  require 'rgl/implicit'
3
4
 
4
5
  module RGL
6
+
5
7
  module Graph
8
+
6
9
  # Returns an RGL::ImplicitGraph where the strongly connected components of
7
10
  # this graph are condensed into single nodes represented by Set instances
8
- # containing the members of each strongly connected component. Edges
11
+ # containing the members of each strongly connected component. Edges
9
12
  # between the different strongly connected components are preserved while
10
13
  # edges within strongly connected components are omitted.
11
14
  #
12
15
  # Raises RGL::NotDirectedError if run on an undirected graph.
16
+ #
13
17
  def condensation_graph
14
18
  raise NotDirectedError,
15
- "condensation_graph only supported for directed graphs" unless directed?
19
+ "condensation_graph only supported for directed graphs" unless directed?
16
20
 
17
21
  # Get the component map for the strongly connected components.
18
22
  comp_map = strongly_connected_components.comp_map
23
+
19
24
  # Invert the map such that for any number, n, in the component map a Set
20
- # instance is created containing all of the nodes which map to n. The Set
25
+ # instance is created containing all of the nodes which map to n. The Set
21
26
  # instances will be used to map to the number, n, with which the elements
22
27
  # of the set are associated.
23
28
  inv_comp_map = {}
@@ -30,18 +35,22 @@ module RGL
30
35
  g.vertex_iterator do |b|
31
36
  inv_comp_map.each_value(&b)
32
37
  end
38
+
33
39
  g.adjacent_iterator do |scc, b|
34
40
  scc.each do |v|
35
41
  each_adjacent(v) do |w|
36
42
  # Do not make the cluster reference itself in the graph.
37
- if comp_map[v] != comp_map[w] then
43
+ if comp_map[v] != comp_map[w]
38
44
  b.call(inv_comp_map[comp_map[w]])
39
45
  end
40
46
  end
41
47
  end
42
48
  end
49
+
43
50
  g.directed = true
44
51
  end
45
52
  end
53
+
46
54
  end
55
+
47
56
  end
@@ -11,7 +11,7 @@ module RGL
11
11
  module Graph
12
12
 
13
13
  # Compute the connected components of an undirected graph, using a
14
- # DFS (Depth-first search)-based approach. A _connected component_ of
14
+ # DFS (Depth-first search)-based approach. A _connected component_ of
15
15
  # an undirected graph is a set of vertices that are all reachable
16
16
  # from each other.
17
17
  #
@@ -19,32 +19,35 @@ module RGL
19
19
  # with an array of vertices for each component.
20
20
  #
21
21
  # It raises an exception if the graph is directed.
22
-
22
+ #
23
23
  def each_connected_component
24
24
  raise NotUndirectedError,
25
- "each_connected_component only works " +
26
- "for undirected graphs." if directed?
25
+ "each_connected_component only works " +
26
+ "for undirected graphs." if directed?
27
+
27
28
  comp = []
28
29
  vis = DFSVisitor.new(self)
29
30
  vis.set_finish_vertex_event_handler { |v| comp << v }
30
- vis.set_start_vertex_event_handler { |v|
31
+
32
+ vis.set_start_vertex_event_handler do |v|
31
33
  yield comp unless comp.empty?
32
34
  comp = []
33
- }
35
+ end
36
+
34
37
  depth_first_search(vis) { |v| }
35
38
  yield comp unless comp.empty?
36
39
  end
37
40
 
38
41
  # This GraphVisitor is used by strongly_connected_components to compute
39
42
  # the strongly connected components of a directed graph.
40
-
43
+ #
41
44
  class TarjanSccVisitor < DFSVisitor
42
45
 
43
46
  attr_reader :comp_map
44
47
 
45
48
  # Creates a new TarjanSccVisitor for graph _g_, which should be directed.
46
-
47
- def initialize (g)
49
+ #
50
+ def initialize(g)
48
51
  super g
49
52
  @root_map = {}
50
53
  @comp_map = {}
@@ -54,25 +57,28 @@ module RGL
54
57
  @stack = []
55
58
  end
56
59
 
57
- def handle_examine_vertex (v)
58
- @root_map[v] = v
59
- @comp_map[v] = -1
60
- @dfs_time += 1
60
+ def handle_examine_vertex(v)
61
+ @root_map[v] = v
62
+ @comp_map[v] = -1
63
+ @dfs_time += 1
61
64
  @discover_time_map[v] = @dfs_time
62
65
  @stack.push(v)
63
66
  end
64
67
 
65
- def handle_finish_vertex (v)
68
+ def handle_finish_vertex(v)
66
69
  # Search adjacent vertex w with earliest discover time
67
70
  root_v = @root_map[v]
71
+
68
72
  graph.each_adjacent(v) do |w|
69
73
  if @comp_map[w] == -1
70
74
  root_v = min_discover_time(root_v, @root_map[w])
71
75
  end
72
76
  end
77
+
73
78
  @root_map[v] = root_v
74
- if root_v == v # v is topmost vertex of a SCC
75
- begin # pop off all vertices until v
79
+
80
+ if root_v == v # v is topmost vertex of a SCC
81
+ begin # pop off all vertices until v
76
82
  w = @stack.pop
77
83
  @comp_map[w] = @c_index
78
84
  end until w == v
@@ -81,32 +87,32 @@ module RGL
81
87
  end
82
88
 
83
89
  # Return the number of components found so far.
84
-
90
+ #
85
91
  def num_comp
86
92
  @c_index
87
93
  end
88
94
 
89
95
  private
90
96
 
91
- def min_discover_time (u, v)
97
+ def min_discover_time(u, v)
92
98
  @discover_time_map[u] < @discover_time_map[v] ? u : v
93
99
  end
94
100
 
95
- end # class TarjanSccVisitor
101
+ end # class TarjanSccVisitor
96
102
 
97
103
  # This is Tarjan's algorithm for strongly connected components, from his
98
- # paper "Depth first search and linear graph algorithms". It calculates
99
- # the components in a single application of DFS. We implement the
104
+ # paper "Depth first search and linear graph algorithms". It calculates
105
+ # the components in a single application of DFS. We implement the
100
106
  # algorithm with the help of the DFSVisitor TarjanSccVisitor.
101
107
  #
102
108
  # === Definition
103
- #
109
+ #
104
110
  # A _strongly connected component_ of a directed graph G=(V,E) is a
105
111
  # maximal set of vertices U which is in V, such that for every pair of
106
- # vertices u and v in U, we have both a path from u to v and a path
112
+ # vertices u and v in U, we have both a path from u to v and a path
107
113
  # from v to u. That is to say, u and v are reachable from each other.
108
- #
109
- # @Article{Tarjan:1972:DFS,
114
+ #
115
+ # @Article!{Tarjan:1972:DFS,
110
116
  # author = "R. E. Tarjan",
111
117
  # key = "Tarjan",
112
118
  # title = "Depth First Search and Linear Graph Algorithms",
@@ -121,18 +127,20 @@ module RGL
121
127
  # bibdate = "Thu Jan 23 09:56:44 1997",
122
128
  # bibsource = "Parallel/Multi.bib, Misc/Reverse.eng.bib",
123
129
  # }
124
- #
130
+ #
125
131
  # The output of the algorithm is recorded in a TarjanSccVisitor _vis_.
126
132
  # vis.comp_map will contain numbers giving the component ID assigned to
127
133
  # each vertex. The number of components is vis.num_comp.
128
-
134
+ #
129
135
  def strongly_connected_components
130
136
  raise NotDirectedError,
131
- "strong_components only works for directed graphs." unless directed?
137
+ "strong_components only works for directed graphs." unless directed?
138
+
132
139
  vis = TarjanSccVisitor.new(self)
133
140
  depth_first_search(vis) { |v| }
134
141
  vis
135
142
  end
136
143
 
137
- end # module Graph
138
- end # module RGL
144
+ end # module Graph
145
+
146
+ end # module RGL