ruby-graphviz 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/AUTHORS +1 -0
- data/README.rdoc +6 -0
- data/Rakefile +7 -1
- data/examples/sample62.rb +1 -1
- data/examples/theory/tests.rb +13 -7
- data/lib/graphviz.rb +34 -11
- data/lib/graphviz/attrs.rb +37 -42
- data/lib/graphviz/constants.rb +3 -3
- data/lib/graphviz/core_ext.rb +7 -0
- data/lib/graphviz/dsl.rb +19 -11
- data/lib/graphviz/edge.rb +164 -184
- data/lib/graphviz/graphml.rb +1 -3
- data/lib/graphviz/node.rb +128 -142
- data/lib/graphviz/theory.rb +283 -249
- data/lib/graphviz/utils/colors.rb +7 -8
- data/lib/graphviz/xml.rb +35 -35
- data/test/test_graph.rb +65 -3
- data/test/test_theory.rb +102 -0
- data/test/test_utils_colors.rb +42 -45
- metadata +7 -4
data/lib/graphviz/graphml.rb
CHANGED
@@ -37,9 +37,7 @@ class GraphViz
|
|
37
37
|
'undirected' => :graph
|
38
38
|
}
|
39
39
|
|
40
|
-
#
|
41
40
|
# Create a new GraphViz object from a GraphML file of string
|
42
|
-
#
|
43
41
|
def initialize( file_or_str )
|
44
42
|
data = ((File.file?( file_or_str )) ? File::new(file_or_str) : file_or_str)
|
45
43
|
@xmlDoc = REXML::Document::new( data )
|
@@ -221,4 +219,4 @@ class GraphViz
|
|
221
219
|
}
|
222
220
|
end
|
223
221
|
end
|
224
|
-
end
|
222
|
+
end
|
data/lib/graphviz/node.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2004
|
1
|
+
# Copyright (C) 2004 - 2011 Gregoire Lejeune <gregoire.lejeune@free.fr>
|
2
2
|
#
|
3
3
|
# This program is free software; you can redistribute it and/or modify
|
4
4
|
# it under the terms of the GNU General Public License as published by
|
@@ -18,153 +18,139 @@ require 'graphviz/attrs'
|
|
18
18
|
require 'graphviz/constants'
|
19
19
|
|
20
20
|
class GraphViz
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#
|
66
|
-
# Set value +xAttrValue+ to the node attribut +xAttrName+
|
67
|
-
#
|
68
|
-
def []=( xAttrName, xAttrValue )
|
69
|
-
xAttrValue = xAttrValue.to_s if xAttrValue.class == Symbol
|
70
|
-
@oAttrNode[xAttrName.to_s] = xAttrValue
|
71
|
-
end
|
72
|
-
|
73
|
-
#
|
74
|
-
# Get the value of the node attribut +xAttrName+
|
75
|
-
#
|
76
|
-
def []( xAttrName )
|
77
|
-
if Hash === xAttrName
|
78
|
-
xAttrName.each do |key, value|
|
79
|
-
self[key] = value
|
80
|
-
end
|
81
|
-
return self
|
82
|
-
else
|
83
|
-
(@oAttrNode[xAttrName.to_s].nil?)?nil:@oAttrNode[xAttrName.to_s].clone
|
21
|
+
class Node
|
22
|
+
include Constants
|
23
|
+
|
24
|
+
# List of nodes that are directly accessible from given node (in a directed graph neighbors == incidents)
|
25
|
+
attr_reader :neighbors
|
26
|
+
# List of nodes that are incident to the given node (in a directed graph neighbors == incidents)
|
27
|
+
attr_reader :incidents
|
28
|
+
|
29
|
+
# Create a new node
|
30
|
+
#
|
31
|
+
# * node_id : ID of the node
|
32
|
+
# * parent_graph : Graph
|
33
|
+
def initialize( node_id, parent_graph )
|
34
|
+
@neighbors = []
|
35
|
+
@incidents = []
|
36
|
+
@node_id = node_id
|
37
|
+
@parent_graph = parent_graph
|
38
|
+
@node_attributs = GraphViz::Attrs::new( nil, "node", NODESATTRS )
|
39
|
+
@index = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the node ID
|
43
|
+
def id
|
44
|
+
@node_id.clone
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the node index
|
48
|
+
def index
|
49
|
+
@index
|
50
|
+
end
|
51
|
+
def index=(i) #:nodoc:
|
52
|
+
@index = i if @index == nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return the root graph
|
56
|
+
def root_graph
|
57
|
+
return( (self.pg.nil?) ? nil : self.pg.root_graph )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set value +attribut_value+ to the node attribut +attribut_name+
|
61
|
+
def []=( attribut_name, attribut_value )
|
62
|
+
attribut_value = attribut_value.to_s if attribut_value.class == Symbol
|
63
|
+
@node_attributs[attribut_name.to_s] = attribut_value
|
84
64
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
attrs = pg.node.to_h.merge attrs
|
65
|
+
|
66
|
+
# Get the value of the node attribut +attribut_name+
|
67
|
+
def []( attribut_name )
|
68
|
+
if Hash === attribut_name
|
69
|
+
attribut_name.each do |key, value|
|
70
|
+
self[key] = value
|
71
|
+
end
|
72
|
+
return self
|
73
|
+
else
|
74
|
+
(@node_attributs[attribut_name.to_s].nil?)?nil:@node_attributs[attribut_name.to_s].clone
|
75
|
+
end
|
97
76
|
end
|
98
|
-
|
99
|
-
|
77
|
+
|
78
|
+
# Calls block once for each attribut of the node, passing the name and value to the
|
79
|
+
# block as a two-element array.
|
80
|
+
#
|
81
|
+
# If global is set to false, the block does not receive the attributs set globally
|
82
|
+
def each_attribut(global = true, &b)
|
83
|
+
attrs = @node_attributs.to_h
|
84
|
+
if global
|
85
|
+
attrs = pg.node.to_h.merge attrs
|
86
|
+
end
|
87
|
+
attrs.each do |k,v|
|
88
|
+
yield(k,v)
|
89
|
+
end
|
100
90
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
else
|
112
|
-
return GraphViz::commonGraph( oNode, self ).add_edge( self, oNode )
|
91
|
+
|
92
|
+
# Create an edge between the current node and the node +node+
|
93
|
+
def <<( node )
|
94
|
+
if( node.class == Array )
|
95
|
+
node.each do |no|
|
96
|
+
self << no
|
97
|
+
end
|
98
|
+
else
|
99
|
+
return GraphViz::commonGraph( node, self ).add_edge( self, node )
|
100
|
+
end
|
113
101
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
def set( &b )
|
131
|
-
yield( self )
|
132
|
-
end
|
133
|
-
|
134
|
-
# Add node options
|
135
|
-
# use node.<option>=<value> or node.<option>( <value> )
|
136
|
-
def method_missing( idName, *args, &block ) #:nodoc:
|
137
|
-
xName = idName.id2name
|
138
|
-
|
139
|
-
self[xName.gsub( /=$/, "" )]=args[0]
|
140
|
-
end
|
141
|
-
|
142
|
-
def pg #:nodoc:
|
143
|
-
@oGParrent
|
144
|
-
end
|
145
|
-
|
146
|
-
def output #:nodoc:
|
147
|
-
#xNodeName = @xNodeName.clone
|
148
|
-
#xNodeName = '"' << xNodeName << '"' if xNodeName.match( /^[a-zA-Z_]+[a-zA-Z0-9_\.]*$/ ).nil?
|
149
|
-
xNodeName = GraphViz.escape(@xNodeName)
|
150
|
-
|
151
|
-
xOut = "" << xNodeName
|
152
|
-
xAttr = ""
|
153
|
-
xSeparator = ""
|
154
|
-
|
155
|
-
if @oAttrNode.data.has_key?("label") and @oAttrNode.data.has_key?("html")
|
156
|
-
@oAttrNode.data.delete("label")
|
102
|
+
alias :> :<<
|
103
|
+
alias :- :<<
|
104
|
+
alias :>> :<<
|
105
|
+
|
106
|
+
# Set node attributs
|
107
|
+
#
|
108
|
+
# Example :
|
109
|
+
# n = graph.add_node( ... )
|
110
|
+
# ...
|
111
|
+
# n.set { |_n|
|
112
|
+
# _n.color = "blue"
|
113
|
+
# _n.fontcolor = "red"
|
114
|
+
# }
|
115
|
+
#
|
116
|
+
def set( &b )
|
117
|
+
yield( self )
|
157
118
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
119
|
+
|
120
|
+
# Add node options
|
121
|
+
# use node.<option>=<value> or node.<option>( <value> )
|
122
|
+
def method_missing( idName, *args, &block ) #:nodoc:
|
123
|
+
xName = idName.id2name
|
124
|
+
|
125
|
+
self[xName.gsub( /=$/, "" )]=args[0]
|
161
126
|
end
|
162
|
-
|
163
|
-
|
127
|
+
|
128
|
+
def pg #:nodoc:
|
129
|
+
@parent_graph
|
164
130
|
end
|
165
|
-
xOut << ";"
|
166
131
|
|
167
|
-
|
168
|
-
|
169
|
-
|
132
|
+
def output #:nodoc:
|
133
|
+
#node_id = @node_id.clone
|
134
|
+
#node_id = '"' << node_id << '"' if node_id.match( /^[a-zA-Z_]+[a-zA-Z0-9_\.]*$/ ).nil?
|
135
|
+
node_id = GraphViz.escape(@node_id)
|
136
|
+
|
137
|
+
xOut = "" << node_id
|
138
|
+
xAttr = ""
|
139
|
+
xSeparator = ""
|
140
|
+
|
141
|
+
if @node_attributs.data.has_key?("label") and @node_attributs.data.has_key?("html")
|
142
|
+
@node_attributs.data.delete("label")
|
143
|
+
end
|
144
|
+
@node_attributs.data.each do |k, v|
|
145
|
+
xAttr << xSeparator + k + " = " + v.to_gv
|
146
|
+
xSeparator = ", "
|
147
|
+
end
|
148
|
+
if xAttr.length > 0
|
149
|
+
xOut << " [" + xAttr + "]"
|
150
|
+
end
|
151
|
+
xOut << ";"
|
152
|
+
|
153
|
+
return( xOut )
|
154
|
+
end
|
155
|
+
end
|
170
156
|
end
|
data/lib/graphviz/theory.rb
CHANGED
@@ -1,252 +1,286 @@
|
|
1
1
|
require 'graphviz/math/matrix'
|
2
2
|
|
3
3
|
class GraphViz
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
end
|
4
|
+
class Theory
|
5
|
+
def initialize( graph )
|
6
|
+
@graph = graph
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return the adjancy matrix of the graph
|
10
|
+
def adjancy_matrix
|
11
|
+
matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count )
|
12
|
+
|
13
|
+
@graph.each_edge { |e|
|
14
|
+
x = @graph.get_node(e.node_one( false )).index
|
15
|
+
y = @graph.get_node(e.node_two( false )).index
|
16
|
+
matrix[x+1, y+1] = 1
|
17
|
+
matrix[y+1, x+1] = 1 if @graph.type == "graph"
|
18
|
+
}
|
19
|
+
|
20
|
+
return matrix
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the incidence matrix of the graph
|
24
|
+
def incidence_matrix
|
25
|
+
tail = (@graph.type == "digraph") ? -1 : 1
|
26
|
+
matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.edge_count )
|
27
|
+
|
28
|
+
@graph.each_edge { |e|
|
29
|
+
x = e.index
|
30
|
+
|
31
|
+
nstart = @graph.get_node(e.node_one( false )).index
|
32
|
+
nend = @graph.get_node(e.node_two( false )).index
|
33
|
+
|
34
|
+
matrix[nstart+1, x+1] = 1
|
35
|
+
matrix[nend+1, x+1] = tail
|
36
|
+
matrix[nend+1, x+1] = 2 if nstart == nend
|
37
|
+
}
|
38
|
+
|
39
|
+
return matrix
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the degree of the given node
|
43
|
+
def degree( node )
|
44
|
+
degree = 0
|
45
|
+
name = node
|
46
|
+
if node.kind_of?(GraphViz::Node)
|
47
|
+
name = node.id
|
48
|
+
end
|
49
|
+
|
50
|
+
@graph.each_edge do |e|
|
51
|
+
degree += 1 if e.node_one(false) == name or e.node_two(false) == name
|
52
|
+
end
|
53
|
+
|
54
|
+
return degree
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the laplacian matrix of the graph
|
58
|
+
def laplacian_matrix
|
59
|
+
return degree_matrix - adjancy_matrix
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return <tt>true</tt> if the graph if symmetric, <tt>false</tt> otherwise
|
63
|
+
def symmetric?
|
64
|
+
adjancy_matrix == adjancy_matrix.transpose
|
65
|
+
end
|
66
|
+
|
67
|
+
# moore_dijkstra(source, destination)
|
68
|
+
def moore_dijkstra( dep, arv )
|
69
|
+
dep = @graph.get_node(dep) unless dep.kind_of?(GraphViz::Node)
|
70
|
+
arv = @graph.get_node(arv) unless arv.kind_of?(GraphViz::Node)
|
71
|
+
|
72
|
+
m = distance_matrix
|
73
|
+
n = @graph.node_count
|
74
|
+
# Table des sommets à choisir
|
75
|
+
c = [dep.index]
|
76
|
+
# Table des distances
|
77
|
+
d = []
|
78
|
+
d[dep.index] = 0
|
79
|
+
|
80
|
+
# Table des predecesseurs
|
81
|
+
pred = []
|
82
|
+
|
83
|
+
@graph.each_node do |name, k|
|
84
|
+
if k != dep
|
85
|
+
d[k.index] = m[dep.index+1,k.index+1]
|
86
|
+
pred[k.index] = dep
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
while c.size < n
|
91
|
+
# trouver y tel que d[y] = min{d[k]; k sommet tel que k n'appartient pas à c}
|
92
|
+
mini = 1.0/0.0
|
93
|
+
y = nil
|
94
|
+
@graph.each_node do |name, k|
|
95
|
+
next if c.include?(k.index)
|
96
|
+
if d[k.index] < mini
|
97
|
+
mini = d[k.index]
|
98
|
+
y = k
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# si ce minimum est ∞ alors sortir de la boucle fin si
|
103
|
+
break unless mini.to_f.infinite?.nil?
|
104
|
+
|
105
|
+
c << y.index
|
106
|
+
@graph.each_node do |name, k|
|
107
|
+
next if c.include?(k.index)
|
108
|
+
if d[k.index] > d[y.index] + m[y.index+1,k.index+1]
|
109
|
+
d[k.index] = d[y.index] + m[y.index+1,k.index+1]
|
110
|
+
pred[k.index] = y
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Construction du chemin le plus court
|
116
|
+
ch = []
|
117
|
+
k = arv
|
118
|
+
while k.index != dep.index
|
119
|
+
ch.unshift(k.id)
|
120
|
+
k = pred[k.index]
|
121
|
+
end
|
122
|
+
ch.unshift(dep.id)
|
123
|
+
|
124
|
+
if d[arv.index].to_f.infinite?
|
125
|
+
return nil
|
126
|
+
else
|
127
|
+
return( {
|
128
|
+
:path => ch,
|
129
|
+
:distance => d[arv.index]
|
130
|
+
} )
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return a liste of range
|
135
|
+
#
|
136
|
+
# If the returned array include nil values, there is one or more circuits in the graph
|
137
|
+
def range
|
138
|
+
matrix = adjancy_matrix
|
139
|
+
unseen = (1..matrix.columns).to_a
|
140
|
+
result = Array.new(matrix.columns)
|
141
|
+
r = 0
|
142
|
+
|
143
|
+
range_recursion( matrix, unseen, result, r )
|
144
|
+
end
|
145
|
+
|
146
|
+
# Return the critical path for a PERT network
|
147
|
+
#
|
148
|
+
# If the given graph is not a PERT network, return nul
|
149
|
+
def critical_path
|
150
|
+
return nil if range.include?(nil) or @graph.type != "digraph"
|
151
|
+
r = [ [0, [1]] ]
|
152
|
+
|
153
|
+
critical_path_recursion( distance_matrix, adjancy_matrix, r, [], 0 ).inject( {:distance => 0, :path => []} ) { |r, item|
|
154
|
+
(r[:distance] < item[0]) ? { :distance => item[0], :path => item[1] } : r
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
# Return the PageRank in an directed graph.
|
159
|
+
#
|
160
|
+
# * damping_factor: PageRank dumping factor.
|
161
|
+
# * max_iterations: Maximum number of iterations.
|
162
|
+
# * min_delta: Smallest variation required to have a new iteration.
|
163
|
+
def pagerank(damping_factor = 0.85, max_iterations = 100, min_delta = 0.00001)
|
164
|
+
return nil unless @graph.directed?
|
165
|
+
|
166
|
+
min_value = (1.0-damping_factor)/@graph.node_count
|
167
|
+
|
168
|
+
pagerank = {}
|
169
|
+
@graph.each_node { |_, node|
|
170
|
+
pagerank[node] = 1.0/@graph.node_count
|
171
|
+
}
|
172
|
+
|
173
|
+
max_iterations.times { |_|
|
174
|
+
diff = 0
|
175
|
+
@graph.each_node { |_, node|
|
176
|
+
rank = min_value
|
177
|
+
incidents(node).each { |referent|
|
178
|
+
rank += damping_factor * pagerank[referent] / neighbors(referent).size
|
179
|
+
}
|
180
|
+
|
181
|
+
diff += (pagerank[node] - rank).abs
|
182
|
+
pagerank[node] = rank
|
183
|
+
}
|
184
|
+
break if diff < min_delta
|
185
|
+
}
|
186
|
+
|
187
|
+
return pagerank
|
188
|
+
end
|
189
|
+
|
190
|
+
# Return the list of nodes that are directly accessible from given node
|
191
|
+
def neighbors(node)
|
192
|
+
if node.class == String
|
193
|
+
@graph.get_node(node).neighbors
|
194
|
+
else
|
195
|
+
node.neighbors
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Return the list of nodes that are incident to the given node (in a directed graph neighbors == incidents)
|
200
|
+
def incidents(node)
|
201
|
+
if node.class == String
|
202
|
+
@graph.get_node(node).incidents
|
203
|
+
else
|
204
|
+
node.incidents
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
def distance_matrix
|
210
|
+
type = @graph.type
|
211
|
+
matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count, (1.0/0.0) )
|
212
|
+
|
213
|
+
@graph.each_edge { |e|
|
214
|
+
x = @graph.get_node(e.node_one( false )).index
|
215
|
+
y = @graph.get_node(e.node_two( false )).index
|
216
|
+
unless x == y
|
217
|
+
weight = ((e[:weight].nil?) ? 1 : e[:weight].to_f)
|
218
|
+
matrix[x+1, y+1] = weight
|
219
|
+
matrix[y+1, x+1] = weight if type == "graph"
|
220
|
+
end
|
221
|
+
}
|
222
|
+
|
223
|
+
return matrix
|
224
|
+
end
|
225
|
+
|
226
|
+
def degree_matrix
|
227
|
+
matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count )
|
228
|
+
@graph.each_node do |name, node|
|
229
|
+
i = node.index
|
230
|
+
matrix[i+1, i+1] = degree(node)
|
231
|
+
end
|
232
|
+
return matrix
|
233
|
+
end
|
234
|
+
|
235
|
+
def range_recursion(matrix, unseen, result, r)
|
236
|
+
remove = []
|
237
|
+
matrix.columns.times do |c|
|
238
|
+
if matrix.sum_of_column(c+1) == 0
|
239
|
+
result[unseen[c]-1] = r
|
240
|
+
remove.unshift( c + 1 )
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
remove.each do |rem|
|
245
|
+
matrix = matrix.remove_line(rem).remove_column(rem)
|
246
|
+
unseen.delete_at(rem-1)
|
247
|
+
end
|
248
|
+
|
249
|
+
if matrix.columns == 1 and matrix.lines == 1
|
250
|
+
if matrix.sum_of_column(1) == 0
|
251
|
+
result[unseen[0]-1] = r+1
|
252
|
+
end
|
253
|
+
elsif remove.size > 0
|
254
|
+
range_recursion( matrix, unseen, result, r+1 )
|
255
|
+
end
|
256
|
+
|
257
|
+
return result
|
258
|
+
end
|
259
|
+
|
260
|
+
def index_of_item( array, item )
|
261
|
+
array.inject( [0,[]] ){|a,i|
|
262
|
+
a[1] << a[0] if i == item
|
263
|
+
a[0] += 1
|
264
|
+
a
|
265
|
+
}[1]
|
266
|
+
end
|
267
|
+
|
268
|
+
def critical_path_recursion( d, a, r, result, level )
|
269
|
+
r.each do |p|
|
270
|
+
node = p[1][-1]
|
271
|
+
index_of_item( a.line(node), 1 ).each do |c|
|
272
|
+
succ = c+1
|
273
|
+
|
274
|
+
cpath = [ (p[0] + d[node,succ]), (p[1].clone << succ) ]
|
275
|
+
|
276
|
+
if index_of_item( a.line(succ), 1 ).size > 0
|
277
|
+
capth = critical_path_recursion( d, a, [cpath], result, level+1 )
|
278
|
+
else
|
279
|
+
result << cpath
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
return result
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|