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