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.
Files changed (107) hide show
  1. data/.gitignore +8 -0
  2. data/README +5 -0
  3. data/RELEASE-NOTES +455 -0
  4. data/TODO +431 -0
  5. data/bench/alg-state.rb +61 -0
  6. data/bench/bench +26 -0
  7. data/bench/bench.rb +10 -0
  8. data/bench/continuous.rb +76 -0
  9. data/bench/diff-bench +86 -0
  10. data/bench/discrete.rb +101 -0
  11. data/bench/euler.rb +50 -0
  12. data/bench/formula.rb +78 -0
  13. data/bench/half-strict.rb +103 -0
  14. data/bench/inertness.rb +116 -0
  15. data/bench/queue.rb +92 -0
  16. data/bench/run +66 -0
  17. data/bench/simple.rb +74 -0
  18. data/bench/strictness.rb +86 -0
  19. data/examples/ball-tkar.rb +72 -0
  20. data/examples/ball.rb +123 -0
  21. data/examples/collide.rb +70 -0
  22. data/examples/connect-parallel.rb +48 -0
  23. data/examples/connect.rb +109 -0
  24. data/examples/constants.rb +27 -0
  25. data/examples/delay.rb +80 -0
  26. data/examples/derivative.rb +77 -0
  27. data/examples/euler.rb +46 -0
  28. data/examples/external-lib.rb +33 -0
  29. data/examples/guard-debugger.rb +77 -0
  30. data/examples/lotka-volterra.rb +33 -0
  31. data/examples/persist-ball.rb +68 -0
  32. data/examples/pid.rb +87 -0
  33. data/examples/ports.rb +60 -0
  34. data/examples/queue.rb +56 -0
  35. data/examples/queue2.rb +98 -0
  36. data/examples/reset-with-event-val.rb +28 -0
  37. data/examples/scheduler.rb +104 -0
  38. data/examples/set-dest.rb +23 -0
  39. data/examples/simulink/README +1 -0
  40. data/examples/simulink/delay.mdl +827 -0
  41. data/examples/simulink/derivative.mdl +655 -0
  42. data/examples/step-discrete-profiler.rb +103 -0
  43. data/examples/subsystem.rb +109 -0
  44. data/examples/sync-deadlock.rb +32 -0
  45. data/examples/sync-queue.rb +91 -0
  46. data/examples/sync-retry.rb +20 -0
  47. data/examples/sync.rb +51 -0
  48. data/examples/thermostat.rb +53 -0
  49. data/examples/zeno.rb +53 -0
  50. data/lib/accessible-index.rb +47 -0
  51. data/lib/redshift.rb +1 -0
  52. data/lib/redshift/component.rb +412 -0
  53. data/lib/redshift/meta.rb +183 -0
  54. data/lib/redshift/mixins/zeno-debugger.rb +69 -0
  55. data/lib/redshift/port.rb +57 -0
  56. data/lib/redshift/queue.rb +104 -0
  57. data/lib/redshift/redshift.rb +111 -0
  58. data/lib/redshift/state.rb +31 -0
  59. data/lib/redshift/syntax.rb +558 -0
  60. data/lib/redshift/target/c.rb +37 -0
  61. data/lib/redshift/target/c/component-gen.rb +1303 -0
  62. data/lib/redshift/target/c/flow-gen.rb +325 -0
  63. data/lib/redshift/target/c/flow/algebraic.rb +85 -0
  64. data/lib/redshift/target/c/flow/buffer.rb +74 -0
  65. data/lib/redshift/target/c/flow/delay.rb +203 -0
  66. data/lib/redshift/target/c/flow/derivative.rb +101 -0
  67. data/lib/redshift/target/c/flow/euler.rb +67 -0
  68. data/lib/redshift/target/c/flow/expr.rb +113 -0
  69. data/lib/redshift/target/c/flow/rk4.rb +80 -0
  70. data/lib/redshift/target/c/library.rb +85 -0
  71. data/lib/redshift/target/c/world-gen.rb +1370 -0
  72. data/lib/redshift/target/spec.rb +34 -0
  73. data/lib/redshift/world.rb +300 -0
  74. data/rakefile +37 -0
  75. data/test/test.rb +52 -0
  76. data/test/test_buffer.rb +58 -0
  77. data/test/test_connect.rb +242 -0
  78. data/test/test_connect_parallel.rb +47 -0
  79. data/test/test_connect_strict.rb +135 -0
  80. data/test/test_constant.rb +74 -0
  81. data/test/test_delay.rb +145 -0
  82. data/test/test_derivative.rb +48 -0
  83. data/test/test_discrete.rb +592 -0
  84. data/test/test_discrete_isolated.rb +92 -0
  85. data/test/test_exit.rb +59 -0
  86. data/test/test_flow.rb +200 -0
  87. data/test/test_flow_link.rb +288 -0
  88. data/test/test_flow_sub.rb +100 -0
  89. data/test/test_flow_trans.rb +292 -0
  90. data/test/test_inherit.rb +127 -0
  91. data/test/test_inherit_event.rb +74 -0
  92. data/test/test_inherit_flow.rb +139 -0
  93. data/test/test_inherit_link.rb +65 -0
  94. data/test/test_inherit_setup.rb +56 -0
  95. data/test/test_inherit_state.rb +66 -0
  96. data/test/test_inherit_transition.rb +168 -0
  97. data/test/test_numerics.rb +34 -0
  98. data/test/test_queue.rb +90 -0
  99. data/test/test_queue_alone.rb +115 -0
  100. data/test/test_reset.rb +209 -0
  101. data/test/test_setup.rb +119 -0
  102. data/test/test_strict_continuity.rb +410 -0
  103. data/test/test_strict_reset_error.rb +30 -0
  104. data/test/test_strictness_error.rb +32 -0
  105. data/test/test_sync.rb +185 -0
  106. data/test/test_world.rb +328 -0
  107. metadata +204 -0
