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.
@@ -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
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Gregoire Lejeune <gregoire.lejeune@free.fr>
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
- class Node
22
- include Constants
23
- @xNodeName
24
- @oAttrNode
25
- @oGParrent
26
-
27
- #
28
- # Create a new node
29
- #
30
- # In:
31
- # * xNodeName : ID of the node
32
- # * oGParrent : Graph
33
- #
34
- def initialize( xNodeName, oGParrent )
35
- @xNodeName = xNodeName
36
- @oGParrent = oGParrent
37
- @oAttrNode = GraphViz::Attrs::new( nil, "node", NODESATTRS )
38
- @index = nil
39
- end
40
-
41
- #
42
- # Get the node ID
43
- #
44
- def id
45
- @xNodeName.clone
46
- end
47
-
48
- #
49
- # Return the node index
50
- #
51
- def index
52
- @index
53
- end
54
- def index=(i) #:nodoc:
55
- @index = i if @index == nil
56
- end
57
-
58
- #
59
- # Return the root graph
60
- #
61
- def root_graph
62
- return( (self.pg.nil?) ? nil : self.pg.root_graph )
63
- end
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
- end
86
-
87
- #
88
- # Calls block once for each attribut of the node, passing the name and value to the
89
- # block as a two-element array.
90
- #
91
- # If global is set to false, the block does not receive the attributs set globally
92
- #
93
- def each_attribut(global = true, &b)
94
- attrs = @oAttrNode.to_h
95
- if global
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
- attrs.each do |k,v|
99
- yield(k,v)
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
- end
102
-
103
- #
104
- # Create an edge between the current node and the node +oNode+
105
- #
106
- def <<( oNode )
107
- if( oNode.class == Array )
108
- oNode.each do |no|
109
- self << no
110
- end
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
- end
115
- alias :> :<<
116
- alias :- :<<
117
- alias :>> :<<
118
-
119
- #
120
- # Set node attributs
121
- #
122
- # Example :
123
- # n = graph.add_node( ... )
124
- # ...
125
- # n.set { |_n|
126
- # _n.color = "blue"
127
- # _n.fontcolor = "red"
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
- @oAttrNode.data.each do |k, v|
159
- xAttr << xSeparator + k + " = " + v.to_gv
160
- xSeparator = ", "
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
- if xAttr.length > 0
163
- xOut << " [" + xAttr + "]"
127
+
128
+ def pg #:nodoc:
129
+ @parent_graph
164
130
  end
165
- xOut << ";"
166
131
 
167
- return( xOut )
168
- end
169
- end
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
@@ -1,252 +1,286 @@
1
1
  require 'graphviz/math/matrix'
2
2
 
3
3
  class GraphViz
