redshift 1.3.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/.gitignore +8 -0
  2. data/README +5 -0
  3. data/RELEASE-NOTES +455 -0
  4. data/TODO +431 -0
  5. data/bench/alg-state.rb +61 -0
  6. data/bench/bench +26 -0
  7. data/bench/bench.rb +10 -0
  8. data/bench/continuous.rb +76 -0
  9. data/bench/diff-bench +86 -0
  10. data/bench/discrete.rb +101 -0
  11. data/bench/euler.rb +50 -0
  12. data/bench/formula.rb +78 -0
  13. data/bench/half-strict.rb +103 -0
  14. data/bench/inertness.rb +116 -0
  15. data/bench/queue.rb +92 -0
  16. data/bench/run +66 -0
  17. data/bench/simple.rb +74 -0
  18. data/bench/strictness.rb +86 -0
  19. data/examples/ball-tkar.rb +72 -0
  20. data/examples/ball.rb +123 -0
  21. data/examples/collide.rb +70 -0
  22. data/examples/connect-parallel.rb +48 -0
  23. data/examples/connect.rb +109 -0
  24. data/examples/constants.rb +27 -0
  25. data/examples/delay.rb +80 -0
  26. data/examples/derivative.rb +77 -0
  27. data/examples/euler.rb +46 -0
  28. data/examples/external-lib.rb +33 -0
  29. data/examples/guard-debugger.rb +77 -0
  30. data/examples/lotka-volterra.rb +33 -0
  31. data/examples/persist-ball.rb +68 -0
  32. data/examples/pid.rb +87 -0
  33. data/examples/ports.rb +60 -0
  34. data/examples/queue.rb +56 -0
  35. data/examples/queue2.rb +98 -0
  36. data/examples/reset-with-event-val.rb +28 -0
  37. data/examples/scheduler.rb +104 -0
  38. data/examples/set-dest.rb +23 -0
  39. data/examples/simulink/README +1 -0
  40. data/examples/simulink/delay.mdl +827 -0
  41. data/examples/simulink/derivative.mdl +655 -0
  42. data/examples/step-discrete-profiler.rb +103 -0
  43. data/examples/subsystem.rb +109 -0
  44. data/examples/sync-deadlock.rb +32 -0
  45. data/examples/sync-queue.rb +91 -0
  46. data/examples/sync-retry.rb +20 -0
  47. data/examples/sync.rb +51 -0
  48. data/examples/thermostat.rb +53 -0
  49. data/examples/zeno.rb +53 -0
  50. data/lib/accessible-index.rb +47 -0
  51. data/lib/redshift.rb +1 -0
  52. data/lib/redshift/component.rb +412 -0
  53. data/lib/redshift/meta.rb +183 -0
  54. data/lib/redshift/mixins/zeno-debugger.rb +69 -0
  55. data/lib/redshift/port.rb +57 -0
  56. data/lib/redshift/queue.rb +104 -0
  57. data/lib/redshift/redshift.rb +111 -0
  58. data/lib/redshift/state.rb +31 -0
  59. data/lib/redshift/syntax.rb +558 -0
  60. data/lib/redshift/target/c.rb +37 -0
  61. data/lib/redshift/target/c/component-gen.rb +1303 -0
  62. data/lib/redshift/target/c/flow-gen.rb +325 -0
  63. data/lib/redshift/target/c/flow/algebraic.rb +85 -0
  64. data/lib/redshift/target/c/flow/buffer.rb +74 -0
  65. data/lib/redshift/target/c/flow/delay.rb +203 -0
  66. data/lib/redshift/target/c/flow/derivative.rb +101 -0
  67. data/lib/redshift/target/c/flow/euler.rb +67 -0
  68. data/lib/redshift/target/c/flow/expr.rb +113 -0
  69. data/lib/redshift/target/c/flow/rk4.rb +80 -0
  70. data/lib/redshift/target/c/library.rb +85 -0
  71. data/lib/redshift/target/c/world-gen.rb +1370 -0
  72. data/lib/redshift/target/spec.rb +34 -0
  73. data/lib/redshift/world.rb +300 -0
  74. data/rakefile +37 -0
  75. data/test/test.rb +52 -0
  76. data/test/test_buffer.rb +58 -0
  77. data/test/test_connect.rb +242 -0
  78. data/test/test_connect_parallel.rb +47 -0
  79. data/test/test_connect_strict.rb +135 -0
  80. data/test/test_constant.rb +74 -0
  81. data/test/test_delay.rb +145 -0
  82. data/test/test_derivative.rb +48 -0
  83. data/test/test_discrete.rb +592 -0
  84. data/test/test_discrete_isolated.rb +92 -0
  85. data/test/test_exit.rb +59 -0
  86. data/test/test_flow.rb +200 -0
  87. data/test/test_flow_link.rb +288 -0
  88. data/test/test_flow_sub.rb +100 -0
  89. data/test/test_flow_trans.rb +292 -0
  90. data/test/test_inherit.rb +127 -0
  91. data/test/test_inherit_event.rb +74 -0
  92. data/test/test_inherit_flow.rb +139 -0
  93. data/test/test_inherit_link.rb +65 -0
  94. data/test/test_inherit_setup.rb +56 -0
  95. data/test/test_inherit_state.rb +66 -0
  96. data/test/test_inherit_transition.rb +168 -0
  97. data/test/test_numerics.rb +34 -0
  98. data/test/test_queue.rb +90 -0
  99. data/test/test_queue_alone.rb +115 -0
  100. data/test/test_reset.rb +209 -0
  101. data/test/test_setup.rb +119 -0
  102. data/test/test_strict_continuity.rb +410 -0
  103. data/test/test_strict_reset_error.rb +30 -0
  104. data/test/test_strictness_error.rb +32 -0
  105. data/test/test_sync.rb +185 -0
  106. data/test/test_world.rb +328 -0
  107. metadata +204 -0
