rgl 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +75 -1
- data/Rakefile +18 -3
- data/install.rb +0 -0
- data/lib/rgl/adjacency.rb +2 -1
- data/lib/rgl/base.rb +1 -1
- data/lib/rgl/condensation.rb +47 -0
- data/lib/rgl/dot.rb +13 -13
- data/lib/rgl/rdot.rb +28 -28
- data/lib/rgl/transitiv_closure.rb +2 -46
- data/lib/rgl/transitivity.rb +179 -0
- data/tests/TestGraph.rb +36 -20
- data/tests/TestRdot.rb +237 -189
- data/tests/TestTransitivity.rb +129 -0
- metadata +138 -127
- data/tests/TestTransitiveClosure.rb +0 -26
data/ChangeLog
CHANGED
@@ -1,6 +1,80 @@
|
|
1
|
+
2008-08-28 01:36 javanthropus
|
2
|
+
|
3
|
+
* ChangeLog (tags: REL_0_4_0): pre-tag commit
|
4
|
+
|
5
|
+
2008-08-28 01:36 javanthropus
|
6
|
+
|
7
|
+
* Rakefile (tags: REL_0_4_0): Update rdoc2rf task to use rsync and
|
8
|
+
more generic remote path specification
|
9
|
+
|
10
|
+
2008-08-27 23:30 javanthropus
|
11
|
+
|
12
|
+
* lib/rgl/base.rb (tags: REL_0_4_0): Preparing for 0.4.0 release
|
13
|
+
|
14
|
+
2008-08-26 20:07 javanthropus
|
15
|
+
|
16
|
+
* lib/rgl/dot.rb, lib/rgl/rdot.rb, tests/TestRdot.rb: Move the DOT
|
17
|
+
module into the RGL module
|
18
|
+
|
19
|
+
* This eliminates a class conflict with the DOT module from rdoc
|
20
|
+
when building RGL's documentation * Also remove the superfluous
|
21
|
+
DOT prefixes from class names in the DOT module
|
22
|
+
|
23
|
+
2008-08-24 06:16 javanthropus
|
24
|
+
|
25
|
+
* Rakefile: Remove some comments I accidentally left in while
|
26
|
+
testing rdoc functionality
|
27
|
+
|
28
|
+
2008-08-24 06:03 javanthropus
|
29
|
+
|
30
|
+
* Rakefile, lib/rgl/transitiv_closure.rb, lib/rgl/transitivity.rb,
|
31
|
+
tests/TestTransitiveClosure.rb, tests/TestTransitivity.rb:
|
32
|
+
Feature 21641: Added transitive reduction functionality
|
33
|
+
|
34
|
+
* Updated the gem description to announce this functionality *
|
35
|
+
Moved the transitive closure functionality into the
|
36
|
+
transitivity.rb file along with the transitive reduction
|
37
|
+
funtionality * Modifed the transitiv_closure.rb file to simply
|
38
|
+
load the transitivity.rb file for backward compatibility * Moved
|
39
|
+
all transitivity tests into TestTransitivity.rb
|
40
|
+
|
41
|
+
2008-08-23 15:45 javanthropus
|
42
|
+
|
43
|
+
* lib/rgl/condensation.rb, lib/rgl/transitiv_closure.rb,
|
44
|
+
tests/TestTransitiveClosure.rb: Defect 21630: Fixed transitive
|
45
|
+
closure
|
46
|
+
|
47
|
+
* The fix is based on the algorithm described in the
|
48
|
+
documentation for the implementation of transitive closure in
|
49
|
+
Boost * Along with the fix, performance is improved to O(|V||E|)
|
50
|
+
* This implementation needs graph condensation, so that function
|
51
|
+
was added as well * More tests were added to cover more corner
|
52
|
+
cases
|
53
|
+
|
54
|
+
2008-08-23 05:40 javanthropus
|
55
|
+
|
56
|
+
* tests/TestGraph.rb: Update basic graph tests to account for
|
57
|
+
graphs with edgeless vertices Also clean up some minor formatting
|
58
|
+
and assertion issues
|
59
|
+
|
60
|
+
2008-08-23 05:37 javanthropus
|
61
|
+
|
62
|
+
* lib/rgl/adjacency.rb: Defect 21609: Fix the to_adjacency method
|
63
|
+
to preserve edgeless vertices
|
64
|
+
|
65
|
+
2008-03-18 15:03 javanthropus
|
66
|
+
|
67
|
+
* lib/rgl/rdot.rb, tests/TestRdot.rb: More reliably detect and
|
68
|
+
handle newlines embedded within IDs and labels
|
69
|
+
|
70
|
+
2008-03-08 10:48 monora
|
71
|
+
|
72
|
+
* ChangeLog, lib/rgl/base.rb (utags: REL_0_3_1): Prepare 0.3.1
|
73
|
+
release
|
74
|
+
|
1
75
|
2008-03-04 20:18 monora
|
2
76
|
|
3
|
-
* Rakefile (tags: REL_0_3_0): pre-tag commit
|
77
|
+
* Rakefile (tags: REL_0_3_1, REL_0_3_0): pre-tag commit
|
4
78
|
|
5
79
|
2008-03-02 18:16 javanthropus
|
6
80
|
|
data/Rakefile
CHANGED
@@ -23,6 +23,17 @@ SOURCES = FileList['lib/**/*.rb']
|
|
23
23
|
CLOBBER.include('TAGS', 'coverage')
|
24
24
|
RDOC_DIR = './rgl'
|
25
25
|
|
26
|
+
# The location for published documents to be copied.
|
27
|
+
remote_user = ENV['REMOTE_USER'] || ''
|
28
|
+
remote_host = ENV['REMOTE_HOST'] || 'rubyforge.org'
|
29
|
+
remote_path = ENV['REMOTE_PATH'] || '/var/www/gforge-projects/rgl'
|
30
|
+
remote_path += '/' unless remote_path[-1, 1] == '/'
|
31
|
+
REMOTE_RDOC_DIR = remote_path
|
32
|
+
REMOTE_RDOC_DIR.insert(
|
33
|
+
0,
|
34
|
+
remote_user + (remote_user.empty? ? '' : '@') + remote_host + ':'
|
35
|
+
) unless remote_host.empty?
|
36
|
+
|
26
37
|
# The default task is run if rake is given no explicit arguments.
|
27
38
|
|
28
39
|
desc "Default Task"
|
@@ -124,6 +135,8 @@ else
|
|
124
135
|
* Connected Components
|
125
136
|
* Strongly Connected Components
|
126
137
|
* Transitive Closure
|
138
|
+
* Transitive Reduction
|
139
|
+
* Graph Condensation
|
127
140
|
* Search cycles (contributed by Shawn Garbett)
|
128
141
|
EOF
|
129
142
|
|
@@ -211,7 +224,9 @@ end
|
|
211
224
|
|
212
225
|
desc "Copy rdoc html to rubyforge"
|
213
226
|
task :rdoc2rf => [:rdoc, :rcov, :changelog] do
|
214
|
-
|
215
|
-
|
216
|
-
|
227
|
+
cp_r 'coverage', RDOC_DIR
|
228
|
+
examples = File.join(RDOC_DIR, 'examples')
|
229
|
+
mkdir_p examples
|
230
|
+
cp Dir.glob('examples/*.jpg'), examples
|
231
|
+
sh "rsync -r --delete \"#{RDOC_DIR}\" \"#{REMOTE_RDOC_DIR}\""
|
217
232
|
end
|
data/install.rb
CHANGED
File without changes
|
data/lib/rgl/adjacency.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# adjacency.rb
|
2
2
|
#
|
3
|
-
# $Id: adjacency.rb,v 1.
|
3
|
+
# $Id: adjacency.rb,v 1.12 2008/08/23 05:37:05 javanthropus Exp $
|
4
4
|
#
|
5
5
|
# The DirectedAdjacencyGraph class implements a generalized adjacency list
|
6
6
|
# graph structure. An AdjacencyGraph is basically a two-dimensional structure
|
@@ -173,6 +173,7 @@ module RGL
|
|
173
173
|
|
174
174
|
def to_adjacency
|
175
175
|
result = (directed? ? DirectedAdjacencyGraph : AdjacencyGraph).new
|
176
|
+
each_vertex { |v| result.add_vertex(v) }
|
176
177
|
each_edge { |u,v| result.add_edge(u, v) }
|
177
178
|
result
|
178
179
|
end
|
data/lib/rgl/base.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rgl/base'
|
2
|
+
require 'rgl/implicit'
|
3
|
+
|
4
|
+
module RGL
|
5
|
+
module Graph
|
6
|
+
# Returns an RGL::ImplicitGraph where the strongly connected components of
|
7
|
+
# this graph are condensed into single nodes represented by Set instances
|
8
|
+
# containing the members of each strongly connected component. Edges
|
9
|
+
# between the different strongly connected components are preserved while
|
10
|
+
# edges within strongly connected components are omitted.
|
11
|
+
#
|
12
|
+
# Raises RGL::NotDirectedError if run on an undirected graph.
|
13
|
+
def condensation_graph
|
14
|
+
raise NotDirectedError,
|
15
|
+
"condensation_graph only supported for directed graphs" unless directed?
|
16
|
+
|
17
|
+
# Get the component map for the strongly connected components.
|
18
|
+
comp_map = strongly_connected_components.comp_map
|
19
|
+
# 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
|
21
|
+
# instances will be used to map to the number, n, with which the elements
|
22
|
+
# of the set are associated.
|
23
|
+
inv_comp_map = {}
|
24
|
+
comp_map.each { |v, n| (inv_comp_map[n] ||= Set.new) << v }
|
25
|
+
|
26
|
+
# Create an ImplicitGraph where the nodes are the strongly connected
|
27
|
+
# components of this graph and the edges are the edges of this graph which
|
28
|
+
# cross between the strongly connected components.
|
29
|
+
ImplicitGraph.new do |g|
|
30
|
+
g.vertex_iterator do |b|
|
31
|
+
inv_comp_map.each_value(&b)
|
32
|
+
end
|
33
|
+
g.adjacent_iterator do |scc, b|
|
34
|
+
scc.each do |v|
|
35
|
+
each_adjacent(v) do |w|
|
36
|
+
# Do not make the cluster reference itself in the graph.
|
37
|
+
if comp_map[v] != comp_map[w] then
|
38
|
+
b.call(inv_comp_map[comp_map[w]])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
g.directed = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/rgl/dot.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# dot.rb
|
2
|
-
#
|
3
|
-
# $Id: dot.rb,v 1.
|
4
|
-
#
|
2
|
+
#
|
3
|
+
# $Id: dot.rb,v 1.8 2008/08/26 20:07:09 javanthropus Exp $
|
4
|
+
#
|
5
5
|
# Minimal Dot support, based on Dave Thomas's dot module (included in rdoc).
|
6
6
|
# rdot.rb is a modified version which also contains support for undirected
|
7
7
|
# graphs.
|
@@ -12,20 +12,20 @@ module RGL
|
|
12
12
|
|
13
13
|
module Graph
|
14
14
|
|
15
|
-
# Return a DOT::
|
15
|
+
# Return a RGL::DOT::Digraph for directed graphs or a DOT::Subgraph for an
|
16
16
|
# undirected Graph. _params_ can contain any graph property specified in
|
17
17
|
# rdot.rb.
|
18
18
|
|
19
19
|
def to_dot_graph (params = {})
|
20
20
|
params['name'] ||= self.class.name.gsub(/:/,'_')
|
21
21
|
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
22
|
-
graph = (directed? ? DOT::
|
23
|
-
edge_class = directed? ? DOT::
|
22
|
+
graph = (directed? ? DOT::Digraph : DOT::Subgraph).new(params)
|
23
|
+
edge_class = directed? ? DOT::DirectedEdge : DOT::Edge
|
24
24
|
each_vertex do |v|
|
25
25
|
name = v.to_s
|
26
|
-
graph << DOT::
|
27
|
-
|
28
|
-
|
26
|
+
graph << DOT::Node.new('name' => name,
|
27
|
+
'fontsize' => fontsize,
|
28
|
+
'label' => name)
|
29
29
|
end
|
30
30
|
each_edge do |u,v|
|
31
31
|
graph << edge_class.new('from' => u.to_s,
|
@@ -41,8 +41,8 @@ module RGL
|
|
41
41
|
s << to_dot_graph(params).to_s << "\n"
|
42
42
|
end
|
43
43
|
|
44
|
-
# Call dotty[http://www.graphviz.org] for the graph which is written to the
|
45
|
-
# in the # current directory.
|
44
|
+
# Call dotty[http://www.graphviz.org] for the graph which is written to the
|
45
|
+
# file 'graph.dot' in the # current directory.
|
46
46
|
|
47
47
|
def dotty (params = {})
|
48
48
|
dotfile = "graph.dot"
|
@@ -58,11 +58,11 @@ module RGL
|
|
58
58
|
def write_to_graphic_file (fmt='png', dotfile="graph")
|
59
59
|
src = dotfile + ".dot"
|
60
60
|
dot = dotfile + "." + fmt
|
61
|
-
|
61
|
+
|
62
62
|
File.open(src, 'w') do |f|
|
63
63
|
f << self.to_dot_graph.to_s << "\n"
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
system( "dot -T#{fmt} #{src} -o #{dot}" )
|
67
67
|
dot
|
68
68
|
end
|
data/lib/rgl/rdot.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# It also supports undirected edges.
|
5
5
|
|
6
|
-
module DOT
|
6
|
+
module RGL; module DOT
|
7
7
|
|
8
8
|
# options for node declaration
|
9
9
|
|
@@ -131,8 +131,8 @@ module DOT
|
|
131
131
|
'layerseq'
|
132
132
|
]
|
133
133
|
|
134
|
-
# Ancestor of
|
135
|
-
class
|
134
|
+
# Ancestor of Edge, Node, and Graph.
|
135
|
+
class Element
|
136
136
|
attr_accessor :name, :options
|
137
137
|
|
138
138
|
def initialize (params = {}, option_list = []) # :nodoc:
|
@@ -151,7 +151,7 @@ module DOT
|
|
151
151
|
id = id.to_s
|
152
152
|
|
153
153
|
# Return the ID verbatim if it looks like a name, a number, or HTML.
|
154
|
-
return id if id =~
|
154
|
+
return id if id =~ /\A([[:alpha:]_][[:alnum:]_]*|-?(\.[[:digit:]]+|[[:digit:]]+(\.[[:digit:]]*)?)|<.*>)\Z/m and id[-1] != ?\n
|
155
155
|
|
156
156
|
# Return a quoted version of the ID otherwise.
|
157
157
|
'"' + id.gsub('\\', '\\\\\\\\').gsub('"', '\\\\"') + '"'
|
@@ -166,7 +166,7 @@ module DOT
|
|
166
166
|
label = label.to_s
|
167
167
|
|
168
168
|
# Return the label verbatim if it looks like a name, a number, or HTML.
|
169
|
-
return label if label =~
|
169
|
+
return label if label =~ /\A([[:alpha:]_][[:alnum:]_]*|-?(\.[[:digit:]]+|[[:digit:]]+(\.[[:digit:]]*)?)|<.*>)\Z/m and label[-1] != ?\n
|
170
170
|
|
171
171
|
# Return a quoted version of the label otherwise.
|
172
172
|
'"' + label.split(/(\\n|\\r|\\l)/).collect do |part|
|
@@ -181,9 +181,9 @@ module DOT
|
|
181
181
|
end
|
182
182
|
|
183
183
|
|
184
|
-
# Ports are used when a
|
184
|
+
# Ports are used when a Node instance has its `shape' option set to
|
185
185
|
# _record_ or _Mrecord_. Ports can be nested.
|
186
|
-
class
|
186
|
+
class Port
|
187
187
|
attr_accessor :name, :label, :ports
|
188
188
|
|
189
189
|
# Create a new port with either an optional name and label or a set of
|
@@ -222,10 +222,10 @@ module DOT
|
|
222
222
|
|
223
223
|
# A node representation. Edges are drawn between nodes. The rendering of a
|
224
224
|
# node depends upon the options set for it.
|
225
|
-
class
|
225
|
+
class Node < Element
|
226
226
|
attr_accessor :ports
|
227
227
|
|
228
|
-
# Creates a new
|
228
|
+
# Creates a new Node with the _params_ Hash providing settings for all
|
229
229
|
# node options. The _option_list_ parameter restricts those options to the
|
230
230
|
# list of valid names it contains. The exception to this is the _ports_
|
231
231
|
# option which, if specified, must be an Enumerable containing a list of
|
@@ -275,14 +275,14 @@ module DOT
|
|
275
275
|
end
|
276
276
|
end
|
277
277
|
|
278
|
-
end # class
|
278
|
+
end # class Node
|
279
279
|
|
280
280
|
# A graph representation. Whether or not it is rendered as directed or
|
281
281
|
# undirected depends on which of the programs *dot* or *neato* is used to
|
282
282
|
# process and render the graph.
|
283
|
-
class
|
283
|
+
class Graph < Element
|
284
284
|
|
285
|
-
# Creates a new
|
285
|
+
# Creates a new Graph with the _params_ Hash providing settings for all
|
286
286
|
# graph options. The _option_list_ parameter restricts those options to the
|
287
287
|
# list of valid names it contains. The exception to this is the _elements_
|
288
288
|
# option which, if specified, must be an Enumerable containing a list of
|
@@ -349,14 +349,14 @@ module DOT
|
|
349
349
|
(elements.empty? ? '' : elements + "\n") + leader + "}"
|
350
350
|
end
|
351
351
|
|
352
|
-
end # class
|
352
|
+
end # class Graph
|
353
353
|
|
354
|
-
# A digraph is a directed graph representation which is the same as a
|
354
|
+
# A digraph is a directed graph representation which is the same as a Graph
|
355
355
|
# except that its header in dot notation has an identifier of _digraph_
|
356
356
|
# instead of _graph_.
|
357
|
-
class
|
357
|
+
class Digraph < Graph
|
358
358
|
|
359
|
-
# Creates a new
|
359
|
+
# Creates a new Digraph with the _params_ Hash providing settings for all
|
360
360
|
# graph options. The _option_list_ parameter restricts those options to the
|
361
361
|
# list of valid names it contains. The exception to this is the _elements_
|
362
362
|
# option which, if specified, must be an Enumerable containing a list of
|
@@ -366,14 +366,14 @@ module DOT
|
|
366
366
|
@dot_string = 'digraph'
|
367
367
|
end
|
368
368
|
|
369
|
-
end # class
|
369
|
+
end # class Digraph
|
370
370
|
|
371
|
-
# A subgraph is a nested graph element and is the same as a
|
371
|
+
# A subgraph is a nested graph element and is the same as a Graph except
|
372
372
|
# that its header in dot notation has an identifier of _subgraph_ instead of
|
373
373
|
# _graph_.
|
374
|
-
class
|
374
|
+
class Subgraph < Graph
|
375
375
|
|
376
|
-
# Creates a new
|
376
|
+
# Creates a new Subgraph with the _params_ Hash providing settings for
|
377
377
|
# all graph options. The _option_list_ parameter restricts those options to
|
378
378
|
# list of valid names it contains. The exception to this is the _elements_
|
379
379
|
# option which, if specified, must be an Enumerable containing a list of
|
@@ -383,10 +383,10 @@ module DOT
|
|
383
383
|
@dot_string = 'subgraph'
|
384
384
|
end
|
385
385
|
|
386
|
-
end # class
|
386
|
+
end # class Subgraph
|
387
387
|
|
388
388
|
# This is an undirected edge representation.
|
389
|
-
class
|
389
|
+
class Edge < Element
|
390
390
|
|
391
391
|
# A node or subgraph reference or instance to be used as the starting point
|
392
392
|
# for an edge.
|
@@ -395,7 +395,7 @@ module DOT
|
|
395
395
|
# for an edge.
|
396
396
|
attr_accessor :to
|
397
397
|
|
398
|
-
# Creates a new
|
398
|
+
# Creates a new Edge with the _params_ Hash providing settings for all
|
399
399
|
# edge options. The _option_list_ parameter restricts those options to the
|
400
400
|
# list of valid names it contains.
|
401
401
|
def initialize (params = {}, option_list = EDGE_OPTS)
|
@@ -431,15 +431,15 @@ module DOT
|
|
431
431
|
'--'
|
432
432
|
end
|
433
433
|
|
434
|
-
end # class
|
434
|
+
end # class Edge
|
435
435
|
|
436
|
-
# A directed edge representation otherwise identical to
|
437
|
-
class
|
436
|
+
# A directed edge representation otherwise identical to Edge.
|
437
|
+
class DirectedEdge < Edge
|
438
438
|
|
439
439
|
private
|
440
440
|
def edge_link
|
441
441
|
'->'
|
442
442
|
end
|
443
443
|
|
444
|
-
end # class
|
445
|
-
end
|
444
|
+
end # class DirectedEdge
|
445
|
+
end; end # module RGL; module DOT
|
@@ -1,46 +1,2 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# == transitive_closure
|
4
|
-
#
|
5
|
-
# The transitive closure of a graph G = (V,E) is a graph G* = (V,E*),
|
6
|
-
# such that E* contains an edge (u,v) if and only if G contains a path
|
7
|
-
# (of at least one edge) from u to v. The transitive_closure() function
|
8
|
-
# transforms the input graph g into the transitive closure graph tc.
|
9
|
-
|
10
|
-
require 'rgl/adjacency'
|
11
|
-
|
12
|
-
module RGL
|
13
|
-
|
14
|
-
module Graph
|
15
|
-
|
16
|
-
# Floyd-Warshal algorithm which should be O(n^3), where n is the number of
|
17
|
-
# nodes. We can probably work a bit on the constant factors!
|
18
|
-
#
|
19
|
-
# In BGL, there is an algorithm with time complexity (worst-case) O(|V||E|)
|
20
|
-
# (see BOOST_DOC/transitive_closure.html), based on the detection of strong
|
21
|
-
# components.
|
22
|
-
|
23
|
-
def transitive_closure_floyd_warshal
|
24
|
-
raise NotDirectedError,
|
25
|
-
"transitive_closure makes sense only for directed graphs." unless directed?
|
26
|
-
tc = to_adjacency # direct links
|
27
|
-
|
28
|
-
# indirect links
|
29
|
-
|
30
|
-
each_vertex do |vi|
|
31
|
-
each_vertex do |vj|
|
32
|
-
each_vertex do |vk|
|
33
|
-
unless tc.has_edge?(vi, vj)
|
34
|
-
tc.add_edge(vi, vj) if has_edge?(vi, vk) and
|
35
|
-
has_edge?(vk, vj)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
tc
|
41
|
-
end
|
42
|
-
|
43
|
-
alias_method :transitive_closure, :transitive_closure_floyd_warshal
|
44
|
-
|
45
|
-
end # module Graph
|
46
|
-
end # module RGL
|
1
|
+
# This file is deprecated and only provided for backward compatibility.
|
2
|
+
require 'rgl/transitivity'
|