redshift 1.3.15

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.
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
data/examples/zeno.rb ADDED
@@ -0,0 +1,53 @@
1
+ require "redshift"
2
+
3
+ include RedShift
4
+
5
+ class Z < Component
6
+ state :A, :B
7
+ start A
8
+ transition A => B, B => A do
9
+ guard {puts "in guard clause: #{self.inspect}"; true}
10
+ action {puts "in action clause: #{self.inspect}"}
11
+ end
12
+ end
13
+
14
+ class ZWorld < World
15
+ # RedShift::ZenoDebugger already has a useful implementation of report_zeno,
16
+ # but we can augment its non-interactive output by adding an interactive
17
+ # debugging shell.
18
+ include ZenoDebugger
19
+
20
+ def report_zeno
21
+ super # the normal zeno output
22
+
23
+ unless @zeno_shell_started
24
+ require 'irb-shell'
25
+ puts
26
+ puts "Zeno debugger shell"
27
+ puts "^D to continue to next zeno step (^Z and Return on Windows)"
28
+ puts "'exit' to exit"
29
+ puts "Variable 'z' has the suspect object."
30
+ puts
31
+ @zeno_shell_started = true
32
+ end
33
+
34
+ z = curr_T[0] ###zeno_watch_list[0]
35
+ IRB.start_session(binding, self)
36
+
37
+ end
38
+ end
39
+
40
+ world = ZWorld.new
41
+
42
+ world.zeno_limit = 10
43
+ #world.zeno_limit = ZENO_UNLIMITED # don't check for zeno
44
+
45
+ world.debug_zeno = true
46
+ # After zeno_limit steps, RedShift starts calling world.step_zeno
47
+
48
+ world.debug_zeno_limit = ZENO_UNLIMITED
49
+ # The user is in control for as long as the user wants to be.
50
+
51
+ world.create(Z)
52
+
53
+ world.step 1
@@ -0,0 +1,47 @@
1
+ module AccessibleIndex
2
+ def index_accessor(h)
3
+ h.each do |sym, idx|
4
+ define_method sym do
5
+ self[idx]
6
+ end
7
+ define_method "#{sym}=" do |val|
8
+ self[idx] = val
9
+ end
10
+ end
11
+ end
12
+
13
+ def index_reader(h)
14
+ h.each do |sym, idx|
15
+ define_method sym do
16
+ self[idx]
17
+ end
18
+ end
19
+ end
20
+
21
+ def index_writer(h)
22
+ h.each do |sym, idx|
23
+ define_method "#{sym}=" do |val|
24
+ self[idx] = val
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ if __FILE__ == $0
31
+
32
+ class TestArray < Array
33
+ extend AccessibleIndex
34
+
35
+ index_accessor :a => 3, :b => 7
36
+ end
37
+
38
+ ta = TestArray[0,1,2,3,4,5,6,7,8,9,10]#.new((0..10).to_a)
39
+
40
+ p ta
41
+
42
+ ta.a = "three"
43
+ ta.b = "seven"
44
+
45
+ p ta
46
+
47
+ end
data/lib/redshift.rb ADDED
@@ -0,0 +1 @@
1
+ require 'redshift/redshift'
@@ -0,0 +1,412 @@
1
+ require 'singleton'
2
+ require 'superhash'
3
+ require 'accessible-index'
4
+ require 'redshift/state'
5
+ require 'redshift/meta'
6
+ require 'redshift/port'
7
+ require 'redshift/queue'
8
+
9
+ module RedShift
10
+
11
+ # Can be raised with a [msg, object] pair instead of just a msg string.
12
+ # In the former case, the +object+ is accessible.
13
+ module AugmentedException
14
+ attr_reader :object
15
+
16
+ def initialize(*msg)
17
+ if msg.empty?
18
+ super; return
19
+ end
20
+
21
+ msg = msg.first if msg.size == 1
22
+ if defined?(msg.first)
23
+ msg, @object = *msg
24
+ s = ((@object.inspect rescue
25
+ @object.to_s) rescue
26
+ "id ##{@object.object_id} of class #{@object.class}")
27
+ msg += " Object is: $!.object == #{s}"
28
+ end
29
+ super msg
30
+ end
31
+ end
32
+
33
+ # For convenience only; not all redshift error classes derive from this.
34
+ class RedShiftError < StandardError
35
+ include AugmentedException
36
+ end
37
+
38
+ class AlgebraicAssignmentError < RedShiftError; end
39
+ class NilLinkError < RedShiftError; end
40
+ class LinkTypeError < TypeError; include AugmentedException; end
41
+ class VarTypeError < TypeError; include AugmentedException; end
42
+ class CircularDefinitionError < RedShiftError; end
43
+ class StrictnessError < RedShiftError; end
44
+ class ConstnessError < RedShiftError; end
45
+ class TransitionError < RedShiftError; end
46
+ class SyntaxError < ::SyntaxError; end
47
+ class UnconnectedInputError < RedShiftError; end
48
+ class NoValueError < RedShiftError; end
49
+
50
+ # Raised when a component tries to perform an action that makes sense only
51
+ # during initialization.
52
+ class AlreadyStarted < RedShiftError; end
53
+
54
+ # These classes are derived from Array for efficient access to contents
55
+ # from C code.
56
+ class XArray < Array
57
+ def inspect; "<#{self.class.name.split("::")[-1]}: #{super[1..-2]}>"; end
58
+ end
59
+
60
+ class Transition < XArray ## put this in meta?
61
+ attr_reader :name
62
+
63
+ extend AccessibleIndex
64
+ G_IDX, S_IDX, A_IDX, R_IDX, E_IDX, P_IDX, C_IDX = *0..10
65
+ index_accessor \
66
+ :guard => G_IDX, :sync => S_IDX, :action => A_IDX,
67
+ :reset => R_IDX, :event => E_IDX, :post => P_IDX,
68
+ :connect => C_IDX
69
+
70
+ def initialize spec
71
+ if spec.kind_of? Hash
72
+ spec = spec.dup
73
+ def spec.method_missing(m, *); self[m]; end
74
+ end
75
+
76
+ @name = spec.name || "transition_#{object_id}".intern
77
+ self.guard = spec.guards; self.sync = spec.syncs
78
+ self.action = spec.actions; self.reset = spec.resets
79
+ self.event = spec.events; self.post = spec.posts
80
+ self.connect = spec.connects
81
+ end
82
+ end
83
+
84
+ class Flow ## rename to equation? formula? put in meta?
85
+ attr_reader :var, :formula
86
+
87
+ # Strict flows change value only over continuous time, and not within the
88
+ # steps of the discrete update. This can be false only for an AlgebraicFlow
89
+ # which depends on non-strict variables. In the algebraic case, a flow is
90
+ # strict iff the RHS of the eqn. has only strictly continuous variables.
91
+ attr_reader :strict
92
+
93
+ def initialize v, f
94
+ @var, @formula = v, f
95
+ @strict = true
96
+ self.class.needed = true
97
+ end
98
+
99
+ class << self; attr_accessor :needed; end
100
+ end
101
+
102
+ class AlgebraicFlow < Flow; end
103
+ class EulerDifferentialFlow < Flow; end
104
+ class RK4DifferentialFlow < Flow; end
105
+
106
+ class DerivativeFlow < Flow
107
+ attr_reader :feedback
108
+ def initialize v, f, feedback
109
+ super(v, f)
110
+ @feedback = feedback
111
+ end
112
+ end
113
+
114
+ class DelayFlow < Flow
115
+ attr_reader :delay_by
116
+ def initialize v, f, delay_by
117
+ super(v, f)
118
+ @delay_by = delay_by
119
+ end
120
+ end
121
+
122
+ class CexprGuard < Flow; end ## Kinda funny...
123
+ class Expr < Flow; end ## Kinda funny...
124
+
125
+ Always = Transition.new :name => :Always, :guard => nil
126
+
127
+ class Component
128
+
129
+ attr_reader :start_state
130
+ attr_accessor :name
131
+
132
+ attach_state(:Enter)
133
+ attach_state(:Exit)
134
+
135
+ class GuardPhase < XArray; end
136
+ class SyncPhase < XArray; end
137
+ class ActionPhase < XArray; end
138
+ class PostPhase < XArray; end
139
+ class EventPhase < XArray; end
140
+ class ResetPhase < XArray
141
+ attr_accessor :value_map
142
+ def inspect
143
+ "<ResetPhase: #{value_map.inspect}>"
144
+ end
145
+ end
146
+ class QMatch < XArray; end
147
+ class ConnectPhase < XArray; end
148
+
149
+ class PhaseItem < XArray; extend AccessibleIndex; end
150
+
151
+ class SyncPhaseItem < PhaseItem
152
+ LINK_NAME_IDX, LINK_OFFSET_IDX, EVENT_IDX = *0..2
153
+ index_accessor :link_name => LINK_NAME_IDX,
154
+ :link_offset => LINK_OFFSET_IDX, :event => EVENT_IDX
155
+ def inspect; "<Sync #{link_name}:#{event}>"; end
156
+ end
157
+
158
+ class EventPhaseItem < PhaseItem
159
+ E_IDX = 0; V_IDX = 1; I_IDX = 2
160
+ index_accessor :event => E_IDX, :value => V_IDX, :index => I_IDX
161
+
162
+ def value=(val)
163
+ self[V_IDX] = case val
164
+ when Proc
165
+ DynamicEventValue.new(&val)
166
+ when String
167
+ ExprEventValue.new val
168
+ when Literal # e.g., literal "x", or literal {...}
169
+ val.literal_value
170
+ else
171
+ val
172
+ end
173
+ end
174
+
175
+ def inspect; "<Event #{event}: #{value.inspect}>"; end
176
+ end
177
+
178
+ class DynamicEventValue < Proc; end
179
+ class ExprEventValue < String; end
180
+
181
+ class Literal
182
+ attr_accessor :literal_value
183
+ def initialize val; self.literal_value = val; end
184
+ end
185
+ def Component.literal val
186
+ Literal.new val
187
+ end
188
+
189
+ # Unique across all Worlds and Components in the process. Components are
190
+ # numbered in the order which this method was called on them and not
191
+ # necessarily in order of creation.
192
+ def comp_id
193
+ @comp_id ||= Component.next_comp_id
194
+ end
195
+
196
+ @next_comp_id = -1
197
+ def Component.next_comp_id
198
+ @next_comp_id += 1
199
+ end
200
+
201
+ def to_s
202
+ "<#{self.class} #{name || comp_id}>"
203
+ end
204
+
205
+ VAR_TYPES = [:constant_variables, :continuous_variables, :link_variables,
206
+ :input_variables]
207
+
208
+ def inspect opts = nil
209
+ old_inspecting = Thread.current[:inspecting]
210
+ Thread.current[:inspecting] = self
211
+ if opts
212
+ float_fmt = opts["float_fmt"]
213
+ data = opts["data"]
214
+ end
215
+
216
+ items = []
217
+
218
+ unless old_inspecting == self
219
+ # avoids inf. recursion when send(name) raises exception that
220
+ # calls inspect again.
221
+
222
+ if state
223
+ items << (trans ? "#{state} => #{dest}" : state)
224
+ end
225
+
226
+ VAR_TYPES.each do |var_type|
227
+ var_list = self.class.send(var_type)
228
+ unless var_list.empty?
229
+ strs = var_list.map {|vname,info| vname.to_s}.sort.map do |vname|
230
+ begin
231
+ val = send(vname)
232
+ val = case val
233
+ when Float
234
+ float_fmt ? float_fmt % val : val
235
+ when Component
236
+ val.to_s
237
+ else
238
+ val.inspect
239
+ end
240
+
241
+ "#{vname} = #{val}"
242
+
243
+ rescue CircularDefinitionError
244
+ "#{vname}: CIRCULAR"
245
+ rescue UnconnectedInputError
246
+ "#{vname}: UNCONNECTED"
247
+ rescue NilLinkError
248
+ "#{vname}: NIL LINK"
249
+ rescue => ex
250
+ "#{vname}: #{ex.inspect}"
251
+ end
252
+ end
253
+ items << strs.join(", ")
254
+ end
255
+ end
256
+
257
+ if trans and (ep=trans.grep(EventPhase).first)
258
+ strs = ep.map do |item|
259
+ e = item.event
260
+ "#{e}: #{send(e).inspect}"
261
+ end
262
+ items << strs.join(", ")
263
+ end
264
+
265
+ items << data if data
266
+ end
267
+
268
+ return "<#{[self, items.join("; ")].join(": ")}>"
269
+
270
+ ensure
271
+ Thread.current[:inspecting] = old_inspecting
272
+ end
273
+
274
+ def initialize(world)
275
+ if $REDSHIFT_DEBUG
276
+ unless caller[1] =~ /redshift\/world.*`create'\z/ or
277
+ caller[0] =~ /`initialize'\z/
278
+ raise ArgumentError, "Components can be created only using " +
279
+ "the create method of a world.", caller
280
+ end
281
+ end
282
+
283
+ __set__world world
284
+ self.var_count = self.class.var_count
285
+
286
+ restore {
287
+ @start_state = Enter
288
+ self.cont_state = self.class.cont_state_class.new
289
+
290
+ do_defaults
291
+ yield self if block_given?
292
+ do_setup
293
+
294
+ if state
295
+ raise RuntimeError, "Can't assign to state.\n" +
296
+ "Occurred in initialization of component of class #{self.class}."
297
+ end ## is this a useful restriction?
298
+
299
+ self.state = @start_state
300
+ }
301
+ end
302
+
303
+ def restore
304
+ if respond_to?(:event_values)
305
+ event_count = self.class.exported_events.size
306
+ ## should cache this size, since it can't change
307
+ self.event_values = Array.new(event_count)
308
+ self.next_event_values = Array.new(event_count)
309
+
310
+ event_values.freeze
311
+ next_event_values.freeze ## should do this deeply?
312
+ end
313
+
314
+ yield if block_given?
315
+
316
+ if state == Exit
317
+ __set__world nil
318
+ else
319
+ init_flags
320
+ update_cache
321
+ clear_ck_strict # update_cache leaves these set assuming finishing a trans
322
+ end
323
+ end
324
+
325
+ def do_defaults
326
+ self.class.do_defaults self
327
+ end
328
+ private :do_defaults
329
+
330
+ def do_setup
331
+ self.class.do_setup self
332
+ end
333
+ private :do_setup
334
+
335
+ def self.do_assignment_map instance, h
336
+ ## could be done in c code
337
+ h.each do |writer, val|
338
+ instance.send writer, val
339
+ end
340
+ end
341
+
342
+ def self.do_defaults instance
343
+ superclass.do_defaults instance if superclass.respond_to? :do_defaults
344
+ do_assignment_map instance, @defaults_map if @defaults_map
345
+ if @defaults_procs
346
+ @defaults_procs.each do |pr|
347
+ instance.instance_eval(&pr)
348
+ end
349
+ end
350
+ end
351
+
352
+ def self.do_setup instance
353
+ ## should be possible to turn off superclass's setup so that
354
+ ## it can be overridden. 'nosupersetup'? explicit 'super'?
355
+ superclass.do_setup instance if superclass.respond_to? :do_setup
356
+ do_assignment_map instance, @setup_map if @setup_map
357
+ if @setup_procs
358
+ @setup_procs.each do |pr|
359
+ instance.instance_eval(&pr)
360
+ end
361
+ end
362
+ end
363
+
364
+ ## shouldn't be necessary
365
+ def insteval_proc pr
366
+ instance_eval(&pr)
367
+ end
368
+
369
+ def disconnect input_var
370
+ connect(input_var, nil, nil)
371
+ end
372
+
373
+ # +var_name+ can be a input var, a continuous var, or a constant var.
374
+ def port var_name
375
+ return nil unless var_name
376
+ @ports ||= {}
377
+ @ports[var_name] ||= begin
378
+ var_name = var_name.to_sym
379
+
380
+ if self.class.input_variables.key? var_name
381
+ Port.new(self, var_name, true)
382
+ elsif self.class.continuous_variables.key? var_name or
383
+ self.class.constant_variables.key? var_name
384
+ Port.new(self, var_name, false)
385
+ else
386
+ raise "No variable #{var_name.inspect} in #{self.class.inspect}"
387
+ end
388
+ end
389
+ end
390
+
391
+ def inc_queue_ready_count
392
+ if @queue_ready_count == 0 || !@queue_ready_count
393
+ @queue_ready_count = 1
394
+ if world.queue_sleep.delete(self)
395
+ world.awake << self
396
+ end
397
+ else
398
+ @queue_ready_count += 1
399
+ end
400
+ end
401
+
402
+ def dec_queue_ready_count
403
+ @queue_ready_count -= 1
404
+ end
405
+ end
406
+
407
+ # The asymmetry between these two states is that components in Enter are active
408
+ # in the continuous and discrete updates. Components in Exit do not evolve.
409
+ Enter = Component::Enter
410
+ Exit = Component::Exit
411
+
412
+ end