gratr 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Grater.xcf +0 -0
- data/README +328 -0
- data/Rakefile +220 -0
- data/examples/graph_self.rb +54 -0
- data/examples/module_graph.jpg +0 -0
- data/examples/module_graph.rb +12 -0
- data/examples/self_graph.jpg +0 -0
- data/examples/visualize.jpg +0 -0
- data/examples/visualize.rb +8 -0
- data/install.rb +49 -0
- data/lib/gratr.rb +33 -0
- data/lib/gratr/adjacency_graph.rb +230 -0
- data/lib/gratr/base.rb +34 -0
- data/lib/gratr/biconnected.rb +116 -0
- data/lib/gratr/chinese_postman.rb +123 -0
- data/lib/gratr/common.rb +73 -0
- data/lib/gratr/comparability.rb +92 -0
- data/lib/gratr/digraph.rb +113 -0
- data/lib/gratr/digraph_distance.rb +185 -0
- data/lib/gratr/dot.rb +90 -0
- data/lib/gratr/edge.rb +145 -0
- data/lib/gratr/graph.rb +315 -0
- data/lib/gratr/graph_api.rb +82 -0
- data/lib/gratr/import.rb +44 -0
- data/lib/gratr/labels.rb +103 -0
- data/lib/gratr/maximum_flow.rb +107 -0
- data/lib/gratr/rdot.rb +326 -0
- data/lib/gratr/search.rb +409 -0
- data/lib/gratr/strong_components.rb +127 -0
- data/lib/gratr/undirected_graph.rb +153 -0
- data/tests/TestBiconnected.rb +53 -0
- data/tests/TestChinesePostman.rb +53 -0
- data/tests/TestComplement.rb +54 -0
- data/tests/TestDigraph.rb +333 -0
- data/tests/TestDigraphDistance.rb +138 -0
- data/tests/TestEdge.rb +171 -0
- data/tests/TestInspection.rb +57 -0
- data/tests/TestMultiEdge.rb +57 -0
- data/tests/TestNeighborhood.rb +64 -0
- data/tests/TestProperties.rb +160 -0
- data/tests/TestSearch.rb +257 -0
- data/tests/TestStrongComponents.rb +85 -0
- data/tests/TestTriagulated.rb +137 -0
- data/tests/TestUndirectedGraph.rb +219 -0
- metadata +92 -0
@@ -0,0 +1,153 @@
|
|
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/adjacency_graph'
|
31
|
+
require 'gratr/search'
|
32
|
+
require 'gratr/biconnected'
|
33
|
+
require 'gratr/comparability'
|
34
|
+
require 'set'
|
35
|
+
|
36
|
+
module GRATR
|
37
|
+
class UndirectedGraph
|
38
|
+
include AdjacencyGraph
|
39
|
+
include Graph::Search
|
40
|
+
include Graph::Biconnected
|
41
|
+
include Graph::Comparability
|
42
|
+
|
43
|
+
def initialize(*params)
|
44
|
+
raise ArgumentError if params.any? do |p|
|
45
|
+
!(p.kind_of? GRATR::Graph or p.kind_of? Array)
|
46
|
+
end if self.class == GRATR::UndirectedGraph
|
47
|
+
super(*params)
|
48
|
+
end
|
49
|
+
|
50
|
+
# UndirectedGraph is by definition undirected, always returns false
|
51
|
+
def directed?() false; end
|
52
|
+
|
53
|
+
# Redefine degree (default was sum)
|
54
|
+
def degree(v) in_degree(v); end
|
55
|
+
|
56
|
+
# A vertex of an undirected graph is balanced by definition
|
57
|
+
def balanced?(v) true; end
|
58
|
+
|
59
|
+
# UndirectedGraph uses Edge for the edge class.
|
60
|
+
def edge_class() @parallel_edges ? GRATR::MultiEdge : GRATR::Edge; end
|
61
|
+
|
62
|
+
def remove_edge!(u, v=nil)
|
63
|
+
unless u.kind_of? GRATR::Arc
|
64
|
+
raise ArgumentError if @parallel_edges
|
65
|
+
u = edge_class[u,v]
|
66
|
+
end
|
67
|
+
super(u.reverse) unless u.source == u.target
|
68
|
+
super(u)
|
69
|
+
end
|
70
|
+
|
71
|
+
# A triangulated graph is an undirected perfect graph that every cycle of length greater than
|
72
|
+
# three possesses a chord. They have also been called chordal, rigid circuit, monotone transitive,
|
73
|
+
# and perfect elimination graphs.
|
74
|
+
#
|
75
|
+
# Implementation taken from Golumbic's, "Algorithmic Graph Theory and
|
76
|
+
# Perfect Graphs" pg. 90
|
77
|
+
def triangulated?
|
78
|
+
a = Hash.new {|h,k| h[k]=Set.new}; sigma=lexicograph_bfs
|
79
|
+
inv_sigma = sigma.inject({}) {|acc,val| acc[val] = sigma.index(val); acc}
|
80
|
+
sigma[0..-2].each do |v|
|
81
|
+
x = adjacent(v).select {|w| inv_sigma[v] < inv_sigma[w] }
|
82
|
+
unless x.empty?
|
83
|
+
u = sigma[x.map {|y| inv_sigma[y]}.min]
|
84
|
+
a[u].merge(x - [u])
|
85
|
+
end
|
86
|
+
return false unless a[v].all? {|z| adjacent?(v,z)}
|
87
|
+
end
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
def chromatic_number
|
92
|
+
return triangulated_chromatic_number if triangulated?
|
93
|
+
raise NotImplementedError
|
94
|
+
end
|
95
|
+
|
96
|
+
# An interval graph can have its vertices into one-to-one
|
97
|
+
# correspondence with a set of intervals F of a linearly ordered
|
98
|
+
# set (like the real line) such that two vertices are connected
|
99
|
+
# by an edge of G if and only if their corresponding intervals
|
100
|
+
# have nonempty intersection.
|
101
|
+
def interval?() triangulated? and complement.comparability?; end
|
102
|
+
|
103
|
+
# A permutation diagram consists of n points on each of two parallel
|
104
|
+
# lines and n straight line segments matchin the points. The intersection
|
105
|
+
# graph of the line segments is called a permutation graph.
|
106
|
+
def permutation?() comparability? and complement.comparability?; end
|
107
|
+
|
108
|
+
# An undirected graph is defined to be split if there is a partition
|
109
|
+
# V = S + K of its vertex set into a stable set S and a complete set K.
|
110
|
+
def split?() triangulated? and complement.triangulated?; end
|
111
|
+
|
112
|
+
private
|
113
|
+
# Implementation taken from Golumbic's, "Algorithmic Graph Theory and
|
114
|
+
# Perfect Graphs" pg. 99
|
115
|
+
def triangulated_chromatic_number
|
116
|
+
chi = 1; s= Hash.new {|h,k| h[k]=0}
|
117
|
+
sigma=lexicograph_bfs
|
118
|
+
inv_sigma = sigma.inject({}) {|acc,val| acc[val] = sigma.index(val); acc}
|
119
|
+
sigma.each do |v|
|
120
|
+
x = adjacent(v).select {|w| inv_sigma[v] < inv_sigma[w] }
|
121
|
+
unless x.empty?
|
122
|
+
u = sigma[x.map {|y| inv_sigma[y]}.min]
|
123
|
+
s[u] = [s[u], x.size-1].max
|
124
|
+
chi = [chi, x.size+1].max if s[v] < x.size
|
125
|
+
end
|
126
|
+
end; chi
|
127
|
+
end
|
128
|
+
|
129
|
+
end # UndirectedGraph
|
130
|
+
|
131
|
+
# This is a UndirectedGraph that allows for parallel edges, but does not
|
132
|
+
# allow loops
|
133
|
+
class UndirectedPseudoGraph < UndirectedGraph
|
134
|
+
def initialize(*params)
|
135
|
+
raise ArgumentError if params.any? do |p|
|
136
|
+
!(p.kind_of? Graph or p.kind_of? Array)
|
137
|
+
end
|
138
|
+
super(:parallel_edges, *params)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# This is a UndirectedGraph that allows for parallel edges and loops
|
143
|
+
class UndirectedMultiGraph < UndirectedGraph
|
144
|
+
def initialize(*params)
|
145
|
+
raise ArgumentError if params.any? do |p|
|
146
|
+
!(p.kind_of? Graph or p.kind_of? Array)
|
147
|
+
end
|
148
|
+
super(:parallel_edges, :loops, *params)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
end # GRATR
|
@@ -0,0 +1,53 @@
|
|
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
|
+
require 'test/unit'
|
29
|
+
require 'gratr/import'
|
30
|
+
|
31
|
+
class TestBiconnected < Test::Unit::TestCase # :nodoc:
|
32
|
+
def test_tarjan
|
33
|
+
tarjan = UndirectedGraph[ 1, 2,
|
34
|
+
1, 5,
|
35
|
+
1, 6,
|
36
|
+
1, 7,
|
37
|
+
2, 3,
|
38
|
+
2, 4,
|
39
|
+
3, 4,
|
40
|
+
2, 5,
|
41
|
+
5, 6,
|
42
|
+
7, 8,
|
43
|
+
7, 9,
|
44
|
+
8, 9 ]
|
45
|
+
graphs, articulations = tarjan.biconnected
|
46
|
+
assert_equal [1,2,7], articulations.sort
|
47
|
+
assert_equal 4, graphs.size
|
48
|
+
assert_equal [1,7], graphs.find {|g| g.size == 2}.vertices.sort
|
49
|
+
assert_equal [1,2,5,6], graphs.find {|g| g.size == 4}.vertices.sort
|
50
|
+
assert_equal [2,3,4], graphs.find {|g| g.size == 3 && g.vertex?(2)}.vertices.sort
|
51
|
+
assert_equal [7,8,9], graphs.find {|g| g.size == 3 && g.vertex?(7)}.vertices.sort
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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 'test/unit'
|
30
|
+
require 'gratr/import'
|
31
|
+
|
32
|
+
class TestChinesePostman < Test::Unit::TestCase # :nodoc:
|
33
|
+
|
34
|
+
def setup
|
35
|
+
@simple=Digraph[ 0,1, 0,2, 1,2, 1,3, 2,3, 3,0 ]
|
36
|
+
@weight = Proc.new {|e| 1}
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_closed_simple_tour
|
40
|
+
tour = @simple.closed_chinese_postman_tour(0, @weight)
|
41
|
+
assert_equal 11, tour.size
|
42
|
+
assert_equal 0, tour[0]
|
43
|
+
assert_equal 0, tour[10]
|
44
|
+
edges = Set.new
|
45
|
+
0.upto(9) do |n|
|
46
|
+
edges << Arc[tour[n],tour[n+1]]
|
47
|
+
assert(@simple.edge?(tour[n],tour[n+1]), "Arc(#{tour[n]},#{tour[n+1]}) from tour not in graph")
|
48
|
+
end
|
49
|
+
assert_equal @simple.edges.size, edges.size, "Not all arcs traversed!"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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 'test/unit'
|
31
|
+
require 'gratr/import'
|
32
|
+
|
33
|
+
class TestComplement < Test::Unit::TestCase # :nodoc:
|
34
|
+
|
35
|
+
def test_square
|
36
|
+
x = UndirectedGraph[:a,:b, :b,:c, :c,:d, :d,:a].complement
|
37
|
+
assert_equal 2, x.edges.size
|
38
|
+
assert x.edges.include?(Edge[:a,:c])
|
39
|
+
assert x.edges.include?(Edge[:b,:d])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_g1
|
43
|
+
g1 = UndirectedGraph[ :a,:b, :a,:d, :a,:e, :a,:i, :a,:g, :a,:h,
|
44
|
+
:b,:c, :b,:f,
|
45
|
+
:c,:d, :c,:h,
|
46
|
+
:d,:h, :d,:e,
|
47
|
+
:e,:f,
|
48
|
+
:f,:g, :f,:h, :f,:i,
|
49
|
+
:h,:i ].complement
|
50
|
+
assert_equal 19, g1.edges.size
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,333 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'test/unit'
|
26
|
+
require 'gratr/import'
|
27
|
+
|
28
|
+
class TestDigraph < Test::Unit::TestCase # :nodoc:
|
29
|
+
|
30
|
+
def setup
|
31
|
+
@single = Digraph[1,2, 2,3, 3,4, 1,2, 2,3, 4,4]
|
32
|
+
@dups = DirectedPseudoGraph[1,2, 2,3, 3,4, 1,2, 2,3, 4,4]
|
33
|
+
@loops = DirectedMultiGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_new
|
37
|
+
assert_equal Digraph[1,2, 2,3, 3,4], @single
|
38
|
+
assert_equal DirectedPseudoGraph.new([1,2, 2,3, 3,4, 1,2, 2,3]), @dups
|
39
|
+
assert_equal DirectedMultiGraph.new([1,2, 2,3, 3,4, 4,4, 1,2, 2,3]), @loops
|
40
|
+
assert_raise(ArgumentError) {Digraph.new(:loops)}
|
41
|
+
assert_raise(ArgumentError) {Digraph.new(:parallel_edges)}
|
42
|
+
assert_raise(ArgumentError) {DirectedMultiGraph.new(:loops)}
|
43
|
+
assert_raise(ArgumentError) {DirectedMultiGraph.new(:parallel_edges)}
|
44
|
+
assert_raise(ArgumentError) {DirectedPseudoGraph.new(:loops)}
|
45
|
+
assert_raise(ArgumentError) {DirectedPseudoGraph.new(:parallel_edges)}
|
46
|
+
assert_raise(ArgumentError) {Digraph.new(1)}
|
47
|
+
assert_equal @single, Digraph.new(@single)
|
48
|
+
assert_equal @dups, DirectedPseudoGraph.new(@dups)
|
49
|
+
assert_equal @loops, DirectedMultiGraph.new(@loops)
|
50
|
+
assert_equal Digraph[1,2, 2,3, 3,4], Digraph.new(@loops)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_edges
|
54
|
+
assert_equal 3,@single.edges.size
|
55
|
+
assert @single.edges.include?(Arc[1,2])
|
56
|
+
assert @single.edges.include?(Arc[2,3])
|
57
|
+
assert @single.edges.include?(Arc[3,4])
|
58
|
+
assert !@single.edges.include?(Arc[4,4])
|
59
|
+
assert @single.edges.include?(Arc[1,2])
|
60
|
+
assert @single.edges.include?(Arc[2,3])
|
61
|
+
assert !@single.edges.include?(Arc[1,3])
|
62
|
+
assert @single.edge?(2,3)
|
63
|
+
assert !@single.edge?(1,4)
|
64
|
+
assert @single.edge?(Arc[1,2])
|
65
|
+
assert !@single.add_edge!(5,5).edge?(5,5)
|
66
|
+
assert !@single.remove_edge!(5,5).edge?(5,5)
|
67
|
+
|
68
|
+
assert_equal 5,@dups.edges.size
|
69
|
+
assert @dups.edges.include?(MultiArc[1,2])
|
70
|
+
assert @dups.edges.include?(MultiArc[2,3])
|
71
|
+
assert @dups.edges.include?(MultiArc[3,4])
|
72
|
+
assert !@dups.edges.include?(MultiArc[4,4])
|
73
|
+
assert @dups.edges.include?(MultiArc[1,2])
|
74
|
+
assert @dups.edges.include?(MultiArc[2,3])
|
75
|
+
assert !@dups.edges.include?(MultiArc[1,3])
|
76
|
+
assert @dups.edge?(2,3)
|
77
|
+
assert !@dups.edge?(1,4)
|
78
|
+
assert @dups.edge?(MultiArc[1,2])
|
79
|
+
assert !@dups.add_edge!(5,5).edge?(5,5)
|
80
|
+
assert_raise(ArgumentError) { @dups.remove_edge!(5,5) }
|
81
|
+
|
82
|
+
assert_equal 5,@dups.edges.size
|
83
|
+
assert @loops.edges.include?(MultiArc[1,2])
|
84
|
+
assert @loops.edges.include?(MultiArc[2,3])
|
85
|
+
assert @loops.edges.include?(MultiArc[3,4])
|
86
|
+
assert @loops.edges.include?(MultiArc[4,4])
|
87
|
+
assert @loops.edges.include?(MultiArc[1,2])
|
88
|
+
assert @loops.edges.include?(MultiArc[2,3])
|
89
|
+
assert !@loops.edges.include?(MultiArc[1,3])
|
90
|
+
assert @loops.edge?(2,3)
|
91
|
+
assert !@loops.edge?(1,4)
|
92
|
+
assert @loops.edge?(MultiArc[1,2])
|
93
|
+
assert @loops.add_edge!(5,5).edge?(5,5)
|
94
|
+
assert_raise(ArgumentError) { @loops.remove_edge!(5,5) }
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_vertices
|
99
|
+
assert_equal [1,2,3,4], @single.vertices.sort
|
100
|
+
assert_equal [1,2,3,4,5], @single.add_vertex!(5).sort
|
101
|
+
assert_equal [1,2,4,5], @single.remove_vertex!(3).sort
|
102
|
+
assert !@single.vertex?(3)
|
103
|
+
assert !@single.edge?(2,3)
|
104
|
+
assert !@single.edge?(3,4)
|
105
|
+
assert @single.add_vertex(:bogus).vertex?(:bogus)
|
106
|
+
assert !@single.add_vertex(:bogus).vertex?(nil)
|
107
|
+
assert !@single.vertex?(:bogus)
|
108
|
+
@single.add_vertex!(:real)
|
109
|
+
assert @single.vertex?(:real)
|
110
|
+
assert @single.add_edge(:here, :there).edge?(Arc[:here, :there])
|
111
|
+
assert !@single.edge?(Arc[:here, :there])
|
112
|
+
assert !@single.vertex?(:here)
|
113
|
+
assert !@single.vertex?(:there)
|
114
|
+
@single.add_edge!(:here, :there)
|
115
|
+
assert @single.edge?(Arc[:here, :there])
|
116
|
+
assert @single.vertex?(:here)
|
117
|
+
assert @single.vertex?(:there)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_properties
|
121
|
+
assert @single.directed?
|
122
|
+
assert @single.empty? == false
|
123
|
+
assert Digraph.new.empty? == true
|
124
|
+
assert_equal 4, @single.size
|
125
|
+
assert_equal 4, @dups.size
|
126
|
+
assert_equal 4, @loops.size
|
127
|
+
assert_equal 4, @single.num_vertices
|
128
|
+
assert_equal 4, @dups.num_vertices
|
129
|
+
assert_equal 3, @single.num_edges
|
130
|
+
assert_equal 5, @dups.num_edges
|
131
|
+
assert_equal 6, @loops.num_edges
|
132
|
+
assert @single.oriented?
|
133
|
+
@single.remove_vertex!(4)
|
134
|
+
assert @single.oriented?
|
135
|
+
assert !@loops.oriented?
|
136
|
+
@loops.remove_vertex!(4)
|
137
|
+
assert @loops.oriented?
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_merge
|
141
|
+
@dups.merge(@single)
|
142
|
+
assert_equal 8, @dups.num_edges
|
143
|
+
assert_equal [1,2,3,4], @dups.vertices.sort
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_operators
|
147
|
+
result = @single + Arc[3,2]
|
148
|
+
assert_equal 4, @single.size
|
149
|
+
assert_equal 3, @single.num_edges
|
150
|
+
assert_equal 4, result.size
|
151
|
+
assert_equal 4, result.num_edges
|
152
|
+
|
153
|
+
result = @single + 5
|
154
|
+
assert_equal 4, @single.size
|
155
|
+
assert_equal 3, @single.num_edges
|
156
|
+
assert_equal 5, result.size
|
157
|
+
assert_equal 3, result.num_edges
|
158
|
+
|
159
|
+
result = @single - Arc[4,4]
|
160
|
+
assert_equal 4, @single.size
|
161
|
+
assert_equal 3, @single.num_edges
|
162
|
+
assert_equal 4, result.size
|
163
|
+
assert_equal 3, result.num_edges
|
164
|
+
|
165
|
+
e = @loops.edges.detect{|e| e.source == 4 and e.target == 4}
|
166
|
+
result = @loops - e
|
167
|
+
assert_equal 4, @single.size
|
168
|
+
assert_equal 3, @single.num_edges
|
169
|
+
assert_equal 4, result.size
|
170
|
+
assert_equal 5, result.num_edges
|
171
|
+
|
172
|
+
result = @single - 4
|
173
|
+
assert_equal 4, @single.size
|
174
|
+
assert_equal 3, @single.num_edges
|
175
|
+
assert_equal 3, result.size
|
176
|
+
assert_equal 2, result.num_edges
|
177
|
+
|
178
|
+
@single << Arc[6,1]
|
179
|
+
assert_equal 5, @single.size
|
180
|
+
assert_equal 4, @single.num_edges
|
181
|
+
assert @single.edge?(6,1)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_reversal
|
185
|
+
reverse = @single.reversal
|
186
|
+
assert [1,2,3,4], reverse.vertices.sort
|
187
|
+
assert reverse.edge?(2,1)
|
188
|
+
assert reverse.edge?(3,2)
|
189
|
+
assert reverse.edge?(4,3)
|
190
|
+
assert !reverse.edge?(4,4)
|
191
|
+
assert 3, reverse.num_edges
|
192
|
+
reverse = @loops.reversal
|
193
|
+
assert reverse.edge?(4,4)
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_complement
|
197
|
+
complement = @single.complement
|
198
|
+
assert_equal [1,2,3,4], complement.vertices.sort
|
199
|
+
assert !complement.edge?(1,1)
|
200
|
+
assert complement.edge?(1,3)
|
201
|
+
assert complement.edge?(1,4)
|
202
|
+
assert complement.edge?(2,1)
|
203
|
+
assert complement.edge?(2,4)
|
204
|
+
assert complement.edge?(3,1)
|
205
|
+
assert complement.edge?(3,2)
|
206
|
+
assert complement.edge?(4,1)
|
207
|
+
assert complement.edge?(4,2)
|
208
|
+
assert complement.edge?(4,3)
|
209
|
+
assert 9, complement.num_edges
|
210
|
+
|
211
|
+
complement = @loops.complement
|
212
|
+
assert_equal [1,2,3,4], complement.vertices.sort
|
213
|
+
assert complement.edge?(1,1)
|
214
|
+
assert complement.edge?(1,3)
|
215
|
+
assert complement.edge?(1,4)
|
216
|
+
assert complement.edge?(2,1)
|
217
|
+
assert complement.edge?(2,2)
|
218
|
+
assert complement.edge?(2,4)
|
219
|
+
assert complement.edge?(3,1)
|
220
|
+
assert complement.edge?(3,2)
|
221
|
+
assert complement.edge?(3,3)
|
222
|
+
assert complement.edge?(4,1)
|
223
|
+
assert complement.edge?(4,2)
|
224
|
+
assert complement.edge?(4,3)
|
225
|
+
assert 12, complement.num_edges
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_induced_subgraph
|
229
|
+
induced = @single.induced_subgraph([1,2])
|
230
|
+
assert [1,2], induced.vertices.sort
|
231
|
+
assert induced.edge?(1,2)
|
232
|
+
assert 1, induced.num_edges
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_include
|
236
|
+
assert @single.include?(4)
|
237
|
+
assert @dups.include?(4)
|
238
|
+
assert !@dups.include?(5)
|
239
|
+
assert !@single.include?(5)
|
240
|
+
assert @single.include?(Arc[1,2])
|
241
|
+
assert @dups.include?(Arc[1,2])
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_adjacent
|
245
|
+
|
246
|
+
assert @single.adjacent?(2, Arc[1,2])
|
247
|
+
assert_equal [2], @single.adjacent(1)
|
248
|
+
|
249
|
+
assert_equal [Arc[1,2]], @single.adjacent(1, :type=>:edges)
|
250
|
+
assert_equal [Arc[1,2]], @single.adjacent(1, :type=>:edges, :direction=> :out)
|
251
|
+
assert_equal [Arc[1,2]], @single.adjacent(2, :type=>:edges, :direction=> :in)
|
252
|
+
assert_equal [Arc[1,2],Arc[2,3]], @single.adjacent(2, :type=>:edges, :direction=> :all).sort
|
253
|
+
|
254
|
+
[[{},1], [{:direction => :out},1], [{:direction => :in},2]].each do |h,v|
|
255
|
+
adj = @dups.adjacent(v, h.merge(:type=>:edges))
|
256
|
+
assert_equal 2, adj.size
|
257
|
+
adj.each {|e| assert e.source == 1; assert e.target == 2}
|
258
|
+
end
|
259
|
+
|
260
|
+
adj = @dups.adjacent(2, {:type=>:edges,:direction=>:all})
|
261
|
+
assert_equal 4, adj.size
|
262
|
+
adj.each do |e|
|
263
|
+
assert((e.source==1 and e.target==2) ||
|
264
|
+
(e.source==2 and e.target==3))
|
265
|
+
end
|
266
|
+
|
267
|
+
assert_equal [2], @single.adjacent(1, :type=>:vertices)
|
268
|
+
assert_equal [2], @single.adjacent(1, :type=>:vertices, :direction=> :out)
|
269
|
+
assert_equal [1], @single.adjacent(2, :type=>:vertices, :direction=> :in)
|
270
|
+
assert_equal [1,3], @single.adjacent(2, :type=>:vertices, :direction=> :all)
|
271
|
+
|
272
|
+
assert_equal [3], @single.adjacent(Arc[2,3], :type=>:vertices)
|
273
|
+
assert_equal [3], @single.adjacent(Arc[2,3], :type=>:vertices, :direction=> :out)
|
274
|
+
assert_equal [2], @single.adjacent(Arc[2,3], :type=>:vertices, :direction=> :in)
|
275
|
+
assert_equal [2,3], @single.adjacent(Arc[2,3], :type=>:vertices, :direction=> :all)
|
276
|
+
|
277
|
+
assert_equal [Arc[3,4]], @single.adjacent(Arc[2,3], :type=>:edges)
|
278
|
+
assert_equal [Arc[3,4]], @single.adjacent(Arc[2,3], :type=>:edges, :direction=> :out)
|
279
|
+
assert_equal [Arc[1,2]], @single.adjacent(Arc[2,3], :type=>:edges, :direction=> :in)
|
280
|
+
assert_equal [Arc[1,2],Arc[3,4]], @single.adjacent(Arc[2,3], :type=>:edges, :direction=> :all).sort
|
281
|
+
|
282
|
+
assert_equal [MultiArc[3,4]], @dups.adjacent(MultiArc[2,3], :type=>:edges)
|
283
|
+
assert_equal [MultiArc[3,4]], @dups.adjacent(MultiArc[2,3], :type=>:edges, :direction=> :out)
|
284
|
+
assert_equal [MultiArc[1,2]]*2, @dups.adjacent(MultiArc[2,3], :type=>:edges, :direction=> :in)
|
285
|
+
assert_equal ([MultiArc[1,2]]*2+[MultiArc[3,4]]), @dups.adjacent(MultiArc[2,3], :type=>:edges, :direction=> :all).sort
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_neighborhood
|
289
|
+
assert_equal [2], @single.neighborhood(1).sort
|
290
|
+
assert_equal [1,3], @single.neighborhood(2).sort
|
291
|
+
assert_equal [Arc[1,2], Arc[3,4]], @single.neighborhood(Arc[2,3]).sort
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_degree
|
295
|
+
assert_equal 0, @single.in_degree(1)
|
296
|
+
assert_equal 1, @single.in_degree(2)
|
297
|
+
assert_equal 1, @single.in_degree(4)
|
298
|
+
assert_equal 3, @loops.degree(4)
|
299
|
+
assert_equal 2, @loops.in_degree(4)
|
300
|
+
assert_equal 1, @single.out_degree(1)
|
301
|
+
assert_equal 1, @single.out_degree(2)
|
302
|
+
assert_equal 0, @single.out_degree(4)
|
303
|
+
assert_equal 1, @loops.out_degree(4)
|
304
|
+
assert_equal 0, @single.add_vertex!(6).out_degree(6)
|
305
|
+
assert_equal 0, @single.add_vertex!(7).in_degree(7)
|
306
|
+
assert_equal 1, @single.add_edge!(4,2).out_degree(4)
|
307
|
+
assert_equal 2, @loops.add_edge!(4,2).out_degree(4)
|
308
|
+
assert_equal 2, @single.in_degree(2)
|
309
|
+
|
310
|
+
assert_equal 0, @single.min_in_degree
|
311
|
+
assert_equal 2, @single.max_in_degree
|
312
|
+
assert_equal 0, @single.min_out_degree
|
313
|
+
assert_equal 1, @single.max_out_degree
|
314
|
+
|
315
|
+
assert_equal 0, @loops.min_in_degree
|
316
|
+
assert_equal 2, @loops.max_in_degree
|
317
|
+
assert_equal 1, @loops.min_out_degree
|
318
|
+
assert_equal 2, @loops.max_out_degree
|
319
|
+
assert_equal 4, @loops.degree(2)
|
320
|
+
assert_equal 1, @single.degree(1)
|
321
|
+
assert !@loops.regular?
|
322
|
+
assert !@single.regular?
|
323
|
+
assert !@dups.regular?
|
324
|
+
end
|
325
|
+
|
326
|
+
def test_include
|
327
|
+
assert @single.include?(2)
|
328
|
+
assert !@single.include?(23)
|
329
|
+
assert @single.include?(Arc[1,2])
|
330
|
+
assert !@single.include?(Arc[1,4])
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|