@@ -0,0 +1,31 @@
1
+ class RedShift::State
2
+ attr_reader :name, :persist_name
3
+
4
+ def initialize n, context
5
+ @name = n
6
+ @persist_name = "#{context}::#{n}".intern
7
+ @context = context
8
+ end
9
+
10
+ def _dump depth
11
+ @persist_name.to_s
12
+ end
13
+
14
+ def self._load str
15
+ pn = str.intern
16
+ ## could cache this lookup in a hash
17
+ ObjectSpace.each_object(State) { |st|
18
+ if st.persist_name == pn
19
+ return st
20
+ end
21
+ }
22
+ end
23
+
24
+ def to_s
25
+ @name.to_s
26
+ end
27
+
28
+ def inspect
29
+ "<#{@name}>"
30
+ end
31
+ end
@@ -0,0 +1,558 @@
1
+ require 'redshift/world'
2
+ require 'redshift/component'
3
+
4
+ module RedShift
5
+
6
+ # Register the given block to be called for instances of this class of World as
7
+ # they are instantiated (before the block passed to #new is called). The
8
+ # registered code is inherited by subclasses of this World class. The block is
9
+ # called with the world as +self+. Any number of blocks can be registered.
10
+ # (There are no per-world defaults. Use the #new block instead.)
11
+ def World.defaults(&block)
12
+ (@defaults_procs ||= []) << block if block
13
+ end
14
+ class << World
15
+ alias default defaults
16
+ end
17
+
18
+ # Register the given block to be called for instances of this class of World
19
+ # just before they are first run. The registered code is inherited by
20
+ # subclasses of this World class. The block is called with the world as +self+.
21
+ # Any number of blocks can be registered.
22
+ def World.setup(&block)
23
+ (@setup_procs ||= []) << block if block
24
+ end
25
+
26
+ # Register the given block to be called for this world just before it is
27
+ # first run. The block is called with the world as +self+.
28
+ # Any number of blocks can be registered.
29
+ class World
30
+ def setup(&block)
31
+ (@setup_procs ||= []) << block if block
32
+ end
33
+ end
34
+
35
+
36
+ class Component
37
+ # Create a component in the same world as this component. This method is
38
+ # provided for convenience. It just calls World#create.
39
+ def create(component_class)
40
+ if block_given?
41
+ world.create(component_class) {|c| yield c}
42
+ else
43
+ world.create(component_class)
44
+ end
45
+ end
46
+
47
+ # Specify the starting state +s+ of the component.
48
+ # To be called only before the component starts running: during the default,
49
+ # setup, or initialization block (block passed to Component#new).
50
+ def start(s)
51
+ raise AlreadyStarted if state
52
+ case s
53
+ when State
54
+ @start_state = s
55
+ else
56
+ @start_state = self.class.const_get(s.to_s)
57
+ end
58
+ end
59
+ end
60
+
61
+ class << Component
62
+ # Specify the starting state +s+ of the component, as a default for the class.
63
+ def start(s)
64
+ default {start s}
65
+ end
66
+
67
+ def make_init_value_map(h)
68
+ h.inject({}) do |hh, (var, val)|
69
+ if val.kind_of? Proc or val.kind_of? String
70
+ raise TypeError,
71
+ "value for '#{var}' must be literal, like #{var} => 1.23"
72
+ end
73
+ hh.update "#{var}=" => val
74
+ end
75
+ end
76
+
77
+ # Register, for the current component class, the given block to be called at
78
+ # the beginning of initialization of an instance.
79
+ # The block is called with the world as +self+.
80
+ # Any number of blocks can be registered.
81
+ def defaults(h = nil, &block)
82
+ (@defaults_procs ||= []) << block if block
83
+ (@defaults_map ||= {}).update make_init_value_map(h) if h
84
+ end
85
+ alias default defaults
86
+
87
+ # Register, for the current component class, the given block to be called
88
+ # later in the initialization of an instance, after defaults and the
89
+ # initialization block (the block passed to Component#new).
90
+ # The block is called with the world as +self+.
91
+ # Any number of blocks can be registered.
92
+ def setup(h = nil, &block)
93
+ (@setup_procs ||= []) << block if block
94
+ (@setup_map ||= {}).update make_init_value_map(h) if h
95
+ end
96
+
97
+ # Define states in this component class, listed in +state_names+. A state
98
+ # name should be a string or symbol beginning with [A-Z] and consisting of
99
+ # alphanumeric (<tt>/\w/</tt>) characters. States are inherited.
100
+ def state(*state_names)
101
+ state_names.flatten!
102
+ state_names.map do |state_name|
103
+ if state_name.kind_of? Symbol
104
+ state_name = state_name.to_s
105
+ else
106
+ begin
107
+ state_name = state_name.to_str
108
+ rescue NoMethodError
109
+ raise SyntaxError, "Not a valid state name: #{state_name.inspect}"
110
+ end
111
+ end
112
+
113
+ unless state_name =~ /^[A-Z]/
114
+ raise SyntaxError,
115
+ "State name #{state_name.inspect} does not begin with [A-Z]."
116
+ end
117
+
118
+ begin
119
+ val = const_get(state_name)
120
+ rescue NameError
121
+ attach_state(state_name)
122
+ else
123
+ case val
124
+ when State
125
+ raise NameError, "state #{state_name} already exists"
126
+ else
127
+ raise NameError,
128
+ "state name '#{state_name}' is already used for a constant " +
129
+ "of type #{val.class}."
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def permissively_continuous(*var_names)
136
+ attach_continuous_variables(:permissive, var_names)
137
+ end
138
+
139
+ def strictly_continuous(*var_names)
140
+ attach_continuous_variables(:strict, var_names)
141
+ end
142
+
143
+ def continuous(*var_names)
144
+ attach_continuous_variables(:piecewise, var_names)
145
+ end
146
+ alias piecewise_continuous continuous
147
+
148
+ def permissively_constant(*var_names)
149
+ attach_constant_variables(:permissive, var_names)
150
+ end
151
+
152
+ def strictly_constant(*var_names)
153
+ attach_constant_variables(:strict, var_names)
154
+ end
155
+
156
+ def constant(*var_names)
157
+ attach_constant_variables(:piecewise, var_names)
158
+ end
159
+ alias piecewise_constant constant
160
+
161
+ def strict_link vars
162
+ attach_link vars, :strict
163
+ end
164
+
165
+ # link :x => MyComponent, :y => :FwdRefComponent
166
+ def link(*vars)
167
+ h = {}
168
+ vars.each do |var|
169
+ case var
170
+ when Hash
171
+ h.update var
172
+ else
173
+ h[var] = Component
174
+ end
175
+ end
176
+ attach_link h, false
177
+ end
178
+
179
+ def input(*var_names)
180
+ attach_input :piecewise, var_names
181
+ end
182
+
183
+ def strict_input(*var_names)
184
+ attach_input :strict, var_names
185
+ end
186
+
187
+ def strict(*var_names)
188
+ var_names.each do |var_name|
189
+ dest = find_var_superhash(var_name)
190
+ case dest
191
+ when nil
192
+ raise VarTypeError, "Variable #{var_name.inspect} not found."
193
+ when link_variables
194
+ var_type = link_variables[var_name].first
195
+ attach_variables(dest, :strict, [var_name], var_type)
196
+ else
197
+ attach_variables(dest, :strict, [var_name])
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ # Defines the flow types that can be used within a flow block.
204
+ module FlowSyntax
205
+ def self.parse block
206
+ FlowParser.new(block).flows
207
+ end
208
+
209
+ class FlowParser
210
+ attr_reader :flows
211
+
212
+ def initialize block
213
+ @flows = []
214
+ instance_eval(&block)
215
+ end
216
+
217
+ def algebraic(*equations)
218
+ equations.each do |equation|
219
+ unless equation =~ /^\s*(\w+)\s*=\s*(.+)/m
220
+ raise SyntaxError, "parse error in\n\t#{equation}."
221
+ end
222
+ @flows << AlgebraicFlow.new($1.intern, $2.strip)
223
+ end
224
+ end
225
+
226
+ def euler(*equations)
227
+ for equation in equations
228
+ unless equation =~ /^\s*(\w+)\s*'\s*=\s*(.+)/m
229
+ raise SyntaxError, "parse error in\n\t#{equation}."
230
+ end
231
+ @flows << EulerDifferentialFlow.new($1.intern, $2.strip)
232
+ end
233
+ end
234
+
235
+ def rk4(*equations)
236
+ for equation in equations
237
+ unless equation =~ /^\s*(\w+)\s*'\s*=\s*(.+)/m
238
+ raise SyntaxError, "parse error in\n\t#{equation}."
239
+ end
240
+ @flows << RK4DifferentialFlow.new($1.intern, $2.strip)
241
+ end
242
+ end
243
+
244
+ def derive(*equations)
245
+ opts = equations.pop
246
+ unless opts and opts.kind_of? Hash and
247
+ (opts[:feedback] == true or opts[:feedback] == false)
248
+ raise SyntaxError, "Missing option: :feedback => <true|false>\n" +
249
+ "Use 'true' when the output of this flow feeds back into another\n" +
250
+ "derivative flow (even after a delay). Also, set <var>_init_rhs.\n"
251
+ ## should false be the default?
252
+ ## rename 'feedback'?
253
+ end
254
+ feedback = opts[:feedback]
255
+ for equation in equations
256
+ unless equation =~ /^\s*(\w+)\s*=\s*(.+)'\s*\z/m
257
+ raise SyntaxError, "parse error in\n\t#{equation}."
258
+ end
259
+ @flows << DerivativeFlow.new($1.intern, $2.strip, feedback)
260
+ end
261
+ end
262
+
263
+ def delay(*equations)
264
+ opts = equations.pop
265
+ unless opts and opts.kind_of? Hash and opts[:by]
266
+ raise SyntaxError, "Missing delay term: :delay => <delay>"
267
+ end
268
+ delay_by = opts[:by]
269
+ equations.each do |equation|
270
+ unless equation =~ /^\s*(\w+)\s*=\s*(.+)/m
271
+ raise SyntaxError, "parse error in\n\t#{equation}."
272
+ end
273
+ @flows << DelayFlow.new($1.intern, $2.strip, delay_by)
274
+ end
275
+ end
276
+
277
+ alias alg algebraic
278
+ alias diff rk4
279
+ alias differential rk4
280
+ end
281
+ end
282
+
283
+ module TransitionSyntax
284
+ def self.parse block
285
+ TransitionParser.new(block)
286
+ end
287
+
288
+ class EventBlockParser
289
+ attr_reader :events
290
+
291
+ def method_missing event_name, *args, &bl
292
+ if args.size > 1 or (args.size == 1 and bl)
293
+ raise SyntaxError, "Too many arguments in event specifier"
294
+ end
295
+
296
+ item = Component::EventPhaseItem.new
297
+ item.event = event_name
298
+ item.value = bl || (args.size > 0 && args[0]) || true
299
+
300
+ @events << item
301
+ end
302
+
303
+ def initialize(block)
304
+ @events = []
305
+ instance_eval(&block)
306
+ end
307
+
308
+ def literal val
309
+ Component.literal val
310
+ end
311
+ end
312
+
313
+ class TransitionParser
314
+ attr_reader :name,
315
+ :guards, :syncs, :actions,
316
+ :resets, :events, :posts,
317
+ :connects
318
+
319
+ def initialize block
320
+ @name = nil
321
+ instance_eval(&block)
322
+ end
323
+
324
+ def name(*n); n.empty? ? @name : @name = n.first; end
325
+
326
+ def sync(*a)
327
+ @syncs ||= Component::SyncPhase.new
328
+
329
+ if a.last.kind_of?(Hash)
330
+ a.concat a.pop.to_a
331
+ end
332
+
333
+ a.each do |link_name, event|
334
+ link_names = case link_name
335
+ when Array; link_name
336
+ else [link_name]
337
+ end
338
+
339
+ events = case event
340
+ when Array; event
341
+ else [event]
342
+ end
343
+
344
+ link_names.each do |ln|
345
+ events.each do |e|
346
+ item = Component::SyncPhaseItem.new
347
+ item.link_name = ln
348
+ item.event = e
349
+ @syncs << item
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+ def wait(*args)
356
+ @guards ||= Component::GuardPhase.new
357
+
358
+ args.each do |arg|
359
+ case arg
360
+ when Hash
361
+ @guards.concat(arg.sort_by {|q,m| q.to_s}.map{|q,m|
362
+ Component::QMatch[q.to_sym,*m]})
363
+ # { :queue => match }
364
+ when Symbol
365
+ @guards << Component::QMatch[arg] # :queue
366
+ else raise SyntaxError
367
+ end
368
+ end
369
+ end
370
+
371
+ def guard(*args, &block)
372
+ @guards ||= Component::GuardPhase.new
373
+
374
+ args.each do |arg|
375
+ case arg
376
+ when String; @guards << arg.strip # "<expression>"
377
+ when Proc; @guards << arg # proc { ... }
378
+ when Symbol; @guards << arg # :method
379
+ when nil, true; # no condition
380
+ when false; @guards << arg
381
+ else raise SyntaxError, "'guard #{arg.inspect}'"
382
+ end
383
+ end
384
+
385
+ @guards << block if block
386
+ end
387
+
388
+ def action(meth = nil, &bl)
389
+ @actions ||= Component::ActionPhase.new
390
+ @actions << meth if meth
391
+ @actions << bl if bl
392
+ end
393
+
394
+ def post(meth = nil, &bl)
395
+ @posts ||= Component::PostPhase.new
396
+ @posts << meth if meth
397
+ @posts << bl if bl
398
+ end
399
+ alias after post
400
+
401
+ # +h+ is a hash of :var => proc {value_expr_ruby} or "value_expr_c".
402
+ def reset(h)
403
+ badkeys = h.keys.reject {|k| k.is_a?(Symbol)}
404
+ unless badkeys.empty?
405
+ raise SyntaxError, "Keys #{badkeys.inspect} in reset must be symbols"
406
+ end
407
+
408
+ @resets ||= Component::ResetPhase.new
409
+ @resets.value_map ||= {}
410
+ @resets.concat [nil, nil, nil] # continuous, constant, link
411
+ @resets.value_map.update h
412
+ end
413
+
414
+ # +h+ is a hash of :var => proc {port_expr_ruby} or [:link, :var].
415
+ def connect(h)
416
+ badkeys = h.keys.reject {|k| k.is_a?(Symbol)}
417
+ unless badkeys.empty?
418
+ raise SyntaxError, "Keys #{badkeys.inspect} in connect must be symbols"
419
+ end
420
+
421
+ @connects ||= Component::ConnectPhase.new
422
+ @connects.concat h.entries
423
+ end
424
+
425
+ # each arg can be an event name (string or symbol), exported with value
426
+ # +true+, or a hash of event_name => value. In the latter case, _value_
427
+ # can be either a Proc, string (C expr), or a literal. If you need to
428
+ # treat a Proc or string as a literal, use the notation
429
+ #
430
+ # :e => literal("str")
431
+ #
432
+ # :e => literal(proc {...})
433
+ #
434
+ def event(*args, &bl)
435
+ @events ||= Component::EventPhase.new
436
+ for arg in args
437
+ case arg
438
+ when Symbol, String
439
+ item = Component::EventPhaseItem.new
440
+ item.event = arg
441
+ item.value = true
442
+ @events << item
443
+
444
+ when Hash
445
+ arg.sort_by {|e,v| e.to_s}.each do |e,v|
446
+ item = Component::EventPhaseItem.new
447
+ item.event = e
448
+ item.value = v
449
+ @events << item
450
+ end
451
+ else
452
+ raise SyntaxError, "unrecognized event specifier #{arg}."
453
+ end
454
+ end
455
+ if bl
456
+ eb = EventBlockParser.new(bl)
457
+ @events.concat(eb.events)
458
+ end
459
+ end
460
+
461
+ def literal val
462
+ Component.literal val
463
+ end
464
+ end
465
+ end
466
+
467
+ # Define flows in this component class. Flows are attached to all of the
468
+ # +states+ listed. The block contains method calls such as:
469
+ #
470
+ # alg "var = expression"
471
+ # diff "var' = expression"
472
+ #
473
+ def Component.flow(*states, &block)
474
+ raise "no flows specified. Put { on same line!" unless block
475
+ states = [Enter] if states == []
476
+
477
+ attach states, FlowSyntax.parse(block)
478
+ end
479
+
480
+ # Define transitions in this component class. Transitions are attached to
481
+ # all of the +edges+ listed as <tt>src => dst</tt>. In fact, edges may
482
+ # also be given as <tt>[s0, s1, ...] => d</tt> and then the transition
483
+ # is attached to all <tt>si => d</tt>.
484
+ #
485
+ # If no edges are specified, <tt>Enter => Enter<\tt> is used.
486
+ # If no block is given, the +Always+ transition is used.
487
+ # It is a TransitionError to omit both the edges and the block.
488
+ # Specifying two outgoing transitions for the same state is warned, but
489
+ # only when this is done within the same call to this method.
490
+ #
491
+ # The block contains method calls to define guards, events, resets, connects,
492
+ # and action and post procs.
493
+ #
494
+ # The block also can have a call to the name method, which defines the name of
495
+ # the transition--this is necessary for overriding the transition in a subclass.
496
+ #
497
+ def Component.transition(edges = {}, &block)
498
+ e = {}
499
+ warn = []
500
+
501
+ unless edges.kind_of?(Hash)
502
+ raise SyntaxError, "transition syntax must be 'S1 => S2, S3 => S4, ...' "
503
+ end
504
+
505
+ edges.each do |s, d|
506
+ must_be_state(d)
507
+
508
+ case s
509
+ when Array
510
+ s.each do |t|
511
+ must_be_state(t)
512
+ warn << t if e[t]
513
+ e[t] = d
514
+ end
515
+
516
+ else
517
+ must_be_state(s)
518
+ warn << s if e[s]
519
+ e[s] = d
520
+ end
521
+ end
522
+
523
+ edges = e
524
+ warn.each do |st|
525
+ warn "Two destinations for state #{st} at #{caller[0]}."
526
+ end
527
+
528
+ if block
529
+ edges = {Enter => Enter} if edges.empty?
530
+ parser = TransitionSyntax.parse(block)
531
+
532
+ if parser.events
533
+ parser.events.each do |event_phase_item|
534
+ event_phase_item.index = export(event_phase_item.event)[0]
535
+ # cache index
536
+ end
537
+ end
538
+
539
+ trans = Transition.new(parser)
540
+
541
+ else
542
+ if edges == {}
543
+ raise TransitionError, "No transition specified."
544
+ else
545
+ trans = Always
546
+ end
547
+ end
548
+
549
+ attach edges, trans
550
+ end
551
+
552
+ def Component.must_be_state s
553
+ unless s.kind_of? State
554
+ raise TypeError, "Not a state: #{s}"
555
+ end
556
+ end
557
+
558
+ end