data/bench/bench ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ def bench_one(name)
4
+ lib = File.join(File.dirname(__FILE__), name)
5
+ cmd = %{
6
+ $REDSHIFT_CLIB_NAME = "#{name}"
7
+ puts "#{name}:"
8
+ #{name.split(/[-_]/).map {|w|w.capitalize}.join}.do_bench {|l| puts l}
9
+ }
10
+ system "ruby", "-r" , "./bench", "-r", lib, "-e", cmd
11
+ end
12
+
13
+ pat = ARGV.pop
14
+ pat = pat ? Regexp.new(pat) : //
15
+ %w{
16
+ alg-state
17
+ continuous
18
+ discrete
19
+ euler
20
+ formula
21
+ half-strict
22
+ inertness
23
+ queue
24
+ }.grep(pat).each do |name|
25
+ bench_one(name)
26
+ end
data/bench/bench.rb ADDED
@@ -0,0 +1,10 @@
1
+ def bench
2
+ times = Process.times
3
+ t0 = times.utime #+ times.stime
4
+
5
+ yield
6
+
7
+ times = Process.times
8
+ t1 = times.utime #+ times.stime
9
+ t1 - t0
10
+ end
@@ -0,0 +1,76 @@
1
+ # Measures performance of redshift as a pure integrator.
2
+ #
3
+ # Formulas are minimal to factor out C library time.
4
+
5
+ require 'redshift'
6
+
7
+ include RedShift
8
+
9
+ module Continuous
10
+ class C < Component
11
+ strictly_continuous :x, :y
12
+ flow do
13
+ diff " x' = y "
14
+ diff " y' = -x "
15
+ end
16
+ end
17
+
18
+ def self.make_world n=1
19
+ w = World.new
20
+ n.times do
21
+ w.create(C) do |c|
22
+ c.x = 0.0
23
+ c.y = 1.0
24
+ end
25
+ end
26
+ w
27
+ end
28
+
29
+ def self.do_bench
30
+ [ [ 1, 1_000_000],
31
+ [ 10, 100_000],
32
+ [ 100, 10_000],
33
+ [ 1_000, 1_000],
34
+ [ 10_000, 100],
35
+ [ 100_000, 10] ].each do
36
+ | n_c, n_s|
37
+
38
+ w = make_world(n_c)
39
+ w.run 1 # warm up
40
+ r = bench do
41
+ w.run(n_s)
42
+ end
43
+
44
+ yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
45
+ end
46
+ end
47
+ end
48
+
49
+ if __FILE__ == $0
50
+
51
+ require 'bench'
52
+ w = Continuous.make_world(1000)
53
+ time = bench do
54
+ w.run(1000)
55
+ end
56
+ p time
57
+
58
+ exit
59
+ require File.join(File.dirname(__FILE__), 'bench')
60
+ puts "continuous:"
61
+ Continuous.do_bench {|l| puts l}
62
+ # puts "continuous:", (1..3).map {Continuous.do_bench}
63
+
64
+ if false
65
+ require 'ruby-prof'
66
+
67
+ w = Continuous.make_world(10_000)
68
+ result = RubyProf.profile do
69
+ w.run(10)
70
+ end
71
+
72
+ printer = RubyProf::GraphPrinter.new(result)
73
+ printer.print(STDOUT, 0)
74
+ end
75
+ end
76
+
data/bench/diff-bench ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'argos'
5
+ require 'object-diff'
6
+
7
+ defaults = {
8
+ "d" => 0.01
9
+ }
10
+ optdef = {
11
+ "d" => proc {|d| Float(d)}
12
+ }
13
+ opts = defaults.merge(Argos.parse_options(ARGV, optdef))
14
+
15
+ case ARGV.size
16
+ when 2
17
+ b0, b1 = [0,1].map {|i| YAML.load(File.read(ARGV[i]))}
18
+ when 1
19
+ b0 = YAML.load(File.read(ARGV[0]))
20
+ b1 = YAML.load($stdin.read)
21
+ end
22
+
23
+ module Enumerable
24
+ def show_results depth=0
25
+ if size > 1 and depth > 0
26
+ puts
27
+ depth*2
28
+ elsif size == 1 and (first_value.size rescue 0) == 1
29
+ puts
30
+ depth*2
31
+ else
32
+ 0
33
+ end
34
+ end
35
+ end
36
+
37
+ class Hash
38
+ def first_value
39
+ values.first
40
+ end
41
+
42
+ def show_results depth=0
43
+ indent = super
44
+
45
+ keys.sort.each do |key|
46
+ val = self[key]
47
+ case val
48
+ when Numeric
49
+ printf "%#{indent}s%#{64-indent}s: %+8.2f\n", "", key, val
50
+ else
51
+ printf "%#{indent}s: ", key
52
+ val.show_results depth + 1
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class Array
59
+ def first_value
60
+ first
61
+ end
62
+
63
+ def show_results depth=0
64
+ indent = super
65
+
66
+ each do |val|
67
+ case val
68
+ when Numeric
69
+ printf "%#{indent}s- %+8.2f\n", "", val
70
+ else
71
+ printf "%#{indent}s- ", ""
72
+ val.show_results depth + 1
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ class Numeric
79
+ def diff other
80
+ super / to_f
81
+ end
82
+ end
83
+
84
+ d_min = opts["d"]
85
+ diffs = b0.diff(b1).filter {|d| d.abs >= d_min}
86
+ diffs.show_results
data/bench/discrete.rb ADDED
@@ -0,0 +1,101 @@
1
+ # Measures performance of redshift for pure discrete event simulation.
2
+ # See also the queue.rb benchmark.
3
+
4
+ require 'redshift'
5
+
6
+ include RedShift
7
+
8
+ module Discrete
9
+ class Clock < Component
10
+ # the only continuous var in the whole system
11
+ strictly_continuous :time
12
+ flow {
13
+ diff " time' = 1 "
14
+ }
15
+ end
16
+
17
+ class Awakener < Component
18
+ export :awake
19
+ end
20
+
21
+ class Sleeper < Awakener
22
+ strict_link :clock => Clock
23
+
24
+ constant :next_wakeup
25
+ strictly_constant :period
26
+
27
+ setup do
28
+ self.next_wakeup = period
29
+ end
30
+
31
+ transition do
32
+ guard " clock.time >= next_wakeup "
33
+ reset :next_wakeup => " clock.time + period "
34
+ ## need something between strict and not:
35
+ ## only changes at end of discrete update, and so
36
+ ## strictness optimizations still apply
37
+ event :awake
38
+ end
39
+ end
40
+
41
+ class Watcher < Awakener
42
+ link :target => Awakener
43
+ transition do
44
+ sync :target => :awake
45
+ event :awake
46
+ end
47
+ end
48
+
49
+ def self.make_world n_sleeper=1, n_watchers=0
50
+ w = World.new
51
+ clock = w.create(Clock)
52
+ n_sleeper.times do |i|
53
+ sleeper = w.create(Sleeper) do |c|
54
+ c.clock = clock
55
+ c.period = ((i % 99)+1) / 10.0
56
+ end
57
+ target = sleeper
58
+ n_watchers.times do
59
+ target = w.create(Watcher) do |c|
60
+ c.target = target
61
+ end
62
+ end
63
+ end
64
+ w
65
+ end
66
+
67
+ def self.do_bench
68
+ [0, 1, 5].each do |n_w|
69
+ [ [ 1, 1_000_000 ],
70
+ [ 10, 100_000 ],
71
+ [ 100, 10_000 ],
72
+ [ 1_000, 1_000 ],
73
+ [ 10_000, 100 ],
74
+ [ 100_000, 10 ] ].each do
75
+ | n_c, n_s|
76
+ if n_w > 1
77
+ do_bench_one(n_c, n_s/n_w, n_w) {|r| yield r}
78
+ else
79
+ do_bench_one(n_c, n_s, n_w) {|r| yield r}
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def self.do_bench_one(n_c, n_s, n_w)
86
+ w = make_world(n_c, n_w)
87
+ r = bench do
88
+ w.run(n_s)
89
+ end
90
+
91
+ yield " - %10d comps X %10d steps X %5d watchers: %8.2f" %
92
+ [n_c, n_s, n_w, r]
93
+ end
94
+ end
95
+
96
+ if __FILE__ == $0
97
+ require File.join(File.dirname(__FILE__), 'bench')
98
+ puts "discrete:"
99
+ Discrete.do_bench_one(100, 5_000, 5) {|l| puts l}
100
+ end
101
+
data/bench/euler.rb ADDED
@@ -0,0 +1,50 @@
1
+ # Measures performance of Euler integration.
2
+
3
+ require 'redshift'
4
+
5
+ include RedShift
6
+
7
+ module Euler
8
+ class Flow_Euler < Component
9
+ flow do
10
+ euler "x' = 1"
11
+ euler "y' = x" # Very bad!
12
+ end
13
+ end
14
+
15
+ def self.make_world n=1
16
+ w = World.new
17
+ n.times do
18
+ w.create(Flow_Euler)
19
+ end
20
+ w
21
+ end
22
+
23
+ def self.do_bench
24
+ [ [ 1, 1_000_000],
25
+ [ 10, 100_000],
26
+ [ 100, 10_000],
27
+ [ 1_000, 1_000],
28
+ [ 10_000, 100],
29
+ [ 100_000, 10] ].each do
30
+ | n_c, n_s|
31
+
32
+ w = make_world(n_c)
33
+ w.run 1 # warm up
34
+ r = bench do
35
+ w.run(n_s)
36
+ end
37
+
38
+ yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
39
+ end
40
+ end
41
+ end
42
+
43
+ if __FILE__ == $0
44
+ require 'bench'
45
+ w = Euler.make_world(1000)
46
+ time = bench do
47
+ w.run(1000)
48
+ end
49
+ p time
50
+ end
data/bench/formula.rb ADDED
@@ -0,0 +1,78 @@
1
+ # Measures performance of redshift as a pure integrator.
2
+ # using more complex formulas than in continuous.rb.
3
+
4
+ # Try replacing
5
+ # CFLAGS = -fPIC -g -O2
6
+ # with
7
+ # CFLAGS = -fPIC -O2 -march=i686 -msse2 -mfpmath=sse
8
+
9
+ case RUBY_PLATFORM
10
+ when /x86_64/
11
+ $CFLAGS="-fPIC -O2 -march=k8 -msse2 -mfpmath=sse"
12
+ else
13
+ $CFLAGS="-fPIC -O2 -march=i686 -msse2 -mfpmath=sse"
14
+ end
15
+
16
+ require 'redshift'
17
+
18
+ include RedShift
19
+
20
+ module Formula
21
+ class C < Component
22
+ strictly_continuous :x, :y
23
+ flow do
24
+ diff " x' = pow(y,3) + sqrt(fabs(sin(y))) "
25
+ diff " y' = -x "
26
+ end
27
+ end
28
+
29
+ def self.make_world n=1
30
+ w = World.new
31
+ n.times do
32
+ w.create(C) do |c|
33
+ c.x = 0.0
34
+ c.y = 1.0
35
+ end
36
+ end
37
+ w
38
+ end
39
+
40
+ def self.do_bench
41
+ [ [ 1, 100_000],
42
+ [ 10, 10_000],
43
+ [ 100, 1_000],
44
+ [ 1_000, 100],
45
+ [ 10_000, 10] ].each do
46
+ | n_c, n_s|
47
+
48
+ w = make_world(n_c)
49
+ w.run 1 # warm up
50
+ r = bench do
51
+ w.run(n_s)
52
+ end
53
+
54
+ yield " - %10d comps X %10d steps: %8.2f" % [n_c, n_s, r]
55
+ end
56
+ end
57
+ end
58
+
59
+ if __FILE__ == $0
60
+
61
+ require File.join(File.dirname(__FILE__), 'bench')
62
+ puts "continuous:"
63
+ Formula.do_bench {|l| puts l}
64
+
65
+ if false
66
+ require 'ruby-prof'
67
+
68
+ w = Formula.make_world(10_000)
69
+ result = RubyProf.profile do
70
+ w.run(10)
71
+ end
72
+
73
+ printer = RubyProf::GraphPrinter.new(result)
74
+ printer.print(STDOUT, 0)
75
+ end
76
+
77
+ end
78
+
@@ -0,0 +1,103 @@
1
+ # Measures benefit of optimizing the case in which only some of the outgoing
2
+ # transitions are strict.
3
+
4
+ require 'redshift'
5
+ require 'enumerator'
6
+
7
+ include RedShift
8
+
9
+ module HalfStrict
10
+ class Clock < Component
11
+ # the only continuous var in the whole system
12
+ strictly_continuous :t_strict
13
+ flow {
14
+ diff " t_strict' = 1 "
15
+ diff " t' = 1 "
16
+ }
17
+ end
18
+
19
+ class Observer < Component
20
+ strict_link :clock => Clock
21
+
22
+ transition do
23
+ guard " clock.t_strict < 0 "
24
+ end
25
+ transition do
26
+ guard " clock.t_strict < 0 "
27
+ end
28
+ transition do
29
+ guard " clock.t_strict < 0 "
30
+ end
31
+ transition do
32
+ guard " clock.t_strict < 0 "
33
+ end
34
+ transition do
35
+ guard " clock.t_strict < 0 "
36
+ end
37
+
38
+ transition do
39
+ guard " clock.t < 0 "
40
+ end
41
+ end
42
+
43
+ class Dummy < Component
44
+ n_states = 10
45
+ my_states = state((0...n_states).map {|i| "S#{i}"})
46
+ start S0
47
+
48
+ flow S0 do
49
+ diff " t' = 1 "
50
+ end
51
+
52
+ transition S0 => S1 do
53
+ guard "t > 0"
54
+ reset :t => 0
55
+ end
56
+
57
+ my_states[1..-1].each_cons(2) do |s, t|
58
+ transition s => t
59
+ end
60
+ transition my_states.last => my_states.first
61
+ end
62
+
63
+ def self.make_world n_observers=1
64
+ w = World.new
65
+ w.create(Dummy)
66
+ clock = w.create(Clock)
67
+ n_observers.times do |i|
68
+ observer = w.create(Observer) do |c|
69
+ c.clock = clock
70
+ end
71
+ end
72
+ w
73
+ end
74
+
75
+ def self.do_bench
76
+ [ [ 10, 100_000 ],
77
+ [ 100, 10_000 ],
78
+ [ 1_000, 1_000 ],
79
+ [ 10_000, 100 ],
80
+ [ 100_000, 10 ] ].each do
81
+ | n_c, n_s|
82
+
83
+ do_bench_one(n_c, n_s) {|r| yield r}
84
+ end
85
+ end
86
+
87
+ def self.do_bench_one(n_c, n_s)
88
+ w = make_world(n_c)
89
+ r = bench do
90
+ w.run(n_s)
91
+ end
92
+
93
+ yield " - %10d comps X %10d steps: %8.2f" %
94
+ [n_c, n_s, r]
95
+ end
96
+ end
97
+
98
+ if __FILE__ == $0
99
+ require File.join(File.dirname(__FILE__), 'bench')
100
+ puts "half-strict:"
101
+ HalfStrict.do_bench_one(10_000, 100) {|l| puts l}
102
+ end
103
+