discrete_event 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|