fsm 0.0.0

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.
@@ -0,0 +1,70 @@
1
+ class Array
2
+ def equality_uniq
3
+ uniq_elements = []
4
+ self.each {|e| uniq_elements.push(e) unless uniq_elements.index(e)}
5
+ uniq_elements
6
+ end
7
+
8
+ def delete_at_indices(indices = [])
9
+ not_deleted = Array.new
10
+ self.each_with_index {|e,i| not_deleted.push(e) if !indices.include?(i)}
11
+ not_deleted
12
+ end
13
+ end
14
+
15
+ class DefaultInitArray < Array
16
+ def initialize(*args, &initblock)
17
+ super(*args)
18
+ @initblock = initblock
19
+ end
20
+
21
+ def [](index)
22
+ super(index) || (self[index] = @initblock.call(index))
23
+ end
24
+ end
25
+
26
+ class ArrayOfArrays < DefaultInitArray
27
+ @@create_array = proc{|i| Array.new}
28
+ def initialize(*args)
29
+ super(*args, &@@create_array)
30
+ end
31
+ end
32
+
33
+ class ArrayOfHashes < DefaultInitArray
34
+ @@create_hash = proc{|i| Hash.new}
35
+ def initialize(*args)
36
+ super(*args, &@@create_hash)
37
+ end
38
+ end
39
+
40
+ # Hash which takes a block that is called to give a default value when a key
41
+ # has the value nil in the hash.
42
+ class DefaultInitHash < Hash
43
+ def initialize(*args, &initblock)
44
+ super(*args)
45
+ @initblock = initblock
46
+ end
47
+
48
+ def [](key)
49
+ super(key) || (self[key] = @initblock.call(key))
50
+ end
51
+ end
52
+
53
+ unless Object.constants.include?("TimesClass")
54
+ TimesClass = (RUBY_VERSION < "1.7") ? Time : Process
55
+ end
56
+
57
+ def time_and_puts(string, &block)
58
+ if $TIME_AND_PUTS_VERBOSE
59
+ print string; STDOUT.flush
60
+ end
61
+ starttime = [Time.new, TimesClass.times]
62
+ block.call
63
+ endtime = [Time.new, TimesClass.times]
64
+ duration = endtime[0] - starttime[0]
65
+ begin
66
+ load = [((endtime[1].utime+endtime[1].stime)-(starttime[1].utime+starttime[1].stime))/duration*100.0, 100.0].min
67
+ puts " (%.2f s %.2f%%)" % [duration, load] if $TIME_AND_PUTS_VERBOSE
68
+ rescue FloatDomainError
69
+ end
70
+ end
@@ -0,0 +1,481 @@
1
+ require 'graph/base_extensions'
2
+ require 'graph/graphviz_dot'
3
+
4
+ class HashOfHash < DefaultInitHash
5
+ def initialize(&initBlock)
6
+ super do
7
+ if initBlock
8
+ DefaultInitHash.new(&initBlock)
9
+ else
10
+ Hash.new
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ GraphLink = Struct.new("GraphLink", :from, :to, :info)
17
+ class GraphLink
18
+ def inspect
19
+ info_str = info ? info.inspect + "-" : ""
20
+ "#{from.inspect}-#{info_str}>#{to.inspect}"
21
+ end
22
+ end
23
+
24
+ class GraphTraversalException < Exception
25
+ attr_reader :node, :links, :link_info
26
+ def initialize(node, links, linkInfo)
27
+ @node, @links, @link_info = node, links, linkInfo
28
+ super(message)
29
+ end
30
+
31
+ def message
32
+ "There is no link from #{@node.inspect} having info #{@link_info.inspect} (valid links are #{@links.inspect})"
33
+ end
34
+ alias inspect message
35
+ end
36
+
37
+ class DirectedGraph
38
+ # This is a memory expensive variant that manages several additional
39
+ # information data structures to cut down on processing when the graph
40
+ # has been built.
41
+
42
+ attr_reader :links
43
+ attr_reader :link_map
44
+ attr_reader :is_root
45
+ attr_reader :is_leaf
46
+
47
+ def initialize
48
+ @link_map = HashOfHash.new {Array.new} # [from][to] -> array of links
49
+ @link_map = Hash.new{|h,from| h[from] = Hash.new{|h2,to| h2[to] = []}}
50
+ @links = Array.new # All links in one array
51
+ @is_root = Hash.new # true iff root node
52
+ @is_leaf = Hash.new # true iff leaf node
53
+ end
54
+
55
+ def nodes
56
+ @is_root.keys
57
+ end
58
+
59
+ def add_node(node)
60
+ unless include_node?(node)
61
+ @is_root[node] = @is_leaf[node] = true
62
+ end
63
+ end
64
+
65
+ def root?(node)
66
+ @is_root[node]
67
+ end
68
+
69
+ def leaf?(node)
70
+ @is_leaf[node]
71
+ end
72
+
73
+ def include_node?(node)
74
+ @is_root.has_key?(node)
75
+ end
76
+
77
+ def links_from_to(from, to)
78
+ @link_map[from][to]
79
+ end
80
+
81
+ def links_from(node)
82
+ @link_map[node].map {|to, links| links}.flatten
83
+ end
84
+
85
+ def children(node)
86
+ @link_map[node].keys.select {|k| @link_map[node][k].length > 0}
87
+ end
88
+
89
+ # (Forced) add link will always add link even if there are already links
90
+ # between the nodes.
91
+ def add_link(from, to, informationOnLink = nil)
92
+ add_link_nodes(from, to)
93
+ link = GraphLink.new(from, to, informationOnLink)
94
+ links_from_to(from, to).push link
95
+ #@link_map[from][to].push link
96
+ add_to_links(link)
97
+ link
98
+ end
99
+
100
+ def add_link_nodes(from, to)
101
+ add_node(from)
102
+ add_node(to)
103
+ @is_leaf[from] = @is_root[to] = false
104
+ end
105
+
106
+ # Add link if not already linked
107
+ def link_nodes(from, to, info = nil)
108
+ links_from_to?(from, to) ? nil : add_link(from, to, info)
109
+ end
110
+
111
+ def links_from_to?(from, to)
112
+ not links_from_to(from, to).empty?
113
+ end
114
+ alias linked? links_from_to?
115
+
116
+ def add_to_links(link)
117
+ @links.push link
118
+ end
119
+ private :add_to_links
120
+
121
+ def each_reachable_node_once_depth_first(node, inclusive = true, &block)
122
+ children(node).each do |c|
123
+ recurse_each_reachable_depth_first_visited(c, Hash.new, &block)
124
+ end
125
+ block.call(node) if inclusive
126
+ end
127
+ alias each_reachable_node each_reachable_node_once_depth_first
128
+
129
+ def recurse_each_reachable_depth_first_visited(node, visited, &block)
130
+ visited[node] = true
131
+ children(node).each do |c|
132
+ unless visited[c]
133
+ recurse_each_reachable_depth_first_visited(c, visited, &block)
134
+ end
135
+ end
136
+ block.call(node)
137
+ end
138
+
139
+ def each_reachable_node_once_breadth_first(node, inclusive = true, &block)
140
+ block.call(node) if inclusive
141
+ children(node).each do |c|
142
+ recurse_each_reachable_breadth_first_visited(c, Hash.new, &block)
143
+ end
144
+ end
145
+ alias each_reachable_node each_reachable_node_once_depth_first
146
+
147
+ def recurse_each_reachable_breadth_first_visited(node, visited, &block)
148
+ visited[node] = true
149
+ block.call(node)
150
+ children(node).each do |c|
151
+ unless visited[c]
152
+ recurse_each_reachable_breadth_first_visited(c, visited, &block)
153
+ end
154
+ end
155
+ end
156
+
157
+ def root_nodes
158
+ @is_root.reject {|key,val| val == false}.keys
159
+ end
160
+ alias_method :roots, :root_nodes
161
+
162
+ def leaf_nodes
163
+ @is_leaf.reject {|key,val| val == false}.keys
164
+ end
165
+ alias_method :leafs, :leaf_nodes
166
+
167
+ def internal_node?(node)
168
+ !root?(node) and !leaf?(node)
169
+ end
170
+
171
+ def internal_nodes
172
+ nodes.reject {|n| root?(n) or leaf?(n)}
173
+ end
174
+
175
+ def recurse_cyclic?(node, visited)
176
+ visited[node] = true
177
+ children(node).each do |c|
178
+ return true if visited[c] || recurse_cyclic?(c, visited)
179
+ end
180
+ false
181
+ end
182
+
183
+ def cyclic?
184
+ visited = Hash.new
185
+ root_nodes.each {|root| return true if recurse_cyclic?(root, visited)}
186
+ false
187
+ end
188
+
189
+ def acyclic?
190
+ not cyclic?
191
+ end
192
+
193
+ def transition(state, linkInfo)
194
+ link = links_from(state).detect {|l| l.info == linkInfo}
195
+ begin
196
+ link.to
197
+ rescue Exception
198
+ raise GraphTraversalException.new(state, links_from(state), linkInfo)
199
+ end
200
+ end
201
+
202
+ def traverse(fromState, alongLinksWithInfo = [])
203
+ state, len = fromState, alongLinksWithInfo.length
204
+ alongLinksWithInfo = alongLinksWithInfo.clone
205
+ while len > 0
206
+ state = transition(state, alongLinksWithInfo.shift)
207
+ len -= 1
208
+ end
209
+ state
210
+ end
211
+
212
+ def to_dot(nodeShaper = nil, nodeLabeler = nil, linkLabeler = nil)
213
+ dgp = DotGraphPrinter.new(links, nodes)
214
+ dgp.node_shaper = nodeShaper if nodeShaper
215
+ dgp.node_labeler = nodeLabeler if nodeLabeler
216
+ dgp.link_labeler = linkLabeler if linkLabeler
217
+ dgp
218
+ end
219
+
220
+ def to_postscript_file(filename, nodeShaper = nil, nodeLabeler = nil,
221
+ linkLabeler = nil)
222
+ to_dot(nodeShaper, nodeLabeler, linkLabeler).write_to_file(filename)
223
+ end
224
+
225
+ # Floyd-Warshal algorithm which should be O(n^3) where n is the number of
226
+ # nodes. We can probably work a bit on the constant factors!
227
+ def transitive_closure_floyd_warshal
228
+ vertices = nodes
229
+ tcg = DirectedGraph.new
230
+ num_nodes = vertices.length
231
+
232
+ # Direct links
233
+ for k in (0...num_nodes)
234
+ for s in (0...num_nodes)
235
+ vk, vs = vertices[k], vertices[s]
236
+ if vk == vs
237
+ tcg.link_nodes(vk,vs)
238
+ elsif linked?(vk, vs)
239
+ tcg.link_nodes(vk,vs)
240
+ end
241
+ end
242
+ end
243
+
244
+ # Indirect links
245
+ for i in (0...num_nodes)
246
+ for j in (0...num_nodes)
247
+ for k in (0...num_nodes)
248
+ vi, vj, vk = vertices[i], vertices[j], vertices[k]
249
+ if not tcg.linked?(vi,vj)
250
+ tcg.link_nodes(vi, vj) if linked?(vi,vk) and linked?(vk,vj)
251
+ end
252
+ end
253
+ end
254
+ end
255
+ tcg
256
+ end
257
+ alias_method :transitive_closure, :transitive_closure_floyd_warshal
258
+
259
+ def num_vertices
260
+ @is_root.size
261
+ end
262
+ alias num_nodes num_vertices
263
+
264
+ # strongly_connected_components uses the algorithm described in
265
+ # following paper.
266
+ # @Article{Tarjan:1972:DFS,
267
+ # author = "R. E. Tarjan",
268
+ # key = "Tarjan",
269
+ # title = "Depth First Search and Linear Graph Algorithms",
270
+ # journal = "SIAM Journal on Computing",
271
+ # volume = "1",
272
+ # number = "2",
273
+ # pages = "146--160",
274
+ # month = jun,
275
+ # year = "1972",
276
+ # CODEN = "SMJCAT",
277
+ # ISSN = "0097-5397 (print), 1095-7111 (electronic)",
278
+ # bibdate = "Thu Jan 23 09:56:44 1997",
279
+ # bibsource = "Parallel/Multi.bib, Misc/Reverse.eng.bib",
280
+ # }
281
+ def strongly_connected_components
282
+ order_cell = [0]
283
+ order_hash = {}
284
+ node_stack = []
285
+ components = []
286
+
287
+ order_hash.default = -1
288
+
289
+ nodes.each {|node|
290
+ if order_hash[node] == -1
291
+ recurse_strongly_connected_components(node, order_cell, order_hash, node_stack, components)
292
+ end
293
+ }
294
+
295
+ components
296
+ end
297
+
298
+ def recurse_strongly_connected_components(node, order_cell, order_hash, node_stack, components)
299
+ order = (order_cell[0] += 1)
300
+ reachable_minimum_order = order
301
+ order_hash[node] = order
302
+ stack_length = node_stack.length
303
+ node_stack << node
304
+
305
+ links_from(node).each {|link|
306
+ nextnode = link.to
307
+ nextorder = order_hash[nextnode]
308
+ if nextorder != -1
309
+ if nextorder < reachable_minimum_order
310
+ reachable_minimum_order = nextorder
311
+ end
312
+ else
313
+ sub_minimum_order = recurse_strongly_connected_components(nextnode, order_cell, order_hash, node_stack, components)
314
+ if sub_minimum_order < reachable_minimum_order
315
+ reachable_minimum_order = sub_minimum_order
316
+ end
317
+ end
318
+ }
319
+
320
+ if order == reachable_minimum_order
321
+ scc = node_stack[stack_length .. -1]
322
+ node_stack[stack_length .. -1] = []
323
+ components << scc
324
+ scc.each {|n|
325
+ order_hash[n] = num_vertices
326
+ }
327
+ end
328
+ return reachable_minimum_order;
329
+ end
330
+ end
331
+
332
+ # Parallel propagation in directed acyclic graphs. Should be faster than
333
+ # traversing all links from each start node if the graph is dense so that
334
+ # many traversals can be merged.
335
+ class DagPropagator
336
+ def initialize(directedGraph, startNodes, &propagationBlock)
337
+ @graph, @block = directedGraph, propagationBlock
338
+ init_start_nodes(startNodes)
339
+ @visited = Hash.new
340
+ end
341
+
342
+ def init_start_nodes(startNodes)
343
+ @startnodes = startNodes
344
+ end
345
+
346
+ def propagate
347
+ @visited.clear
348
+ propagate_recursive
349
+ end
350
+
351
+ def propagate_recursive
352
+ next_start_nodes = Array.new
353
+ @startnodes.each do |parent|
354
+ @visited[parent] = true
355
+ @graph.children(parent).each do |child|
356
+ @block.call(parent, child)
357
+ unless @visited[child] or next_start_nodes.include?(child)
358
+ next_start_nodes.push(child)
359
+ end
360
+ end
361
+ end
362
+ if next_start_nodes.length > 0
363
+ @startnodes = next_start_nodes
364
+ propagate_recursive
365
+ end
366
+ end
367
+ end
368
+
369
+ # Directed graph with fast traversal from children to parents (back)
370
+ class BackLinkedDirectedGraph < DirectedGraph
371
+ def initialize(*args)
372
+ super
373
+ @back_link_map = HashOfHash.new {Array.new} # [to][from] -> array of links
374
+ @incoming_links_info = DefaultInitHash.new {Array.new}
375
+ end
376
+
377
+ def add_link(from, to, informationOnLink = nil)
378
+ link = super
379
+ links_to_from(to, from).push link
380
+ if informationOnLink and
381
+ !@incoming_links_info[to].include?(informationOnLink)
382
+ @incoming_links_info[to].push informationOnLink
383
+ end
384
+ link
385
+ end
386
+
387
+ def incoming_links_info(node)
388
+ @incoming_links_info[node]
389
+ end
390
+
391
+ def back_transition(node, backLinkInfo)
392
+ link = links_to(node).detect {|l| l.info == backLinkInfo}
393
+ begin
394
+ link.from
395
+ rescue Exception
396
+ raise GraphTraversalException.new(node, links_to(node), backLinkInfo)
397
+ end
398
+ end
399
+
400
+ def back_traverse(state, alongLinksWithInfo = [])
401
+ len = alongLinksWithInfo.length
402
+ alongLinksWithInfo = alongLinksWithInfo.clone
403
+ while len > 0
404
+ state = back_transition(state, alongLinksWithInfo.pop)
405
+ len -= 1
406
+ end
407
+ state
408
+ end
409
+
410
+ def links_to(node)
411
+ @back_link_map[node].map {|from, links| links}.flatten
412
+ end
413
+
414
+ protected
415
+
416
+ def links_to_from(to, from)
417
+ @back_link_map[to][from]
418
+ end
419
+ end
420
+
421
+ def calc_masks(start, stop, masks = Array.new)
422
+ mask = 1 << start
423
+ (start..stop).each {|i| masks[i] = mask; mask <<= 1}
424
+ masks
425
+ end
426
+
427
+ class BooleanMatrix
428
+ def initialize(objects)
429
+ @index, @objects, @matrix = Hash.new, objects, Array.new
430
+ cnt = 0
431
+ objects.each do |o|
432
+ @index[o] = cnt
433
+ @matrix[cnt] = 0 # Use Integers to represent the booleans
434
+ cnt += 1
435
+ end
436
+ @num_obects = cnt
437
+ end
438
+
439
+ @@masks_max = 1000
440
+ @@masks = calc_masks(0,@@masks_max)
441
+
442
+ def mask(index)
443
+ mask = @@masks[index]
444
+ unless mask
445
+ calc_masks(@@masks_max+1, index, @@masks)
446
+ mask = @masks[index]
447
+ end
448
+ mask
449
+ end
450
+
451
+ def or(index1, index2)
452
+ @matrix[index1] |= @matrix[index2]
453
+ end
454
+
455
+ def indices(anInteger)
456
+ index = 0
457
+ while anInteger > 0
458
+ yeild(index) if anInteger & 1
459
+ anInteger >>= 1
460
+ index += 1
461
+ end
462
+ end
463
+
464
+ def directed_graph
465
+ dg = Directedgraph.new
466
+ @matrix.each_with_index do |v,i|
467
+ indices(v) do |index|
468
+ dg.link_nodes(@objects[i], @objects[index])
469
+ end
470
+ end
471
+ dg
472
+ end
473
+
474
+ def transitive_closure
475
+ for i in (0..@num_obects)
476
+ for j in (0..@num_obects)
477
+
478
+ end
479
+ end
480
+ end
481
+ end