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.
- data/.gitignore +8 -0
- data/README +5 -0
- data/RELEASE-NOTES +455 -0
- data/TODO +431 -0
- data/bench/alg-state.rb +61 -0
- data/bench/bench +26 -0
- data/bench/bench.rb +10 -0
- data/bench/continuous.rb +76 -0
- data/bench/diff-bench +86 -0
- data/bench/discrete.rb +101 -0
- data/bench/euler.rb +50 -0
- data/bench/formula.rb +78 -0
- data/bench/half-strict.rb +103 -0
- data/bench/inertness.rb +116 -0
- data/bench/queue.rb +92 -0
- data/bench/run +66 -0
- data/bench/simple.rb +74 -0
- data/bench/strictness.rb +86 -0
- data/examples/ball-tkar.rb +72 -0
- data/examples/ball.rb +123 -0
- data/examples/collide.rb +70 -0
- data/examples/connect-parallel.rb +48 -0
- data/examples/connect.rb +109 -0
- data/examples/constants.rb +27 -0
- data/examples/delay.rb +80 -0
- data/examples/derivative.rb +77 -0
- data/examples/euler.rb +46 -0
- data/examples/external-lib.rb +33 -0
- data/examples/guard-debugger.rb +77 -0
- data/examples/lotka-volterra.rb +33 -0
- data/examples/persist-ball.rb +68 -0
- data/examples/pid.rb +87 -0
- data/examples/ports.rb +60 -0
- data/examples/queue.rb +56 -0
- data/examples/queue2.rb +98 -0
- data/examples/reset-with-event-val.rb +28 -0
- data/examples/scheduler.rb +104 -0
- data/examples/set-dest.rb +23 -0
- data/examples/simulink/README +1 -0
- data/examples/simulink/delay.mdl +827 -0
- data/examples/simulink/derivative.mdl +655 -0
- data/examples/step-discrete-profiler.rb +103 -0
- data/examples/subsystem.rb +109 -0
- data/examples/sync-deadlock.rb +32 -0
- data/examples/sync-queue.rb +91 -0
- data/examples/sync-retry.rb +20 -0
- data/examples/sync.rb +51 -0
- data/examples/thermostat.rb +53 -0
- data/examples/zeno.rb +53 -0
- data/lib/accessible-index.rb +47 -0
- data/lib/redshift.rb +1 -0
- data/lib/redshift/component.rb +412 -0
- data/lib/redshift/meta.rb +183 -0
- data/lib/redshift/mixins/zeno-debugger.rb +69 -0
- data/lib/redshift/port.rb +57 -0
- data/lib/redshift/queue.rb +104 -0
- data/lib/redshift/redshift.rb +111 -0
- data/lib/redshift/state.rb +31 -0
- data/lib/redshift/syntax.rb +558 -0
- data/lib/redshift/target/c.rb +37 -0
- data/lib/redshift/target/c/component-gen.rb +1303 -0
- data/lib/redshift/target/c/flow-gen.rb +325 -0
- data/lib/redshift/target/c/flow/algebraic.rb +85 -0
- data/lib/redshift/target/c/flow/buffer.rb +74 -0
- data/lib/redshift/target/c/flow/delay.rb +203 -0
- data/lib/redshift/target/c/flow/derivative.rb +101 -0
- data/lib/redshift/target/c/flow/euler.rb +67 -0
- data/lib/redshift/target/c/flow/expr.rb +113 -0
- data/lib/redshift/target/c/flow/rk4.rb +80 -0
- data/lib/redshift/target/c/library.rb +85 -0
- data/lib/redshift/target/c/world-gen.rb +1370 -0
- data/lib/redshift/target/spec.rb +34 -0
- data/lib/redshift/world.rb +300 -0
- data/rakefile +37 -0
- data/test/test.rb +52 -0
- data/test/test_buffer.rb +58 -0
- data/test/test_connect.rb +242 -0
- data/test/test_connect_parallel.rb +47 -0
- data/test/test_connect_strict.rb +135 -0
- data/test/test_constant.rb +74 -0
- data/test/test_delay.rb +145 -0
- data/test/test_derivative.rb +48 -0
- data/test/test_discrete.rb +592 -0
- data/test/test_discrete_isolated.rb +92 -0
- data/test/test_exit.rb +59 -0
- data/test/test_flow.rb +200 -0
- data/test/test_flow_link.rb +288 -0
- data/test/test_flow_sub.rb +100 -0
- data/test/test_flow_trans.rb +292 -0
- data/test/test_inherit.rb +127 -0
- data/test/test_inherit_event.rb +74 -0
- data/test/test_inherit_flow.rb +139 -0
- data/test/test_inherit_link.rb +65 -0
- data/test/test_inherit_setup.rb +56 -0
- data/test/test_inherit_state.rb +66 -0
- data/test/test_inherit_transition.rb +168 -0
- data/test/test_numerics.rb +34 -0
- data/test/test_queue.rb +90 -0
- data/test/test_queue_alone.rb +115 -0
- data/test/test_reset.rb +209 -0
- data/test/test_setup.rb +119 -0
- data/test/test_strict_continuity.rb +410 -0
- data/test/test_strict_reset_error.rb +30 -0
- data/test/test_strictness_error.rb +32 -0
- data/test/test_sync.rb +185 -0
- data/test/test_world.rb +328 -0
- 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
|
data/examples/collide.rb
ADDED
@@ -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
|
data/examples/connect.rb
ADDED
@@ -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
|