3 # A simple graph library
7 # A container for all the parts of a graph. The graph can have arbitrary attributes,
8 # treated as method names.
9 class Graph
< OpenStruct
10 def initialize(values
= {})
12 self.edges
= Array
.new
13 self.vertices
= Array
.new
17 # Add a Vertex or Edge to the graph.
19 if other
.class.ancestors
.include? Vertex
20 self.vertices
<< other
21 elsif other
.class.ancestors
.include? Edge
27 # Connects two vertices, creating and storing a new edge
28 # Also adds the vertices, unless they're already in the graph
29 def connect(vertex1
, vertex2
, edge_attributes
= {})
30 self.vertices
<< vertex1
unless self.vertices
.include? vertex1
31 self.vertices
<< vertex2
unless self.vertices
.include? vertex2
32 edge
= Edge
.new(edge_attributes
)
34 edge
<< vertex1
<< vertex2
37 # Form a product graph of this graph and the other.
38 # Return the product graph.
40 product_graph
= Graph
.new
41 self.vertices
.each
do |v1
|
42 other
.vertices
.each
do |v2
|
43 product_graph
<< Vertex
.new({:g1_vertex => v1
, :g2_vertex => v2
})
46 self.edges
.each
do |e1
|
47 e1_vertices
= e1
.vertices
48 other
.edges
.each
do |e2
|
50 e2_vertices
= e2
.vertices
51 source
= product_graph
.vertices
.find
{|v
| v
.g1_vertex
== e1_vertices
[0] and v
.g2_vertex
== e2_vertices
[0]}
52 destination
= product_graph
.vertices
.find
{|v
| v
.g1_vertex
== e1_vertices
[1] and v
.g2_vertex
== e2_vertices
[1]}
53 product_graph
.connect source
, destination
54 source
= product_graph
.vertices
.find
{|v
| v
.g1_vertex
== e1_vertices
[0] and v
.g2_vertex
== e2_vertices
[1]}
55 destination
= product_graph
.vertices
.find
{|v
| v
.g1_vertex
== e1_vertices
[1] and v
.g2_vertex
== e2_vertices
[0]}
56 product_graph
.connect source
, destination
60 product_graph
.vertices
.reject
! {|v
| v
.neighbours
.empty
?}
65 def initial_similarity
66 self.vertices
.each
do |v
|
68 v
.initial_similarity
= yield v
70 v
.initial_similarity
= 1.0
72 v
.similarity
= v
.initial_similarity
76 # Performs similarity flooding on a graph
77 # Assumes that the initial similarity has already been calculated
78 def similarity_flood(opts
= {})
79 max_iterations
= opts
[:iterations] || 100
80 max_residual
= opts
[:max_residual] || 0.001
82 residual
= max_residual
+ 1
83 while residual
> max_residual
and iteration
<= max_iterations
84 # puts "Starting iteration #{iteration}"
85 self.vertices
.each
do |v
|
86 v
.last_similarity
= v
.similarity
88 self.vertices
.each
do |v
|
89 # puts "Processing vertex #{v.name}"
90 edge_groups
= v
.edges
.group_by
{|e
| e
.type
}
91 # puts " Edge groups {#{edge_groups.keys.map {|t| t.to_s + ' => {' + edge_groups[t].map {|e| e.to_s}.join(', ')}.join('; ')}}"
92 edge_groups
.each
do |type
, edges
|
93 # puts " Processing group type #{type}"
96 e
.other_end(v
).similarity
+= v
.last_similarity
/ n
100 # self.vertices.each do |v|
101 # n = v.neighbours.length
102 # v.neighbours.each do |neighbour|
103 # neighbour.similarity += v.last_similarity / n
106 max_similarity
= vertices
.map
{|v
| v
.similarity
}.max
107 self.vertices
.each
do |v
|
108 v
.similarity
= v
.similarity
/ max_similarity
110 residual
= Math
.sqrt(self.vertices
.reduce(0) {|a
, v
| a
+= (v
.similarity
- v
.last_similarity
) ** 2})
111 # puts "Residual = #{residual.round(3)}, sims = #{self.vertices.map {|v| v.name + " = " + v.similarity.round(2).to_s}}"