discrete_event 2.0.0 → 3.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 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.