4
- class Theory
5
- def initialize( graph )
6
- @graph = graph
7
- end
8
-
9
- #
10
- # Return the adjancy matrix of the graph
11
- #
12
- def adjancy_matrix
13
- matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count )
14
-
15
- @graph.each_edge { |e|
16
- x = @graph.get_node(e.node_one( false )).index
17
- y = @graph.get_node(e.node_two( false )).index
18
- matrix[x+1, y+1] = 1
19
- matrix[y+1, x+1] = 1 if @graph.type == "graph"
20
- }
21
-
22
- return matrix
23
- end
24
-
25
- #
26
- # Return the incidence matrix of the graph
27
- #
28
- def incidence_matrix
29
- tail = (@graph.type == "digraph") ? -1 : 1
30
- matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.edge_count )
31
-
32
- @graph.each_edge { |e|
33
- x = e.index
34
-
35
- nstart = @graph.get_node(e.node_one( false )).index
36
- nend = @graph.get_node(e.node_two( false )).index
37
-
38
- matrix[nstart+1, x+1] = 1
39
- matrix[nend+1, x+1] = tail
40
- matrix[nend+1, x+1] = 2 if nstart == nend
41
- }
42
-
43
- return matrix
44
- end
45
-
46
- #
47
- # Return the degree of the given node
48
- #
49
- def degree( node )
50
- degree = 0
51
- name = node
52
- if node.kind_of?(GraphViz::Node)
53
- name = node.id
54
- end
55
-
56
- @graph.each_edge do |e|
57
- degree += 1 if e.node_one(false) == name or e.node_two(false) == name
58
- end
59
-
60
- return degree
61
- end
62
-
63
- #
64
- # Return the laplacian matrix of the graph
65
- #
66
- def laplacian_matrix
67
- return degree_matrix - adjancy_matrix
68
- end
69
-
70
- #
71
- # Return <tt>true</tt> if the graph if symmetric, <tt>false</tt> otherwise
72
- #
73
- def symmetric?
74
- adjancy_matrix == adjancy_matrix.transpose
75
- end
76
-
77
- #
78
- # moore_dijkstra(source, destination)
79
- #
80
- def moore_dijkstra( dep, arv )
81
- dep = @graph.get_node(dep) unless dep.kind_of?(GraphViz::Node)
82
- arv = @graph.get_node(arv) unless arv.kind_of?(GraphViz::Node)
83
-
84
- m = distance_matrix
85
- n = @graph.node_count
86
- # Table des sommets à choisir
87
- c = [dep.index]
88
- # Table des distances
89
- d = []
90
- d[dep.index] = 0
91
-
92
- # Table des predecesseurs
93
- pred = []
94
-
95
- @graph.each_node do |name, k|
96
- if k != dep
97
- d[k.index] = m[dep.index+1,k.index+1]
98
- pred[k.index] = dep
99
- end
100
- end
101
-
102
- while c.size < n
103
- # trouver y tel que d[y] = min{d[k]; k sommet tel que k n'appartient pas à c}
104
- mini = 1.0/0.0
105
- y = nil
106
- @graph.each_node do |name, k|
107
- next if c.include?(k.index)
108
- if d[k.index] < mini
109
- mini = d[k.index]
110
- y = k
111
- end
112
- end
113
-
114
- # si ce minimum est ∞ alors sortir de la boucle fin si
115
- break unless mini.to_f.infinite?.nil?
116
-
117
- c << y.index
118
- @graph.each_node do |name, k|
119
- next if c.include?(k.index)
120
- if d[k.index] > d[y.index] + m[y.index+1,k.index+1]
121
- d[k.index] = d[y.index] + m[y.index+1,k.index+1]
122
- pred[k.index] = y
123
- end
124
- end
125
- end
126
-
127
- # Construction du chemin le plus court
128
- ch = []
129
- k = arv
130
- while k.index != dep.index
131
- ch.unshift(k.id)
132
- k = pred[k.index]
133
- end
134
- ch.unshift(dep.id)
135
-
136
- if d[arv.index].to_f.infinite?
137
- return nil
138
- else
139
- return( {
140
- :path => ch,
141
- :distance => d[arv.index]
142
- } )
143
- end
144
- end
145
-
146
- #
147
- # Return a liste of range
148
- #
149
- # If the returned array include nil values, there is one or more circuits in the graph
150
- #
151
- def range
152
- matrix = adjancy_matrix
153
- unseen = (1..matrix.columns).to_a
154
- result = Array.new(matrix.columns)
155
- r = 0
156
-
157
- range_recursion( matrix, unseen, result, r )
158
- end
159
-
160
- #
161
- # Return the critical path for a PERT network
162
- #
163
- # If the given graph is not a PERT network, return nul
164
- #
165
- def critical_path
166
- return nil if range.include?(nil) or @graph.type != "digraph"
167
- r = [ [0, [1]] ]
168
-
169
- critical_path_recursion( distance_matrix, adjancy_matrix, r, [], 0 ).inject( {:distance => 0, :path => []} ) { |r, item|
170
- (r[:distance] < item[0]) ? { :distance => item[0], :path => item[1] } : r
171
- }
172
- end
173
-
174
- private
175
- def distance_matrix
176
- type = @graph.type
177
- matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count, (1.0/0.0) )
178
-
179
- @graph.each_edge { |e|
180
- x = @graph.get_node(e.node_one( false )).index
181
- y = @graph.get_node(e.node_two( false )).index
182
- unless x == y
183
- weight = ((e[:weight].nil?) ? 1 : e[:weight].to_f)
184
- matrix[x+1, y+1] = weight
185
- matrix[y+1, x+1] = weight if type == "graph"
186
- end
187
- }
188
-
189
- return matrix
190
- end
191
-
192
- def degree_matrix
193
- matrix = GraphViz::Math::Matrix.new( @graph.node_count, @graph.node_count )
194
- @graph.each_node do |name, node|
195
- i = node.index
196
- matrix[i+1, i+1] = degree(node)
197
- end
198
- return matrix
199
- end
200
-
201
- def range_recursion(matrix, unseen, result, r)
202
- remove = []
203
- matrix.columns.times do |c|
204
- if matrix.sum_of_column(c+1) == 0
205
- result[unseen[c]-1] = r
206
- remove.unshift( c + 1 )
207
- end
208
- end
209
-
210
- remove.each do |rem|
211
- matrix = matrix.remove_line(rem).remove_column(rem)
212
- unseen.delete_at(rem-1)
213
- end
214
-
215
- if matrix.columns == 1 and matrix.lines == 1
216
- if matrix.sum_of_column(1) == 0
217
- result[unseen[0]-1] = r+1
218
- end
219
- elsif remove.size > 0
220
- range_recursion( matrix, unseen, result, r+1 )
221
- end
222
-
223
- return result
224
- end
225
-
226
- def index_of_item( array, item )
227
- array.inject( [0,[]] ){|a,i|
228
- a[1] << a[0] if i == item
229
- a[0] += 1
230
- a
231
- }[1]
232
- end
233
-
234
- def critical_path_recursion( d, a, r, result, level )
235
- r.each do |p|
236
- node = p[1][-1]
237
- index_of_item( a.line(node), 1 ).each do |c|
238
- succ = c+1
239
-
240
- cpath = [ (p[0] + d[node,succ]), (p[1].clone << succ) ]
241
-
242
- if index_of_item( a.line(succ), 1 ).size > 0
243
- capth = critical_path_recursion( d, a, [cpath], result, level+1 )
244
- else
245
- result << cpath
246
- end
247
- end
248
- end
249
- return result
250
- end
251
- end
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