discrete_event 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9e7875c381a853a1246453ae83b5cfb1f1b50f05
4
- data.tar.gz: 1e405666d2a7e03b0123f2ace8e4e1ca571242c1
2
+ SHA256:
3
+ metadata.gz: 35f2e860969cab8e0fd92d569be1c21fc7dc9acf2f35152e71b31cab4f4f98c6
4
+ data.tar.gz: 432630c67f1dcd4c67ea43290fc861420404370cb30ec4f4055b753fa8a3a423
5
5
  SHA512:
6
- metadata.gz: 318572c57f5188a0e4450aa37ecc7516d2152a4d65865b238ba97cf7ed0c797803aa54e78d0aa3f2d8681281601071f97ac37c3210acb14ff9f08211dbc35531
7
- data.tar.gz: d0f301f5b7de3140efb41f812e269b750c466331ba583868c5c50e1b98b768c9e6b03add22fdc5c80a1323bca8c68a38889a6e8586aed58d83a56cb49d31ac21
6
+ metadata.gz: 68c742044c94180e12cf68e52c66213bf37c4cf5deb4c496378b891c780d6c34817af4ee3d8808ae778fe08fa7d2573366fe4e255507fd28e04219714144fe76
7
+ data.tar.gz: f7728fdcae8da2b1b65ab25baa3c9d1e5d148d8a09aa68d7dd9d6e1be72a4fcd5d43543bded3068173eca1b7c155a29455651dd0289fba8adc0e3b9dee81c29d
data/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # discrete_event
2
+
3
+ http://github.com/jdleesmiller/discrete_event
4
+
5
+ ![CI status](https://github.com/jdleesmiller/discrete_event/actions/workflows/ruby.yml/badge.svg)
6
+
7
+ ## SYNOPSIS
8
+
9
+ This gem provides some tools for discrete event simulation (DES) in ruby. The
10
+ main one is a DiscreteEvent::EventQueue that stores actions (ruby blocks) to
11
+ be executed at chosen times.
12
+
13
+ The example below uses the DiscreteEvent::Simulation class, which is a
14
+ subclass of DiscreteEvent::EventQueue, to simulate an M/M/1 queueing system.
15
+
16
+ ```rb
17
+ require 'discrete_event'
18
+
19
+ #
20
+ # A single-server queueing system with Markovian arrival and service
21
+ # processes.
22
+ #
23
+ # Note that the simulation runs indefinitely, and that it doesn't collect
24
+ # statistics; this is left to the user. See mm1_queue_demo, below, for
25
+ # an example of how to collect statistics and how to stop the simulation
26
+ # by throwing the :stop symbol.
27
+ #
28
+ class MM1Queue < DiscreteEvent::Simulation
29
+ Customer = Struct.new(:arrival_time, :queue_on_arrival,
30
+ :service_begin, :service_end)
31
+
32
+ attr_reader :arrival_rate, :service_rate, :system, :served
33
+
34
+ def initialize(arrival_rate, service_rate)
35
+ super()
36
+ @arrival_rate = arrival_rate
37
+ @service_rate = service_rate
38
+ @system = []
39
+ @served = []
40
+ end
41
+
42
+ # Sample from Exponential distribution with given mean rate.
43
+ def rand_exp(rate)
44
+ -Math.log(rand) / rate
45
+ end
46
+
47
+ # Customer arrival process.
48
+ # The after method is provided by {DiscreteEvent::Simulation}.
49
+ # The given action (a Ruby block) will run after the random delay
50
+ # computed by rand_exp. When it runs, the last thing the action does is
51
+ # call new_customer, which creates an event for the next customer.
52
+ def new_customer
53
+ after rand_exp(arrival_rate) do
54
+ system << Customer.new(now, queue_length)
55
+ serve_customer if system.size == 1
56
+ new_customer
57
+ end
58
+ end
59
+
60
+ # Customer service process.
61
+ def serve_customer
62
+ system.first.service_begin = now
63
+ after rand_exp(service_rate) do
64
+ system.first.service_end = now
65
+ served << system.shift
66
+ serve_customer unless system.empty?
67
+ end
68
+ end
69
+
70
+ # Number of customers currently waiting for service (does not include
71
+ # the one (if any) currently being served).
72
+ def queue_length
73
+ if system.empty?
74
+ 0
75
+ else
76
+ system.length - 1
77
+ end
78
+ end
79
+
80
+ # Called by super.run.
81
+ def start
82
+ new_customer
83
+ end
84
+ end
85
+
86
+ #
87
+ # Run until a fixed number of passengers has been served.
88
+ #
89
+ def mm1_queue_demo(arrival_rate, service_rate, num_pax)
90
+ # Run simulation and accumulate stats.
91
+ q = MM1Queue.new arrival_rate, service_rate
92
+ num_served = 0
93
+ total_queue = 0.0
94
+ total_wait = 0.0
95
+ q.run do
96
+ unless q.served.empty?
97
+ raise 'confused' if q.served.size > 1
98
+ c = q.served.shift
99
+ total_queue += c.queue_on_arrival
100
+ total_wait += c.service_begin - c.arrival_time
101
+ num_served += 1
102
+ end
103
+ throw :stop if num_served >= num_pax
104
+ end
105
+
106
+ # Use standard formulas for comparison.
107
+ rho = arrival_rate / service_rate
108
+ expected_mean_wait = rho / (service_rate - arrival_rate)
109
+ expected_mean_queue = arrival_rate * expected_mean_wait
110
+
111
+ [
112
+ total_queue / num_served, expected_mean_queue,
113
+ total_wait / num_served, expected_mean_wait
114
+ ]
115
+ end
116
+ ```
117
+
118
+ This and other examples are available in the `test/discrete_event` directory.
119
+
120
+ In this example, the whole simulation happens in a single object; if you have
121
+ multiple objects, you can use the DiscreteEvent::Events mix-in to make them
122
+ easily share a single event queue.
123
+
124
+ ## INSTALLATION
125
+
126
+ ```
127
+ gem install discrete_event
128
+ ```
129
+
130
+ ## REFERENCES
131
+
132
+ - http://en.wikipedia.org/wiki/Discrete_event_simulation
133
+
134
+ You may also be interested in the Ruby bindings of the GNU Science Library,
135
+ which provides a variety of pseudo-random number generators and functions for
136
+ generating random variates from various distributions. It also provides useful
137
+ things like histograms.
138
+
139
+ - http://www.gnu.org/software/gsl/
140
+ - https://rubygems.org/gems/rb-gsl
141
+ - The libgsl-ruby package in Debian.
142
+
143
+
144
+ ## HISTORY
145
+
146
+ ### 3.0.0
147
+ - drop support for rubies older than 2.6
148
+ - change priority queue implementation to `priority_queue_cxx` (https://rubygems.org/gems/priority_queue_cxx) for performance
149
+
150
+ ### 2.0.0
151
+ - drop support for rubies older than 2.2
152
+ - allow installation with PQueue 2.1
153
+ - updated dev dependencies
154
+ - code has been linted with rubocop
155
+
156
+ ### 1.1.0
157
+ - compatibility with PQueue 2.x; fix for event cancellation (thanks: joshcarter)
158
+ - added DiscreteEvent::EventQueue#run_to
159
+ - updated dependency versions
160
+
161
+ ### 1.0.0:
162
+ - split DiscreteEvent::EventQueue out of DiscreteEvent::Simulation for easier sharing between objects
163
+ - added DiscreteEvent::Events mix-in
164
+
165
+ ### 0.3.0
166
+ - reorganized for compatibility with gemma 2.0; no functional changes
167
+ - added major, minor and patch version constants
168
+
169
+ ### 0.2.0
170
+ - added DiscreteEvent::Simulation#at_each_index (removed in 1.0.0)
171
+ - added DiscreteEvent::Simulation#recur_after
172
+ - added DiscreteEvent::Simulation#every
173
+ - DiscreteEvent::FakeRand now supports the `Kernel::rand(n)` form.
174
+
175
+ ### 0.1.0
176
+ - first release
177
+
178
+
179
+ ## LICENSE
180
+
181
+ Copyright (c) 2010–2021 John Lees-Miller
182
+
183
+ Permission is hereby granted, free of charge, to any person obtaining a copy
184
+ of this software and associated documentation files (the "Software"), to deal
185
+ in the Software without restriction, including without limitation the rights
186
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
187
+ copies of the Software, and to permit persons to whom the Software is
188
+ furnished to do so, subject to the following conditions:
189
+
190
+ The above copyright notice and this permission notice shall be included in all
191
+ copies or substantial portions of the Software.
192
+
193
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
194
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
195
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
196
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
198
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
199
+ SOFTWARE.
@@ -36,7 +36,7 @@ module DiscreteEvent
36
36
 
37
37
  def initialize(now = 0.0)
38
38
  @now = now
39
- @events = PQueue.new { |a, b| a.time < b.time }
39
+ @events = FastContainers::PriorityQueue.new(:min)
40
40
  @recur_interval = nil
41
41
  end
42
42
 
@@ -53,7 +53,7 @@ module DiscreteEvent
53
53
  def at(time, &action)
54
54
  raise 'cannot schedule event in the past' if time < now
55
55
  event = Event.new(time, action)
56
- @events.push(event)
56
+ @events.push(event, time)
57
57
  event
58
58
  end
59
59
 
@@ -81,13 +81,14 @@ module DiscreteEvent
81
81
  def cancel(event)
82
82
  # not very efficient but hopefully not used very often
83
83
  temp = []
84
- until @events.empty? || @events.top.time > event.time
85
- e = @events.pop
84
+ until @events.empty? || @events.top_key > event.time
85
+ e = @events.top
86
+ @events.pop
86
87
  break if e.equal?(event)
87
88
  temp << e
88
89
  end
89
90
  temp.each do |temp_event|
90
- @events.push(temp_event)
91
+ @events.push(temp_event, temp_event.time)
91
92
  end
92
93
  nil
93
94
  end
@@ -226,8 +227,7 @@ module DiscreteEvent
226
227
  # @return [Numeric, nil]
227
228
  #
228
229
  def next_event_time
229
- event = @events.top
230
- event.time if event
230
+ @events.top_key unless @events.empty?
231
231
  end
232
232
 
233
233
  #
@@ -236,23 +236,23 @@ module DiscreteEvent
236
236
  # @return [Boolean] false if there are no more events.
237
237
  #
238
238
  def run_next
239
- event = @events.pop
240
- if event
241
- # run the action
242
- @now = event.time
243
- event.action.call
239
+ return false if @events.empty?
244
240
 
245
- # Handle recurring events.
246
- if @recur_interval
247
- event.time = @now + @recur_interval
248
- @events.push(event)
249
- @recur_interval = nil
250
- end
241
+ event = @events.top
242
+ @events.pop
251
243
 
252
- true
253
- else
254
- false
244
+ # run the action
245
+ @now = event.time
246
+ event.action.call
247
+
248
+ # Handle recurring events.
249
+ if @recur_interval
250
+ event.time = @now + @recur_interval
251
+ @events.push(event, event.time)
252
+ @recur_interval = nil
255
253
  end
254
+
255
+ true
256
256
  end
257
257
 
258
258
  #
@@ -309,7 +309,7 @@ module DiscreteEvent
309
309
  #
310
310
  def reset(now = 0.0)
311
311
  @now = now
312
- @events.clear
312
+ @events = FastContainers::PriorityQueue.new(:min)
313
313
  self
314
314
  end
315
315
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DiscreteEvent
4
- VERSION_MAJOR = 2
4
+ VERSION_MAJOR = 3
5
5
  VERSION_MINOR = 0
6
6
  VERSION_PATCH = 0
7
7
  VERSION = [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH].join('.')
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pqueue'
3
+ require 'fc'
4
4
 
5
5
  require 'discrete_event/event_queue'
6
6
  require 'discrete_event/events'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discrete_event
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Lees-Miller
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-10 00:00:00.000000000 Z
11
+ date: 2021-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pqueue
14
+ name: priority_queue_cxx
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: 0.3.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: 0.3.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: gemma
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,9 +60,9 @@ email:
60
60
  executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files:
63
- - README.rdoc
63
+ - README.md
64
64
  files:
65
- - README.rdoc
65
+ - README.md
66
66
  - lib/discrete_event.rb
67
67
  - lib/discrete_event/event_queue.rb
68
68
  - lib/discrete_event/events.rb
@@ -73,12 +73,12 @@ files:
73
73
  homepage: http://github.com/jdleesmiller/discrete_event
74
74
  licenses: []
75
75
  metadata: {}
76
- post_install_message:
76
+ post_install_message:
77
77
  rdoc_options:
78
78
  - "--main"
79
- - README.rdoc
79
+ - README.md
80
80
  - "--title"
81
- - discrete_event-2.0.0 Documentation
81
+ - discrete_event-3.0.0 Documentation
82
82
  require_paths:
83
83
  - lib
84
84
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -92,9 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
94
  requirements: []
95
- rubyforge_project: discrete_event
96
- rubygems_version: 2.6.8
97
- signing_key:
95
+ rubygems_version: 3.2.22
96
+ signing_key:
98
97
  specification_version: 4
99
98
  summary: Event-based discrete event simulation.
100
99
  test_files:
data/README.rdoc DELETED
@@ -1,185 +0,0 @@
1
- = discrete_event
2
-
3
- http://github.com/jdleesmiller/discrete_event
4
-
5
- {<img src="https://secure.travis-ci.org/jdleesmiller/discrete_event.png"/>}[http://travis-ci.org/jdleesmiller/discrete_event]
6
-
7
- == SYNOPSIS
8
-
9
- This gem provides some tools for discrete event simulation (DES) in ruby. The
10
- main one is a {DiscreteEvent::EventQueue} that stores actions (ruby blocks) to
11
- be executed at chosen times.
12
-
13
- The example below uses the {DiscreteEvent::Simulation} class, which is a
14
- subclass of {DiscreteEvent::EventQueue}, to simulate an M/M/1 queueing system.
15
-
16
- require 'rubygems'
17
- require 'discrete_event'
18
-
19
- #
20
- # A single-server queueing system with Markovian arrival and service
21
- # processes.
22
- #
23
- # Note that the simulation runs indefinitely, and that it doesn't collect
24
- # statistics; this is left to the user. See mm1_queue_demo, below, for
25
- # an example of how to collect statistics and how to stop the simulation
26
- # by throwing the :stop symbol.
27
- #
28
- class MM1Queue < DiscreteEvent::Simulation
29
- Customer = Struct.new(:arrival_time, :queue_on_arrival,
30
- :service_begin, :service_end)
31
-
32
- attr_reader :arrival_rate, :service_rate, :system, :served
33
-
34
- def initialize(arrival_rate, service_rate)
35
- super()
36
- @arrival_rate = arrival_rate
37
- @service_rate = service_rate
38
- @system = []
39
- @served = []
40
- end
41
-
42
- # Sample from Exponential distribution with given mean rate.
43
- def rand_exp(rate)
44
- -Math.log(rand) / rate
45
- end
46
-
47
- # Customer arrival process.
48
- # The after method is provided by {DiscreteEvent::Simulation}.
49
- # The given action (a Ruby block) will run after the random delay
50
- # computed by rand_exp. When it runs, the last thing the action does is
51
- # call new_customer, which creates an event for the next customer.
52
- def new_customer
53
- after rand_exp(arrival_rate) do
54
- system << Customer.new(now, queue_length)
55
- serve_customer if system.size == 1
56
- new_customer
57
- end
58
- end
59
-
60
- # Customer service process.
61
- def serve_customer
62
- system.first.service_begin = now
63
- after rand_exp(service_rate) do
64
- system.first.service_end = now
65
- served << system.shift
66
- serve_customer unless system.empty?
67
- end
68
- end
69
-
70
- # Number of customers currently waiting for service (does not include
71
- # the one (if any) currently being served).
72
- def queue_length
73
- if system.empty?
74
- 0
75
- else
76
- system.length - 1
77
- end
78
- end
79
-
80
- # Called by super.run.
81
- def start
82
- new_customer
83
- end
84
- end
85
-
86
- #
87
- # Run until a fixed number of passengers has been served.
88
- #
89
- def mm1_queue_demo(arrival_rate, service_rate, num_pax)
90
- # Run simulation and accumulate stats.
91
- q = MM1Queue.new arrival_rate, service_rate
92
- num_served = 0
93
- total_queue = 0.0
94
- total_wait = 0.0
95
- q.run do
96
- unless q.served.empty?
97
- raise 'confused' if q.served.size > 1
98
- c = q.served.shift
99
- total_queue += c.queue_on_arrival
100
- total_wait += c.service_begin - c.arrival_time
101
- num_served += 1
102
- end
103
- throw :stop if num_served >= num_pax
104
- end
105
-
106
- # Use standard formulas for comparison.
107
- rho = arrival_rate / service_rate
108
- expected_mean_wait = rho / (service_rate - arrival_rate)
109
- expected_mean_queue = arrival_rate * expected_mean_wait
110
-
111
- [
112
- total_queue / num_served, expected_mean_queue,
113
- total_wait / num_served, expected_mean_wait
114
- ]
115
- end
116
-
117
-
118
- This and other examples are available in the <tt>test/discrete_event</tt>
119
- directory.
120
-
121
- In this example, the whole simulation happens in a single object; if you have
122
- multiple objects, you can use the {DiscreteEvent::Events} mix-in to make them
123
- easily share a single event queue.
124
-
125
- == INSTALLATION
126
-
127
- gem install discrete_event
128
-
129
- == REFERENCES
130
-
131
- * {http://en.wikipedia.org/wiki/Discrete_event_simulation}
132
-
133
- You may also be interested in the Ruby bindings of the GNU Science Library, which provides a variety of pseudo-random number generators and functions for generating random variates from various distributions. It also provides useful things like histograms.
134
-
135
- * {http://www.gnu.org/software/gsl/}
136
- * {http://rb-gsl.rubyforge.org/}
137
- * The libgsl-ruby package in Debian.
138
-
139
- == HISTORY
140
-
141
- <em>1.1.0:</em>
142
- * compatibility with PQueue 2.x; fix for event cancellation (thanks: joshcarter)
143
- * added {DiscreteEvent::EventQueue#run_to}
144
- * updated dependency versions
145
-
146
- <em>1.0.0:</em>
147
- * split {DiscreteEvent::EventQueue} out of DiscreteEvent::Simulation for
148
- easier sharing between objects
149
- * added {DiscreteEvent::Events} mix-in
150
-
151
- <em>0.3.0:</em>
152
- * reorganized for compatibility with gemma 2.0; no functional changes
153
- * added major, minor and patch version constants
154
-
155
- <em>0.2.0:</em>
156
- * added DiscreteEvent::Simulation#at_each_index (removed in 1.0.0)
157
- * added DiscreteEvent::Simulation#recur_after
158
- * added DiscreteEvent::Simulation#every
159
- * {DiscreteEvent::FakeRand} now supports the <tt>Kernel::rand(n)</tt> form.
160
-
161
- <em>0.1.0:</em>
162
- * first release
163
-
164
- == LICENSE
165
-
166
- Copyright (c) 2010-2017 John Lees-Miller
167
-
168
- Permission is hereby granted, free of charge, to any person obtaining
169
- a copy of this software and associated documentation files (the
170
- "Software"), to deal in the Software without restriction, including
171
- without limitation the rights to use, copy, modify, merge, publish,
172
- distribute, sublicense, and/or sell copies of the Software, and to
173
- permit persons to whom the Software is furnished to do so, subject to
174
- the following conditions:
175
-
176
- The above copyright notice and this permission notice shall be
177
- included in all copies or substantial portions of the Software.
178
-
179
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
180
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
181
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
182
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
183
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
184
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
185
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.