rgl 0.2.2
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.
- data/ChangeLog +74 -0
- data/Makefile +72 -0
- data/README +240 -0
- data/Rakefile +210 -0
- data/TAGS +209 -0
- data/examples/canvas.rb +103 -0
- data/examples/codegraph +238 -0
- data/examples/example.jpg +0 -0
- data/examples/examples.rb +112 -0
- data/examples/graph.dot +54 -0
- data/examples/graph.png +0 -0
- data/examples/module_graph.jpg +0 -0
- data/examples/north.rb +12 -0
- data/examples/north/Graph.log +128 -0
- data/examples/north/g.10.0.graphml +28 -0
- data/examples/north/g.10.1.graphml +28 -0
- data/examples/north/g.10.11.graphml +31 -0
- data/examples/north/g.10.12.graphml +27 -0
- data/examples/north/g.10.13.graphml +27 -0
- data/examples/north/g.10.14.graphml +27 -0
- data/examples/north/g.10.15.graphml +26 -0
- data/examples/north/g.10.16.graphml +26 -0
- data/examples/north/g.10.17.graphml +26 -0
- data/examples/north/g.10.19.graphml +37 -0
- data/examples/north/g.10.2.graphml +28 -0
- data/examples/north/g.10.20.graphml +38 -0
- data/examples/north/g.10.22.graphml +43 -0
- data/examples/north/g.10.24.graphml +30 -0
- data/examples/north/g.10.25.graphml +45 -0
- data/examples/north/g.10.27.graphml +38 -0
- data/examples/north/g.10.28.graphml +30 -0
- data/examples/north/g.10.29.graphml +38 -0
- data/examples/north/g.10.3.graphml +26 -0
- data/examples/north/g.10.30.graphml +34 -0
- data/examples/north/g.10.31.graphml +42 -0
- data/examples/north/g.10.34.graphml +42 -0
- data/examples/north/g.10.37.graphml +28 -0
- data/examples/north/g.10.38.graphml +38 -0
- data/examples/north/g.10.39.graphml +36 -0
- data/examples/north/g.10.4.graphml +26 -0
- data/examples/north/g.10.40.graphml +37 -0
- data/examples/north/g.10.41.graphml +37 -0
- data/examples/north/g.10.42.graphml +26 -0
- data/examples/north/g.10.45.graphml +28 -0
- data/examples/north/g.10.46.graphml +32 -0
- data/examples/north/g.10.5.graphml +31 -0
- data/examples/north/g.10.50.graphml +30 -0
- data/examples/north/g.10.56.graphml +29 -0
- data/examples/north/g.10.57.graphml +32 -0
- data/examples/north/g.10.58.graphml +32 -0
- data/examples/north/g.10.6.graphml +26 -0
- data/examples/north/g.10.60.graphml +32 -0
- data/examples/north/g.10.61.graphml +34 -0
- data/examples/north/g.10.62.graphml +34 -0
- data/examples/north/g.10.68.graphml +30 -0
- data/examples/north/g.10.69.graphml +32 -0
- data/examples/north/g.10.7.graphml +29 -0
- data/examples/north/g.10.70.graphml +26 -0
- data/examples/north/g.10.71.graphml +27 -0
- data/examples/north/g.10.72.graphml +28 -0
- data/examples/north/g.10.74.graphml +29 -0
- data/examples/north/g.10.75.graphml +29 -0
- data/examples/north/g.10.78.graphml +27 -0
- data/examples/north/g.10.79.graphml +34 -0
- data/examples/north/g.10.8.graphml +29 -0
- data/examples/north/g.10.80.graphml +34 -0
- data/examples/north/g.10.82.graphml +35 -0
- data/examples/north/g.10.83.graphml +32 -0
- data/examples/north/g.10.85.graphml +34 -0
- data/examples/north/g.10.86.graphml +34 -0
- data/examples/north/g.10.88.graphml +37 -0
- data/examples/north/g.10.89.graphml +29 -0
- data/examples/north/g.10.9.graphml +26 -0
- data/examples/north/g.10.90.graphml +32 -0
- data/examples/north/g.10.91.graphml +31 -0
- data/examples/north/g.10.92.graphml +26 -0
- data/examples/north/g.10.93.graphml +32 -0
- data/examples/north/g.10.94.graphml +34 -0
- data/examples/north/g.12.8.graphml +40 -0
- data/examples/north/g.14.9.graphml +36 -0
- data/examples/north2.rb +21 -0
- data/examples/rdep-rgl.rb +395 -0
- data/install.rb +49 -0
- data/lib/rgl/adjacency.rb +151 -0
- data/lib/rgl/base.rb +299 -0
- data/lib/rgl/connected_components.rb +125 -0
- data/lib/rgl/dot.rb +63 -0
- data/lib/rgl/graphxml.rb +52 -0
- data/lib/rgl/implicit.rb +151 -0
- data/lib/rgl/mutable.rb +54 -0
- data/lib/rgl/rdot.rb +264 -0
- data/lib/rgl/topsort.rb +61 -0
- data/lib/rgl/transitiv_closure.rb +34 -0
- data/lib/rgl/traversal.rb +296 -0
- data/tests/TestComponents.rb +67 -0
- data/tests/TestDirectedGraph.rb +100 -0
- data/tests/TestEdge.rb +33 -0
- data/tests/TestGraphXML.rb +57 -0
- data/tests/TestImplicit.rb +52 -0
- data/tests/TestTransitiveClosure.rb +29 -0
- data/tests/TestTraversal.rb +222 -0
- data/tests/TestUnDirectedGraph.rb +98 -0
- metadata +163 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# This file contains the algorithms for the connected components of an
|
|
2
|
+
# undirected graph (each_connected_component) and strongly connected components
|
|
3
|
+
# for directed graphs (strongly_connected_components).
|
|
4
|
+
#
|
|
5
|
+
require 'rgl/traversal'
|
|
6
|
+
|
|
7
|
+
module RGL
|
|
8
|
+
module Graph
|
|
9
|
+
# Compute the connected components of an undirected graph using a DFS-based
|
|
10
|
+
# approach. A <b>connected component</b> of an undirected graph is a set of
|
|
11
|
+
# vertices that are all reachable from each other.
|
|
12
|
+
#
|
|
13
|
+
# The function is implemented as an iterator which calls the client with an
|
|
14
|
+
# array of vertices for each component.
|
|
15
|
+
#
|
|
16
|
+
# It raises an exception if the graph is directed.
|
|
17
|
+
def each_connected_component
|
|
18
|
+
raise NotUndirectedError, "each_connected_component only works for undirected graphs." if directed?
|
|
19
|
+
comp = []
|
|
20
|
+
vis = DFSVisitor.new(self)
|
|
21
|
+
vis.set_finish_vertex_event_handler {|v| comp << v}
|
|
22
|
+
vis.set_start_vertex_event_handler {|v|
|
|
23
|
+
yield comp unless comp.empty?
|
|
24
|
+
comp = []
|
|
25
|
+
}
|
|
26
|
+
depth_first_search(vis) {|v|}
|
|
27
|
+
yield comp unless comp.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# This GraphVisitor is used by strongly_connected_components to compute the
|
|
31
|
+
# strongly connected components of a directed graph.
|
|
32
|
+
#
|
|
33
|
+
#
|
|
34
|
+
class TarjanSccVisitor < DFSVisitor
|
|
35
|
+
attr_reader :comp_map
|
|
36
|
+
|
|
37
|
+
# Creates a new TarjanSccVisitor for graph _g_, which should be directed.
|
|
38
|
+
def initialize(g)
|
|
39
|
+
super g
|
|
40
|
+
@root_map = {}
|
|
41
|
+
@comp_map = {}
|
|
42
|
+
@discover_time_map = {}
|
|
43
|
+
@dfs_time = 0
|
|
44
|
+
@c_index = 0
|
|
45
|
+
@stack = []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handle_examine_vertex(v)
|
|
49
|
+
@root_map[v] = v
|
|
50
|
+
@comp_map[v] = -1
|
|
51
|
+
@dfs_time += 1
|
|
52
|
+
@discover_time_map[v] = @dfs_time
|
|
53
|
+
@stack.push v
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_finish_vertex(v)
|
|
57
|
+
# Search adjacent vertex w with earliest discover time
|
|
58
|
+
root_v = @root_map[v]
|
|
59
|
+
graph.each_adjacent(v) do |w|
|
|
60
|
+
if @comp_map[w] == -1
|
|
61
|
+
root_v = min_discover_time(root_v,@root_map[w])
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
@root_map[v] = root_v
|
|
65
|
+
if root_v == v # v is topmost vertex of a SCC
|
|
66
|
+
begin # pop off all vertices until v
|
|
67
|
+
w = @stack.pop
|
|
68
|
+
@comp_map[w] = @c_index
|
|
69
|
+
end until w == v
|
|
70
|
+
@c_index += 1
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Return the number of components found so far.
|
|
75
|
+
def num_comp
|
|
76
|
+
@c_index
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def min_discover_time(u,v)
|
|
82
|
+
@discover_time_map[u] < @discover_time_map[v] ? u : v
|
|
83
|
+
end
|
|
84
|
+
end # TarjanSccVisitor
|
|
85
|
+
|
|
86
|
+
# This is Tarjan's algorithm for strongly connected components
|
|
87
|
+
# from his paper "Depth first search and linear graph algorithms".
|
|
88
|
+
# It calculates the components in a single application of DFS.
|
|
89
|
+
# We implement the algorithm with the help of the DFSVisitor
|
|
90
|
+
# TarjanSccVisitor.
|
|
91
|
+
#
|
|
92
|
+
# === Definition
|
|
93
|
+
#
|
|
94
|
+
# A <b>strongly connected component</b> of a directed graph G=(V,E) is a
|
|
95
|
+
# maximal set of vertices U which is in V such that for every pair of
|
|
96
|
+
# vertices u and v in U, we have both a path from u to v and path from v to
|
|
97
|
+
# u. That is to say that u and v are reachable from each other.
|
|
98
|
+
#
|
|
99
|
+
# @Article{Tarjan:1972:DFS,
|
|
100
|
+
# author = "R. E. Tarjan",
|
|
101
|
+
# key = "Tarjan",
|
|
102
|
+
# title = "Depth First Search and Linear Graph Algorithms",
|
|
103
|
+
# journal = "SIAM Journal on Computing",
|
|
104
|
+
# volume = "1",
|
|
105
|
+
# number = "2",
|
|
106
|
+
# pages = "146--160",
|
|
107
|
+
# month = jun,
|
|
108
|
+
# year = "1972",
|
|
109
|
+
# CODEN = "SMJCAT",
|
|
110
|
+
# ISSN = "0097-5397 (print), 1095-7111 (electronic)",
|
|
111
|
+
# bibdate = "Thu Jan 23 09:56:44 1997",
|
|
112
|
+
# bibsource = "Parallel/Multi.bib, Misc/Reverse.eng.bib",
|
|
113
|
+
# }
|
|
114
|
+
#
|
|
115
|
+
# The output of the algorithm is recorded in a TarjanSccVisitor _vis_.
|
|
116
|
+
# vis.comp_map will contain numbers giving the component ID assigned to each
|
|
117
|
+
# vertex. The number of components is vis.num_comp.
|
|
118
|
+
def strongly_connected_components
|
|
119
|
+
raise NotDirectedError, "strong_components only works for directed graphs." unless directed?
|
|
120
|
+
vis = TarjanSccVisitor.new(self)
|
|
121
|
+
depth_first_search(vis) {|v|}
|
|
122
|
+
vis
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/rgl/dot.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#
|
|
2
|
+
# $Id: dot.rb,v 1.4 2002/11/13 21:53:27 monora Exp $
|
|
3
|
+
#
|
|
4
|
+
# Minimal Dot support based on Dave Thomas dot module (included in
|
|
5
|
+
# rdoc). rdot.rb is a modified version which also contains support for
|
|
6
|
+
# undirected graphs.
|
|
7
|
+
|
|
8
|
+
require 'rgl/rdot'
|
|
9
|
+
|
|
10
|
+
module RGL
|
|
11
|
+
module Graph
|
|
12
|
+
# Return a DOT::DOTDigraph for directed graphs or a DOT::DOTSubgraph for an
|
|
13
|
+
# undirected Graph. _params_ can contain any graph property specified in
|
|
14
|
+
# rdot.rb.
|
|
15
|
+
def to_dot_graph( params = {} )
|
|
16
|
+
params['name'] ||= self.class.name.gsub(/:/,'_')
|
|
17
|
+
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
|
18
|
+
graph = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)
|
|
19
|
+
edge_class = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge
|
|
20
|
+
each_vertex do |v|
|
|
21
|
+
name = v.to_s
|
|
22
|
+
graph << DOT::DOTNode.new('name' => '"' + name + '"',
|
|
23
|
+
'fontsize' => fontsize,
|
|
24
|
+
'label' => name)
|
|
25
|
+
end
|
|
26
|
+
each_edge do |u,v|
|
|
27
|
+
graph << edge_class.new('from' => '"'+ u.to_s + '"',
|
|
28
|
+
'to' => '"'+ v.to_s + '"',
|
|
29
|
+
'fontsize' => fontsize)
|
|
30
|
+
end
|
|
31
|
+
graph
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Output the DOT-graph to stream _s_.
|
|
35
|
+
def print_dotted_on (params = {}, s=$stdout)
|
|
36
|
+
s << to_dot_graph(params).to_s << "\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Call +dotty+ for the graph which is written to the file 'graph.dot' in the
|
|
40
|
+
# current directory.
|
|
41
|
+
def dotty( params = {} )
|
|
42
|
+
dotfile = "graph.dot"
|
|
43
|
+
File.open(dotfile, "w") {|f|
|
|
44
|
+
print_dotted_on(params, f)
|
|
45
|
+
}
|
|
46
|
+
system("dotty", dotfile)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Use +do+ to create a graphical representation of the graph. Returns the
|
|
50
|
+
# filename of the graphics file.
|
|
51
|
+
def write_to_graphic_file(fmt='png', dotfile="graph")
|
|
52
|
+
src = dotfile + ".dot"
|
|
53
|
+
dot = dotfile + "." + fmt
|
|
54
|
+
|
|
55
|
+
File.open(src, 'w') do |f|
|
|
56
|
+
f << self.to_dot_graph.to_s << "\n"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
system( "dot -T#{fmt} #{src} -o #{dot}" )
|
|
60
|
+
dot
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/rgl/graphxml.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# This file contains minimal support for creating RGL graphs from the GraphML
|
|
2
|
+
# format (see http://www.graphdrawing.org/graphml). The main purpose is to
|
|
3
|
+
# have a rich set of example graphs to have some more tests.
|
|
4
|
+
#
|
|
5
|
+
# See the examples directory which contains a subdirectory _north_ with the
|
|
6
|
+
# Graph catalog GraphViz (see http://www.research.att.com/sw/tools/graphviz/refs.html).
|
|
7
|
+
#
|
|
8
|
+
# We use REXML::StreamListener from the REXML library
|
|
9
|
+
# (http://www.germane-software.com/software/rexml) to parse the grapml files.
|
|
10
|
+
|
|
11
|
+
require 'rgl/mutable'
|
|
12
|
+
require 'rexml/document'
|
|
13
|
+
require 'rexml/streamlistener'
|
|
14
|
+
|
|
15
|
+
module RGL
|
|
16
|
+
# Module GraphXML adds to each class including module MutableGraph a class
|
|
17
|
+
# method from_graphxml.
|
|
18
|
+
#
|
|
19
|
+
# Attention: Because append_features is used to provide the
|
|
20
|
+
# functionality GraphXML must be loaded before the concrete class
|
|
21
|
+
# implementing including MutableGraph is loaded.
|
|
22
|
+
module GraphXML
|
|
23
|
+
class MutableGraphParser
|
|
24
|
+
include REXML::StreamListener
|
|
25
|
+
attr_reader :graph
|
|
26
|
+
def initialize(graph)
|
|
27
|
+
@graph = graph
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def tag_start(name, attrs)
|
|
31
|
+
case name
|
|
32
|
+
when 'edge'
|
|
33
|
+
@graph.add_edge(attrs['source'],
|
|
34
|
+
attrs['target'])
|
|
35
|
+
when 'node'
|
|
36
|
+
@graph.add_vertex(attrs['id'])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def MutableGraph.append_features(includingClass)
|
|
42
|
+
super
|
|
43
|
+
|
|
44
|
+
# Create a new MutableGraph from the XML-Source _source_.
|
|
45
|
+
def includingClass.from_graphxml (source)
|
|
46
|
+
listener = MutableGraphParser.new(self.new)
|
|
47
|
+
REXML::Document.parse_stream(source, listener)
|
|
48
|
+
listener.graph
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/rgl/implicit.rb
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# This file contains the definition of the class RGL::ImplicitGraph which
|
|
2
|
+
# defines vertex and edge iterators using blocks (which again call blocks).
|
|
3
|
+
#
|
|
4
|
+
# An ImplicitGraph provides a handy way to define graphs on the fly using two
|
|
5
|
+
# blocks for the two iterators defining a graph. The directed cyclic graph with
|
|
6
|
+
# 5 vertices can be created as follows:
|
|
7
|
+
#
|
|
8
|
+
# g = RGL::ImplicitGraph.new { |g|
|
|
9
|
+
# g.vertex_iterator { |b| 0.upto(4,&b) }
|
|
10
|
+
# g.adjacent_iterator { |x, b| b.call((x+1)%5) }
|
|
11
|
+
# g.directed = true
|
|
12
|
+
# }
|
|
13
|
+
# g.to_s => "(0-1)(1-2)(2-3)(3-4)(4-0)"
|
|
14
|
+
#
|
|
15
|
+
# Other examples are given by the methods vertices_filtered_by and
|
|
16
|
+
# edges_filtered_by, which can be applied to any graph.
|
|
17
|
+
require 'rgl/base'
|
|
18
|
+
|
|
19
|
+
module RGL
|
|
20
|
+
class ImplicitGraph
|
|
21
|
+
include Graph
|
|
22
|
+
|
|
23
|
+
attr_writer :directed
|
|
24
|
+
|
|
25
|
+
EMPTY_VERTEX_ITERATOR = proc { |b| }
|
|
26
|
+
EMPTY_NEIGHBOR_ITERATOR = proc { |x, b| }
|
|
27
|
+
|
|
28
|
+
# Create a new ImplicitGraph, which is by default empty. The caller should
|
|
29
|
+
# configure the with vertex and neighbor iterators. If the graph is directed
|
|
30
|
+
# the client should set directed to true. The default value for _directed_
|
|
31
|
+
# is false.
|
|
32
|
+
def initialize
|
|
33
|
+
@directed = false
|
|
34
|
+
@vertex_iterator = EMPTY_VERTEX_ITERATOR
|
|
35
|
+
@adjacent_iterator = EMPTY_NEIGHBOR_ITERATOR
|
|
36
|
+
yield self if block_given? # let client overwrite defaults
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns the value of @directed.
|
|
40
|
+
def directed?; @directed; end
|
|
41
|
+
|
|
42
|
+
def each_vertex (&block) # :nodoc:
|
|
43
|
+
@vertex_iterator.call(block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def each_adjacent (v, &block) # :nodoc:
|
|
47
|
+
@adjacent_iterator.call(v,block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def each_edge (&block) # :nodoc:
|
|
51
|
+
if defined? @edge_iterator
|
|
52
|
+
@edge_iterator.call(block)
|
|
53
|
+
else
|
|
54
|
+
super # use default implementation
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Sets the vertex_iterator to _block_ which must be a block of one parameter
|
|
59
|
+
# which again is the block called by each_vertex.
|
|
60
|
+
def vertex_iterator (&block)
|
|
61
|
+
@vertex_iterator = block
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Sets the adjacent_iterator to _block_ which must be a block of two
|
|
65
|
+
# parameters:
|
|
66
|
+
# The first parameter is the vertex the neighbors of which are to be
|
|
67
|
+
# traversed. The second is the block which will be called for each neighbor
|
|
68
|
+
# of this vertex.
|
|
69
|
+
def adjacent_iterator (&block)
|
|
70
|
+
@adjacent_iterator = block
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Sets the edge_iterator to _block_ which must be a block of two parameters:
|
|
74
|
+
# The first parameter is the source of the edges an the second is the target
|
|
75
|
+
# of the edge.
|
|
76
|
+
def edge_iterator (&block)
|
|
77
|
+
@edge_iterator = block
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module Graph
|
|
82
|
+
# ---
|
|
83
|
+
# === Graph adaptors
|
|
84
|
+
#
|
|
85
|
+
# Return a new ImplicitGraph which has as vertices all vertices of the
|
|
86
|
+
# receiver which satisfy the predicate _filter_.
|
|
87
|
+
#
|
|
88
|
+
# The methods provides similar functionaty as the BGL graph adapter
|
|
89
|
+
# filtered_graph (see BOOST_DOC/filtered_graph.html).
|
|
90
|
+
#
|
|
91
|
+
# ==== Example
|
|
92
|
+
#
|
|
93
|
+
# def complete (n)
|
|
94
|
+
# set = n.integer? ? (1..n) : n
|
|
95
|
+
# RGL::ImplicitGraph.new { |g|
|
|
96
|
+
# g.vertex_iterator { |b| set.each(&b) }
|
|
97
|
+
# g.adjacent_iterator { |x, b|
|
|
98
|
+
# set.each { |y| b.call(y) unless x == y }
|
|
99
|
+
# }
|
|
100
|
+
# }
|
|
101
|
+
# end
|
|
102
|
+
#
|
|
103
|
+
# complete(4).to_s => "(1=2)(1=3)(1=4)(2=3)(2=4)(3=4)"
|
|
104
|
+
# complete(4).vertices_filtered_by {|v| v != 4}.to_s => "(1=2)(1=3)(2=3)"
|
|
105
|
+
def vertices_filtered_by (&filter)
|
|
106
|
+
implicit_graph {|g|
|
|
107
|
+
g.vertex_iterator { |b|
|
|
108
|
+
self.each_vertex { |v| b.call(v) if filter.call(v) }
|
|
109
|
+
}
|
|
110
|
+
g.adjacent_iterator { |v, b|
|
|
111
|
+
self.each_adjacent(v) { |u| b.call(u) if filter.call(u) }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Return a new ImplicitGraph which has as edges all edges of the receiver
|
|
117
|
+
# which satisfy the predicate _filter_ (a block with to parameters).
|
|
118
|
+
#
|
|
119
|
+
# ==== Example
|
|
120
|
+
#
|
|
121
|
+
# g = complete(7).edges_filtered_by {|u,v| u+v == 7}
|
|
122
|
+
# g.to_s => "(1=6)(2=5)(3=4)"
|
|
123
|
+
# g.vertices => [1, 2, 3, 4, 5, 6, 7]
|
|
124
|
+
def edges_filtered_by (&filter)
|
|
125
|
+
implicit_graph {
|
|
126
|
+
|g|
|
|
127
|
+
g.adjacent_iterator { |v, b|
|
|
128
|
+
self.each_adjacent(v) { |u|
|
|
129
|
+
b.call(u) if filter.call(v,u)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
g.edge_iterator { |b|
|
|
133
|
+
self.each_edge {|u,v| b.call(u,v) if filter.call(u,v)}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Return a new ImplicitGraph which is isomorphic (i.e. has same edges and
|
|
139
|
+
# vertices) to the receiver. It is a shortcut also used by
|
|
140
|
+
# edges_filtered_by and vertices_filtered_by.
|
|
141
|
+
def implicit_graph
|
|
142
|
+
result = ImplicitGraph.new {|g|
|
|
143
|
+
g.vertex_iterator { |b| self.each_vertex(&b)}
|
|
144
|
+
g.adjacent_iterator { |v, b| self.each_adjacent(v,&b)}
|
|
145
|
+
g.directed = self.directed?
|
|
146
|
+
}
|
|
147
|
+
yield result if block_given? # let client overwrite defaults
|
|
148
|
+
result
|
|
149
|
+
end
|
|
150
|
+
end # module Graph
|
|
151
|
+
end
|
data/lib/rgl/mutable.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'rgl/base'
|
|
2
|
+
|
|
3
|
+
module RGL
|
|
4
|
+
# A MutableGraph can be changed via the addition or removal of edges and
|
|
5
|
+
# vertices.
|
|
6
|
+
module MutableGraph
|
|
7
|
+
include Graph
|
|
8
|
+
|
|
9
|
+
# Add a new vertex _v_ to the graph. If the vertex is already in (using
|
|
10
|
+
# eql?) the method does nothing.
|
|
11
|
+
def add_vertex(v); raise NotImplementedError; end
|
|
12
|
+
|
|
13
|
+
# Inserts the edge (u,v) into the graph.
|
|
14
|
+
#
|
|
15
|
+
# Note that for undirected graphs, (u,v) is the same edge as (v,u), so after
|
|
16
|
+
# a call to the function add_edge(), this implies that edge (u,v) will
|
|
17
|
+
# appear in the out-edges of u and (u,v) (or equivalently (v,u)) will appear
|
|
18
|
+
# in the out-edges of
|
|
19
|
+
# v. Put another way, v will be adjacent to u and u will be adjacent to v.
|
|
20
|
+
def add_edge(u, v); raise NotImplementedError; end
|
|
21
|
+
|
|
22
|
+
# Add all objects in _a_ to the vertex set.
|
|
23
|
+
def add_vertices (*a)
|
|
24
|
+
a.each {|v| add_vertex v}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add all edges in the _edges_ array to the edge set. Element of the array
|
|
28
|
+
# can be both two element arrays or instances of DirectedEdge or
|
|
29
|
+
# UnDirectedEdge.
|
|
30
|
+
def add_edges (*edges)
|
|
31
|
+
edges.each {|edge| add_edge(edge[0],edge[1])}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Remove u from the vertex set of the graph. All edges who's target is _v_
|
|
35
|
+
# are also removed from the edge set of the graph.
|
|
36
|
+
#
|
|
37
|
+
# Postcondition: num_vertices is one less, _v_ no longer appears in the
|
|
38
|
+
# vertex set of the graph and there no edge with source or target _v_.
|
|
39
|
+
def remove_vertex(v); raise NotImplementedError; end
|
|
40
|
+
|
|
41
|
+
# Remove the edge (u,v) from the graph. If the graph allows parallel edges
|
|
42
|
+
# this remove all occurrences of (u,v).
|
|
43
|
+
#
|
|
44
|
+
# Precondition: u and v are vertices in the graph.
|
|
45
|
+
# Postcondition: (u,v) is no longer in the edge set for g.
|
|
46
|
+
def remove_edge(u, v); raise NotImplementedError; end
|
|
47
|
+
|
|
48
|
+
# Remove all vertices specified by the array a from the graph calling
|
|
49
|
+
# remove_vertex.
|
|
50
|
+
def remove_vertices (*a)
|
|
51
|
+
a.each {|v| remove_vertex v}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|