redshift 1.3.21 → 1.3.22
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/RELEASE-NOTES +14 -0
- data/bench/algebraic.rb +61 -0
- data/bench/bench +3 -0
- data/bench/connect.rb +75 -0
- data/bench/linked-flows.rb +71 -0
- data/examples/step-discrete-hook.rb +201 -0
- data/lib/redshift/component.rb +4 -1
- data/lib/redshift/meta.rb +2 -0
- data/lib/redshift/mixins/zeno-debugger.rb +1 -1
- data/lib/redshift/redshift.rb +48 -11
- data/lib/redshift/syntax.rb +9 -1
- data/lib/redshift/target/c/component-gen.rb +185 -91
- data/lib/redshift/target/c/flow-gen.rb +11 -9
- data/lib/redshift/target/c/flow/algebraic.rb +32 -37
- data/lib/redshift/target/c/flow/delay.rb +34 -32
- data/lib/redshift/target/c/flow/derivative.rb +30 -33
- data/lib/redshift/target/c/flow/euler.rb +24 -22
- data/lib/redshift/target/c/flow/expr.rb +40 -43
- data/lib/redshift/target/c/flow/rk4.rb +28 -32
- data/lib/redshift/target/c/library.rb +2 -8
- data/lib/redshift/target/c/world-gen.rb +31 -35
- data/lib/redshift/world.rb +6 -0
- metadata +12 -14
- data/.bnsignore +0 -27
- data/bench/after-flow-cache +0 -66
- data/bench/before-flow-cache +0 -66
data/RELEASE-NOTES
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
redshift 1.3.22
|
2
|
+
|
3
|
+
- handling of env vars is more consistent
|
4
|
+
|
5
|
+
- added World#alg_depth_limit and #input_depth_limit
|
6
|
+
|
7
|
+
- optimized generated flow code to use less instruction cache
|
8
|
+
|
9
|
+
- added algebraic, connect, and linked-flows benchmarks
|
10
|
+
|
11
|
+
- added step-discrete-hook example
|
12
|
+
|
13
|
+
- World#input_depth_limit and #alg_depth_limit accessors
|
14
|
+
|
1
15
|
redshift 1.3.21
|
2
16
|
|
3
17
|
- share flow wrapper classes when equation is same
|
data/bench/algebraic.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Measures performance of redshift's integrator with algebraic flows.
|
2
|
+
#
|
3
|
+
# Formulas are minimal to factor out C library time.
|
4
|
+
|
5
|
+
require 'redshift'
|
6
|
+
|
7
|
+
include RedShift
|
8
|
+
|
9
|
+
module Algebraic
|
10
|
+
class C < Component
|
11
|
+
strictly_continuous :x, :y, :xa, :ya
|
12
|
+
flow do
|
13
|
+
diff " x' = ya "
|
14
|
+
diff " y' = -xa "
|
15
|
+
alg " ya = y "
|
16
|
+
alg " xa = x "
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.make_world n=1
|
21
|
+
w = World.new
|
22
|
+
n.times do
|
23
|
+
w.create(C) do |c|
|
24
|
+
c.x = 0.0
|
25
|
+
c.y = 1.0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
w
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.do_bench
|
32
|
+
[ [ 1, 1_000_000],
|
33
|
+
[ 10, 100_000],
|
34
|
+
[ 100, 10_000],
|
35
|
+
[ 1_000, 1_000],
|
36
|
+
[ 10_000, 100],
|
37
|
+
[ 100_000, 10] ].each do
|
38
|
+
| n_c, n_s|
|
39
|
+
|
40
|
+
w = make_world(n_c)
|
41
|
+
w.run 1 # warm up
|
42
|
+
r = bench do
|
43
|
+
w.run(n_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if __FILE__ == $0
|
52
|
+
|
53
|
+
require 'bench'
|
54
|
+
w = Algebraic.make_world(1000)
|
55
|
+
time = bench do
|
56
|
+
w.run(1000)
|
57
|
+
end
|
58
|
+
p time
|
59
|
+
|
60
|
+
end
|
61
|
+
|
data/bench/bench
CHANGED
@@ -14,12 +14,15 @@ pat = ARGV.pop
|
|
14
14
|
pat = pat ? Regexp.new(pat) : //
|
15
15
|
%w{
|
16
16
|
alg-state
|
17
|
+
algebraic
|
18
|
+
connect
|
17
19
|
continuous
|
18
20
|
discrete
|
19
21
|
euler
|
20
22
|
formula
|
21
23
|
half-strict
|
22
24
|
inertness
|
25
|
+
linked-flows
|
23
26
|
queues
|
24
27
|
}.grep(pat).each do |name|
|
25
28
|
bench_one(name)
|
data/bench/connect.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Measures performance of connected ports.
|
2
|
+
#
|
3
|
+
# Formulas are minimal to factor out C library time.
|
4
|
+
|
5
|
+
require 'redshift'
|
6
|
+
|
7
|
+
include RedShift
|
8
|
+
|
9
|
+
module Connect
|
10
|
+
class Source < Component
|
11
|
+
continuous :t
|
12
|
+
flow do
|
13
|
+
diff " t' = 1 "
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Sink < Component
|
18
|
+
input :t
|
19
|
+
flow do
|
20
|
+
diff " u' = t "
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Connector < Component
|
25
|
+
input :t
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.make_world n=1
|
29
|
+
w = World.new
|
30
|
+
w.input_depth_limit = n
|
31
|
+
source = w.create(Source)
|
32
|
+
prev = source
|
33
|
+
n.times do
|
34
|
+
w.create(Connector) do |c|
|
35
|
+
c.port(:t) << prev.port(:t)
|
36
|
+
prev = c
|
37
|
+
end
|
38
|
+
end
|
39
|
+
sink = w.create(Sink) do |s|
|
40
|
+
s.port(:t) << prev.port(:t)
|
41
|
+
end
|
42
|
+
w
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.do_bench
|
46
|
+
[ #[ 1, 1_000_000],
|
47
|
+
[ 10, 100_000],
|
48
|
+
[ 100, 10_000],
|
49
|
+
[ 1_000, 1_000],
|
50
|
+
[ 10_000, 100],
|
51
|
+
[ 100_000, 10] ].each do
|
52
|
+
| n_c, n_s|
|
53
|
+
|
54
|
+
w = make_world(n_c)
|
55
|
+
w.run 1 # warm up
|
56
|
+
r = bench do
|
57
|
+
w.run(n_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if __FILE__ == $0
|
66
|
+
|
67
|
+
require 'bench'
|
68
|
+
w = Connect.make_world(1000)
|
69
|
+
time = bench do
|
70
|
+
w.run(1000)
|
71
|
+
end
|
72
|
+
p time
|
73
|
+
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Measures performance of redshift as an integrator with dynamically
|
2
|
+
# linked flows.
|
3
|
+
#
|
4
|
+
# Formulas are minimal to factor out C library time.
|
5
|
+
|
6
|
+
require 'redshift'
|
7
|
+
|
8
|
+
include RedShift
|
9
|
+
|
10
|
+
module LinkedFlows
|
11
|
+
class C < Component
|
12
|
+
strictly_continuous :x
|
13
|
+
strict_link :d => 'LinkedFlows::D'
|
14
|
+
flow do
|
15
|
+
diff " x' = d.y "
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class D < Component
|
20
|
+
strictly_continuous :y
|
21
|
+
strict_link :c => C
|
22
|
+
flow do
|
23
|
+
diff " y' = -c.x "
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.make_world n=1
|
28
|
+
w = World.new
|
29
|
+
n.times do
|
30
|
+
w.create(C) do |c|
|
31
|
+
c.d = w.create(D) do |d|
|
32
|
+
d.c = c
|
33
|
+
d.y = 1.0
|
34
|
+
end
|
35
|
+
c.x = 0.0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
w
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.do_bench
|
42
|
+
[ [ 1, 1_000_000],
|
43
|
+
[ 10, 100_000],
|
44
|
+
[ 100, 10_000],
|
45
|
+
[ 1_000, 1_000],
|
46
|
+
[ 10_000, 100],
|
47
|
+
[ 100_000, 10] ].each do
|
48
|
+
| n_c, n_s|
|
49
|
+
|
50
|
+
w = make_world(n_c)
|
51
|
+
w.run 1 # warm up
|
52
|
+
r = bench do
|
53
|
+
w.run(n_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if __FILE__ == $0
|
62
|
+
|
63
|
+
require 'bench'
|
64
|
+
w = LinkedFlows.make_world(500)
|
65
|
+
time = bench do
|
66
|
+
w.run(500)
|
67
|
+
end
|
68
|
+
p time
|
69
|
+
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# Show some of the hooks that can be dynamically compiled into a redshift
|
2
|
+
# simulation. All you have to do is define the following methods in the
|
3
|
+
# World class or your own subclass, and the hooks will be called when
|
4
|
+
# the world runs. Some hooks take arguments, such the component being
|
5
|
+
# handled. In all cases, if you do not define the hook, the code to call it
|
6
|
+
# is not generated, so there is no cost.
|
7
|
+
#
|
8
|
+
# grep -o -P 'hook_\w+' world-gen.rb | sort | uniq
|
9
|
+
#
|
10
|
+
# hook_begin
|
11
|
+
# hook_begin_eval_events
|
12
|
+
# hook_begin_eval_resets
|
13
|
+
# hook_begin_parallel_assign
|
14
|
+
# hook_begin_step
|
15
|
+
# hook_call_action
|
16
|
+
# hook_can_sync
|
17
|
+
# hook_end
|
18
|
+
# hook_end_eval_events
|
19
|
+
# hook_end_eval_resets
|
20
|
+
# hook_end_parallel_assign
|
21
|
+
# hook_end_step
|
22
|
+
# hook_enter_action_phase
|
23
|
+
# hook_enter_eval_phase
|
24
|
+
# hook_enter_guard_phase
|
25
|
+
# hook_enter_post_phase
|
26
|
+
# hook_enter_sync_phase
|
27
|
+
# hook_eval_event
|
28
|
+
# hook_eval_guard
|
29
|
+
# hook_eval_port_connect
|
30
|
+
# hook_eval_reset_constant
|
31
|
+
# hook_eval_reset_continuous
|
32
|
+
# hook_eval_reset_link
|
33
|
+
# hook_export_events
|
34
|
+
# hook_finish_transition
|
35
|
+
# hook_leave_action_phase
|
36
|
+
# hook_leave_eval_phase
|
37
|
+
# hook_leave_guard_phase
|
38
|
+
# hook_leave_post_phase
|
39
|
+
# hook_leave_sync_phase
|
40
|
+
# hook_pat
|
41
|
+
# hook_remove_comp
|
42
|
+
# hook_start_transition
|
43
|
+
# hook_sync_step
|
44
|
+
|
45
|
+
require 'redshift'
|
46
|
+
|
47
|
+
include RedShift
|
48
|
+
|
49
|
+
class Example < Component
|
50
|
+
state :S
|
51
|
+
continuous :x, :y
|
52
|
+
|
53
|
+
flow S do
|
54
|
+
diff "x' = 1"
|
55
|
+
end
|
56
|
+
|
57
|
+
transition Enter => S do
|
58
|
+
reset :x => 0
|
59
|
+
end
|
60
|
+
|
61
|
+
transition S => S do
|
62
|
+
guard "x > 1"
|
63
|
+
reset :x => 0, :y => "y+1"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Can do this in World itself, or in a subclass
|
68
|
+
class ExampleWorld < World
|
69
|
+
def hook_begin
|
70
|
+
puts "world step #{step_count}"
|
71
|
+
puts " hook_begin"
|
72
|
+
end
|
73
|
+
|
74
|
+
def hook_end
|
75
|
+
puts " hook_end #{discrete_step}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def hook_begin_step
|
79
|
+
puts " hook_begin_step #{discrete_step}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def hook_end_step
|
83
|
+
puts " hook_end_step #{discrete_step}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def hook_enter_guard_phase
|
87
|
+
puts " hook_enter_guard_phase #{discrete_step}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def hook_leave_guard_phase
|
91
|
+
puts " hook_leave_guard_phase #{discrete_step}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def hook_enter_action_phase
|
95
|
+
puts " hook_enter_proc_phase #{discrete_step}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def hook_leave_action_phase
|
99
|
+
puts " hook_leave_proc_phase #{discrete_step}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def hook_begin_parallel_assign
|
103
|
+
puts " hook_begin_parallel_assign #{discrete_step}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def hook_end_parallel_assign
|
107
|
+
puts " hook_end_parallel_assign #{discrete_step}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def hook_start_transition(comp, trans, dest)
|
111
|
+
puts " hook_start_transition:"
|
112
|
+
puts " %p" % [[comp, trans, dest]]
|
113
|
+
end
|
114
|
+
|
115
|
+
def hook_finish_transition(comp, trans, dest)
|
116
|
+
puts " hook_finish_transition:"
|
117
|
+
puts " %p" % [[comp, trans.class, dest]]
|
118
|
+
end
|
119
|
+
|
120
|
+
def hook_eval_guard(comp, guard, enabled, trans, dest)
|
121
|
+
puts " hook_eval_guard:"
|
122
|
+
puts " %p" % [[comp, guard, enabled, trans, dest]]
|
123
|
+
end
|
124
|
+
|
125
|
+
def hook_enter_sync_phase
|
126
|
+
puts " hook_enter_sync_phase #{discrete_step}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def hook_leave_sync_phase
|
130
|
+
puts " hook_leave_sync_phase #{discrete_step}"
|
131
|
+
end
|
132
|
+
|
133
|
+
def hook_sync_step curr_S, changed
|
134
|
+
puts " hook_sync_step, changed = #{changed.inspect}:"
|
135
|
+
puts " %p" % [curr_S]
|
136
|
+
end
|
137
|
+
|
138
|
+
def hook_can_sync comp, can_sync
|
139
|
+
puts " hook_can_sync, can_sync = #{can_sync.inspect}:"
|
140
|
+
puts " %p" % [comp]
|
141
|
+
end
|
142
|
+
|
143
|
+
def hook_call_action(comp, pr)
|
144
|
+
puts " hook_call_proc:"
|
145
|
+
puts " %p" % [[comp, pr]]
|
146
|
+
file, lineno = pr.inspect.scan(/@(.*):(\d+)>\Z/)[0] ## better way?
|
147
|
+
lineno = lineno.to_i - 1
|
148
|
+
puts extract_code_block(file, lineno)
|
149
|
+
rescue Errno::ENOENT
|
150
|
+
puts " can't open file #{File.expand_path(file)}"
|
151
|
+
end
|
152
|
+
|
153
|
+
## put in a lib somewhere--as inspect methods for proc subclasses?
|
154
|
+
def extract_code_block(file, lineno)
|
155
|
+
result = ""
|
156
|
+
File.open(file) do |f|
|
157
|
+
lineno.times {f.gets}
|
158
|
+
line = f.gets
|
159
|
+
result << line
|
160
|
+
|
161
|
+
first_indent = (line =~ /\S/)
|
162
|
+
loop do
|
163
|
+
str = f.gets
|
164
|
+
indent = (str =~ /\S/)
|
165
|
+
if indent > first_indent or
|
166
|
+
(indent == first_indent and str =~ /\A\s*(\}|end)/)
|
167
|
+
result << str
|
168
|
+
end
|
169
|
+
break if indent <= first_indent
|
170
|
+
end
|
171
|
+
end
|
172
|
+
result
|
173
|
+
end
|
174
|
+
|
175
|
+
def hook_eval_event(comp, event, event_value)
|
176
|
+
puts " hook_export_event:"
|
177
|
+
puts " %p" % [[comp, event, event_value]]
|
178
|
+
end
|
179
|
+
|
180
|
+
def hook_eval_reset_constant(comp, var, val)
|
181
|
+
puts " reset constant #{var} to #{val} in #{comp}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def hook_eval_reset_continuous(comp, var, val)
|
185
|
+
puts " reset continuous #{var.name} to #{val} in #{comp}"
|
186
|
+
end
|
187
|
+
|
188
|
+
def hook_eval_reset_link(comp, var, val)
|
189
|
+
puts " reset link #{var} to #{val} in #{comp}"
|
190
|
+
end
|
191
|
+
|
192
|
+
def hook_not_a_known_hook
|
193
|
+
# This will generate a warning, since it is not a known hook.
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
world = ExampleWorld.new
|
198
|
+
comp = world.create(Example)
|
199
|
+
|
200
|
+
world.evolve 1.2
|
201
|
+
p world
|