redshift 1.3.15

Sign up to get free protection for your applications and to get access to all the features.
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
+