plexus 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/Gemfile +3 -0
  2. data/LICENSE +37 -0
  3. data/README.md +208 -0
  4. data/Rakefile +25 -0
  5. data/lib/plexus.rb +90 -0
  6. data/lib/plexus/adjacency_graph.rb +225 -0
  7. data/lib/plexus/arc.rb +60 -0
  8. data/lib/plexus/arc_number.rb +50 -0
  9. data/lib/plexus/biconnected.rb +84 -0
  10. data/lib/plexus/chinese_postman.rb +91 -0
  11. data/lib/plexus/classes/graph_classes.rb +28 -0
  12. data/lib/plexus/common.rb +63 -0
  13. data/lib/plexus/comparability.rb +63 -0
  14. data/lib/plexus/directed_graph.rb +78 -0
  15. data/lib/plexus/directed_graph/algorithms.rb +95 -0
  16. data/lib/plexus/directed_graph/distance.rb +167 -0
  17. data/lib/plexus/dot.rb +94 -0
  18. data/lib/plexus/edge.rb +38 -0
  19. data/lib/plexus/ext.rb +79 -0
  20. data/lib/plexus/graph.rb +628 -0
  21. data/lib/plexus/graph_api.rb +35 -0
  22. data/lib/plexus/labels.rb +112 -0
  23. data/lib/plexus/maximum_flow.rb +77 -0
  24. data/lib/plexus/ruby_compatibility.rb +17 -0
  25. data/lib/plexus/search.rb +510 -0
  26. data/lib/plexus/strong_components.rb +93 -0
  27. data/lib/plexus/support/support.rb +9 -0
  28. data/lib/plexus/undirected_graph.rb +56 -0
  29. data/lib/plexus/undirected_graph/algorithms.rb +90 -0
  30. data/lib/plexus/version.rb +6 -0
  31. data/spec/biconnected_spec.rb +27 -0
  32. data/spec/chinese_postman_spec.rb +27 -0
  33. data/spec/community_spec.rb +44 -0
  34. data/spec/complement_spec.rb +27 -0
  35. data/spec/digraph_distance_spec.rb +121 -0
  36. data/spec/digraph_spec.rb +339 -0
  37. data/spec/dot_spec.rb +48 -0
  38. data/spec/edge_spec.rb +158 -0
  39. data/spec/inspection_spec.rb +38 -0
  40. data/spec/multi_edge_spec.rb +32 -0
  41. data/spec/neighborhood_spec.rb +36 -0
  42. data/spec/properties_spec.rb +146 -0
  43. data/spec/search_spec.rb +227 -0
  44. data/spec/spec.opts +4 -0
  45. data/spec/spec_helper.rb +59 -0
  46. data/spec/strong_components_spec.rb +61 -0
  47. data/spec/triangulated_spec.rb +125 -0
  48. data/spec/undirected_graph_spec.rb +220 -0
  49. data/vendor/priority-queue/CHANGELOG +33 -0
  50. data/vendor/priority-queue/Makefile +140 -0
  51. data/vendor/priority-queue/README +133 -0
  52. data/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
  53. data/vendor/priority-queue/compare_comments.rb +49 -0
  54. data/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  55. data/vendor/priority-queue/doc/compare_big.gp +14 -0
  56. data/vendor/priority-queue/doc/compare_big.png +0 -0
  57. data/vendor/priority-queue/doc/compare_small.gp +15 -0
  58. data/vendor/priority-queue/doc/compare_small.png +0 -0
  59. data/vendor/priority-queue/doc/results.csv +37 -0
  60. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  61. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
  62. data/vendor/priority-queue/lib/priority_queue.rb +14 -0
  63. data/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  64. data/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  65. data/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
  66. data/vendor/priority-queue/priority_queue.so +0 -0
  67. data/vendor/priority-queue/setup.rb +1551 -0
  68. data/vendor/priority-queue/test/priority_queue_test.rb +371 -0
  69. data/vendor/rdot.rb +360 -0
  70. metadata +100 -10
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,59 @@
1
+ require File.expand_path("../../lib/plexus.rb", __FILE__)
2
+
3
+ require 'plexus'
4
+ include Plexus
5
+
6
+ module AncestryHelper
7
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
8
+ # Golumbic, 1980, Academic Press, page 38, Figure 2.6
9
+ def assign_dfsnumber_ancestry(graph, dfsnumber, father, start)
10
+ i = 0
11
+ dfsnumber.clear
12
+ father.clear
13
+ ev = Proc.new {|v| dfsnumber[v] = (i+=1) }
14
+ te = Proc.new {|e| father[e.target] = e.source }
15
+ graph.dfs({:enter_vertex => ev, :tree_edge => te, :start => start})
16
+ end
17
+
18
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
19
+ # Golumbic, 1980, Academic Press, page 40, Figure 2.7
20
+ def assign_bfsnumber_ancestry(graph, bfsnum, level, father, start)
21
+ i = 0
22
+ bfsnum.clear
23
+ level.clear
24
+ father.clear
25
+ rt = Proc.new {|v| level[v] = 0 }
26
+ ev = Proc.new {|v| bfsnum[v]=(i+=1);level[v]=(level[father[v]]+1) if father[v]}
27
+ te = Proc.new {|e| father[e.target] = e.source }
28
+ graph.dfs({:enter_vertex => ev, :tree_edge => te,
29
+ :root_vertex => rt, :start => start})
30
+ end
31
+
32
+
33
+ # Is v an ancestor of u?
34
+ def ancestor?(father, u, v)
35
+ i = 1
36
+ while v
37
+ return i if father[v] == u
38
+ v = father[v]
39
+ i += 1
40
+ end; nil
41
+ end
42
+
43
+ # Is there any relationship?
44
+ def related?(father,u,v)
45
+ ancestor?(father,u,v) or ancestor?(father,v,u)
46
+ end
47
+
48
+ end
49
+
50
+ RSpec.configure do |config|
51
+ # Remove this line if you don't want RSpec's should and should_not
52
+ # methods or matchers
53
+ require 'rspec/expectations'
54
+ config.include RSpec::Matchers
55
+ config.include AncestryHelper
56
+
57
+ # == Mock Framework
58
+ config.mock_with :rspec
59
+ end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Strong Components" do # :nodoc:
4
+
5
+ # Test from boost strong_components.cpp
6
+ # Original Copyright 1997-2001, University of Notre Dame.
7
+ # Original Authors: Andrew Lumsdaine, Lie-Quan Lee, Jermey G. Siek
8
+ describe "boost" do
9
+ it do
10
+ g = Digraph[ 'a', 'b', 'a', 'f', 'a', 'h',
11
+ 'b', 'c', 'b', 'a',
12
+ 'c', 'd', 'c', 'b',
13
+ 'd', 'e',
14
+ 'e', 'd',
15
+ 'f', 'g',
16
+ 'g', 'f', 'g', 'd',
17
+ 'h', 'i',
18
+ 'i', 'h', 'i', 'j', 'i', 'e', 'i', 'c']
19
+
20
+ c = g.strong_components.map {|x| x.sort}
21
+ g.vertices.size.should == 10
22
+ c.size.should == 4
23
+ c.should include(['d','e'])
24
+ c.should include(['f','g'])
25
+ c.should include(['j'])
26
+ c.should include(['a','b','c','h','i'])
27
+
28
+ cg = g.condensation
29
+ cg_vertices = cg.map {|v| v.sort}
30
+ cg_vertices.size.should == 4
31
+ cg_vertices.should include(['j'])
32
+ cg_vertices.should include(['d','e'])
33
+ cg_vertices.should include(['f', 'g'])
34
+ cg_vertices.should include(['a', 'b', 'c', 'h', 'i'])
35
+ cg.edges.map {|e| [e.source.sort.join, e.target.sort.join] }.to_a.sort.should ==
36
+ [["abchi", "abchi"], ["abchi", "de"], ["abchi", "fg"], ["abchi", "j"], ["de", "de"], ["fg", "de"], ["fg", "fg"]]
37
+ end
38
+ end
39
+
40
+
41
+ # Figure #3, from 'Depth-First Search and Linear Graph Algorithms'
42
+ # by Robert Tarjan, SIAM J. Comput. Vol 1, No.2, June 1972
43
+ describe "tarjan_fig_3" do
44
+ it do
45
+ g = Digraph[ 1,2,
46
+ 2,3, 2,8,
47
+ 3,4, 3,7,
48
+ 4,5,
49
+ 5,3, 5,6,
50
+ 7,4, 7,6,
51
+ 8,1, 8,7 ]
52
+
53
+ c = g.strong_components.map {|x| x.sort}
54
+ g.vertices.size.should == 8
55
+ c.size.should == 3
56
+ c.should include([6])
57
+ c.should include([1,2,8])
58
+ c.should include([3,4,5,7])
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,125 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Triangulated" do #:nodoc:
4
+
5
+ describe "berge_mystery" do
6
+ it do
7
+ berge_mystery = UndirectedGraph[
8
+ :abe, :eddie,
9
+ :abe, :burt,
10
+ :abe, :desmond,
11
+ :eddie, :burt,
12
+ :eddie, :ida,
13
+ :eddie, :charlotte,
14
+ :charlotte, :ida,
15
+ :charlotte, :desmond,
16
+ :burt, :ida,
17
+ :ida, :desmond]
18
+
19
+ berge_mystery.should_not be_triangulated
20
+ berge_mystery.remove_vertex!(:desmond)
21
+ berge_mystery.should be_triangulated
22
+ berge_mystery.chromatic_number.should == 3
23
+ end
24
+ end
25
+
26
+ describe "house" do
27
+ it do
28
+ house = UndirectedGraph[
29
+ :roof, :left_gutter,
30
+ :roof, :right_gutter,
31
+ :left_gutter, :left_foundation,
32
+ :right_gutter, :right_foundation,
33
+ :left_foundation, :right_foundation
34
+ ]
35
+ house.should_not be_triangulated
36
+ house.remove_vertex!(:left_foundation) # Becomes a bulls head graph
37
+ house.should be_triangulated
38
+ ##
39
+ # This assertion wasn't running correctly in GRATR (`assert`
40
+ # when it should have been `assert_equal`), failing now, and
41
+ # may not be valid.
42
+ # house.chromatic_number.should == 3
43
+ end
44
+
45
+ end
46
+
47
+ # A triangulated, but not interval graph test
48
+ describe "non_interval" do
49
+ it do
50
+ non_interval = UndirectedGraph[
51
+ :ao, :ai,
52
+ :ai, :bi,
53
+ :ai, :ci,
54
+ :bo, :bi,
55
+ :bi, :ci,
56
+ :co, :ci
57
+ ]
58
+ non_interval.should be_triangulated
59
+ non_interval.chromatic_number.should == 3
60
+ end
61
+
62
+ end
63
+
64
+ describe "simple" do
65
+ it do
66
+ simple = UndirectedGraph[
67
+ :a, :b,
68
+ :b, :c,
69
+ :c, :d,
70
+ :d, :e,
71
+ :e, :f,
72
+ :f, :g,
73
+ :g, :a,
74
+ :g, :b,
75
+ :b, :f,
76
+ :f, :c,
77
+ ]
78
+ simple.should_not be_triangulated
79
+ simple.add_edge!(:c, :e)
80
+ simple.should be_triangulated
81
+ simple.chromatic_number.should == 3
82
+ UndirectedGraph[:a, :b].chromatic_number.should == 2
83
+ end
84
+ end
85
+
86
+ describe "simple2" do
87
+ it do
88
+ simple2 = UndirectedGraph[
89
+ :x, :p,
90
+ :p, :z,
91
+ :z, :r,
92
+ :r, :x,
93
+ :p, :y,
94
+ :y, :r,
95
+ :y, :q,
96
+ :q, :z]
97
+ simple2.should_not be_triangulated
98
+ end
99
+ end
100
+
101
+ describe "lexicographic_queue" do
102
+ it do
103
+ q = Plexus::Search::LexicographicQueue.new([1,2,3,4,5,6,7,8,9])
104
+ q.pop.should == 9
105
+ q.add_lexeme([3,4,5,6,7,8])
106
+ q.pop.should == 8
107
+ q.add_lexeme([2,6,7,9])
108
+ q.pop.should == 7
109
+ q.add_lexeme([8,9])
110
+ q.pop.should == 6
111
+ q.add_lexeme([1,5,8,9])
112
+ q.pop.should == 5
113
+ q.add_lexeme([6,9])
114
+ q.pop.should == 4
115
+ q.add_lexeme([3,9])
116
+ q.pop.should == 3
117
+ q.add_lexeme([4,9])
118
+ q.pop.should == 2
119
+ q.add_lexeme([8])
120
+ q.pop.should == 1
121
+ q.pop.should == nil
122
+ end
123
+ end
124
+
125
+ end
@@ -0,0 +1,220 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "UndirectedGraph" do # :nodoc:
4
+
5
+ before do
6
+ @single = UndirectedGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
7
+ @dups = UndirectedPseudoGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
8
+ @loops = UndirectedMultiGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
9
+ end
10
+
11
+ describe "new" do
12
+ it do
13
+ @single.should == UndirectedGraph[1,2, 2,3, 3,4, 4,4]
14
+ @dups.should == UndirectedPseudoGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
15
+ @loops.should == UndirectedMultiGraph[1,2, 2,3, 3,4, 4,4, 1,2, 2,3]
16
+ proc { UndirectedGraph.new(:bomb) }.should raise_error(ArgumentError)
17
+ proc { UndirectedGraph.new(1) }.should raise_error(ArgumentError)
18
+ UndirectedGraph.new(@single).should == @single
19
+ end
20
+ end
21
+
22
+ describe "edges" do
23
+ it do
24
+ @single.edges.should include(Edge[1,2])
25
+ @single.edges.should include(Edge[2,3])
26
+ @single.edges.should include(Edge[3,4])
27
+ @single.edges.should_not include(Edge[4,4])
28
+ @loops.edges.should include(MultiEdge[4,4])
29
+ @single.edges.should include(Edge[1,2])
30
+ @single.edges.should include(Edge[2,3])
31
+ @single.edges.should_not include(Edge[1,3])
32
+ @single.should be_edge(2,3)
33
+ @single.should_not be_edge(1,4)
34
+ @single.should be_edge(Edge[1,2])
35
+ @single.add_edge!(5,5).should_not be_edge(5,5)
36
+ @dups.add_edge!(5,5).should_not be_edge(5,5)
37
+ @loops.add_edge!(5,5).should be_edge(5,5)
38
+ @single.remove_edge!(5,5).should_not be_edge(5,5)
39
+ end
40
+ end
41
+
42
+ describe "vertices" do
43
+ it do
44
+ @single.vertices.to_a.sort.should == [1,2,3,4]
45
+ @single.add_vertex!(5).to_a.sort.should == [1,2,3,4,5]
46
+ @single.remove_vertex!(3).to_a.sort.should == [1,2,4,5]
47
+ @single.should_not be_vertex(3)
48
+ @single.should_not be_edge(2,3)
49
+ @single.should_not be_edge(3,4)
50
+ end
51
+ end
52
+
53
+ describe "properties" do
54
+ it do
55
+ @single.should_not be_directed
56
+ @single.should_not be_empty
57
+ UndirectedGraph.new.should be_empty
58
+ @single.size.should == 4
59
+ @dups.size.should == 4
60
+ @single.num_vertices.should == 4
61
+ @dups.num_vertices.should == 4
62
+ @single.num_edges.should == 3
63
+ @loops.num_edges.should == 6
64
+ @dups.num_edges.should == 5
65
+ end
66
+ end
67
+
68
+ describe "merge" do
69
+ it do
70
+ @dups.merge(@single)
71
+ @dups.num_edges.should == 8
72
+ @dups.vertices.sort.should == [1,2,3,4]
73
+ end
74
+ end
75
+
76
+ describe "operators" do
77
+ it do
78
+ result = @single + Edge[3,2]
79
+ @single.size.should == 4
80
+ @single.num_edges.should == 3
81
+ result.size.should == 4
82
+ result.num_edges.should == 3
83
+
84
+ result = @single + 5
85
+ @single.size.should == 4
86
+ @single.num_edges.should == 3
87
+ result.size.should == 5
88
+ result.num_edges.should == 3
89
+
90
+ result = @single - Edge[4,4]
91
+ @single.size.should == 4
92
+ @single.num_edges.should == 3
93
+ result.size.should == 4
94
+ result.num_edges.should == 3
95
+
96
+ result = @single - 4
97
+ @single.size.should == 4
98
+ @single.num_edges.should == 3
99
+ result.size.should == 3
100
+ result.num_edges.should == 2
101
+
102
+ @single << Edge[6,1]
103
+ @single.size.should == 5
104
+ @single.num_edges.should == 4
105
+ @single.should be_edge(6,1)
106
+ end
107
+ end
108
+
109
+ describe "complement" do
110
+ it do
111
+ complement = @single.complement
112
+ complement.vertices.sort.should == [1,2,3,4]
113
+ complement.should_not be_edge(1,1)
114
+ complement.should be_edge(1,3)
115
+ complement.should be_edge(1,4)
116
+ complement.should_not be_edge(2,2)
117
+ complement.should be_edge(2,4)
118
+ complement.should be_edge(3,1)
119
+ complement.should_not be_edge(3,3)
120
+ complement.should be_edge(4,1)
121
+ complement.should be_edge(4,2)
122
+ ##
123
+ # This assertion wasn't running correctly in GRATR (`assert`
124
+ # when it should have been `assert_equal`), failing now, and
125
+ # may not be valid.
126
+ # complement.num_edges.should == 7
127
+ end
128
+ end
129
+
130
+ describe "induced_subgraph" do
131
+ it do
132
+ induced = @single.induced_subgraph([1,2])
133
+ induced.vertices.sort.should == [1,2]
134
+ induced.should be_edge(1,2)
135
+ induced.num_edges.should == 1
136
+ end
137
+ end
138
+
139
+ describe "include" do
140
+ it do
141
+ @single.should include(4)
142
+ @dups.should include(4)
143
+ @dups.should_not include(5)
144
+ @single.should_not include(5)
145
+ @single.should include(Edge[1,2])
146
+ @dups.should include(Edge[1,2])
147
+ end
148
+ end
149
+
150
+ describe "adjacent" do
151
+ it do
152
+
153
+ @single.should be_adjacent(2, Edge[1,2])
154
+ @single.adjacent(1).should == [2]
155
+
156
+ @single.adjacent(1, :type=>:edges).should == [Edge[1,2]]
157
+ @single.adjacent(1, :type=>:edges, :direction=> :out).should == [Edge[1,2]]
158
+ @single.adjacent(2, :type=>:edges, :direction=> :in).sort.should == [Edge[1,2],Edge[2,3]]
159
+ @single.adjacent(2, :type=>:edges, :direction=> :all).sort.should == [Edge[1,2],Edge[2,3]]
160
+
161
+ @dups.adjacent(1, :type=>:edges).should == [MultiEdge[1,2]]*2
162
+ @dups.adjacent(1, :type=>:edges, :direction=> :out).should == [MultiEdge[1,2]]*2
163
+ @dups.adjacent(2, :type=>:edges, :direction=> :in).sort.should == ([MultiEdge[1,2]]*2 + [MultiEdge[2,3]]*2)
164
+ @dups.adjacent(2, :type=>:edges, :direction=> :all).sort.should == ([MultiEdge[1,2]]*2 + [MultiEdge[2,3]]*2)
165
+
166
+ @single.adjacent(1, :type=>:vertices).should == [2]
167
+ @single.adjacent(1, :type=>:vertices, :direction=> :out).should == [2]
168
+ @single.adjacent(2, :type=>:vertices, :direction=> :in).should == [1,3]
169
+ @single.adjacent(2, :type=>:vertices, :direction=> :all).should == [1,3]
170
+
171
+ @single.adjacent(Edge[2,3], :type=>:vertices).should == [2,3]
172
+ @single.adjacent(Edge[2,3], :type=>:vertices, :direction=> :out).should == [2,3]
173
+ @single.adjacent(Edge[2,3], :type=>:vertices, :direction=> :in).should == [2,3]
174
+ @single.adjacent(Edge[2,3], :type=>:vertices, :direction=> :all).should == [2,3]
175
+
176
+ @single.adjacent(Edge[2,3], :type=>:edges).sort.should == [Edge[1,2],Edge[3,4]]
177
+ @single.adjacent(Edge[2,3], :type=>:edges, :direction=> :out).sort.should == [Edge[1,2],Edge[3,4]]
178
+ @single.adjacent(Edge[2,3], :type=>:edges, :direction=> :in).sort.should == [Edge[1,2],Edge[3,4]]
179
+ @single.adjacent(Edge[2,3], :type=>:edges, :direction=> :all).sort.should == [Edge[1,2],Edge[3,4]]
180
+ @dups.adjacent(MultiEdge[2,3], :type=>:edges).sort.should == ([MultiEdge[1,2]]*2 + [MultiEdge[3,4]])
181
+ @dups.adjacent(MultiEdge[2,3], :type=>:edges, :direction=>:out).sort.should == ([MultiEdge[1,2]]*2 + [MultiEdge[3,4]])
182
+ @dups.adjacent(MultiEdge[2,3], :type=>:edges, :direction=>:in).sort.should == ([MultiEdge[1,2]]*2 + [MultiEdge[3,4]])
183
+ @dups.adjacent(MultiEdge[2,3], :type=>:edges, :direction=> :all).sort.should == ([MultiEdge[1,2]]*2+[MultiEdge[3,4]])
184
+ end
185
+
186
+ describe "neighborhood" do
187
+ it do
188
+ @single.neighborhood(1).sort.should == [2]
189
+ @single.neighborhood(2).sort.should == [1, 3]
190
+ @single.neighborhood(Edge[2,3]).sort.should == [Edge[1,2], Edge[3,4]]
191
+ end
192
+ end
193
+
194
+ describe "degree" do
195
+ it do
196
+ @single.in_degree(1).should == 1
197
+ @single.in_degree(2).should == 2
198
+ @single.in_degree(4).should == 1
199
+ @single.out_degree(1).should == 1
200
+ @single.out_degree(2).should == 2
201
+ @single.out_degree(4).should == 1
202
+ @single.add_vertex!(6).out_degree(6).should == 0
203
+ @single.add_vertex!(7).in_degree(7).should == 0
204
+ @single.add_edge!(4,2).out_degree(4).should == 2
205
+ @single.in_degree(2).should == 3
206
+ end
207
+ end
208
+
209
+ describe "include" do
210
+ it do
211
+ @single.should include(2)
212
+ @single.should_not include(23)
213
+ @single.should include(Edge[1,2])
214
+ @single.should_not include(Edge[1,4])
215
+ end
216
+ end
217
+
218
+ end
219
+
220
+ end