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