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,34 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Note: This is not a complete program specification, since Procs and methods
|
5
|
+
# cannot be dumped. It is useful for inspecting the vars, flows, and also
|
6
|
+
# transitions, to the extent that these are defined without Procs.
|
7
|
+
#
|
8
|
+
|
9
|
+
class World
|
10
|
+
def World.new(*args)
|
11
|
+
exit ## could just call the code below and then exit...
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
END {
|
16
|
+
## this duplicates some code from Library
|
17
|
+
cc = []
|
18
|
+
ObjectSpace.each_object(Class) do |cl|
|
19
|
+
cc << cl if cl < Component # Component is abstract
|
20
|
+
end
|
21
|
+
cc = cc.sort_by {|c| c.ancestors.reverse!.map!{|d|d.to_s}}
|
22
|
+
|
23
|
+
cc.each do |c|
|
24
|
+
puts "=" * 60
|
25
|
+
puts "=" * 20 + "class #{c.name}"
|
26
|
+
c.instance_eval do
|
27
|
+
instance_variables.each do |var|
|
28
|
+
val = eval(var)
|
29
|
+
puts "#{var}:"
|
30
|
+
pp val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
module RedShift
|
4
|
+
|
5
|
+
class ZenoError < RuntimeError; end
|
6
|
+
|
7
|
+
# Set zeno_level to this to turn off zeno checking.
|
8
|
+
ZENO_UNLIMITED = -1
|
9
|
+
|
10
|
+
class World
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
{
|
14
|
+
:Debugger => "debugger",
|
15
|
+
:ZenoDebugger => "zeno-debugger"
|
16
|
+
}.each {|m,f| autoload(m, "redshift/mixins/#{f}")}
|
17
|
+
|
18
|
+
class ComponentList
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
attr_reader :summands
|
22
|
+
|
23
|
+
def initialize(*summands)
|
24
|
+
@summands = summands
|
25
|
+
end
|
26
|
+
|
27
|
+
def each(&block)
|
28
|
+
summands.each { |enum| enum.each(&block) }
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
summands.inject(0) { |sum,enum| sum + enum.size }
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
to_a.inspect # looks better in irb, but slow
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](idx)
|
41
|
+
warn "World#[] is deprecated"
|
42
|
+
to_a[idx] ## very inefficient
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear
|
46
|
+
summands.each {|list| list.clear}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@subclasses = []
|
51
|
+
|
52
|
+
class << self
|
53
|
+
# World is not included in subclasses. This returns nil when called on subs.
|
54
|
+
attr_reader :subclasses
|
55
|
+
|
56
|
+
def inherited(sub)
|
57
|
+
World.subclasses << sub
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.new(*args, &block)
|
62
|
+
RedShift.require_target # redefines World.new
|
63
|
+
new(*args, &block) # which is what this line calls
|
64
|
+
end
|
65
|
+
|
66
|
+
attr_reader :components
|
67
|
+
|
68
|
+
def default_options
|
69
|
+
{
|
70
|
+
:name => "#{self.class}_#{@@count}",
|
71
|
+
:time_unit => "second",
|
72
|
+
:time_step => 0.1,
|
73
|
+
:zeno_limit => 100,
|
74
|
+
:clock_start => 0.0,
|
75
|
+
:clock_finish => Infinity,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
@@count = 0
|
80
|
+
|
81
|
+
attr_accessor :name, :time_unit
|
82
|
+
|
83
|
+
def started?; @started; end
|
84
|
+
def running?; @running; end
|
85
|
+
|
86
|
+
# Can override the options using assignments in the block.
|
87
|
+
# Note that clock_start should not be assigned after the block.
|
88
|
+
def initialize # :yields: world
|
89
|
+
self.curr_A = []; self.curr_P = []; self.curr_CR = []
|
90
|
+
self.curr_S = []; self.next_S = []; self.curr_T = []
|
91
|
+
self.active_E = []; self.prev_active_E = []
|
92
|
+
self.awake = []; self.prev_awake = []
|
93
|
+
self.strict_sleep = []; self.inert = []
|
94
|
+
self.diff_list = []; self.queue_sleep = {}
|
95
|
+
@components = ComponentList.new \
|
96
|
+
awake, prev_awake, curr_T, strict_sleep, inert # _not_ diff_list
|
97
|
+
|
98
|
+
options = default_options
|
99
|
+
|
100
|
+
@name = options[:name]
|
101
|
+
@time_unit = options[:time_unit]
|
102
|
+
self.time_step = options[:time_step]
|
103
|
+
self.zeno_limit = options[:zeno_limit]
|
104
|
+
self.clock_start = options[:clock_start]
|
105
|
+
self.clock_finish = options[:clock_finish]
|
106
|
+
|
107
|
+
self.step_count = 0
|
108
|
+
|
109
|
+
@@count += 1
|
110
|
+
|
111
|
+
do_defaults
|
112
|
+
yield self if block_given?
|
113
|
+
|
114
|
+
self.base_step_count = 0
|
115
|
+
self.base_clock = clock_start
|
116
|
+
end
|
117
|
+
|
118
|
+
def do_defaults
|
119
|
+
self.class.do_defaults self
|
120
|
+
end
|
121
|
+
private :do_defaults
|
122
|
+
|
123
|
+
def self.do_defaults instance
|
124
|
+
superclass.do_defaults instance if superclass.respond_to? :do_defaults
|
125
|
+
if @defaults_procs
|
126
|
+
@defaults_procs.each do |pr|
|
127
|
+
instance.instance_eval(&pr)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def do_setup
|
133
|
+
self.class.do_setup self
|
134
|
+
if @setup_procs
|
135
|
+
@setup_procs.each do |pr|
|
136
|
+
instance_eval(&pr)
|
137
|
+
end
|
138
|
+
@setup_procs = nil # so we can serialize
|
139
|
+
end
|
140
|
+
end
|
141
|
+
private :do_setup
|
142
|
+
|
143
|
+
def self.do_setup instance
|
144
|
+
superclass.do_setup instance if superclass.respond_to? :do_setup
|
145
|
+
if @setup_procs
|
146
|
+
@setup_procs.each do |pr|
|
147
|
+
instance.instance_eval(&pr)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def create(component_class)
|
153
|
+
component =
|
154
|
+
if block_given?
|
155
|
+
component_class.new(self) {|c| yield c}
|
156
|
+
else
|
157
|
+
component_class.new(self)
|
158
|
+
end
|
159
|
+
|
160
|
+
unless component.is_a? Component # Component is abstract
|
161
|
+
raise TypeError, "#{component.class} is not a Component class"
|
162
|
+
end
|
163
|
+
|
164
|
+
awake << component if component.world == self
|
165
|
+
component
|
166
|
+
end
|
167
|
+
|
168
|
+
## is this a good idea? tests? #add ?
|
169
|
+
def remove c
|
170
|
+
if components.summands.any? {|list| list.delete(c)}
|
171
|
+
raise unless c.world == self
|
172
|
+
c.__set__world(nil)
|
173
|
+
else
|
174
|
+
raise "Tried to remove #{c} from #{self}, but its world is #{c.world}."
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# All evolution methods untimately call step, which can be overridden.
|
179
|
+
# After each step, yields to block. It is the block's responsibility to
|
180
|
+
# step_discrete at this point after changing any vars.
|
181
|
+
def step(steps = 1)
|
182
|
+
@running = true
|
183
|
+
|
184
|
+
unless @started
|
185
|
+
do_setup
|
186
|
+
@started = true
|
187
|
+
end
|
188
|
+
|
189
|
+
step_discrete
|
190
|
+
steps.to_i.times do
|
191
|
+
break if clock > clock_finish
|
192
|
+
self.step_count += 1
|
193
|
+
step_continuous
|
194
|
+
step_discrete
|
195
|
+
@running = false
|
196
|
+
yield self if block_given?
|
197
|
+
@running = true
|
198
|
+
end
|
199
|
+
|
200
|
+
self
|
201
|
+
|
202
|
+
ensure
|
203
|
+
@running = false
|
204
|
+
## how to continue stepping after an exception?
|
205
|
+
end
|
206
|
+
|
207
|
+
def run(*args, &block)
|
208
|
+
## warn "World#run is deprecated -- use #step or #evolve"
|
209
|
+
step(*args, &block)
|
210
|
+
end
|
211
|
+
|
212
|
+
def evolve(time = 1.0, &block)
|
213
|
+
run((time.to_f/time_step).round, &block)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Default implementation is to raise RedShift::ZenoError.
|
217
|
+
def step_zeno
|
218
|
+
raise RedShift::ZenoError, "Exceeded zeno limit of #{zeno_limit}."
|
219
|
+
end
|
220
|
+
|
221
|
+
## is this a good idea? tests?
|
222
|
+
def garbage_collect
|
223
|
+
self.components.clear
|
224
|
+
GC.start
|
225
|
+
ObjectSpace.each_object(Component) do |c|
|
226
|
+
components << c if c.world == self
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def each(&b)
|
231
|
+
@components.each(&b)
|
232
|
+
end
|
233
|
+
|
234
|
+
def size
|
235
|
+
@components.size
|
236
|
+
end
|
237
|
+
|
238
|
+
def include? component
|
239
|
+
component.world == self
|
240
|
+
end
|
241
|
+
alias member? include?
|
242
|
+
|
243
|
+
def inspect
|
244
|
+
if @started
|
245
|
+
digits = -Math.log10(time_step).floor
|
246
|
+
digits = 0 if digits < 0
|
247
|
+
|
248
|
+
data = []
|
249
|
+
data << "%d step%s" % [step_count, ("s" if step_count != 1)]
|
250
|
+
data << "%.#{digits}f #{@time_unit}%s" % [clock, ("s" if clock != 1)]
|
251
|
+
data << "%d component%s" % [size, ("s" if size != 1)]
|
252
|
+
data << "discrete step = #{discrete_step}" ## only if in step_discrete?
|
253
|
+
else
|
254
|
+
data = ["not started. Do 'run 0' to setup, or 'run n' to run."]
|
255
|
+
end
|
256
|
+
|
257
|
+
str = [name, data.join("; ")].join(": ")
|
258
|
+
"<#{str}>"
|
259
|
+
end
|
260
|
+
|
261
|
+
def save filename = @name
|
262
|
+
raise "Can't save world during its run method." if @running
|
263
|
+
File.delete(filename) rescue SystemCallError
|
264
|
+
store = PStore.new filename
|
265
|
+
store.transaction do
|
266
|
+
store['world'] = self
|
267
|
+
yield store if block_given?
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def World.open filename
|
272
|
+
RedShift.require_target
|
273
|
+
commit
|
274
|
+
|
275
|
+
world = nil
|
276
|
+
store = PStore.new filename
|
277
|
+
store.transaction do
|
278
|
+
if store.root? 'world'
|
279
|
+
world = store['world']
|
280
|
+
yield store if block_given?
|
281
|
+
end
|
282
|
+
end
|
283
|
+
world
|
284
|
+
end
|
285
|
+
|
286
|
+
# This is for when Component#world is nonpersistent
|
287
|
+
# def __restore__world__refs
|
288
|
+
# ## need this list because @components is not restored yet
|
289
|
+
# [awake, prev_awake, curr_T, strict_sleep, inert].each do |list|
|
290
|
+
# list.each do |c|
|
291
|
+
# c.send :__set__world, self
|
292
|
+
# end
|
293
|
+
# end
|
294
|
+
# ## could be in C
|
295
|
+
# end
|
296
|
+
# private :__restore__world__refs
|
297
|
+
|
298
|
+
end # class World
|
299
|
+
|
300
|
+
end # module RedShift
|
data/rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bones'
|
3
|
+
rescue LoadError
|
4
|
+
abort '### please install the bones gem ###'
|
5
|
+
end
|
6
|
+
|
7
|
+
ensure_in_path 'lib'
|
8
|
+
require 'redshift'
|
9
|
+
|
10
|
+
Bones {
|
11
|
+
name 'redshift'
|
12
|
+
authors 'Joel VanderWerf'
|
13
|
+
email 'vjoel@users.sourceforge.net'
|
14
|
+
url 'http://rubyforge.org/projects/redshift'
|
15
|
+
rubyforge.name 'redshift'
|
16
|
+
version RedShift::VERSION
|
17
|
+
readme_file 'README'
|
18
|
+
|
19
|
+
summary 'Simulation of hybrid automata'
|
20
|
+
description <<END
|
21
|
+
A framework for simulation of networks of hybrid automata, similar to SHIFT and Lambda-SHIFT. Includes ruby-based DSL for defining simulation components, and ruby/C code generation and runtime.
|
22
|
+
END
|
23
|
+
|
24
|
+
depend_on 'cgen'
|
25
|
+
|
26
|
+
history_file 'RELEASE-NOTES'
|
27
|
+
changes File.read(history_file)[/^\w.*?(?=^\w)/m]
|
28
|
+
(rdoc.dir File.readlink(rdoc.dir)) rescue nil
|
29
|
+
|
30
|
+
spec.opts << '--color'
|
31
|
+
test.files = Dir["test/test-*.rb"]
|
32
|
+
}
|
33
|
+
|
34
|
+
task :release => ["rubyforge:release", "rubyforge:doc_release"]
|
35
|
+
|
36
|
+
# EOF
|
37
|
+
|
data/test/test.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if RUBY_VERSION == "1.6.6"
|
4
|
+
puts "DEBUG mode is turned off in Ruby 1.6.6 due to bug."
|
5
|
+
$DEBUG = false
|
6
|
+
else
|
7
|
+
$DEBUG = true
|
8
|
+
end
|
9
|
+
|
10
|
+
if RUBY_VERSION == "1.8.0"
|
11
|
+
class Module
|
12
|
+
alias instance_methods_with_warning instance_methods
|
13
|
+
def instance_methods(include_super=true)
|
14
|
+
instance_methods_with_warning(include_super)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$REDSHIFT_DEBUG=3 ## what should this be?
|
20
|
+
|
21
|
+
### have to fix the CLIB_NAME problem before can do this
|
22
|
+
### require 'redshift' ## all the tests will need this anyway
|
23
|
+
|
24
|
+
pat = ARGV.join("|")
|
25
|
+
tests = Dir["test_*.rb"].grep(/#{pat}/)
|
26
|
+
tests.sort!
|
27
|
+
|
28
|
+
#trap("CLD") do
|
29
|
+
# # trapping "INT" doesn't work because child gets the signal
|
30
|
+
# exit!
|
31
|
+
#end
|
32
|
+
|
33
|
+
require 'rbconfig'
|
34
|
+
ruby = Config::CONFIG["RUBY_INSTALL_NAME"]
|
35
|
+
|
36
|
+
failed = tests.reject do |file|
|
37
|
+
puts "_"*50 + "\nStarting #{file}...\n"
|
38
|
+
system "#{ruby} #{file}"
|
39
|
+
# pid = fork { ## should use popen3 so we can weed out successful output
|
40
|
+
# $REDSHIFT_CLIB_NAME = file
|
41
|
+
# require 'redshift'
|
42
|
+
# load file
|
43
|
+
# }
|
44
|
+
# Process.waitpid(pid)
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "_"*50
|
48
|
+
if failed.empty?
|
49
|
+
puts "All tests passed."
|
50
|
+
else
|
51
|
+
puts "Some tests failed:", failed
|
52
|
+
end
|
data/test/test_buffer.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
|
3
|
+
include RedShift
|
4
|
+
|
5
|
+
RedShift.with_library do |library|
|
6
|
+
require "redshift/target/c/flow/buffer"
|
7
|
+
library.define_buffer
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
class TestBuffer < Test::Unit::TestCase
|
13
|
+
|
14
|
+
class T < Component
|
15
|
+
RedShift.with_library do
|
16
|
+
shadow_attr_accessor :b => "Buffer b"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@world = World.new
|
22
|
+
@t = @world.create(T)
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown
|
26
|
+
@world = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_accessor
|
30
|
+
a = [1.0, 2.0, 3.0, 4.0]
|
31
|
+
@t.b = a
|
32
|
+
assert_equal(a, @t.b)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_empty
|
36
|
+
assert_equal([], @t.b)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_marshal
|
40
|
+
t2 = Marshal.load(Marshal.dump(@t))
|
41
|
+
assert_equal(@t.b, t2.b)
|
42
|
+
end
|
43
|
+
|
44
|
+
def make_garbage
|
45
|
+
5.times do
|
46
|
+
@world = World.new
|
47
|
+
@world.create(T)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_gc
|
52
|
+
make_garbage
|
53
|
+
n1 = ObjectSpace.each_object(T) {}
|
54
|
+
GC.start
|
55
|
+
n2 = ObjectSpace.each_object(T) {}
|
56
|
+
assert(n2 < n1)
|
57
|
+
end
|
58
|
+
end
|