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