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
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