plexus 0.5.4 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/LICENSE +37 -0
- data/README.md +208 -0
- data/Rakefile +25 -0
- data/lib/plexus.rb +90 -0
- data/lib/plexus/adjacency_graph.rb +225 -0
- data/lib/plexus/arc.rb +60 -0
- data/lib/plexus/arc_number.rb +50 -0
- data/lib/plexus/biconnected.rb +84 -0
- data/lib/plexus/chinese_postman.rb +91 -0
- data/lib/plexus/classes/graph_classes.rb +28 -0
- data/lib/plexus/common.rb +63 -0
- data/lib/plexus/comparability.rb +63 -0
- data/lib/plexus/directed_graph.rb +78 -0
- data/lib/plexus/directed_graph/algorithms.rb +95 -0
- data/lib/plexus/directed_graph/distance.rb +167 -0
- data/lib/plexus/dot.rb +94 -0
- data/lib/plexus/edge.rb +38 -0
- data/lib/plexus/ext.rb +79 -0
- data/lib/plexus/graph.rb +628 -0
- data/lib/plexus/graph_api.rb +35 -0
- data/lib/plexus/labels.rb +112 -0
- data/lib/plexus/maximum_flow.rb +77 -0
- data/lib/plexus/ruby_compatibility.rb +17 -0
- data/lib/plexus/search.rb +510 -0
- data/lib/plexus/strong_components.rb +93 -0
- data/lib/plexus/support/support.rb +9 -0
- data/lib/plexus/undirected_graph.rb +56 -0
- data/lib/plexus/undirected_graph/algorithms.rb +90 -0
- data/lib/plexus/version.rb +6 -0
- data/spec/biconnected_spec.rb +27 -0
- data/spec/chinese_postman_spec.rb +27 -0
- data/spec/community_spec.rb +44 -0
- data/spec/complement_spec.rb +27 -0
- data/spec/digraph_distance_spec.rb +121 -0
- data/spec/digraph_spec.rb +339 -0
- data/spec/dot_spec.rb +48 -0
- data/spec/edge_spec.rb +158 -0
- data/spec/inspection_spec.rb +38 -0
- data/spec/multi_edge_spec.rb +32 -0
- data/spec/neighborhood_spec.rb +36 -0
- data/spec/properties_spec.rb +146 -0
- data/spec/search_spec.rb +227 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/strong_components_spec.rb +61 -0
- data/spec/triangulated_spec.rb +125 -0
- data/spec/undirected_graph_spec.rb +220 -0
- data/vendor/priority-queue/CHANGELOG +33 -0
- data/vendor/priority-queue/Makefile +140 -0
- data/vendor/priority-queue/README +133 -0
- data/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
- data/vendor/priority-queue/compare_comments.rb +49 -0
- data/vendor/priority-queue/doc/c-vs-rb.png +0 -0
- data/vendor/priority-queue/doc/compare_big.gp +14 -0
- data/vendor/priority-queue/doc/compare_big.png +0 -0
- data/vendor/priority-queue/doc/compare_small.gp +15 -0
- data/vendor/priority-queue/doc/compare_small.png +0 -0
- data/vendor/priority-queue/doc/results.csv +37 -0
- data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
- data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
- data/vendor/priority-queue/lib/priority_queue.rb +14 -0
- data/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
- data/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
- data/vendor/priority-queue/priority_queue.so +0 -0
- data/vendor/priority-queue/setup.rb +1551 -0
- data/vendor/priority-queue/test/priority_queue_test.rb +371 -0
- data/vendor/rdot.rb +360 -0
- metadata +100 -10
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Copyright (c) 2010 Jean-Denis Vauguet <jd@vauguet.fr>
|
2
|
+
|
3
|
+
Copyright (c) 2009 Bruce Williams
|
4
|
+
|
5
|
+
Copyright (c) 2007,2006 Shawn Patrick Garbett
|
6
|
+
|
7
|
+
Copyright (c) 2002,2004,2005 by Horst Duchene
|
8
|
+
|
9
|
+
Copyright (c) 2000,2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
|
10
|
+
|
11
|
+
All rights reserved.
|
12
|
+
|
13
|
+
Jeremy Siek was one of the principal developers of the Boost Graph library.
|
14
|
+
Since this work is derivative, his name is included in the copyright list.
|
15
|
+
|
16
|
+
Redistribution and use in source and binary forms, with or without modification,
|
17
|
+
are permitted provided that the following conditions are met:
|
18
|
+
|
19
|
+
* Redistributions of source code must retain the above copyright notice(s),
|
20
|
+
this list of conditions and the following disclaimer.
|
21
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
22
|
+
this list of conditions and the following disclaimer in the documentation
|
23
|
+
and/or other materials provided with the distribution.
|
24
|
+
* Neither the name of the Bruce Williams nor the names of its contributors
|
25
|
+
may be used to endorse or promote products derived from this software
|
26
|
+
without specific prior written permission.
|
27
|
+
|
28
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
29
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
30
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
31
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
32
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
33
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
34
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
35
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
36
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
37
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# Plexus (was Graphy). A framework for graph theory, graph data structures and associated algorithms.
|
2
|
+
|
3
|
+
Graph algorithms currently provided are:
|
4
|
+
|
5
|
+
* Topological Sort
|
6
|
+
* Strongly Connected Components
|
7
|
+
* Transitive Closure
|
8
|
+
* Rural Chinese Postman
|
9
|
+
* Biconnected
|
10
|
+
|
11
|
+
These are based on more general algorithm patterns:
|
12
|
+
|
13
|
+
* Breadth First Search
|
14
|
+
* Depth First Search
|
15
|
+
* A* Search
|
16
|
+
* Floyd-Warshall
|
17
|
+
* Best First Search
|
18
|
+
* Djikstra's Algorithm
|
19
|
+
* Lexicographic Search
|
20
|
+
|
21
|
+
## A quick Tour
|
22
|
+
|
23
|
+
### Arcs
|
24
|
+
|
25
|
+
There are two vertices bound classes, `Plexus::Arc` and `Plexus::Edge`. The
|
26
|
+
former defines directional edges, the latter undirected edges.
|
27
|
+
|
28
|
+
### Vertices
|
29
|
+
|
30
|
+
Vertices can be any `Object`.
|
31
|
+
|
32
|
+
### Graph Types
|
33
|
+
|
34
|
+
There are a number of different graph types, each of which provide
|
35
|
+
different features and constraints:
|
36
|
+
|
37
|
+
`Plexus::Digraph` and its alias `Plexus::DirectedGraph`:
|
38
|
+
|
39
|
+
* Single directed edges (arcs) between vertices
|
40
|
+
* Loops are forbidden
|
41
|
+
|
42
|
+
`Plexus::DirectedPseudoGraph`:
|
43
|
+
|
44
|
+
* Multiple directed edges (arcs) between vertices
|
45
|
+
* Loops are forbidden
|
46
|
+
|
47
|
+
`Plexus::DirectedMultiGraph`:
|
48
|
+
|
49
|
+
* Multiple directed edges (arcs) between vertices
|
50
|
+
* Loops on vertices
|
51
|
+
|
52
|
+
`Plexus::UndirectedGraph`, `Plexus::UndirectedPseudoGraph`, and
|
53
|
+
`Graph::UndirectedMultiGraph` are similar but all edges are undirected.
|
54
|
+
|
55
|
+
### Data Structures
|
56
|
+
|
57
|
+
In order to modelize data structures, make use of the `Plexus::AdjacencyGraph`
|
58
|
+
module which provides a generalized adjacency list and an edge list adaptor.
|
59
|
+
|
60
|
+
The `Plexus::Digraph` class is the general purpose "swiss army knife" of graph
|
61
|
+
classes, most of the other classes are just modifications to this class.
|
62
|
+
It is optimized for efficient access to just the out-edges, fast vertex
|
63
|
+
insertion and removal at the cost of extra space overhead, etc.
|
64
|
+
|
65
|
+
## Example Usage
|
66
|
+
|
67
|
+
Using IRB, first require the library:
|
68
|
+
|
69
|
+
``` bash
|
70
|
+
require 'rubygems' # only if you are using ruby 1.8.x
|
71
|
+
require 'plexus'
|
72
|
+
```
|
73
|
+
|
74
|
+
If you'd like to include all the classes in the current scope (so you
|
75
|
+
don't have to prefix with `Plexus::`), just:
|
76
|
+
|
77
|
+
``` bash
|
78
|
+
include Plexus
|
79
|
+
```
|
80
|
+
|
81
|
+
Let's play with the library a bit in IRB:
|
82
|
+
|
83
|
+
``` bash
|
84
|
+
>> dg = Digraph[1,2, 2,3, 2,4, 4,5, 6,4, 1,6]
|
85
|
+
=> Plexus::Digraph[[2, 3], [1, 6], [2, 4], [4, 5], [1, 2], [6, 4]]
|
86
|
+
```
|
87
|
+
|
88
|
+
A few properties of the graph we just created:
|
89
|
+
|
90
|
+
``` bash
|
91
|
+
>> dg.directed?
|
92
|
+
=> true
|
93
|
+
>> dg.vertex?(4)
|
94
|
+
=> true
|
95
|
+
>> dg.edge?(2,4)
|
96
|
+
=> true
|
97
|
+
>> dg.edge?(4,2)
|
98
|
+
=> false
|
99
|
+
>> dg.vertices
|
100
|
+
=> [1, 2, 3, 4, 5, 6]
|
101
|
+
```
|
102
|
+
|
103
|
+
Every object could be a vertex, even the class object `Object`:
|
104
|
+
|
105
|
+
``` bash
|
106
|
+
>> dg.vertex?(Object)
|
107
|
+
=> false
|
108
|
+
|
109
|
+
>> UndirectedGraph.new(dg).edges.sort.to_s
|
110
|
+
=> "[Plexus::Edge[1,2,nil], Plexus::Edge[2,3,nil], Plexus::Edge[2,4,nil],
|
111
|
+
Plexus::Edge[4,5,nil], Plexus::Edge[1,6,nil], Plexus::Edge[6,4,nil]]"
|
112
|
+
```
|
113
|
+
|
114
|
+
Add inverse edge `(4-2)` to directed graph:
|
115
|
+
|
116
|
+
``` bash
|
117
|
+
>> dg.add_edge!(4,2)
|
118
|
+
=> Plexus::DirectedGraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil],
|
119
|
+
Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[4,2,nil],
|
120
|
+
Plexus::Arc[6,4,nil]]
|
121
|
+
```
|
122
|
+
|
123
|
+
`(4-2) == (2-4)` in the undirected graph (4-2 doesn't show up):
|
124
|
+
|
125
|
+
``` bash
|
126
|
+
>> UndirectedGraph.new(dg).edges.sort.to_s
|
127
|
+
=> "[Plexus::Edge[1,2,nil], Plexus::Edge[2,3,nil], Plexus::Edge[2,4,nil],
|
128
|
+
Plexus::Edge[4,5,nil], Plexus::Edge[1,6,nil], Plexus::Edge[6,4,nil]]"
|
129
|
+
```
|
130
|
+
|
131
|
+
`(4-2) != (2-4)` in directed graphs (both show up):
|
132
|
+
|
133
|
+
``` bash
|
134
|
+
>> dg.edges.sort.to_s
|
135
|
+
=> "[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil],
|
136
|
+
Plexus::Arc[2,4,nil], Plexus::Arc[4,2,nil], Plexus::Arc[4,5,nil],
|
137
|
+
Plexus::Arc[6,4,nil]]"
|
138
|
+
|
139
|
+
>> dg.remove_edge! 4,2
|
140
|
+
=> Plexus::DirectedGraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil],
|
141
|
+
Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[6,4,nil]]
|
142
|
+
```
|
143
|
+
|
144
|
+
Topological sorting is realized with an iterator:
|
145
|
+
|
146
|
+
``` bash
|
147
|
+
>> dg.topsort
|
148
|
+
=> [1, 6, 2, 4, 5, 3]
|
149
|
+
>> y = 0; dg.topsort { |v| y += v }; y
|
150
|
+
=> 21
|
151
|
+
```
|
152
|
+
|
153
|
+
You can use DOT to visualize the graph:
|
154
|
+
|
155
|
+
``` bash
|
156
|
+
>> require 'plexus/dot'
|
157
|
+
>> dg.write_to_graphic_file('jpg','visualize')
|
158
|
+
```
|
159
|
+
|
160
|
+
Here's an example showing the module inheritance hierarchy:
|
161
|
+
|
162
|
+
``` bash
|
163
|
+
>> module_graph = Digraph.new
|
164
|
+
>> ObjectSpace.each_object(Module) do |m|
|
165
|
+
>> m.ancestors.each {|a| module_graph.add_edge!(m,a) if m != a}
|
166
|
+
>> end
|
167
|
+
>> gv = module_graph.vertices.select {|v| v.to_s.match(/Plexus/) }
|
168
|
+
>> module_graph.induced_subgraph(gv).write_to_graphic_file('jpg','module_graph')
|
169
|
+
```
|
170
|
+
|
171
|
+
Look for more in the examples directory.
|
172
|
+
|
173
|
+
## History
|
174
|
+
|
175
|
+
This library is based on [GRATR][1] by Shawn Garbett (itself a fork of
|
176
|
+
Horst Duchene's [RGL][2] library) which is heavily influenced by the [Boost][3]
|
177
|
+
Graph Library (BGL).
|
178
|
+
|
179
|
+
This fork attempts to modernize and extend the API and tests.
|
180
|
+
|
181
|
+
## References
|
182
|
+
|
183
|
+
For more information on Graph Theory, you may want to read:
|
184
|
+
|
185
|
+
* the [documentation][3] for the Boost Graph Library
|
186
|
+
* [the Dictionary of Algorithms and Data Structures][4]
|
187
|
+
|
188
|
+
## Credits
|
189
|
+
|
190
|
+
See CREDITS.markdown
|
191
|
+
|
192
|
+
## TODO
|
193
|
+
|
194
|
+
See TODO.markdown
|
195
|
+
|
196
|
+
## CHANGELOG
|
197
|
+
|
198
|
+
See CHANGELOG.markdown
|
199
|
+
|
200
|
+
## License
|
201
|
+
|
202
|
+
[MIT License](http://en.wikipedia.org/wiki/MIT_License). See the LICENSE file.
|
203
|
+
|
204
|
+
[1]: http://gratr.rubyforge.org
|
205
|
+
[2]: http://rgl.rubyforge.org
|
206
|
+
[3]: http://www.boost.org/libs/graph/doc
|
207
|
+
[4]: http://www.nist.gov/dads/HTML/graph.html
|
208
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
16
|
+
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'Plexus'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README.rdoc')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
data/lib/plexus.rb
ADDED
@@ -0,0 +1,90 @@
|
|
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
|
+
require 'set'
|
30
|
+
|
31
|
+
module Plexus
|
32
|
+
# Plexus internals: graph builders and additionnal behaviors
|
33
|
+
autoload :GraphAPI, 'plexus/graph_api'
|
34
|
+
|
35
|
+
autoload :GraphBuilder, 'plexus/graph'
|
36
|
+
autoload :AdjacencyGraphBuilder, 'plexus/adjacency_graph'
|
37
|
+
|
38
|
+
autoload :DirectedGraphBuilder, 'plexus/directed_graph'
|
39
|
+
autoload :DigraphBuilder, 'plexus/directed_graph'
|
40
|
+
autoload :DirectedPseudoGraphBuilder, 'plexus/directed_graph'
|
41
|
+
autoload :DirectedMultiGraphBuilder, 'plexus/directed_graph'
|
42
|
+
|
43
|
+
autoload :UndirectedGraphBuilder, 'plexus/undirected_graph'
|
44
|
+
autoload :UndirectedPseudoGraphBuilder, 'plexus/undirected_graph'
|
45
|
+
autoload :UndirectedMultiGraphBuilder, 'plexus/undirected_graph'
|
46
|
+
|
47
|
+
autoload :Arc, 'plexus/arc'
|
48
|
+
autoload :ArcNumber, 'plexus/arc_number'
|
49
|
+
autoload :Biconnected, 'plexus/biconnected'
|
50
|
+
autoload :ChinesePostman, 'plexus/chinese_postman'
|
51
|
+
autoload :Common, 'plexus/common'
|
52
|
+
autoload :Comparability, 'plexus/comparability'
|
53
|
+
|
54
|
+
autoload :Dot, 'plexus/dot'
|
55
|
+
autoload :Edge, 'plexus/edge'
|
56
|
+
autoload :Labels, 'plexus/labels'
|
57
|
+
autoload :MaximumFlow, 'plexus/maximum_flow'
|
58
|
+
#autoload :Rdot, 'plexus/dot'
|
59
|
+
autoload :Search, 'plexus/search'
|
60
|
+
autoload :StrongComponents, 'plexus/strong_components'
|
61
|
+
|
62
|
+
# Plexus classes
|
63
|
+
autoload :AdjacencyGraph, 'plexus/classes/graph_classes'
|
64
|
+
autoload :DirectedGraph, 'plexus/classes/graph_classes'
|
65
|
+
autoload :Digraph, 'plexus/classes/graph_classes'
|
66
|
+
autoload :DirectedPseudoGraph, 'plexus/classes/graph_classes'
|
67
|
+
autoload :DirectedMultiGraph, 'plexus/classes/graph_classes'
|
68
|
+
autoload :UndirectedGraph, 'plexus/classes/graph_classes'
|
69
|
+
autoload :UndirectedPseudoGraph, 'plexus/classes/graph_classes'
|
70
|
+
autoload :UndirectedMultiGraph, 'plexus/classes/graph_classes'
|
71
|
+
|
72
|
+
# ruby stdlib extensions
|
73
|
+
require 'plexus/ext'
|
74
|
+
# ruby 1.8.x/1.9.x compatibility
|
75
|
+
require 'plexus/ruby_compatibility'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Vendored libraries
|
79
|
+
|
80
|
+
require 'pathname'
|
81
|
+
path = Pathname.new(__FILE__)
|
82
|
+
$LOAD_PATH.unshift(path + '../../vendor') # http://ruby.brian-amberg.de/priority-queue/
|
83
|
+
$LOAD_PATH.unshift(path + '../../vendor/priority-queue/lib')
|
84
|
+
|
85
|
+
require 'rdot'
|
86
|
+
require 'facets/hash'
|
87
|
+
|
88
|
+
require 'priority_queue/ruby_priority_queue'
|
89
|
+
PriorityQueue = RubyPriorityQueue
|
90
|
+
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Plexus
|
2
|
+
|
3
|
+
# This module provides the basic routines needed to implement the specialized builders:
|
4
|
+
# {DigraphBuilder}, {UndirectedGraphBuilder}, {DirectedPseudoGraphBuilder},
|
5
|
+
# {UndirectedPseudoGraphBuilder}, {DirectedMultiGraphBuilder} and {UndirectedMultiGraphBuilder}
|
6
|
+
# modules, each of them streamlining {AdjacencyGraphBuilder}'s behavior. Those
|
7
|
+
# implementations rely on the {GraphBuilder}, under the control of the {GraphAPI}.
|
8
|
+
module AdjacencyGraphBuilder
|
9
|
+
|
10
|
+
# Defines a useful `push` -> `add` alias for arrays.
|
11
|
+
class ArrayWithAdd < Array
|
12
|
+
alias add push
|
13
|
+
end
|
14
|
+
|
15
|
+
# This method is called by the specialized implementations
|
16
|
+
# upon graph creation.
|
17
|
+
#
|
18
|
+
# Initialization parameters can include:
|
19
|
+
#
|
20
|
+
# * an array of edges to add
|
21
|
+
# * one or several graphs to copy (will be merged if multiple)
|
22
|
+
# * `:parallel_edges` denotes that duplicate edges are allowed
|
23
|
+
# * `:loops denotes` that loops are allowed
|
24
|
+
#
|
25
|
+
# @param *params [Hash] the initialization parameters
|
26
|
+
#
|
27
|
+
def implementation_initialize(*params)
|
28
|
+
@vertex_dict = Hash.new
|
29
|
+
clear_all_labels
|
30
|
+
|
31
|
+
# FIXME: could definitely make use of the activesupport helper
|
32
|
+
# extract_options! and facets' reverse_merge! technique
|
33
|
+
# to handle parameters
|
34
|
+
args = (params.pop if params.last.is_a? Hash) || {}
|
35
|
+
|
36
|
+
# Basic configuration of adjacency.
|
37
|
+
@allow_loops = args[:loops] || false
|
38
|
+
@parallel_edges = args[:parallel_edges] || false
|
39
|
+
@edgelist_class = @parallel_edges ? ArrayWithAdd : Set
|
40
|
+
if @parallel_edges
|
41
|
+
@edge_number = Hash.new
|
42
|
+
@next_edge_number = 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Copy any given graph into this graph.
|
46
|
+
params.select { |p| p.is_a? Plexus::GraphBuilder }.each do |g|
|
47
|
+
g.edges.each do |e|
|
48
|
+
add_edge!(e)
|
49
|
+
edge_label_set(e, edge_label(e)) if edge_label(e)
|
50
|
+
end
|
51
|
+
g.vertices.each do |v|
|
52
|
+
add_vertex!(v)
|
53
|
+
vertex_label_set(v, vertex_label(v)) if vertex_label(v)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add all array edges specified.
|
58
|
+
params.select { |p| p.is_a? Array }.each do |a|
|
59
|
+
0.step(a.size-1, 2) { |i| add_edge!(a[i], a[i+1]) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns true if v is a vertex of this Graph
|
64
|
+
# (an "O(1)" implementation of `vertex?`).
|
65
|
+
#
|
66
|
+
# @param [vertex] v
|
67
|
+
# @return [Boolean]
|
68
|
+
def vertex?(v)
|
69
|
+
@vertex_dict.has_key?(v)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true if [u,v] or u is an {Arc}
|
73
|
+
# (an "O(1)" implementation of `edge?`).
|
74
|
+
#
|
75
|
+
# @param [vertex] u
|
76
|
+
# @param [vertex] v (nil)
|
77
|
+
# @return [Boolean]
|
78
|
+
def edge?(u, v = nil)
|
79
|
+
u, v = u.source, u.target if u.is_a? Plexus::Arc
|
80
|
+
vertex?(u) and @vertex_dict[u].include?(v)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Adds a vertex to the graph with an optional label.
|
84
|
+
#
|
85
|
+
# @param [vertex(Object)] vertex any kind of Object can act as a vertex
|
86
|
+
# @param [#to_s] label (nil)
|
87
|
+
def add_vertex!(vertex, label = nil)
|
88
|
+
@vertex_dict[vertex] ||= @edgelist_class.new
|
89
|
+
self[vertex] = label if label
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds an edge to the graph.
|
94
|
+
#
|
95
|
+
# Can be called in two basic ways, label is optional:
|
96
|
+
# @overload add_edge!(arc)
|
97
|
+
# Using an explicit {Arc}
|
98
|
+
# @param [Arc] arc an {Arc}[source, target, label = nil] object
|
99
|
+
# @return [AdjacencyGraph] `self`
|
100
|
+
# @overload add_edge!(source, target, label = nil)
|
101
|
+
# Using vertices to define an arc implicitly
|
102
|
+
# @param [vertex] u
|
103
|
+
# @param [vertex] v (nil)
|
104
|
+
# @param [Label] l (nil)
|
105
|
+
# @param [Integer] n (nil) {Arc arc} number of `(u, v)` (if `nil` and if `u`
|
106
|
+
# has an {ArcNumber}, then it will be used)
|
107
|
+
# @return [AdjacencyGraph] `self`
|
108
|
+
#
|
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.is_a? Plexus::Arc
|
112
|
+
|
113
|
+
return self if not @allow_loops and u == v
|
114
|
+
|
115
|
+
n = (@next_edge_number += 1) unless n if @parallel_edges
|
116
|
+
add_vertex!(u)
|
117
|
+
add_vertex!(v)
|
118
|
+
@vertex_dict[u].add(v)
|
119
|
+
(@edge_number[u] ||= @edgelist_class.new).add(n) if @parallel_edges
|
120
|
+
|
121
|
+
unless directed?
|
122
|
+
@vertex_dict[v].add(u)
|
123
|
+
(@edge_number[v] ||= @edgelist_class.new).add(n) if @parallel_edges
|
124
|
+
end
|
125
|
+
|
126
|
+
self[n ? edge_class[u,v,n] : edge_class[u,v]] = l if l
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
# Removes a given vertex from the graph.
|
131
|
+
#
|
132
|
+
# @param [vertex] v
|
133
|
+
# @return [AdjacencyGraph] `self`
|
134
|
+
def remove_vertex!(v)
|
135
|
+
# FIXME This is broken for multi graphs
|
136
|
+
@vertex_dict.delete(v)
|
137
|
+
@vertex_dict.each_value { |adjList| adjList.delete(v) }
|
138
|
+
@vertex_dict.keys.each do |u|
|
139
|
+
delete_label(edge_class[u,v])
|
140
|
+
delete_label(edge_class[v,u])
|
141
|
+
end
|
142
|
+
delete_label(v)
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
# Removes an edge from the graph.
|
147
|
+
#
|
148
|
+
# Can be called with both source and target as vertex,
|
149
|
+
# or with source and object of {Plexus::Arc} derivation.
|
150
|
+
#
|
151
|
+
# @overload remove_edge!(a)
|
152
|
+
# @param [Plexus::Arc] a
|
153
|
+
# @return [AdjacencyGraph] `self`
|
154
|
+
# @raise [ArgumentError] if parallel edges are enabled
|
155
|
+
# @overload remove_edge!(u, v)
|
156
|
+
# @param [vertex] u
|
157
|
+
# @param [vertex] v
|
158
|
+
# @return [AdjacencyGraph] `self`
|
159
|
+
# @raise [ArgumentError] if parallel edges are enabled and the {ArcNumber} of `u` is zero
|
160
|
+
def remove_edge!(u, v = nil)
|
161
|
+
unless u.is_a? Plexus::Arc
|
162
|
+
raise ArgumentError if @parallel_edges
|
163
|
+
u = edge_class[u,v]
|
164
|
+
end
|
165
|
+
raise ArgumentError if @parallel_edges and (u.number || 0) == 0
|
166
|
+
return self unless @vertex_dict[u.source] # It doesn't exist
|
167
|
+
delete_label(u) # Get rid of label
|
168
|
+
if @parallel_edges
|
169
|
+
index = @edge_number[u.source].index(u.number)
|
170
|
+
raise NoArcError unless index
|
171
|
+
@vertex_dict[u.source].delete_at(index)
|
172
|
+
@edge_number[u.source].delete_at(index)
|
173
|
+
else
|
174
|
+
@vertex_dict[u.source].delete(u.target)
|
175
|
+
end
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns an array of vertices that the graph has.
|
180
|
+
#
|
181
|
+
# @return [Array] graph's vertices
|
182
|
+
def vertices
|
183
|
+
@vertex_dict.keys
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns an array of edges, most likely of class {Arc} or {Edge} depending
|
187
|
+
# upon the type of graph.
|
188
|
+
#
|
189
|
+
# @return [Array]
|
190
|
+
def edges
|
191
|
+
@vertex_dict.keys.inject(Set.new) do |a,v|
|
192
|
+
if @parallel_edges and @edge_number[v]
|
193
|
+
@vertex_dict[v].zip(@edge_number[v]).each do |w|
|
194
|
+
s, t, n = v, w[0], w[1]
|
195
|
+
a.add(edge_class[s, t, n, edge_label(s, t, n)])
|
196
|
+
end
|
197
|
+
else
|
198
|
+
@vertex_dict[v].each do |w|
|
199
|
+
a.add(edge_class[v, w, edge_label(v, w)])
|
200
|
+
end
|
201
|
+
end
|
202
|
+
a
|
203
|
+
end.to_a
|
204
|
+
end
|
205
|
+
|
206
|
+
# FIXME, EFFED UP (but why?)
|
207
|
+
#
|
208
|
+
# @fixme
|
209
|
+
def adjacent(x, options = {})
|
210
|
+
options[:direction] ||= :out
|
211
|
+
if !x.is_a?(Plexus::Arc) and (options[:direction] == :out || !directed?)
|
212
|
+
if options[:type] == :edges
|
213
|
+
i = -1
|
214
|
+
@parallel_edges ?
|
215
|
+
@vertex_dict[x].map { |v| e = edge_class[x, v, @edge_number[x][i+=1]]; e.label = self[e]; e } :
|
216
|
+
@vertex_dict[x].map { |v| e = edge_class[x, v]; e.label = self[e]; e }
|
217
|
+
else
|
218
|
+
@vertex_dict[x].to_a
|
219
|
+
end
|
220
|
+
else
|
221
|
+
graph_adjacent(x,options)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|