plexus 0.5.4 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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