ruby-graphviz 1.0.1 → 1.0.2
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.
- 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
|