graph.njae 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,6 +8,5 @@ source "http://rubygems.org"
8
8
  group :development do
9
9
  gem "rspec", "~> 2.8.0"
10
10
  gem "bundler", "~> 1.0.0"
11
- gem "jeweler", "~> 1.6.2"
12
- gem "rdoc"
11
+ gem "jeweler", "~> 1.8.0"
13
12
  end
@@ -3,10 +3,11 @@ GEM
3
3
  specs:
4
4
  diff-lcs (1.1.3)
5
5
  git (1.2.5)
6
- jeweler (1.6.4)
6
+ jeweler (1.8.3)
7
7
  bundler (~> 1.0)
8
8
  git (>= 1.2.5)
9
9
  rake
10
+ rdoc
10
11
  json (1.6.5)
11
12
  rake (0.9.2.2)
12
13
  rdoc (3.12)
@@ -25,6 +26,5 @@ PLATFORMS
25
26
 
26
27
  DEPENDENCIES
27
28
  bundler (~> 1.0.0)
28
- jeweler (~> 1.6.2)
29
- rdoc
29
+ jeweler (~> 1.8.0)
30
30
  rspec (~> 2.8.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "graph.njae"
8
- s.version = "0.2.4"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Neil Smith"]
12
- s.date = "2012-03-05"
12
+ s.date = "2012-05-18"
13
13
  s.description = "A simple graph library"
14
14
  s.email = "neil.github@njae.me.uk"
15
15
  s.extra_rdoc_files = [
@@ -47,19 +47,16 @@ Gem::Specification.new do |s|
47
47
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
48
  s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
49
49
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
- s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
51
- s.add_development_dependency(%q<rdoc>, [">= 0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.0"])
52
51
  else
53
52
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
54
53
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
55
- s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
56
- s.add_dependency(%q<rdoc>, [">= 0"])
54
+ s.add_dependency(%q<jeweler>, ["~> 1.8.0"])
57
55
  end
58
56
  else
59
57
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
60
58
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
- s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
62
- s.add_dependency(%q<rdoc>, [">= 0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.8.0"])
63
60
  end
64
61
  end
65
62
 
@@ -10,8 +10,8 @@ module GraphNjae
10
10
  # Each connection is handled by a Graph::Connection object, so that each end
11
11
  # of the Edge can have it's own attributes.
12
12
  class Edge < OpenStruct
13
- def initialize
14
- super
13
+ def initialize(values = {})
14
+ super(values)
15
15
  self.connections = []
16
16
  self
17
17
  end
@@ -20,6 +20,7 @@ module GraphNjae
20
20
  def <<(other)
21
21
  c = Connection.new
22
22
  c.end = other
23
+ other.edges << self unless other.edges.include? self
23
24
  self.connections << c
24
25
  self
25
26
  end
@@ -33,13 +34,27 @@ module GraphNjae
33
34
  def connection_at(vertex)
34
35
  self.connections.find {|c| c.end.equal? vertex}
35
36
  end
37
+
38
+ # Return the vertex at the other end of the one given.
39
+ # Self-loops should still return the vertex
40
+ def other_end(vertex)
41
+ if self.vertices[0] == vertex
42
+ self.vertices[1]
43
+ else
44
+ self.vertices[0]
45
+ end
46
+ end
47
+
48
+ def to_s
49
+ '<E: ' + self.type.to_s + ' [' + self.vertices.map {|n| n.to_s}.join(', ') + '] >'
50
+ end
36
51
  end
37
52
 
38
53
  # A connection between an Edge and a Vertex.The connection can have arbitrary attributes,
39
54
  # treated as method names.
40
55
  class Connection < OpenStruct
41
- def initialize
42
- super
56
+ def initialize(values = {})
57
+ super(values)
43
58
  self
44
59
  end
45
60
  end
@@ -1,5 +1,9 @@
1
1
  require 'ostruct'
2
2
 
3
+ require 'logger'
4
+ $log = Logger.new(STDERR)
5
+ $log.level = Logger::WARN
6
+
3
7
  # A simple graph library
4
8
 
5
9
  module GraphNjae
@@ -7,8 +11,8 @@ module GraphNjae
7
11
  # A container for all the parts of a graph. The graph can have arbitrary attributes,
8
12
  # treated as method names.
9
13
  class Graph < OpenStruct
10
- def initialize
11
- super
14
+ def initialize(values = {})
15
+ super(values)
12
16
  self.edges = Array.new
13
17
  self.vertices = Array.new
14
18
  self
@@ -23,5 +27,94 @@ module GraphNjae
23
27
  end
24
28
  self
25
29
  end
26
- end
30
+
31
+ # Connects two vertices, creating and storing a new edge
32
+ # Also adds the vertices, unless they're already in the graph
33
+ def connect(vertex1, vertex2, edge_attributes = {})
34
+ self.vertices << vertex1 unless self.vertices.include? vertex1
35
+ self.vertices << vertex2 unless self.vertices.include? vertex2
36
+ edge = Edge.new(edge_attributes)
37
+ self.edges << edge
38
+ edge << vertex1 << vertex2
39
+ end
40
+
41
+ # Form a product graph of this graph and the other.
42
+ # Return the product graph.
43
+ def product(other)
44
+ product_graph = Graph.new
45
+ self.vertices.each do |v1|
46
+ other.vertices.each do |v2|
47
+ product_graph << Vertex.new({:g1_vertex => v1, :g2_vertex => v2})
48
+ end
49
+ end
50
+ self.edges.each do |e1|
51
+ e1_vertices = e1.vertices
52
+ other.edges.each do |e2|
53
+ if e1.type == e2.type
54
+ e2_vertices = e2.vertices
55
+ source = product_graph.vertices.find {|v| v.g1_vertex == e1_vertices[0] and v.g2_vertex == e2_vertices[0]}
56
+ destination = product_graph.vertices.find {|v| v.g1_vertex == e1_vertices[1] and v.g2_vertex == e2_vertices[1]}
57
+ product_graph.connect source, destination
58
+ source = product_graph.vertices.find {|v| v.g1_vertex == e1_vertices[0] and v.g2_vertex == e2_vertices[1]}
59
+ destination = product_graph.vertices.find {|v| v.g1_vertex == e1_vertices[1] and v.g2_vertex == e2_vertices[0]}
60
+ product_graph.connect source, destination
61
+ end
62
+ end
63
+ end
64
+ product_graph.vertices.reject! {|v| v.neighbours.empty?}
65
+ product_graph
66
+ end
67
+
68
+
69
+ def initial_similarity
70
+ self.vertices.each do |v|
71
+ if block_given?
72
+ v.initial_similarity = yield v
73
+ else
74
+ v.initial_similarity = 1.0
75
+ end
76
+ v.similarity = v.initial_similarity
77
+ end
78
+ end
79
+
80
+ # Performs similarity flooding on a graph
81
+ # Assumes that the initial similarity has already been calculated
82
+ def similarity_flood(opts = {})
83
+ max_iterations = opts[:iterations] || 100
84
+ max_residual = opts[:max_residual] || 0.001
85
+ iteration = 1
86
+ residual = max_residual + 1
87
+ while residual > max_residual and iteration <= max_iterations
88
+ $log.debug { "Starting iteration #{iteration}" }
89
+ self.vertices.each do |v|
90
+ v.last_similarity = v.similarity
91
+ end
92
+ self.vertices.each do |v|
93
+ if block_given?
94
+ v.similarity = yield v
95
+ else
96
+ $log.debug { "Processing vertex #{v.name}" }
97
+ edge_groups = v.edges.group_by {|e| e.type }
98
+ $log.debug { " Edge groups {#{edge_groups.keys.map {|t| t.to_s + ' => {' + edge_groups[t].map {|e| e.to_s}.join(', ')}.join('; ')}}" }
99
+ edge_groups.each do |type, edges|
100
+ $log.debug { " Processing group type #{type}" }
101
+ n = edges.length
102
+ edges.each do |e|
103
+ e.other_end(v).similarity += v.last_similarity / n
104
+ end
105
+ end
106
+ end
107
+ end
108
+ max_similarity = vertices.map {|v| v.similarity}.max
109
+ self.vertices.each do |v|
110
+ v.similarity = v.similarity / max_similarity
111
+ end
112
+ residual = Math.sqrt(self.vertices.reduce(0) {|a, v| a += (v.similarity - v.last_similarity) ** 2})
113
+ $log.debug { puts "Residual = #{residual.round(3)}, sims = #{self.vertices.map {|v| v.name + " = " + v.similarity.round(2).to_s}}" }
114
+ iteration += 1
115
+ end
116
+
117
+ end
118
+
119
+ end # class
27
120
  end
@@ -6,19 +6,19 @@ module GraphNjae
6
6
  # A vertex in a graph. The edge can have arbitrary attributes,treated as
7
7
  # method names.
8
8
  class Vertex < OpenStruct
9
- def initialize
10
- super
9
+ def initialize(values = {})
10
+ super(values)
11
11
  self.edges = []
12
12
  self
13
13
  end
14
14
 
15
15
  # Connect this vertex to another, creating an Edge to do so, and returning
16
16
  # the Edge
17
- def connect(other)
18
- e = Edge.new
17
+ def connect(other, edge_attributes = {})
18
+ e = Edge.new edge_attributes
19
19
  e << self << other
20
- self.edges << e
21
- other.edges << e unless self === other
20
+ # self.edges << e
21
+ # other.edges << e unless self === other
22
22
  e
23
23
  end
24
24
 
@@ -39,5 +39,9 @@ module GraphNjae
39
39
  e.vertices.drop_while {|v| v != self}[1..-1]}.flatten
40
40
  end
41
41
 
42
+ def to_s
43
+ '<V: ' + self.name + '>'
44
+ end
45
+
42
46
  end
43
47
  end
@@ -8,6 +8,14 @@ module GraphNjae
8
8
  e = Edge.new
9
9
  e.connections.should be_empty
10
10
  end
11
+
12
+ it "creates an edge with some parameters" do
13
+ e = Edge.new :value1 => 1, :value2 => "value2", :value3 => :v3
14
+ e.value1.should == 1
15
+ e.value2.should == "value2"
16
+ e.value3.should == :v3
17
+ e.value4.should be_nil
18
+ end
11
19
  end # #initialize
12
20
 
13
21
  describe "adds attribues" do
@@ -20,23 +28,25 @@ module GraphNjae
20
28
  describe "#<<" do
21
29
  it "adds a new vertex to an edge (with a connection)" do
22
30
  e.connections.should be_empty
23
- v1 = Vertex.new
24
- v2 = Vertex.new
31
+ v1 = Vertex.new :name => :v1
32
+ v2 = Vertex.new :name => :v2
25
33
  e << v1
26
34
  e.should have(1).connections
27
35
  e.should have(1).vertices
28
36
  e.vertices.should include(v1)
37
+ v1.edges.should include(e)
29
38
  e << v2
30
39
  e.should have(2).connections
31
40
  e.should have(2).vertices
32
41
  e.vertices.should include(v1)
33
42
  e.vertices.should include(v2)
43
+ v2.edges.should include(e)
34
44
  end
35
45
 
36
46
  it "adds several vertices to an edge" do
37
47
  e.connections.should be_empty
38
- v1 = Vertex.new
39
- v2 = Vertex.new
48
+ v1 = Vertex.new :name => :v1
49
+ v2 = Vertex.new :name => :v2
40
50
  e << v1 << v2
41
51
  e.vertices.should include(v1)
42
52
  e.vertices.should include(v2)
@@ -56,8 +66,8 @@ module GraphNjae
56
66
  describe "connection_at" do
57
67
  it "returns the connection that links to a vertex" do
58
68
  e.connections.should be_empty
59
- v1 = Vertex.new
60
- v2 = Vertex.new
69
+ v1 = Vertex.new :name => :v1
70
+ v2 = Vertex.new :name => :v2
61
71
  e << v1 << v2
62
72
 
63
73
  e.connection_at(v1).end.should be v1
@@ -66,9 +76,9 @@ module GraphNjae
66
76
 
67
77
  it "returns nil if there is no connection to that vertex" do
68
78
  e.connections.should be_empty
69
- v1 = Vertex.new
70
- v2 = Vertex.new
71
- v3 = Vertex.new
79
+ v1 = Vertex.new :name => :v1
80
+ v2 = Vertex.new :name => :v2
81
+ v3 = Vertex.new :name => :v3
72
82
  e << v1 << v2
73
83
 
74
84
  e.connection_at(v3).should be nil
@@ -76,14 +86,49 @@ module GraphNjae
76
86
 
77
87
  it "returns the vertex for a self-loop" do
78
88
  e.connections.should be_empty
79
- v1 = Vertex.new
89
+ v1 = Vertex.new :name => :v1
80
90
  e << v1 << v1
81
91
 
82
92
  e.connection_at(v1).end.should be v1
83
93
  end
84
-
85
-
86
94
  end # #connection_at
95
+
96
+ describe "other_end" do
97
+ it "returns the vertex at the other end of the given one" do
98
+ e.connections.should be_empty
99
+ v1 = Vertex.new :name => :v1
100
+ v2 = Vertex.new :name => :v2
101
+ e << v1 << v2
102
+
103
+ e.other_end(v1).should be v2
104
+ e.other_end(v2).should be v1
105
+ end
106
+
107
+ it "returns the same vertex in a self-loop" do
108
+ e.connections.should be_empty
109
+ v1 = Vertex.new :name => :v1
110
+ e << v1 << v1
111
+
112
+ e.other_end(v1).should be v1
113
+ end
114
+
115
+ it "returns one of the connected edges if given a vertex not connected to it" do
116
+ e.connections.should be_empty
117
+ v1 = Vertex.new :name => :v1
118
+ v2 = Vertex.new :name => :v2
119
+ v3 = Vertex.new :name => :v3
120
+ e << v1 << v2
121
+
122
+ [v1, v2].should include e.other_end(v3)
123
+ end
124
+
125
+ it "returns nil if it can't return something sensible" do
126
+ v1 = Vertex.new :name => :v1
127
+ e.other_end(v1).should be_nil
128
+ e << v1
129
+ e.other_end(v1).should be_nil
130
+ end
131
+ end # other_end
87
132
  end # Edge
88
133
 
89
134
  describe Connection do
@@ -16,12 +16,21 @@ module GraphNjae
16
16
  g.edges.should be_empty
17
17
  g.vertices.should be_empty
18
18
  end
19
+
20
+ it "creates a graph with some parameters" do
21
+ g = Graph.new :value1 => 1, :value2 => "value2", :value3 => :v3
22
+ g.value1.should == 1
23
+ g.value2.should == "value2"
24
+ g.value3.should == :v3
25
+ g.value4.should be_nil
26
+ end
27
+
19
28
  end # #initialize
20
29
 
21
- describe "adds attribues" do
30
+ describe "adds attributes" do
22
31
  it "adds then reports arbitrary attributes" do
23
32
  g.score = 15
24
- g.score == 15
33
+ g.score.should == 15
25
34
  end
26
35
  end # adds attributes
27
36
 
@@ -75,8 +84,402 @@ module GraphNjae
75
84
 
76
85
  describe "connect" do
77
86
  it "adds and records an edge between vertices" do
87
+ g.vertices.should be_empty
88
+ g.edges.should be_empty
89
+ v1 = Vertex.new(:name => :v1)
90
+ v2 = Vertex.new(:name => :v2)
91
+ g.connect(v1, v2)
92
+
93
+ g.should have(2).vertices
94
+ g.vertices.should include(v1)
95
+ g.vertices.should include(v2)
96
+ g.should have(1).edges
97
+ end
98
+
99
+ it "adds and records an edge with attributes between vertices" do
100
+ g.vertices.should be_empty
101
+ g.edges.should be_empty
102
+ v1 = Vertex.new(:name => :v1)
103
+ v2 = Vertex.new(:name => :v2)
104
+ g.connect(v1, v2, :type => :edge_type)
105
+
106
+ g.should have(2).vertices
107
+ g.vertices.should include(v1)
108
+ g.vertices.should include(v2)
109
+ g.should have(1).edges
110
+ g.edges[0].type.should == :edge_type
78
111
  end
79
112
  end # #connect
80
113
 
114
+ describe "product" do
115
+ it "finds a product graph of a pair of one-vertex graphs" do
116
+ g1 = Graph.new
117
+ g2 = Graph.new
118
+ g1v1 = Vertex.new
119
+ g1 << g1v1
120
+ g2v1 = Vertex.new
121
+ g2 << g2v1
122
+ product = g1.product g2
123
+
124
+ product.vertices.should be_empty
125
+ #product.vertices.first.g1_vertex.should == g1v1
126
+ #product.vertices.first.g2_vertex.should == g2v1
127
+ product.edges.should be_empty
128
+ end
129
+
130
+ it "finds a product graph of a pair of simple graphs" do
131
+ g1 = Graph.new
132
+ g2 = Graph.new
133
+ g1v1 = Vertex.new(:name => :g1v1)
134
+ g1v2 = Vertex.new(:name => :g1v2)
135
+ g1.connect(g1v1, g1v2)
136
+ g2v1 = Vertex.new(:name => :g2v1)
137
+ g2v2 = Vertex.new(:name => :g2v2)
138
+ g2.connect(g2v1, g2v2)
139
+ pg = g1.product g2
140
+
141
+ pg.should have(4).vertices
142
+ pg.should have(2).edges
143
+ end
144
+
145
+ it "finds a product graph of not-quite-simple graph" do
146
+ g1 = Graph.new
147
+ g2 = Graph.new
148
+ g1v1 = Vertex.new(:name => :g1v1)
149
+ g1v2 = Vertex.new(:name => :g1v2)
150
+ g1v3 = Vertex.new(:name => :g1v3)
151
+ g1.connect(g1v1, g1v2)
152
+ g1.connect(g1v2, g1v3)
153
+ g2v1 = Vertex.new(:name => :g2v1)
154
+ g2v2 = Vertex.new(:name => :g2v2)
155
+ g2v3 = Vertex.new(:name => :g2v3)
156
+ g2.connect(g2v1, g2v2)
157
+ g2.connect(g2v2, g2v3)
158
+ pg = g1.product g2
159
+
160
+ pg.should have(9).vertices
161
+ pg.should have(8).edges
162
+ end
163
+
164
+ it "finds a product graph of a simple graph with edge types" do
165
+ g1 = Graph.new
166
+ g2 = Graph.new
167
+ g1v1 = Vertex.new(:name => :g1v1)
168
+ g1v2 = Vertex.new(:name => :g1v2)
169
+ g1v3 = Vertex.new(:name => :g1v3)
170
+ g1.connect(g1v1, g1v2, :type => :t1)
171
+ g1.connect(g1v2, g1v3, :type => :t2)
172
+ g2v1 = Vertex.new(:name => :g2v1)
173
+ g2v2 = Vertex.new(:name => :g2v2)
174
+ g2v3 = Vertex.new(:name => :g2v3)
175
+ g2.connect(g2v1, g2v2, :type => :t1)
176
+ g2.connect(g2v2, g2v3, :type => :t2)
177
+ pg = g1.product g2
178
+
179
+ pg.should have(7).vertices
180
+ pg.should have(4).edges
181
+ end
182
+
183
+ it "finds a product graph of the example graph from paper" do
184
+ pending "implementation of directed graphs as operands of the product graph"
185
+ g1 = Graph.new
186
+ g2 = Graph.new
187
+ a = Vertex.new(:name => :a)
188
+ a1 = Vertex.new(:name => :a1)
189
+ a2 = Vertex.new(:name => :a2)
190
+ g1.connect(a, a1, :type => :l1)
191
+ g1.connect(a, a2, :type => :l1)
192
+ g1.connect(a1, a2, :type => :l2)
193
+ b = Vertex.new(:name => :b)
194
+ b1 = Vertex.new(:name => :b1)
195
+ b2 = Vertex.new(:name => :b2)
196
+ g2.connect(b, b1, :type => :l1)
197
+ g2.connect(b, b2, :type => :l2)
198
+ g2.connect(b1, b2, :type => :l2)
199
+ pg = g1.product g2
200
+
201
+ pg.should have(4).edges
202
+ pg.should have(6).vertices
203
+ end
204
+
205
+
206
+ end #product
207
+
208
+ describe "initial_similarity" do
209
+ before(:each) do
210
+ g1 = Graph.new
211
+ g2 = Graph.new
212
+ g1v1 = Vertex.new(:name => :g1v1)
213
+ g1v2 = Vertex.new(:name => :g1v2)
214
+ g1.connect(g1v1, g1v2)
215
+ g2v1 = Vertex.new(:name => :g2v1)
216
+ g2v2 = Vertex.new(:name => :g2v2)
217
+ g2.connect(g2v1, g2v2)
218
+ @pg = g1.product g2
219
+ end
220
+
221
+ def simple_name_similarity(n1, n2)
222
+ 1 - n1.to_s.codepoints.to_a.delete_if {|c| n2.to_s.codepoints.to_a.include? c}.length / n1.to_s.length.to_f
223
+ end
224
+
225
+ it "should give all nodes an initial similarity of 1 if no block is given" do
226
+ @pg.initial_similarity
227
+ @pg.vertices.each do |v|
228
+ v.initial_similarity.should be_within(0.001).of(1.0)
229
+ v.similarity.should be_within(0.001).of(1.0)
230
+ end
231
+ end
232
+
233
+ it "should give all nodes the similarity as defined by the given block" do
234
+ @pg.initial_similarity {|v| simple_name_similarity v.g1_vertex.name, v.g2_vertex.name}
235
+ @pg.vertices.each do |v|
236
+ v.initial_similarity.should be_within(0.001).of( simple_name_similarity v.g1_vertex.name, v.g2_vertex.name )
237
+ v.similarity.should be_within(0.001).of( simple_name_similarity v.g1_vertex.name, v.g2_vertex.name )
238
+ end
239
+
240
+ end
241
+ end #initial similarity
242
+
243
+ describe "similarity flood" do
244
+ it "similarity floods a graph of two nodes" do
245
+ g1 = Graph.new
246
+ g2 = Graph.new
247
+ g1v1 = Vertex.new(:name => :g1v1)
248
+ g1v2 = Vertex.new(:name => :g1v2)
249
+ g1.connect(g1v1, g1v2)
250
+ g2v1 = Vertex.new(:name => :g2v1)
251
+ g2v2 = Vertex.new(:name => :g2v2)
252
+ g2.connect(g2v1, g2v2)
253
+ pg = g1.product g2
254
+
255
+ pg.initial_similarity
256
+ pg.similarity_flood
257
+ pg.vertices.each do |v|
258
+ v.similarity.should be_within(0.001).of(1.0)
259
+ end
260
+ end
261
+
262
+ it "similarity floods a graph of three nodes, a -- b -- c" do
263
+ g1 = Graph.new
264
+ g2 = Graph.new
265
+ g1v1 = Vertex.new(:name => :g1v1)
266
+ g1v2 = Vertex.new(:name => :g1v2)
267
+ g1v3 = Vertex.new(:name => :g1v3)
268
+ g1.connect(g1v1, g1v2, :type => :t1)
269
+ g1.connect(g1v2, g1v3, :type => :t2)
270
+ g2v1 = Vertex.new(:name => :g2v1)
271
+ g2v2 = Vertex.new(:name => :g2v2)
272
+ g2v3 = Vertex.new(:name => :g2v3)
273
+ g2.connect(g2v1, g2v2, :type => :t1)
274
+ g2.connect(g2v2, g2v3, :type => :t2)
275
+ pg = g1.product g2
276
+
277
+ pg.initial_similarity
278
+ pg.similarity_flood
279
+ expected_similarities = {
280
+ "g1v1:g2v1" => 0.5,
281
+ "g1v1:g2v2" => 0.6666666666666666,
282
+ "g1v2:g2v1" => 0.6666666666666666,
283
+ "g1v2:g2v2" => 1.0,
284
+ "g1v2:g2v3" => 0.6666666666666666,
285
+ "g1v3:g2v2" => 0.6666666666666666,
286
+ "g1v3:g2v3" => 0.5}
287
+ pg.vertices.each do |v|
288
+ name = v.g1_vertex.name.to_s + ':' + v.g2_vertex.name.to_s
289
+ v.similarity.should be_within(0.001).of(expected_similarities[name])
290
+ end
291
+ end
292
+
293
+ it "simialrity floods the sample graph from the paper" do
294
+ pg = Graph.new
295
+ ab = Vertex.new(:name => "a:b")
296
+ a1b1 = Vertex.new(:name => "a1:b1")
297
+ a2b1 = Vertex.new(:name => "a2:b1")
298
+ a1b2 = Vertex.new(:name => "a1:b2")
299
+ a1b = Vertex.new(:name => "a1:b")
300
+ a2b2 = Vertex.new(:name => "a2:b2")
301
+ pg.connect(ab, a1b1, :type => :l1)
302
+ pg.connect(ab, a2b1, :type => :l1)
303
+ pg.connect(a2b1, a1b2, :type => :l2)
304
+ pg.connect(a1b, a2b2, :type => :l2)
305
+ pg.initial_similarity
306
+ pg.similarity_flood
307
+
308
+ expected_similarities = {
309
+ "a:b" => 1.0,
310
+ "a2:b1" => 0.92,
311
+ "a1:b2" => 0.71,
312
+ "a1:b1" => 0.38,
313
+ "a1:b" => 0.0,
314
+ "a2:b2" => 0.0}
315
+ pg.vertices.each do |v|
316
+ v.similarity.should be_within(0.02).of(expected_similarities[v.name])
317
+ end
318
+ end
319
+
320
+ it "similarity floods a graph of three nodes, a -- b -- c, given a block that performs the default update" do
321
+ g1 = Graph.new
322
+ g2 = Graph.new
323
+ g1v1 = Vertex.new(:name => :g1v1)
324
+ g1v2 = Vertex.new(:name => :g1v2)
325
+ g1v3 = Vertex.new(:name => :g1v3)
326
+ g1.connect(g1v1, g1v2, :type => :t1)
327
+ g1.connect(g1v2, g1v3, :type => :t2)
328
+ g2v1 = Vertex.new(:name => :g2v1)
329
+ g2v2 = Vertex.new(:name => :g2v2)
330
+ g2v3 = Vertex.new(:name => :g2v3)
331
+ g2.connect(g2v1, g2v2, :type => :t1)
332
+ g2.connect(g2v2, g2v3, :type => :t2)
333
+ pg = g1.product g2
334
+
335
+ pg.initial_similarity
336
+ pg.similarity_flood do |v|
337
+ edge_groups = v.edges.group_by {|e| e.type }
338
+ edge_groups.each do |type, edges|
339
+ n = edges.length
340
+ edges.each do |e|
341
+ e.other_end(v).similarity += v.last_similarity / n
342
+ end
343
+ end
344
+ v.similarity
345
+ end
346
+ expected_similarities = {
347
+ "g1v1:g2v1" => 0.5,
348
+ "g1v1:g2v2" => 0.6666666666666666,
349
+ "g1v2:g2v1" => 0.6666666666666666,
350
+ "g1v2:g2v2" => 1.0,
351
+ "g1v2:g2v3" => 0.6666666666666666,
352
+ "g1v3:g2v2" => 0.6666666666666666,
353
+ "g1v3:g2v3" => 0.5}
354
+ pg.vertices.each do |v|
355
+ name = v.g1_vertex.name.to_s + ':' + v.g2_vertex.name.to_s
356
+ v.similarity.should be_within(0.001).of(expected_similarities[name])
357
+ end
358
+ end
359
+
360
+ it "similarity floods a graph of three nodes, a -- b -- c, given a block that performs a different update (method A)" do
361
+ g1 = Graph.new
362
+ g2 = Graph.new
363
+ g1v1 = Vertex.new(:name => :g1v1)
364
+ g1v2 = Vertex.new(:name => :g1v2)
365
+ g1v3 = Vertex.new(:name => :g1v3)
366
+ g1.connect(g1v1, g1v2, :type => :t1)
367
+ g1.connect(g1v2, g1v3, :type => :t2)
368
+ g2v1 = Vertex.new(:name => :g2v1)
369
+ g2v2 = Vertex.new(:name => :g2v2)
370
+ g2v3 = Vertex.new(:name => :g2v3)
371
+ g2.connect(g2v1, g2v2, :type => :t1)
372
+ g2.connect(g2v2, g2v3, :type => :t2)
373
+ pg = g1.product g2
374
+
375
+ pg.initial_similarity
376
+ pg.similarity_flood do |v|
377
+ v.similarity = v.initial_similarity
378
+ edge_groups = v.edges.group_by {|e| e.type }
379
+ edge_groups.each do |type, edges|
380
+ n = edges.length
381
+ edges.each do |e|
382
+ e.other_end(v).similarity += v.last_similarity / n
383
+ end
384
+ end
385
+ v.similarity
386
+ end
387
+ expected_similarities = {
388
+ "g1v1:g2v1" => 0.9269662921348315,
389
+ "g1v1:g2v2" => 1.0,
390
+ "g1v2:g2v1" => 0.6179775280898876,
391
+ "g1v2:g2v2" => 1.0,
392
+ "g1v2:g2v3" => 1.0,
393
+ "g1v3:g2v2" => 0.6179775280898876,
394
+ "g1v3:g2v3" => 0.6179775280898876}
395
+ pg.vertices.each do |v|
396
+ name = v.g1_vertex.name.to_s + ':' + v.g2_vertex.name.to_s
397
+ v.similarity.should be_within(0.001).of(expected_similarities[name])
398
+ end
399
+ end
400
+
401
+ it "similarity floods a graph of three nodes, a -- b -- c, given a block that performs a different update (method B)" do
402
+ g1 = Graph.new
403
+ g2 = Graph.new
404
+ g1v1 = Vertex.new(:name => :g1v1)
405
+ g1v2 = Vertex.new(:name => :g1v2)
406
+ g1v3 = Vertex.new(:name => :g1v3)
407
+ g1.connect(g1v1, g1v2, :type => :t1)
408
+ g1.connect(g1v2, g1v3, :type => :t2)
409
+ g2v1 = Vertex.new(:name => :g2v1)
410
+ g2v2 = Vertex.new(:name => :g2v2)
411
+ g2v3 = Vertex.new(:name => :g2v3)
412
+ g2.connect(g2v1, g2v2, :type => :t1)
413
+ g2.connect(g2v2, g2v3, :type => :t2)
414
+ pg = g1.product g2
415
+
416
+ pg.initial_similarity
417
+ pg.similarity_flood do |v|
418
+ v.similarity = 0.0
419
+ edge_groups = v.edges.group_by {|e| e.type }
420
+ edge_groups.each do |type, edges|
421
+ n = edges.length
422
+ edges.each do |e|
423
+ e.other_end(v).similarity += (v.initial_similarity + v.last_similarity) / n
424
+ end
425
+ end
426
+ v.similarity
427
+ end
428
+ expected_similarities = {
429
+ "g1v1:g2v1" => 1.0,
430
+ "g1v1:g2v2" => 1.0,
431
+ "g1v2:g2v1" => 0.0,
432
+ "g1v2:g2v2" => 1.0,
433
+ "g1v2:g2v3" => 1.0,
434
+ "g1v3:g2v2" => 0.0,
435
+ "g1v3:g2v3" => 0.0}
436
+ pg.vertices.each do |v|
437
+ name = v.g1_vertex.name.to_s + ':' + v.g2_vertex.name.to_s
438
+ v.similarity.should be_within(0.001).of(expected_similarities[name])
439
+ end
440
+ end
441
+
442
+ it "similarity floods a graph of three nodes, a -- b -- c, given a block that performs a different update (method C)" do
443
+ g1 = Graph.new
444
+ g2 = Graph.new
445
+ g1v1 = Vertex.new(:name => :g1v1)
446
+ g1v2 = Vertex.new(:name => :g1v2)
447
+ g1v3 = Vertex.new(:name => :g1v3)
448
+ g1.connect(g1v1, g1v2, :type => :t1)
449
+ g1.connect(g1v2, g1v3, :type => :t2)
450
+ g2v1 = Vertex.new(:name => :g2v1)
451
+ g2v2 = Vertex.new(:name => :g2v2)
452
+ g2v3 = Vertex.new(:name => :g2v3)
453
+ g2.connect(g2v1, g2v2, :type => :t1)
454
+ g2.connect(g2v2, g2v3, :type => :t2)
455
+ pg = g1.product g2
456
+
457
+ pg.initial_similarity
458
+ pg.similarity_flood do |v|
459
+ v.similarity = v.initial_similarity + v.last_similarity
460
+ edge_groups = v.edges.group_by {|e| e.type }
461
+ edge_groups.each do |type, edges|
462
+ n = edges.length
463
+ edges.each do |e|
464
+ e.other_end(v).similarity += (v.initial_similarity + v.last_similarity) / n
465
+ end
466
+ end
467
+ v.similarity
468
+ end
469
+ expected_similarities = {
470
+ "g1v1:g2v1" => 0.8282781862745098,
471
+ "g1v1:g2v2" => 1.0,
472
+ "g1v2:g2v1" => 0.41421568627450983,
473
+ "g1v2:g2v2" => 1.0,
474
+ "g1v2:g2v3" => 1.0,
475
+ "g1v3:g2v2" => 0.41421568627450983,
476
+ "g1v3:g2v3" => 0.41421568627450983}
477
+ pg.vertices.each do |v|
478
+ name = v.g1_vertex.name.to_s + ':' + v.g2_vertex.name.to_s
479
+ v.similarity.should be_within(0.001).of(expected_similarities[name])
480
+ end
481
+ end
482
+ end # similarity_flood
483
+
81
484
  end
82
485
  end
@@ -9,6 +9,14 @@ module GraphNjae
9
9
  v = Vertex.new
10
10
  v.edges.should be_empty
11
11
  end
12
+
13
+ it "creates a vertex with some parameters" do
14
+ v = Vertex.new :value1 => 1, :value2 => "value2", :value3 => :v3
15
+ v.value1.should == 1
16
+ v.value2.should == "value2"
17
+ v.value3.should == :v3
18
+ v.value4.should be_nil
19
+ end
12
20
  end # #initialize
13
21
 
14
22
  describe "adds attribues" do
@@ -77,9 +85,16 @@ module GraphNjae
77
85
  e.vertices.should include(v)
78
86
  e.vertices.should include(v1)
79
87
 
80
- e.should have(2).connections
88
+ e.should have(2).connections
81
89
  end
82
90
 
91
+ it "connects two vertices by an edge with attributes" do
92
+ v1 = Vertex.new
93
+ v1.id = :v1
94
+ e = v.connect(v1, {:type => :edge_type})
95
+ e.type.should == :edge_type
96
+ end
97
+
83
98
  it "creates a self-connection" do
84
99
  e = v.connect v
85
100
 
@@ -96,6 +111,11 @@ module GraphNjae
96
111
  e.should have(2).connections
97
112
  end
98
113
 
114
+ it "creates a self-connection with an edge with attributes" do
115
+ e = v.connect(v, {:type => :edge_type})
116
+ e.type.should == :edge_type
117
+ end
118
+
99
119
  end # #connect
100
120
 
101
121
  describe "#neighbours" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graph.njae
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-05 00:00:00.000000000 Z
12
+ date: 2012-05-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &85433160 !ruby/object:Gem::Requirement
16
+ requirement: &76981430 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 2.8.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *85433160
24
+ version_requirements: *76981430
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &85432740 !ruby/object:Gem::Requirement
27
+ requirement: &76981190 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,29 +32,18 @@ dependencies:
32
32
  version: 1.0.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *85432740
35
+ version_requirements: *76981190
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: jeweler
38
- requirement: &85432370 !ruby/object:Gem::Requirement
38
+ requirement: &76980930 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
42
42
  - !ruby/object:Gem::Version
43
- version: 1.6.2
43
+ version: 1.8.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *85432370
47
- - !ruby/object:Gem::Dependency
48
- name: rdoc
49
- requirement: &85431930 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: *85431930
46
+ version_requirements: *76980930
58
47
  description: A simple graph library
59
48
  email: neil.github@njae.me.uk
60
49
  executables: []
@@ -95,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
84
  version: '0'
96
85
  segments:
97
86
  - 0
98
- hash: 841005965
87
+ hash: 727460339
99
88
  required_rubygems_version: !ruby/object:Gem::Requirement
100
89
  none: false
101
90
  requirements: