redshift 1.3.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
|
3
|
+
include RedShift
|
4
|
+
|
5
|
+
# This example is a simple profiler for measuring time in guard phases and
|
6
|
+
# proc/reset phases. Finer grained measurements can be done using other hooks.
|
7
|
+
|
8
|
+
# See examples/step-discrete-hook.rb for more examples of hook methods.
|
9
|
+
|
10
|
+
# Note that ruby's profiler.rb can be used to profile the ruby methods, it just
|
11
|
+
# doesn't give any results _within_ World#step_discrete.
|
12
|
+
|
13
|
+
n_components = 1000
|
14
|
+
n_seconds = 1000
|
15
|
+
|
16
|
+
class ProfilingExample < Component
|
17
|
+
continuous :x
|
18
|
+
|
19
|
+
def x_less_than_0
|
20
|
+
x < 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_x
|
24
|
+
self.x = 2
|
25
|
+
end
|
26
|
+
|
27
|
+
transition do
|
28
|
+
# guard :x_less_than_0 # 36.1 ms
|
29
|
+
# guard {x < 0} # 61.0 ms
|
30
|
+
guard "x < 0" # 3.2 ms
|
31
|
+
|
32
|
+
# action :reset_x # 1.8 ms
|
33
|
+
# action {self.x = 2} # 4.5 ms
|
34
|
+
|
35
|
+
reset :x => 2 # 1.3 ms
|
36
|
+
end
|
37
|
+
flow { diff "x' = -1" }
|
38
|
+
end
|
39
|
+
|
40
|
+
class ProfilingWorld < World
|
41
|
+
attr_accessor :guard_time, :proc_time, :reset_time
|
42
|
+
|
43
|
+
def cpu_time
|
44
|
+
#Time.now
|
45
|
+
Process.times.utime # for short runs, this doesn't have enough granularity
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(*args)
|
49
|
+
super
|
50
|
+
@guard_time = 0
|
51
|
+
@proc_time = 0
|
52
|
+
@reset_time = 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def hook_enter_guard_phase
|
56
|
+
@guard_start = cpu_time
|
57
|
+
end
|
58
|
+
|
59
|
+
def hook_leave_guard_phase
|
60
|
+
t = cpu_time
|
61
|
+
@guard_time += t - @guard_start
|
62
|
+
end
|
63
|
+
|
64
|
+
def hook_enter_action_phase
|
65
|
+
@proc_start = cpu_time
|
66
|
+
end
|
67
|
+
|
68
|
+
def hook_leave_action_phase
|
69
|
+
t = cpu_time
|
70
|
+
@proc_time += t - @proc_start
|
71
|
+
end
|
72
|
+
|
73
|
+
def hook_begin_eval_resets(comp)
|
74
|
+
@reset_start = cpu_time
|
75
|
+
end
|
76
|
+
|
77
|
+
def hook_end_eval_resets(comp)
|
78
|
+
t = cpu_time
|
79
|
+
@reset_time += cpu_time - @reset_start
|
80
|
+
end
|
81
|
+
|
82
|
+
def hook_begin_parallel_assign
|
83
|
+
@reset_start = cpu_time
|
84
|
+
end
|
85
|
+
|
86
|
+
def hook_end_parallel_assign
|
87
|
+
t = cpu_time
|
88
|
+
@reset_time += cpu_time - @reset_start
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
w = ProfilingWorld.new
|
93
|
+
n_components.times do
|
94
|
+
w.create(ProfilingExample)
|
95
|
+
end
|
96
|
+
|
97
|
+
w.evolve n_seconds
|
98
|
+
|
99
|
+
x = n_components * n_seconds
|
100
|
+
puts "Times are averages per component, per second of simulation."
|
101
|
+
printf "Guard time: %10.3f ms\n", (w.guard_time/x)*1_000_000
|
102
|
+
printf "Proc time: %10.3f ms\n", (w.proc_time/x)*1_000_000
|
103
|
+
printf "Reset time: %10.3f ms\n", (w.reset_time/x)*1_000_000
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Shows how to use subsystems in redshift like in simulink.
|
2
|
+
|
3
|
+
require 'redshift'
|
4
|
+
include RedShift
|
5
|
+
|
6
|
+
# First, let's define some "blocks" to put inside the subsystem.
|
7
|
+
|
8
|
+
# A generic integrator block.
|
9
|
+
class Integrator < Component
|
10
|
+
input :dx_dt
|
11
|
+
flow { diff " x' = dx_dt " }
|
12
|
+
end
|
13
|
+
|
14
|
+
# A specific algebraic equation.
|
15
|
+
class Alg < Component
|
16
|
+
input :in
|
17
|
+
flow { alg " out = pow(in, 2) - 6 * in - 10 " }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Now, the subsystem container:
|
21
|
+
class Subsystem < Component
|
22
|
+
input :in
|
23
|
+
input :out # declared as input because it is input from subcomponent
|
24
|
+
end
|
25
|
+
|
26
|
+
# We need a data source, so let's just use a linear input:
|
27
|
+
class Timer < Component
|
28
|
+
flow { diff " time' = 1 " }
|
29
|
+
end
|
30
|
+
|
31
|
+
# A component to check results by solving analytically:
|
32
|
+
class Checker < Component
|
33
|
+
input :in
|
34
|
+
constant :c # for integration constant
|
35
|
+
flow { alg " true_result = pow(in, 3)/3 - 3 * pow(in, 2) - 10 * in + c " }
|
36
|
+
# symbolically integrated
|
37
|
+
end
|
38
|
+
|
39
|
+
# In practice, the subsystem, with the sub-blocks and connections created below,
|
40
|
+
# could be coded in much simpler way in redshift, by combining the differential
|
41
|
+
# and algebraic equations and even the timer. Sometimes, however, input ports
|
42
|
+
# are a convenient way to reuse functionality. They have the advantage over link
|
43
|
+
# variables of not needing to know the class of the source component, so it's
|
44
|
+
# possible to have independent libraries of them.
|
45
|
+
class MuchSimpler < Component
|
46
|
+
flow {
|
47
|
+
diff " t' = 1 "
|
48
|
+
diff " x' = pow(t, 2) - 6 * t - 10 "
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Enough definitions. Now, before we can run anything, we need to construct
|
53
|
+
# instances of these classes in a world.
|
54
|
+
world = World.new
|
55
|
+
|
56
|
+
# Create the subsystem and its sub-blocks.
|
57
|
+
subsystem = world.create Subsystem do |sys|
|
58
|
+
# create the sub-blocks (note that the vars are local, so the
|
59
|
+
# sub-blocks are encapsulated at the syntactic level):
|
60
|
+
int = world.create(Integrator)
|
61
|
+
alg = world.create(Alg)
|
62
|
+
|
63
|
+
# specify an initial condition
|
64
|
+
int.x = 200
|
65
|
+
|
66
|
+
# wire everything together
|
67
|
+
sys.port(:in) >> alg.port(:in)
|
68
|
+
alg.port(:out) >> int.port(:dx_dt)
|
69
|
+
int.port(:x) >> sys.port(:out)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create the data source and connect to the subsystem:
|
73
|
+
timer = world.create(Timer)
|
74
|
+
timer.time = 0
|
75
|
+
timer.port(:time) >> subsystem.port(:in)
|
76
|
+
|
77
|
+
# Create the checker and connect to timer:
|
78
|
+
checker = world.create(Checker)
|
79
|
+
checker.port(:in) << timer.port(:time)
|
80
|
+
checker.c = subsystem.out # must agree on initial condition
|
81
|
+
|
82
|
+
# Compare with a single-component implementation.
|
83
|
+
much_simpler = world.create(MuchSimpler)
|
84
|
+
much_simpler.x = subsystem.out # must agree in initial condition
|
85
|
+
|
86
|
+
result = []; error = []; error2 = []
|
87
|
+
gather = proc do
|
88
|
+
result << [timer.time, subsystem.out]
|
89
|
+
error << [timer.time, subsystem.out - checker.true_result]
|
90
|
+
error2 << [timer.time, subsystem.out - much_simpler.x]
|
91
|
+
end
|
92
|
+
|
93
|
+
gather.call
|
94
|
+
world.evolve 20 do
|
95
|
+
gather.call
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'sci/plot'
|
99
|
+
include Plot::PlotUtils
|
100
|
+
|
101
|
+
gnuplot do |plot|
|
102
|
+
plot.command %{set title "Subsystem"}
|
103
|
+
plot.command %{set xlabel "time"}
|
104
|
+
plot.add result, %{title "subsystem f(t)" with lines}
|
105
|
+
plot.add error, %{title "error in f(t)" with lines}
|
106
|
+
plot.add error2, %{title "error2 in f(t)" with lines}
|
107
|
+
end
|
108
|
+
|
109
|
+
sleep 1 if /mswin32|mingw32/ =~ RUBY_PLATFORM
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Shows how not all combinations of transitions are
|
2
|
+
# checked, because to do so would invite indeterminate
|
3
|
+
# behavior, and require exponential cpu.
|
4
|
+
#
|
5
|
+
# The first transition in c1 could sync with the second
|
6
|
+
# in c2, or the second in c2 with the first in c1. The
|
7
|
+
# problem is that neither of these pairs can be chosen in a
|
8
|
+
# natural way, so neither gets chosen.
|
9
|
+
|
10
|
+
require 'redshift'
|
11
|
+
|
12
|
+
class C < RedShift::Component
|
13
|
+
link :other
|
14
|
+
transition Enter => Exit do
|
15
|
+
sync :other => :e
|
16
|
+
event :f
|
17
|
+
end
|
18
|
+
transition Enter => Exit do
|
19
|
+
sync :other => :f
|
20
|
+
event :e
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
w = RedShift::World.new
|
25
|
+
c1 = w.create C
|
26
|
+
c2 = w.create C
|
27
|
+
c1.other = c2
|
28
|
+
c2.other = c1
|
29
|
+
|
30
|
+
w.run 1
|
31
|
+
p c1
|
32
|
+
p c2
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# A more complex example of the sync construct:
|
2
|
+
# clients make a request to a server (using a queue)
|
3
|
+
# the server syncs with the head of the queue, and
|
4
|
+
# provides the requested information.
|
5
|
+
#
|
6
|
+
# This shows the use of bi-directional sync, as well as
|
7
|
+
# using a guard to ensure that only the proper client can
|
8
|
+
# sync.
|
9
|
+
|
10
|
+
require 'redshift'
|
11
|
+
|
12
|
+
class Client < RedShift::Component
|
13
|
+
state :Working, :Waiting
|
14
|
+
start Working
|
15
|
+
|
16
|
+
link :server
|
17
|
+
|
18
|
+
flow Working do
|
19
|
+
diff " time_left' = -1 "
|
20
|
+
end
|
21
|
+
|
22
|
+
transition Working => Waiting do
|
23
|
+
guard "time_left <= 0"
|
24
|
+
action do
|
25
|
+
if server
|
26
|
+
puts "%4.2f: #{self} requesting service" % world.clock
|
27
|
+
server.clients << self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
transition Waiting => Working do
|
33
|
+
guard {server.client == self} # N.b.!
|
34
|
+
sync :server => :serve
|
35
|
+
event :accept
|
36
|
+
post do
|
37
|
+
self.time_left = server.serve # can do this as reset
|
38
|
+
puts "%4.2f: #{self} receiving service, time_left=#{time_left}" %
|
39
|
+
world.clock
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Server < RedShift::Component
|
45
|
+
state :Waiting, :Serving
|
46
|
+
start Waiting
|
47
|
+
|
48
|
+
# the client currently being served
|
49
|
+
link :client
|
50
|
+
|
51
|
+
queue :clients
|
52
|
+
|
53
|
+
transition Waiting => Serving do
|
54
|
+
wait :clients
|
55
|
+
action do
|
56
|
+
cs = clients.pop
|
57
|
+
case cs
|
58
|
+
when RedShift::SimultaneousQueueEntries
|
59
|
+
self.client = cs.shift # NOT deterministic--should rank clients
|
60
|
+
clients.unpop cs # put the rest back on the head of the queue
|
61
|
+
when RedShift::Component
|
62
|
+
self.client = cs
|
63
|
+
else
|
64
|
+
raise "Error!"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
transition Serving => Waiting do
|
70
|
+
guard {client.server == self} # N.b.!
|
71
|
+
sync :client => :accept
|
72
|
+
event :serve => proc {1.0 + rand()}
|
73
|
+
action do
|
74
|
+
puts "%4.2f: #{self} serving #{client}" % world.clock
|
75
|
+
self.client = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
srand 54321
|
81
|
+
|
82
|
+
w = RedShift::World.new
|
83
|
+
s = w.create(Server)
|
84
|
+
10.times do
|
85
|
+
w.create Client do |c|
|
86
|
+
c.server = s
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
w.evolve 10.0
|
91
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
|
3
|
+
class Sync < RedShift::Component
|
4
|
+
link :next
|
5
|
+
setup {self.next = nil} # or a comp that doesn't emit :no_such_event
|
6
|
+
transition Enter => Exit do
|
7
|
+
# this transition is checked first, but can't sync
|
8
|
+
sync :next => :no_such_event
|
9
|
+
end
|
10
|
+
transition Enter => Exit do
|
11
|
+
# so this trans is checked second
|
12
|
+
action do
|
13
|
+
puts "else (if no sync), take this transition"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
w = RedShift::World.new
|
19
|
+
w.create(Sync)
|
20
|
+
w.run 1
|
data/examples/sync.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Example of the sync construct: synchronizing transitions
|
2
|
+
# in two or more components.
|
3
|
+
#
|
4
|
+
# This is an example of unidirectional sync: component a takes
|
5
|
+
# its transition without requiring sync (it has a guard, but the
|
6
|
+
# guard only looks at the current state of the world, not at who
|
7
|
+
# else might emit some event), while b takes its transition
|
8
|
+
# _only_ if it can sync with a on the event e.
|
9
|
+
#
|
10
|
+
# See sync-queue.rb for a bi-directional example.
|
11
|
+
|
12
|
+
require 'redshift'
|
13
|
+
|
14
|
+
class A < RedShift::Component
|
15
|
+
state :A1, :A2
|
16
|
+
start A1
|
17
|
+
|
18
|
+
flow A1 do
|
19
|
+
diff " t' = 1 "
|
20
|
+
end
|
21
|
+
|
22
|
+
transition A1 => A2 do
|
23
|
+
guard "t > 0.5"
|
24
|
+
event :e
|
25
|
+
action do
|
26
|
+
puts "#{self} taking transition #{self.state} => #{self.dest}" +
|
27
|
+
" at time #{world.clock}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class B < RedShift::Component
|
33
|
+
state :B1, :B2
|
34
|
+
start B1
|
35
|
+
|
36
|
+
link :a
|
37
|
+
|
38
|
+
transition B1 => B2 do
|
39
|
+
sync :a => :e # remove this line to see the difference
|
40
|
+
action do
|
41
|
+
puts "#{self} taking transition #{self.state} => #{self.dest}" +
|
42
|
+
" at time #{world.clock}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
w = RedShift::World.new
|
48
|
+
b = w.create(B)
|
49
|
+
b.a = w.create(A)
|
50
|
+
|
51
|
+
w.evolve 1.0
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
|
3
|
+
class Thermostat < RedShift::Component
|
4
|
+
continuous :temp
|
5
|
+
|
6
|
+
state :Heat, :Off
|
7
|
+
|
8
|
+
flow Heat do
|
9
|
+
diff "temp' = (68 - temp)/3"
|
10
|
+
end
|
11
|
+
|
12
|
+
flow Off do
|
13
|
+
diff "temp' = (45 - temp)/10"
|
14
|
+
end
|
15
|
+
|
16
|
+
transition Heat => Off do
|
17
|
+
guard "temp > 68 - 0.1"
|
18
|
+
end
|
19
|
+
|
20
|
+
transition Off => Heat do
|
21
|
+
guard "temp < 66"
|
22
|
+
end
|
23
|
+
|
24
|
+
setup do
|
25
|
+
start Off
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
world = RedShift::World.new do |w|
|
30
|
+
w.time_step = 0.1
|
31
|
+
end
|
32
|
+
|
33
|
+
thermostat = world.create Thermostat do |t|
|
34
|
+
t.temp = 55
|
35
|
+
end
|
36
|
+
|
37
|
+
temp_history = []
|
38
|
+
world.evolve 30 do |w|
|
39
|
+
point = [w.clock, thermostat.temp]
|
40
|
+
#puts point.join(" ")
|
41
|
+
temp_history << point
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'sci/plot'
|
45
|
+
include Plot::PlotUtils
|
46
|
+
|
47
|
+
gnuplot do |plot|
|
48
|
+
plot.command %{set title "Thermostat control"}
|
49
|
+
plot.command %{set xlabel "time"}
|
50
|
+
plot.add temp_history, %{title "temperature" with lines}
|
51
|
+
end
|
52
|
+
|
53
|
+
sleep 1 if /mswin32|mingw32/ =~ RUBY_PLATFORM
|