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