discrete_event 1.1.0 → 2.0.0
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.
- checksums.yaml +7 -0
- data/README.rdoc +18 -13
- data/lib/discrete_event.rb +3 -1
- data/lib/discrete_event/event_queue.rb +24 -27
- data/lib/discrete_event/events.rb +9 -8
- data/lib/discrete_event/fake_rand.rb +9 -8
- data/lib/discrete_event/simulation.rb +6 -6
- data/lib/discrete_event/version.rb +4 -2
- data/test/discrete_event/discrete_event_test.rb +67 -61
- metadata +47 -34
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9e7875c381a853a1246453ae83b5cfb1f1b50f05
|
4
|
+
data.tar.gz: 1e405666d2a7e03b0123f2ace8e4e1ca571242c1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 318572c57f5188a0e4450aa37ecc7516d2152a4d65865b238ba97cf7ed0c797803aa54e78d0aa3f2d8681281601071f97ac37c3210acb14ff9f08211dbc35531
|
7
|
+
data.tar.gz: d0f301f5b7de3140efb41f812e269b750c466331ba583868c5c50e1b98b768c9e6b03add22fdc5c80a1323bca8c68a38889a6e8586aed58d83a56cb49d31ac21
|
data/README.rdoc
CHANGED
@@ -31,16 +31,17 @@ subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
|
|
31
31
|
|
32
32
|
attr_reader :arrival_rate, :service_rate, :system, :served
|
33
33
|
|
34
|
-
def initialize
|
34
|
+
def initialize(arrival_rate, service_rate)
|
35
35
|
super()
|
36
|
-
@arrival_rate
|
36
|
+
@arrival_rate = arrival_rate
|
37
|
+
@service_rate = service_rate
|
37
38
|
@system = []
|
38
39
|
@served = []
|
39
40
|
end
|
40
41
|
|
41
42
|
# Sample from Exponential distribution with given mean rate.
|
42
|
-
def rand_exp
|
43
|
-
-Math
|
43
|
+
def rand_exp(rate)
|
44
|
+
-Math.log(rand) / rate
|
44
45
|
end
|
45
46
|
|
46
47
|
# Customer arrival process.
|
@@ -69,7 +70,11 @@ subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
|
|
69
70
|
# Number of customers currently waiting for service (does not include
|
70
71
|
# the one (if any) currently being served).
|
71
72
|
def queue_length
|
72
|
-
if system.empty?
|
73
|
+
if system.empty?
|
74
|
+
0
|
75
|
+
else
|
76
|
+
system.length - 1
|
77
|
+
end
|
73
78
|
end
|
74
79
|
|
75
80
|
# Called by super.run.
|
@@ -81,7 +86,7 @@ subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
|
|
81
86
|
#
|
82
87
|
# Run until a fixed number of passengers has been served.
|
83
88
|
#
|
84
|
-
def mm1_queue_demo
|
89
|
+
def mm1_queue_demo(arrival_rate, service_rate, num_pax)
|
85
90
|
# Run simulation and accumulate stats.
|
86
91
|
q = MM1Queue.new arrival_rate, service_rate
|
87
92
|
num_served = 0
|
@@ -89,7 +94,7 @@ subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
|
|
89
94
|
total_wait = 0.0
|
90
95
|
q.run do
|
91
96
|
unless q.served.empty?
|
92
|
-
raise
|
97
|
+
raise 'confused' if q.served.size > 1
|
93
98
|
c = q.served.shift
|
94
99
|
total_queue += c.queue_on_arrival
|
95
100
|
total_wait += c.service_begin - c.arrival_time
|
@@ -103,12 +108,13 @@ subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
|
|
103
108
|
expected_mean_wait = rho / (service_rate - arrival_rate)
|
104
109
|
expected_mean_queue = arrival_rate * expected_mean_wait
|
105
110
|
|
106
|
-
|
107
|
-
|
111
|
+
[
|
112
|
+
total_queue / num_served, expected_mean_queue,
|
113
|
+
total_wait / num_served, expected_mean_wait
|
114
|
+
]
|
108
115
|
end
|
109
116
|
|
110
117
|
|
111
|
-
|
112
118
|
This and other examples are available in the <tt>test/discrete_event</tt>
|
113
119
|
directory.
|
114
120
|
|
@@ -150,14 +156,14 @@ You may also be interested in the Ruby bindings of the GNU Science Library, whic
|
|
150
156
|
* added DiscreteEvent::Simulation#at_each_index (removed in 1.0.0)
|
151
157
|
* added DiscreteEvent::Simulation#recur_after
|
152
158
|
* added DiscreteEvent::Simulation#every
|
153
|
-
* {DiscreteEvent::FakeRand} now supports the <tt>Kernel::rand(n)</tt> form.
|
159
|
+
* {DiscreteEvent::FakeRand} now supports the <tt>Kernel::rand(n)</tt> form.
|
154
160
|
|
155
161
|
<em>0.1.0:</em>
|
156
162
|
* first release
|
157
163
|
|
158
164
|
== LICENSE
|
159
165
|
|
160
|
-
Copyright (c) 2010-
|
166
|
+
Copyright (c) 2010-2017 John Lees-Miller
|
161
167
|
|
162
168
|
Permission is hereby granted, free of charge, to any person obtaining
|
163
169
|
a copy of this software and associated documentation files (the
|
@@ -177,4 +183,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
177
183
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
178
184
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
179
185
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
180
|
-
|
data/lib/discrete_event.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pqueue'
|
2
4
|
|
3
5
|
require 'discrete_event/event_queue'
|
@@ -14,7 +16,7 @@ module DiscreteEvent
|
|
14
16
|
#
|
15
17
|
# @return [Simulation]
|
16
18
|
#
|
17
|
-
def self.simulation
|
19
|
+
def self.simulation(*args, &block)
|
18
20
|
sim = DiscreteEvent::Simulation.new(*args)
|
19
21
|
sim.instance_eval(&block)
|
20
22
|
sim
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DiscreteEvent
|
2
4
|
#
|
3
5
|
# Queue of pending events; also keeps track of the clock (the current time).
|
@@ -32,9 +34,9 @@ module DiscreteEvent
|
|
32
34
|
#
|
33
35
|
attr_reader :events
|
34
36
|
|
35
|
-
def initialize
|
37
|
+
def initialize(now = 0.0)
|
36
38
|
@now = now
|
37
|
-
@events = PQueue.new {|a,b| a.time < b.time}
|
39
|
+
@events = PQueue.new { |a, b| a.time < b.time }
|
38
40
|
@recur_interval = nil
|
39
41
|
end
|
40
42
|
|
@@ -48,8 +50,8 @@ module DiscreteEvent
|
|
48
50
|
#
|
49
51
|
# @return [Event]
|
50
52
|
#
|
51
|
-
def at
|
52
|
-
raise
|
53
|
+
def at(time, &action)
|
54
|
+
raise 'cannot schedule event in the past' if time < now
|
53
55
|
event = Event.new(time, action)
|
54
56
|
@events.push(event)
|
55
57
|
event
|
@@ -65,7 +67,7 @@ module DiscreteEvent
|
|
65
67
|
#
|
66
68
|
# @return [Event]
|
67
69
|
#
|
68
|
-
def after
|
70
|
+
def after(delay, &action)
|
69
71
|
at(@now + delay, &action)
|
70
72
|
end
|
71
73
|
|
@@ -76,7 +78,7 @@ module DiscreteEvent
|
|
76
78
|
#
|
77
79
|
# @return [nil]
|
78
80
|
#
|
79
|
-
def cancel
|
81
|
+
def cancel(event)
|
80
82
|
# not very efficient but hopefully not used very often
|
81
83
|
temp = []
|
82
84
|
until @events.empty? || @events.top.time > event.time
|
@@ -84,8 +86,8 @@ module DiscreteEvent
|
|
84
86
|
break if e.equal?(event)
|
85
87
|
temp << e
|
86
88
|
end
|
87
|
-
temp.each do |
|
88
|
-
@events.push(
|
89
|
+
temp.each do |temp_event|
|
90
|
+
@events.push(temp_event)
|
89
91
|
end
|
90
92
|
nil
|
91
93
|
end
|
@@ -93,7 +95,7 @@ module DiscreteEvent
|
|
93
95
|
#
|
94
96
|
# Schedule +action+ (a block) to run for each element in the given list
|
95
97
|
# (possibly at different times).
|
96
|
-
#
|
98
|
+
#
|
97
99
|
# This method may be of interest if you have a large number of events that
|
98
100
|
# occur at known times. You could use {#at} to add each one to the event
|
99
101
|
# queue at the start of the simulation, but this will make adding other
|
@@ -125,7 +127,7 @@ module DiscreteEvent
|
|
125
127
|
#
|
126
128
|
# @return [nil]
|
127
129
|
#
|
128
|
-
def at_each
|
130
|
+
def at_each(elements, time = nil, &action)
|
129
131
|
raise ArgumentError, 'no action given' unless block_given?
|
130
132
|
|
131
133
|
unless elements.empty?
|
@@ -133,11 +135,11 @@ module DiscreteEvent
|
|
133
135
|
if time.nil?
|
134
136
|
element_time = element.time
|
135
137
|
elsif time.is_a? Proc
|
136
|
-
element_time = time.call(element)
|
138
|
+
element_time = time.call(element)
|
137
139
|
elsif time.is_a? Symbol
|
138
140
|
element_time = element.send(time)
|
139
|
-
else
|
140
|
-
raise ArgumentError,
|
141
|
+
else
|
142
|
+
raise ArgumentError, 'bad time'
|
141
143
|
end
|
142
144
|
|
143
145
|
at element_time do
|
@@ -178,13 +180,13 @@ module DiscreteEvent
|
|
178
180
|
#
|
179
181
|
# @return [nil]
|
180
182
|
#
|
181
|
-
def recur_after
|
182
|
-
raise
|
183
|
+
def recur_after(interval)
|
184
|
+
raise 'cannot recur twice' if @recur_interval
|
183
185
|
@recur_interval = interval
|
184
186
|
nil
|
185
187
|
end
|
186
188
|
|
187
|
-
#
|
189
|
+
#
|
188
190
|
# Schedule +action+ (a block) to run periodically.
|
189
191
|
#
|
190
192
|
# This is useful for statistics collection.
|
@@ -206,7 +208,7 @@ module DiscreteEvent
|
|
206
208
|
#
|
207
209
|
# @return [nil]
|
208
210
|
#
|
209
|
-
def every
|
211
|
+
def every(interval, start = 0)
|
210
212
|
at start do
|
211
213
|
yield
|
212
214
|
recur_after interval
|
@@ -221,15 +223,11 @@ module DiscreteEvent
|
|
221
223
|
# (that is, the current event hasn't finished yet, so it's still in some
|
222
224
|
# sense the next event).
|
223
225
|
#
|
224
|
-
# @return [Numeric, nil]
|
226
|
+
# @return [Numeric, nil]
|
225
227
|
#
|
226
228
|
def next_event_time
|
227
229
|
event = @events.top
|
228
|
-
if event
|
229
|
-
event.time
|
230
|
-
else
|
231
|
-
nil
|
232
|
-
end
|
230
|
+
event.time if event
|
233
231
|
end
|
234
232
|
|
235
233
|
#
|
@@ -266,7 +264,7 @@ module DiscreteEvent
|
|
266
264
|
#
|
267
265
|
# @return [nil]
|
268
266
|
#
|
269
|
-
def run_to
|
267
|
+
def run_to(time)
|
270
268
|
# add an event to ensure that we actually stop at the given time, even if
|
271
269
|
# there isn't an event in the queue
|
272
270
|
at time do
|
@@ -304,16 +302,15 @@ module DiscreteEvent
|
|
304
302
|
self
|
305
303
|
end
|
306
304
|
|
307
|
-
#
|
305
|
+
#
|
308
306
|
# Clear any pending events in the event queue and reset {#now}.
|
309
307
|
#
|
310
308
|
# @return [self]
|
311
309
|
#
|
312
|
-
def reset
|
310
|
+
def reset(now = 0.0)
|
313
311
|
@now = now
|
314
312
|
@events.clear
|
315
313
|
self
|
316
314
|
end
|
317
315
|
end
|
318
316
|
end
|
319
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DiscreteEvent
|
2
4
|
#
|
3
5
|
# Mix-in for simulations with multiple objects that have to share the same
|
@@ -16,7 +18,7 @@ module DiscreteEvent
|
|
16
18
|
#
|
17
19
|
# @return [Event]
|
18
20
|
#
|
19
|
-
def at
|
21
|
+
def at(time, &action)
|
20
22
|
event_queue.at(time, &action)
|
21
23
|
end
|
22
24
|
|
@@ -29,7 +31,7 @@ module DiscreteEvent
|
|
29
31
|
#
|
30
32
|
# @return [Event]
|
31
33
|
#
|
32
|
-
def after
|
34
|
+
def after(delay, &action)
|
33
35
|
event_queue.after(delay, &action)
|
34
36
|
end
|
35
37
|
|
@@ -38,7 +40,7 @@ module DiscreteEvent
|
|
38
40
|
#
|
39
41
|
# @return [nil]
|
40
42
|
#
|
41
|
-
def cancel
|
43
|
+
def cancel(event)
|
42
44
|
event_queue.cancel(event)
|
43
45
|
end
|
44
46
|
|
@@ -63,7 +65,7 @@ module DiscreteEvent
|
|
63
65
|
#
|
64
66
|
# @return [nil]
|
65
67
|
#
|
66
|
-
def at_each
|
68
|
+
def at_each(elements, time = nil, &action)
|
67
69
|
event_queue.at_each(elements, time, &action)
|
68
70
|
end
|
69
71
|
|
@@ -74,11 +76,11 @@ module DiscreteEvent
|
|
74
76
|
#
|
75
77
|
# @return [nil]
|
76
78
|
#
|
77
|
-
def recur_after
|
79
|
+
def recur_after(interval)
|
78
80
|
event_queue.recur_after(interval)
|
79
81
|
end
|
80
82
|
|
81
|
-
#
|
83
|
+
#
|
82
84
|
# See {EventQueue#every}.
|
83
85
|
#
|
84
86
|
# @param [Numeric] interval non-negative
|
@@ -86,7 +88,7 @@ module DiscreteEvent
|
|
86
88
|
# @param [Numeric] start block first runs at this time
|
87
89
|
#
|
88
90
|
# @return [nil]
|
89
|
-
def every
|
91
|
+
def every(interval, start = 0, &action)
|
90
92
|
event_queue.every(interval, start, &action)
|
91
93
|
end
|
92
94
|
|
@@ -100,4 +102,3 @@ module DiscreteEvent
|
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
103
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DiscreteEvent
|
2
4
|
#
|
3
5
|
# A utility for testing objects that use the built-in Ruby pseudorandom
|
@@ -36,16 +38,16 @@ module DiscreteEvent
|
|
36
38
|
# @param [Array] fakes sequence of numbers to return
|
37
39
|
# @return [nil]
|
38
40
|
#
|
39
|
-
def self.for
|
41
|
+
def self.for(object, *fakes)
|
40
42
|
undo_for(object) # in case rand is already faked
|
41
43
|
(class << object; self; end).instance_eval do
|
42
44
|
define_method :rand do |*args|
|
43
|
-
raise
|
45
|
+
raise 'out of fake_rand numbers' if fakes.empty?
|
44
46
|
r = fakes.shift
|
45
47
|
|
46
48
|
# can be either the rand() or rand(n) form
|
47
49
|
n = args.shift || 0
|
48
|
-
if n
|
50
|
+
if n.zero?
|
49
51
|
r
|
50
52
|
else
|
51
53
|
(r * n).to_i
|
@@ -62,11 +64,10 @@ module DiscreteEvent
|
|
62
64
|
# @param [Object] object to modify
|
63
65
|
# @return [nil]
|
64
66
|
#
|
65
|
-
def self.undo_for
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
67
|
+
def self.undo_for(object)
|
68
|
+
return unless object.methods.map(&:to_s).member?('rand')
|
69
|
+
(class << object; self; end).instance_eval do
|
70
|
+
remove_method :rand
|
70
71
|
end
|
71
72
|
end
|
72
73
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DiscreteEvent
|
2
4
|
#
|
3
5
|
# A simulation, including an {EventQueue}, the current time, and various
|
@@ -12,17 +14,16 @@ module DiscreteEvent
|
|
12
14
|
#
|
13
15
|
# @abstract
|
14
16
|
#
|
15
|
-
def start
|
16
|
-
end
|
17
|
+
def start; end
|
17
18
|
|
18
19
|
#
|
19
|
-
# Run (or continue, if there are existing events) the simulation until
|
20
|
+
# Run (or continue, if there are existing events) the simulation until
|
20
21
|
# +:stop+ is thrown, or there are no more events.
|
21
22
|
#
|
22
|
-
# @yield [] after each event runs
|
23
|
+
# @yield [] after each event runs
|
23
24
|
# @return [nil]
|
24
25
|
#
|
25
|
-
def run
|
26
|
+
def run
|
26
27
|
start if @events.empty?
|
27
28
|
catch :stop do
|
28
29
|
if block_given?
|
@@ -34,4 +35,3 @@ module DiscreteEvent
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
37
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'discrete_event/test_helper'
|
2
4
|
|
3
5
|
require 'discrete_event/ex_consumer.rb'
|
@@ -6,10 +8,12 @@ require 'discrete_event/ex_mm1_queue.rb'
|
|
6
8
|
include DiscreteEvent
|
7
9
|
include DiscreteEvent::Example
|
8
10
|
|
9
|
-
class TestDiscreteEvent < Test
|
10
|
-
def assert_near
|
11
|
-
assert(
|
12
|
-
|
11
|
+
class TestDiscreteEvent < MiniTest::Test
|
12
|
+
def assert_near(expected, observed, tol = 1e-6)
|
13
|
+
assert(
|
14
|
+
(expected - observed).abs < tol,
|
15
|
+
"expected |#{expected} - #{observed}| < #{tol}"
|
16
|
+
)
|
13
17
|
end
|
14
18
|
|
15
19
|
def test_fake_rand
|
@@ -25,7 +29,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
25
29
|
assert_equal [0.125, 0.375, 0.875], c.consumed
|
26
30
|
|
27
31
|
# Now have run out of fakes.
|
28
|
-
|
32
|
+
assert_raises(RuntimeError) { c.reset.run }
|
29
33
|
|
30
34
|
# See what happens if we fake twice.
|
31
35
|
FakeRand.for(c, 0.5, 0.25, 0.125)
|
@@ -33,7 +37,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
33
37
|
assert_equal [0.5, 0.75, 0.875], c.consumed
|
34
38
|
|
35
39
|
# Now have run out of fakes, again.
|
36
|
-
|
40
|
+
assert_raises(RuntimeError) { c.reset.run }
|
37
41
|
|
38
42
|
# Can undo and get original behavior back.
|
39
43
|
FakeRand.undo_for(c)
|
@@ -57,13 +61,13 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
57
61
|
assert_equal 10, o.test
|
58
62
|
|
59
63
|
# Now have run out of fakes.
|
60
|
-
|
64
|
+
assert_raises(RuntimeError) { o.test }
|
61
65
|
end
|
62
|
-
|
66
|
+
|
63
67
|
def test_mm1_queue_not_busy
|
64
68
|
# Service begins immediately when queue is not busy.
|
65
69
|
q = MM1Queue.new 0.5, 1.0
|
66
|
-
fakes = [1, 1, 1, 1, 1].map {|x| 1/Math::E**x}
|
70
|
+
fakes = [1, 1, 1, 1, 1].map { |x| 1 / Math::E**x }
|
67
71
|
FakeRand.for(q, *fakes)
|
68
72
|
q.run do
|
69
73
|
throw :stop if q.served.size >= 2
|
@@ -75,15 +79,17 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
75
79
|
assert_near 4.0, q.served[1].service_begin
|
76
80
|
assert_near 5.0, q.served[1].service_end
|
77
81
|
end
|
78
|
-
|
82
|
+
|
79
83
|
def test_mm1_queue_busy
|
80
84
|
# Service begins after previous customer when queue is busy.
|
81
85
|
q = MM1Queue.new 0.5, 1.0
|
82
|
-
fakes = [
|
86
|
+
fakes = [
|
87
|
+
0.1, 0.1, # arrival, service for first customer
|
83
88
|
0.01, 0.01, # arrival times for second two customers
|
84
89
|
1, # arrival for forth customer
|
85
90
|
0.1, 0.1, # service times for second two customers
|
86
|
-
1
|
91
|
+
1
|
92
|
+
].map { |x| 1 / Math::E**x }
|
87
93
|
FakeRand.for(q, *fakes)
|
88
94
|
q.run do
|
89
95
|
throw :stop if q.served.size >= 3
|
@@ -101,14 +107,14 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
101
107
|
|
102
108
|
def test_recur_after
|
103
109
|
output = []
|
104
|
-
DiscreteEvent.simulation
|
110
|
+
DiscreteEvent.simulation do
|
105
111
|
at 0 do
|
106
112
|
output << now
|
107
113
|
recur_after 5 if now < 20
|
108
114
|
end
|
109
115
|
|
110
116
|
run
|
111
|
-
|
117
|
+
end
|
112
118
|
assert_equal [0, 5, 10, 15, 20], output
|
113
119
|
end
|
114
120
|
|
@@ -117,7 +123,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
117
123
|
# displace the root element, even if you call after(0), which is just an
|
118
124
|
# edge case anyway.
|
119
125
|
output = []
|
120
|
-
DiscreteEvent.simulation
|
126
|
+
DiscreteEvent.simulation do
|
121
127
|
at 0 do
|
122
128
|
output << now
|
123
129
|
after 0 do
|
@@ -130,64 +136,64 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
130
136
|
end
|
131
137
|
|
132
138
|
run
|
133
|
-
|
139
|
+
end
|
134
140
|
assert_equal [0, 42, 13, 5, 42, 13, 10, 42, 13], output
|
135
141
|
end
|
136
142
|
|
137
143
|
def test_every
|
138
144
|
output = []
|
139
|
-
DiscreteEvent.simulation
|
145
|
+
DiscreteEvent.simulation do
|
140
146
|
every 3 do
|
141
147
|
output << now
|
142
148
|
throw :stop if now > 10
|
143
149
|
end
|
144
150
|
run
|
145
|
-
|
146
|
-
assert_equal [0,3,6,9,12], output
|
151
|
+
end
|
152
|
+
assert_equal [0, 3, 6, 9, 12], output
|
147
153
|
end
|
148
154
|
|
149
155
|
Alert = Struct.new(:when, :message)
|
150
156
|
|
151
157
|
def test_at_each_with_symbol
|
152
158
|
output = []
|
153
|
-
DiscreteEvent.simulation
|
154
|
-
alerts = [Alert.new(12,
|
159
|
+
DiscreteEvent.simulation do
|
160
|
+
alerts = [Alert.new(12, 'ha!'), Alert.new(42, 'ah!')] # and many more
|
155
161
|
at_each alerts, :when do |alert|
|
156
162
|
output << now << alert.message
|
157
163
|
end
|
158
164
|
run
|
159
|
-
|
165
|
+
end
|
160
166
|
assert_equal [12, 'ha!', 42, 'ah!'], output
|
161
167
|
end
|
162
168
|
|
163
169
|
def test_at_each_with_proc
|
164
170
|
output = []
|
165
|
-
DiscreteEvent.simulation
|
166
|
-
alerts = [Alert.new(12,
|
167
|
-
at_each(alerts, proc{|alert| alert.when}) do |alert|
|
171
|
+
DiscreteEvent.simulation do
|
172
|
+
alerts = [Alert.new(12, 'ha!'), Alert.new(42, 'ah!')] # and many more
|
173
|
+
at_each(alerts, proc { |alert| alert.when }) do |alert|
|
168
174
|
output << now << alert.message
|
169
175
|
end
|
170
176
|
run
|
171
|
-
|
177
|
+
end
|
172
178
|
assert_equal [12, 'ha!', 42, 'ah!'], output
|
173
179
|
end
|
174
180
|
|
175
181
|
Alert2 = Struct.new(:time, :message)
|
176
182
|
def test_at_each_with_default
|
177
183
|
output = []
|
178
|
-
DiscreteEvent.simulation
|
179
|
-
alerts = [Alert2.new(12,
|
184
|
+
DiscreteEvent.simulation do
|
185
|
+
alerts = [Alert2.new(12, 'ha!'), Alert2.new(42, 'ah!')] # and many more
|
180
186
|
at_each alerts do |alert|
|
181
187
|
output << now << alert.message
|
182
188
|
end
|
183
189
|
run
|
184
|
-
|
190
|
+
end
|
185
191
|
assert_equal [12, 'ha!', 42, 'ah!'], output
|
186
192
|
end
|
187
193
|
|
188
194
|
def test_next_event_time
|
189
|
-
output= []
|
190
|
-
s = DiscreteEvent.simulation
|
195
|
+
output = []
|
196
|
+
s = DiscreteEvent.simulation do
|
191
197
|
at 0 do
|
192
198
|
output << next_event_time
|
193
199
|
end
|
@@ -195,7 +201,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
195
201
|
at 5 do
|
196
202
|
output << next_event_time
|
197
203
|
end
|
198
|
-
|
204
|
+
end
|
199
205
|
assert_equal 0, s.next_event_time
|
200
206
|
assert s.run_next
|
201
207
|
assert_equal 5, s.next_event_time
|
@@ -215,16 +221,16 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
215
221
|
eq.at 42 do
|
216
222
|
output << 'bye'
|
217
223
|
end
|
218
|
-
|
224
|
+
eq.to_enum.each do |t|
|
219
225
|
output_times << t
|
220
226
|
end
|
221
|
-
assert_equal %w
|
227
|
+
assert_equal %w[hi bye], output
|
222
228
|
assert_equal [13, 42], output_times
|
223
229
|
end
|
224
|
-
|
230
|
+
|
225
231
|
def test_mm1_queue_demo
|
226
232
|
# Just run the demo... 1000 isn't enough to get a reliable average.
|
227
|
-
|
233
|
+
_obs_q, exp_q, _obs_w, exp_w = mm1_queue_demo(0.25, 0.5, 1000)
|
228
234
|
assert_near exp_q, 0.5 # mean queue = rho^2 / (1 - rho)
|
229
235
|
assert_near exp_w, 2.0 # mean wait = rho / (mu - lambda)
|
230
236
|
end
|
@@ -232,7 +238,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
232
238
|
def test_producer_consumer
|
233
239
|
event_queue = EventQueue.new(0)
|
234
240
|
consumer = Consumer.new(event_queue)
|
235
|
-
producer = Producer.new(event_queue, %w
|
241
|
+
producer = Producer.new(event_queue, %w[a b c d], consumer)
|
236
242
|
|
237
243
|
FakeRand.for(consumer, 2, 2, 2, 2)
|
238
244
|
FakeRand.for(producer, 1, 1, 1, 1)
|
@@ -243,14 +249,15 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
243
249
|
output << [now, consumer.consumed.dup]
|
244
250
|
end
|
245
251
|
assert_equal [
|
246
|
-
[1, []],
|
247
|
-
[2, []],
|
248
|
-
[3, [
|
249
|
-
[3, [
|
250
|
-
[4, [
|
251
|
-
[4, [
|
252
|
-
[5, [
|
253
|
-
[6, [
|
252
|
+
[1, []], # first object produced
|
253
|
+
[2, []], # second object produced
|
254
|
+
[3, %w[a]], # third object produced / first consumed
|
255
|
+
[3, %w[a]],
|
256
|
+
[4, %w[a b]], # fourth object produced / second consumed
|
257
|
+
[4, %w[a b]],
|
258
|
+
[5, %w[a b c]], # third and fourth objects consumed
|
259
|
+
[6, %w[a b c d]]
|
260
|
+
], output
|
254
261
|
end
|
255
262
|
|
256
263
|
def test_cancel_single
|
@@ -259,7 +266,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
259
266
|
#
|
260
267
|
out = []
|
261
268
|
q = EventQueue.new(0)
|
262
|
-
e_a = q.at(
|
269
|
+
e_a = q.at(5) { out << :a }
|
263
270
|
q.cancel e_a
|
264
271
|
assert !q.run_next
|
265
272
|
assert_equal [], out
|
@@ -271,7 +278,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
271
278
|
#
|
272
279
|
out = []
|
273
280
|
q = EventQueue.new(0)
|
274
|
-
e_a = q.at(
|
281
|
+
e_a = q.at(5) { out << :a }
|
275
282
|
assert q.run_next
|
276
283
|
assert_equal [:a], out
|
277
284
|
q.cancel e_a
|
@@ -284,8 +291,8 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
284
291
|
#
|
285
292
|
out = []
|
286
293
|
q = EventQueue.new(0)
|
287
|
-
e_a = q.at(
|
288
|
-
|
294
|
+
e_a = q.at(5) { out << :a }
|
295
|
+
q.at(10) { out << :b }
|
289
296
|
q.cancel e_a
|
290
297
|
nil while q.run_next
|
291
298
|
assert_equal [:b], out
|
@@ -295,20 +302,20 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
295
302
|
out = []
|
296
303
|
q = EventQueue.new(0)
|
297
304
|
|
298
|
-
e_a = q.at(
|
305
|
+
e_a = q.at(5) do
|
299
306
|
out << :a
|
300
307
|
|
301
|
-
|
302
|
-
|
308
|
+
q.at(6) { out << :c }
|
309
|
+
|
303
310
|
q.cancel e_a # This should have no effect
|
304
311
|
end
|
305
312
|
|
306
|
-
|
313
|
+
q.at(10) do
|
307
314
|
out << :b
|
308
315
|
end
|
309
|
-
|
316
|
+
|
310
317
|
nil while q.run_next
|
311
|
-
assert_equal [
|
318
|
+
assert_equal %i[a c b], out
|
312
319
|
end
|
313
320
|
|
314
321
|
def test_cancel_last
|
@@ -317,7 +324,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
317
324
|
#
|
318
325
|
out = []
|
319
326
|
q = EventQueue.new(0)
|
320
|
-
|
327
|
+
q.at(5) { out << :a }
|
321
328
|
e_b = q.at(10) { out << :b }
|
322
329
|
q.cancel e_b
|
323
330
|
nil while q.run_next
|
@@ -330,12 +337,12 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
330
337
|
#
|
331
338
|
out = []
|
332
339
|
q = EventQueue.new(0)
|
333
|
-
|
340
|
+
q.at(5) { out << :a }
|
334
341
|
e_b = q.at(10) { out << :b }
|
335
|
-
|
342
|
+
q.at(13) { out << :c }
|
336
343
|
q.cancel e_b
|
337
344
|
nil while q.run_next
|
338
|
-
assert_equal [
|
345
|
+
assert_equal %i[a c], out
|
339
346
|
end
|
340
347
|
|
341
348
|
def test_cancel_at_same_time_1
|
@@ -363,7 +370,7 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
363
370
|
#
|
364
371
|
out = []
|
365
372
|
q = EventQueue.new(0)
|
366
|
-
|
373
|
+
q.at 10 do
|
367
374
|
out << :a
|
368
375
|
end
|
369
376
|
e_b = q.at 10 do
|
@@ -430,4 +437,3 @@ class TestDiscreteEvent < Test::Unit::TestCase
|
|
430
437
|
assert_equal [:a], out
|
431
438
|
end
|
432
439
|
end
|
433
|
-
|
metadata
CHANGED
@@ -1,39 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discrete_event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- John Lees-Miller
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2017-07-10 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: pqueue
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: 2.0
|
19
|
+
version: '2.0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
25
27
|
- !ruby/object:Gem::Dependency
|
26
28
|
name: gemma
|
27
|
-
requirement:
|
28
|
-
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
37
|
requirements:
|
30
|
-
- - ~>
|
38
|
+
- - "~>"
|
31
39
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.14'
|
33
48
|
type: :development
|
34
49
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
-
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.14'
|
55
|
+
description: |-
|
56
|
+
Some simple primitives for event-based discrete event
|
57
|
+
simulation.
|
37
58
|
email:
|
38
59
|
- jdleesmiller@gmail.com
|
39
60
|
executables: []
|
@@ -41,48 +62,40 @@ extensions: []
|
|
41
62
|
extra_rdoc_files:
|
42
63
|
- README.rdoc
|
43
64
|
files:
|
65
|
+
- README.rdoc
|
44
66
|
- lib/discrete_event.rb
|
45
|
-
- lib/discrete_event/events.rb
|
46
|
-
- lib/discrete_event/version.rb
|
47
67
|
- lib/discrete_event/event_queue.rb
|
48
|
-
- lib/discrete_event/
|
68
|
+
- lib/discrete_event/events.rb
|
49
69
|
- lib/discrete_event/fake_rand.rb
|
50
|
-
-
|
70
|
+
- lib/discrete_event/simulation.rb
|
71
|
+
- lib/discrete_event/version.rb
|
51
72
|
- test/discrete_event/discrete_event_test.rb
|
52
73
|
homepage: http://github.com/jdleesmiller/discrete_event
|
53
74
|
licenses: []
|
75
|
+
metadata: {}
|
54
76
|
post_install_message:
|
55
77
|
rdoc_options:
|
56
|
-
- --main
|
78
|
+
- "--main"
|
57
79
|
- README.rdoc
|
58
|
-
- --title
|
59
|
-
- discrete_event-
|
80
|
+
- "--title"
|
81
|
+
- discrete_event-2.0.0 Documentation
|
60
82
|
require_paths:
|
61
83
|
- lib
|
62
84
|
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
85
|
requirements:
|
65
|
-
- -
|
86
|
+
- - ">="
|
66
87
|
- !ruby/object:Gem::Version
|
67
88
|
version: '0'
|
68
|
-
segments:
|
69
|
-
- 0
|
70
|
-
hash: 326435089
|
71
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
90
|
requirements:
|
74
|
-
- -
|
91
|
+
- - ">="
|
75
92
|
- !ruby/object:Gem::Version
|
76
93
|
version: '0'
|
77
|
-
segments:
|
78
|
-
- 0
|
79
|
-
hash: 326435089
|
80
94
|
requirements: []
|
81
95
|
rubyforge_project: discrete_event
|
82
|
-
rubygems_version:
|
96
|
+
rubygems_version: 2.6.8
|
83
97
|
signing_key:
|
84
|
-
specification_version:
|
98
|
+
specification_version: 4
|
85
99
|
summary: Event-based discrete event simulation.
|
86
100
|
test_files:
|
87
101
|
- test/discrete_event/discrete_event_test.rb
|
88
|
-
has_rdoc:
|