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/ball.rb ADDED
@@ -0,0 +1,123 @@
1
+ require 'redshift'
2
+
3
+ include RedShift
4
+
5
+ class Observer < Component
6
+ link :ball => :Ball
7
+ state :Observing, :Decision
8
+ attr_reader :counter
9
+
10
+ setup do
11
+ @counter = 0
12
+ end
13
+
14
+ start Observing
15
+
16
+ transition Observing => Decision do
17
+ sync :ball => :impact
18
+ action do
19
+ print "\n\n ***** Time of impact #{world.clock}.\n\n"
20
+ @counter += 1
21
+ end
22
+ end
23
+
24
+ transition Decision => Exit do
25
+ guard {@counter == 2}
26
+ action {print " ***** Observer leaving after 2 bounces.\n\n"}
27
+ end
28
+
29
+ transition Decision => Observing do
30
+ guard {@counter < 2}
31
+ end
32
+ end
33
+
34
+ class Ball < Component
35
+ constant :y0, :v0, :a, :bounce_count
36
+ state :Falling, :Rising
37
+
38
+ flow Falling, Rising do
39
+ differential " y' = v "
40
+ euler " v' = a "
41
+
42
+ euler " t_elapsed' = 1.0 "
43
+ algebraic " true_y = y0 + v0 * t_elapsed +
44
+ 0.5 * a * pow(t_elapsed, 2) "
45
+ algebraic " y_err = fabs(true_y - y) "
46
+ end
47
+
48
+ transition Falling => Rising do
49
+ guard "y <= 0"
50
+ event :impact
51
+ reset :v => "-v",
52
+ :y0 => "y",
53
+ :v0 => "-v",
54
+ :t_elapsed => 0,
55
+ :bounce_count => "bounce_count + 1"
56
+ # The reset is essentially the same as:
57
+ # action {
58
+ # self.v = -v
59
+ # self.y0 = y; self.v0 = v
60
+ # self.t_elapsed = 0.0
61
+ # self.bounce_count += 1
62
+ # }
63
+ # The difference: reset is faster, and has parallel semantics,
64
+ # which is why ':v0 => "-v"', in place of 'self.v0 = v'.
65
+ end
66
+
67
+ transition Rising => Falling do
68
+ guard "v <= 0"
69
+ end
70
+
71
+ transition Rising => Exit, Falling => Exit do
72
+ guard "bounce_count >= 3"
73
+ end
74
+
75
+ defaults {
76
+ start Falling
77
+ self.y0 = 100.0
78
+ self.v0 = 0.0
79
+ self.a = -9.8
80
+ }
81
+
82
+ setup {
83
+ self.y = y0; self.v = v0
84
+ self.t_elapsed = 0.0
85
+ self.bounce_count = 0
86
+ }
87
+
88
+ def inspect
89
+ sprintf "y = %8.4f, v = %8.4f, y_err = %8.6f%16s",
90
+ y, v, y_err, state
91
+ end
92
+ end
93
+
94
+ w = World.new
95
+ w.time_step = 0.01
96
+
97
+ ball = w.create(Ball) {|b| b.a = -9.8}
98
+ w.create(Observer) {|o|o.ball = ball}
99
+
100
+ y = [[w.clock, ball.y]]
101
+
102
+ while w.size > 0 do
103
+ t = w.clock
104
+ if t == t.floor
105
+ print "\nTime #{t}\n"
106
+ end
107
+ p ball unless ball.state == Exit
108
+
109
+ w.run
110
+
111
+ y << [w.clock, ball.y]
112
+ end
113
+
114
+ if ARGV.delete('-p')
115
+ require 'sci/plot'
116
+ include Plot::PlotUtils
117
+
118
+ gnuplot do |plot|
119
+ plot.add y, 'title "height" with lines'
120
+ end
121
+ else
122
+ puts "use -p switch to show plot"
123
+ end
@@ -0,0 +1,70 @@
1
+ # Example of using sync and events to exchange data in parallel. This assumes that
2
+ # the two components are already linked, so it doesn't scale well. A scalable
3
+ # solution would separate detection into a cell mechanism or something, and use
4
+ # queues to notify the components. This example is more about collision _handling_
5
+ # than _detection_.
6
+
7
+ require 'redshift'
8
+
9
+ class Ball < RedShift::Component
10
+ link :other => Ball
11
+ continuous :v
12
+ constant :dir
13
+
14
+ state :Moving
15
+
16
+ transition Enter => Moving do
17
+ action do
18
+ self.dir = other.x < x ? -1 : 1
19
+ end
20
+ end
21
+
22
+ flow Moving do
23
+ diff " x' = v "
24
+ end
25
+
26
+ transition Moving => Moving do
27
+ guard " dir * (other.x - x) < 0 "
28
+ guard {other.other == self}
29
+ # meaning: only if other is colliding with self
30
+ # The guard above is a bit unnecessary in this model,
31
+ # but in general it is needed.
32
+ sync :other => :collision
33
+ event :collision
34
+ reset :v => "other.v", :x => "other.x"
35
+ end
36
+ end
37
+
38
+ w = RedShift::World.new
39
+ b1 = w.create Ball
40
+ b2 = w.create Ball
41
+ b1.other = b2
42
+ b2.other = b1
43
+ b1.x = 0.0
44
+ b1.v = 30.0
45
+ b2.x = 100.0
46
+ b2.v = -70.0
47
+
48
+ b1_x, b2_x = [], []
49
+ gather = proc do
50
+ time = w.clock
51
+ b1_x << [time, b1.x]
52
+ b2_x << [time, b2.x]
53
+ end
54
+
55
+ gather.call
56
+ w.evolve 1.2 do
57
+ gather.call
58
+ end
59
+
60
+ require 'sci/plot'
61
+ include Plot::PlotUtils
62
+
63
+ gnuplot do |plot|
64
+ plot.command %{set title "Bouncing balls"}
65
+ plot.command %{set xlabel "time"}
66
+ plot.add b1_x, %{title "b1.x" with lines}
67
+ plot.add b2_x, %{title "b2.x" with lines}
68
+ end
69
+
70
+ sleep 1 if /mswin32|mingw32/ =~ RUBY_PLATFORM
@@ -0,0 +1,48 @@
1
+ # Shows how to connect input variables in parallel, and also in
2
+ # parallel with resets.
3
+
4
+ require "redshift"
5
+ include RedShift
6
+
7
+ class A < Component
8
+ input :in
9
+ continuous :x => 1
10
+ constant :k => 1
11
+ link :comp
12
+
13
+ state :T
14
+
15
+ flow T do
16
+ diff " x' = k*in "
17
+ end
18
+
19
+ transition Enter => T do
20
+ reset :comp => nil # in parallel with the connect
21
+ if true
22
+ connect :in => proc {comp.port(:x)}
23
+ # alternate syntaxes:
24
+ # connect port(:in).to {comp.port(:x)}
25
+ # port(:in).connect {comp.port(:x)}
26
+ # connect { in {comp.port :x}; ... }
27
+ else
28
+ connect :in => [:comp, :x] # special case: literals
29
+ end
30
+
31
+ # The non-parallel equivalent, which would fail after
32
+ # resetting comp to nil:
33
+ #
34
+ # action do
35
+ # port(:in) << comp.port(:x)
36
+ # end
37
+ ## actually this would fail as a post, but not as action
38
+ end
39
+ end
40
+
41
+ w = World.new
42
+ w.time_step = 0.001
43
+ a0, a1 = (0..1).map {w.create(A)}
44
+ a0.comp = a1
45
+ a1.comp = a0
46
+
47
+ w.evolve 1
48
+ p a0, a1
@@ -0,0 +1,109 @@
1
+ require "redshift"
2
+ include RedShift
3
+
4
+ class A < Component
5
+ continuous :x
6
+ constant :k => 6.78
7
+ flow do
8
+ diff " x' = 2*x "
9
+ end
10
+ end
11
+
12
+ class B < Component
13
+ input :y
14
+ end
15
+
16
+ world = World.new
17
+
18
+ a = world.create(A)
19
+ b = world.create(B)
20
+
21
+ begin
22
+ b.y
23
+ rescue RedShift::UnconnectedInputError => ex
24
+ puts "As expected: #{ex}"
25
+ end
26
+
27
+ b.connect(:y, a, :x)
28
+ # doesn't construct Port objects, so slightly more efficent
29
+ p(b.port(:y) << a.port(:x)) # Port#<< is alias for Port#connect
30
+
31
+ begin
32
+ a.port(:x) << b.port(:y)
33
+ rescue TypeError => ex
34
+ puts "As expected: #{ex}"
35
+ end
36
+
37
+ begin
38
+ b.y = 1.23 # error
39
+ rescue NoMethodError => ex
40
+ puts "As expected: #{ex}"
41
+ end
42
+
43
+ a.x = 4.56
44
+ p b.y # ok
45
+ p b
46
+
47
+ p b.port(:y).source == a.port(:x) # true
48
+ p b.port(:y).source_component # a
49
+ p b.port(:y).source_variable # :x
50
+
51
+ p a.port(:x).component # a
52
+ p a.port(:x).variable # :x
53
+
54
+ b.disconnect :y
55
+ b.port(:y).disconnect # same
56
+ b.port(:y) << nil # same
57
+
58
+ begin
59
+ b.y
60
+ rescue RedShift::UnconnectedInputError => ex
61
+ puts "As expected: #{ex}"
62
+ end
63
+
64
+ p A.offset_table
65
+ p B.offset_table
66
+
67
+ b.port(:y) << a.port(:x)
68
+ world.evolve 1 do
69
+ p [a.x, b.y]
70
+ end
71
+
72
+ b.port(:y) << a.port(:k) # reconnect, but this time to a constant
73
+ world.evolve 0.5 do
74
+ p [a.k, b.y]
75
+ end
76
+
77
+ puts <<END
78
+
79
+ This example shows four things:
80
+
81
+ 1. you can connect an input to an input (b1 to b2, and b2 to b3).
82
+ 2. >> as an alternative to <<
83
+ 3. chaining >> (or <<)
84
+ 4. two input ports connected to the same var (b1 and b4 conn to a)
85
+
86
+ END
87
+
88
+ b1 = world.create(B)
89
+ b2 = world.create(B)
90
+ b3 = world.create(B)
91
+ b4 = world.create(B)
92
+
93
+ a.port(:x) >> b4.port(:y)
94
+ a.port(:x) >> b1.port(:y) >> b2.port(:y) >> b3.port(:y)
95
+
96
+ a.x = 3
97
+ p b3.y
98
+
99
+ puts <<END
100
+
101
+ This example shows that connections are treated dynamically, not statically.
102
+ Changing an upstream connection during a run affects all downstream vars.
103
+
104
+ END
105
+
106
+ a2 = world.create(A)
107
+ a2.x = 4
108
+ a2.port(:x) >> b1.port(:y)
109
+ p b2.y
@@ -0,0 +1,27 @@
1
+ # Overview of the different kinds of "constants" in redshift.
2
+ #
3
+ # 1. constants defined in a ruby module
4
+ #
5
+ # 2. constants defined in an external library -- see external-lib.rb
6
+ #
7
+ # 3. per component constant _functions_ (strict or piecewise).
8
+
9
+ require 'redshift'
10
+
11
+ RedShift.with_library do |library|
12
+ library.declare_external_constant "M_E" # GNU C math lib
13
+ end
14
+
15
+ class C < RedShift::Component
16
+ K = 10
17
+ L = 456
18
+ constant :kk => 2 # per instance, and can change discretely
19
+
20
+ flow do
21
+ alg "x = kk*K + #{L*1000} + M_E"
22
+ end
23
+ end
24
+
25
+ w = RedShift::World.new
26
+ c = w.create C
27
+ p c.x
data/examples/delay.rb ADDED
@@ -0,0 +1,80 @@
1
+ # Delay of a continuous signal by a given time.
2
+
3
+ # Compare with simulink/delay.mdl -- note that redshift is more accurate
4
+ # because the delay flow operates at all integrator steps, not just at
5
+ # the simulation timesteps.
6
+
7
+ require 'redshift'
8
+ include RedShift
9
+
10
+ class C < Component
11
+ constant :pi => Math::PI
12
+ constant :d => 0.3
13
+
14
+ state :A, :B
15
+ start A
16
+
17
+ flow A, B do
18
+ diff " t' = 1 "
19
+ alg " u = sin(t*pi/2) "
20
+ alg " shift_u = sin((t-d)*pi/2) " # u shifted by d
21
+ delay " delay_u = u ", # delayed output from u (can be any expr)
22
+ :by => "d" # delayed by d (can be any expr)
23
+ alg " err = shift_u - delay_u "
24
+
25
+ # Check how delay interacts with integration:
26
+ diff " idu' = delay_u "
27
+ diff " iu' = u"
28
+ delay " diu = iu ", :by => "d"
29
+ alg "iddi_err = idu - diu"
30
+ end
31
+
32
+ constant :new_d => 0.5 # change this to see how varying delay works
33
+ constant :t_new_d => 5.0
34
+ transition A => B do
35
+ guard "t >= t_new_d"
36
+ reset :d => "new_d"
37
+ end
38
+ # Note that the buffered u values are preserved in the transition
39
+ end
40
+
41
+ world = World.new
42
+ world.time_step = 0.1
43
+ c = world.create(C)
44
+
45
+ u, shift_u, delay_u, err, iddi_err = [], [], [], [], []
46
+ gather = proc do
47
+ time = c.t
48
+ u << [time, c.u]
49
+ shift_u << [time, c.shift_u]
50
+ delay_u << [time, c.delay_u]
51
+ err << [time, c.err]
52
+ iddi_err<< [time, c.iddi_err]
53
+ end
54
+
55
+ gather.call
56
+ world.evolve 10 do
57
+ gather.call
58
+ end
59
+
60
+ # The buffer used to store u's history is available:
61
+ if false
62
+ p c.delay_u_buffer_data
63
+ p c.delay_u_buffer_offset
64
+ p c.delay_u_delay
65
+ end
66
+
67
+ require 'sci/plot'
68
+ include Plot::PlotUtils
69
+
70
+ gnuplot do |plot|
71
+ plot.command %{set title "Time delay"}
72
+ plot.command %{set xlabel "time"}
73
+ plot.add u, %{title "u" with lines}
74
+ plot.add shift_u, %{title "shift_u" with lines}
75
+ plot.add delay_u, %{title "delay_u" with lines}
76
+ plot.add err, %{title "err" with lines}
77
+ plot.add iddi_err, %{title "iddi_err" with lines}
78
+ end
79
+
80
+ sleep 1 if /mswin32|mingw32/ =~ RUBY_PLATFORM