Skip to content

Commit b2d08e2

Browse files
committed
Update queue theory and simulation modules
1 parent f310b88 commit b2d08e2

23 files changed

Lines changed: 240 additions & 106 deletions

most_queue/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
Most-Queue: A Python package for simulating and analyzing queueing systems.
3+
4+
This package provides both simulation and numerical calculation methods
5+
for various types of queueing systems and networks.
6+
"""
7+
8+
__version__ = "2.05"
9+
10+
# Import main simulation and theory classes for easy access
11+
from most_queue.sim.base import QsSim
12+
from most_queue.theory.base_queue import BaseQueue
13+
14+
__all__ = [
15+
"QsSim",
16+
"BaseQueue",
17+
"__version__",
18+
]

most_queue/constants.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Constants used throughout the most_queue package.
3+
"""
4+
5+
# Default number of states for simulation
6+
DEFAULT_NUM_STATES: int = 100000
7+
8+
# Default number of jobs for simulation
9+
DEFAULT_NUM_JOBS: int = 1000000
10+
11+
# Default number of probabilities to calculate
12+
DEFAULT_P_NUM: int = 1000
13+
14+
# Numerical tolerances
15+
DEFAULT_TOLERANCE: float = 1e-12
16+
LSTSQ_RCOND: float = 1e-8 # rcond parameter for numpy.linalg.lstsq
17+
18+
# Maximum iterations for fitting algorithms
19+
MAX_FIT_ITERATIONS: int = 10000
20+
21+
# Default number of dots for approximation
22+
DEFAULT_DOTS_NUM: int = 10000

most_queue/random/utils/fit.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import numpy as np
1010

11+
from most_queue.constants import MAX_FIT_ITERATIONS
1112
from most_queue.random.utils.params import (
1213
Cox2Params,
1314
ErlangParams,
@@ -60,7 +61,7 @@ def fit_h2(moments: list[float]) -> H2Params:
6061
res = H2Params(p1=q_max, mu1=mu1, mu2=1e6)
6162
return res
6263

63-
max_iteration = 10000
64+
max_iteration = MAX_FIT_ITERATIONS
6465
tec = 0
6566
t1 = 0
6667
t2 = 0

most_queue/sim/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Simulation methods for queueing systems.
3+
4+
This module contains discrete-event simulation implementations for
5+
various types of queueing systems and networks.
6+
"""
7+
8+
from most_queue.sim.base import QsSim
9+
10+
__all__ = ["QsSim"]

most_queue/sim/base.py

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from colorama import Fore, Style, init
99
from tqdm import tqdm
1010

11+
from most_queue.constants import DEFAULT_NUM_STATES
1112
from most_queue.random.utils.create import create_distribution
1213
from most_queue.random.utils.load import calc_qs_load
1314
from most_queue.sim.utils.qs_queue import QsQueueDeque, QsQueueList
@@ -39,7 +40,7 @@ def __init__(self, num_of_channels, buffer=None, verbose=True, buffer_type="list
3940
self.generator = np.random.default_rng()
4041

4142
self.free_channels = self.n
42-
self.num_of_states = 100000
43+
self.num_of_states = DEFAULT_NUM_STATES
4344
self.load = 0 # utilization factor
4445

4546
# to track the length of the continuous channel occupancy period:
@@ -131,11 +132,13 @@ def set_servers(self, params, kendall_notation: str = "M"):
131132
for _i in range(self.n)
132133
]
133134

134-
def calc_load(self):
135-
"""
136-
Calculates the load factor of the QS
135+
def calc_load(self) -> float:
137136
"""
137+
Calculates the load factor (utilization) of the queueing system.
138138
139+
Returns:
140+
float: The utilization factor (load) of the system, between 0 and 1.
141+
"""
139142
return calc_qs_load(
140143
self.source_kendall_notation,
141144
self.source_params,
@@ -144,9 +147,13 @@ def calc_load(self):
144147
self.n,
145148
)
146149

147-
def send_task_to_channel(self, is_warm_start=False, tsk=None):
150+
def send_task_to_channel(self, is_warm_start: bool = False, tsk=None) -> None:
148151
"""
149-
Sends a job to the service channel
152+
Sends a job to an available service channel.
153+
154+
Args:
155+
is_warm_start: Whether this is a warm start scenario.
156+
tsk: Task to send. If None, a new task is created.
150157
"""
151158

152159
if tsk is None:
@@ -169,9 +176,13 @@ def send_task_to_channel(self, is_warm_start=False, tsk=None):
169176

170177
break
171178

172-
def send_task_to_queue(self, new_tsk=None):
179+
def send_task_to_queue(self, new_tsk=None) -> None:
173180
"""
174-
Send Task to Queue
181+
Send a task to the queue.
182+
183+
Args:
184+
new_tsk: Task to add to queue. If None, a new task is created.
185+
If buffer is full, the task will be dropped.
175186
"""
176187

177188
if new_tsk is None:
@@ -188,9 +199,13 @@ def send_task_to_queue(self, new_tsk=None):
188199
self.dropped += 1
189200
self.in_sys -= 1
190201

191-
def arrival(self, moment=None, ts=None):
202+
def arrival(self, moment: float | None = None, ts=None) -> None:
192203
"""
193-
Actions upon arrival of the job by the QS.
204+
Handle job arrival event in the queueing system.
205+
206+
Args:
207+
moment: Optional specific arrival moment. If None, uses current arrival_time.
208+
ts: Optional task object. If None, a new task is created.
194209
"""
195210

196211
self.arrived += 1
@@ -215,9 +230,16 @@ def arrival(self, moment=None, ts=None):
215230
else: # there are free channels:
216231
self.send_task_to_channel(tsk=ts)
217232

218-
def serving(self, c, is_network=False):
233+
def serving(self, c: int, is_network: bool = False):
219234
"""
220-
Actions upon receipt of a service job with - channel number
235+
Handle service completion event for a channel.
236+
237+
Args:
238+
c: Channel number that completed service.
239+
is_network: Whether this is part of a network simulation.
240+
241+
Returns:
242+
Task: The task that completed service.
221243
"""
222244
time_to_end = self.servers[c].time_to_end_service
223245
end_ts = self.servers[c].end_service()
@@ -244,9 +266,13 @@ def serving(self, c, is_network=False):
244266

245267
return end_ts
246268

247-
def send_head_of_queue_to_channel(self, channel_num, is_network=False):
269+
def send_head_of_queue_to_channel(self, channel_num: int, is_network: bool = False) -> None:
248270
"""
249-
Send first Task (head of queue) to Channel
271+
Send the first task from the queue (head) to an available channel.
272+
273+
Args:
274+
channel_num: Channel number to send the task to.
275+
is_network: Whether this is part of a network simulation.
250276
"""
251277
que_ts = self.queue.pop()
252278

@@ -262,9 +288,9 @@ def send_head_of_queue_to_channel(self, channel_num, is_network=False):
262288
self.servers[channel_num].start_service(que_ts, self.ttek)
263289
self.free_channels -= 1
264290

265-
def run_one_step(self):
291+
def run_one_step(self) -> None:
266292
"""
267-
Run Open step of simulation
293+
Execute one step of the simulation (either an arrival or service completion event).
268294
"""
269295

270296
num_of_server_earlier = -1
@@ -286,9 +312,15 @@ def run_one_step(self):
286312
# Arrival
287313
self.arrival()
288314

289-
def run(self, total_served) -> QueueResults:
315+
def run(self, total_served: int) -> QueueResults:
290316
"""
291-
Run simulation process
317+
Run the complete simulation process.
318+
319+
Args:
320+
total_served: Number of jobs to serve before stopping the simulation.
321+
322+
Returns:
323+
QueueResults: Results object containing statistics from the simulation.
292324
"""
293325
start = time.process_time()
294326

@@ -322,29 +354,40 @@ def run(self, total_served) -> QueueResults:
322354

323355
return QueueResults(v=v, w=w, p=p, duration=self.time_spent, utilization=self.calc_load())
324356

325-
def refresh_busy_stat(self, new_a):
357+
def refresh_busy_stat(self, new_a: float) -> None:
326358
"""
327-
Updating statistics of the busy period
359+
Update statistics of the busy period.
360+
361+
Args:
362+
new_a: New busy period duration to include in statistics.
328363
"""
329364
self.busy = refresh_moments_stat(self.busy, new_a, self.busy_moments)
330365

331-
def refresh_v_stat(self, new_a):
366+
def refresh_v_stat(self, new_a: float) -> None:
332367
"""
333-
Updating statistics of sojourn times
368+
Update statistics of sojourn times.
369+
370+
Args:
371+
new_a: New sojourn time value to include in statistics.
334372
"""
335373
self.v = refresh_moments_stat(self.v, new_a, self.served)
336374

337-
def refresh_w_stat(self, new_a):
375+
def refresh_w_stat(self, new_a: float) -> None:
338376
"""
339-
Updating statistics of wait times
377+
Update statistics of wait times.
378+
379+
Args:
380+
new_a: New waiting time value to include in statistics.
340381
"""
341382
self.w = refresh_moments_stat(self.w, new_a, self.taked)
342383

343-
def get_p(self):
384+
def get_p(self) -> list[float]:
344385
"""
345-
Returns a list with probabilities of QS states
346-
p[j] - the probability that there will be exactly j jobs
347-
in the QS at a random moment in time
386+
Returns a list with probabilities of queueing system states.
387+
388+
Returns:
389+
list[float]: List where p[j] is the probability that there will be exactly j jobs
390+
in the system at a random moment in time.
348391
"""
349392
res = [0.0] * len(self.p)
350393
for j in range(0, self.num_of_states):

most_queue/sim/flow_sum.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from tqdm import tqdm
99

10+
from most_queue.constants import DEFAULT_NUM_JOBS
1011
from most_queue.random.distributions import (
1112
ErlangDistribution,
1213
GammaDistribution,
@@ -21,7 +22,7 @@ class FlowSumSim:
2122
"""
2223

2324
def __init__(
24-
self, a, distr="Gamma", verbose=True, num_of_moments=4, num_of_jobs=1000000
25+
self, a, distr="Gamma", verbose=True, num_of_moments=4, num_of_jobs=DEFAULT_NUM_JOBS
2526
): # pylint: disable=too-many-positional-arguments, too-many-arguments
2627
self.n = len(a)
2728
self.a = a
@@ -102,7 +103,7 @@ def sum_flows(self):
102103
self.result_flow = self.a[0]
103104

104105
@staticmethod
105-
def sum_2_H2_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
106+
def sum_2_H2_flows(a1, a2, num_of_moments=4, num_of_sim=DEFAULT_NUM_JOBS):
106107
"""
107108
суммирование двух потоков c коэффициентами вариации > 1
108109
Аппроксимация H2-распределением
@@ -151,7 +152,7 @@ def get_cv(a: list[complex]):
151152
return cv
152153

153154
@staticmethod
154-
def sum_2_Pa_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
155+
def sum_2_Pa_flows(a1, a2, num_of_moments=4, num_of_sim=DEFAULT_NUM_JOBS):
155156
"""
156157
суммирование двух потоков
157158
Аппроксимация Pa-распределением
@@ -191,7 +192,7 @@ def sum_2_Pa_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
191192
return f
192193

193194
@staticmethod
194-
def sum_2_Erlang_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
195+
def sum_2_Erlang_flows(a1, a2, num_of_moments=4, num_of_sim=DEFAULT_NUM_JOBS):
195196
"""
196197
Summing two Erlang flows with parameters a1 and a2.
197198
num_of_moments: number of moments to calculate.
@@ -230,7 +231,7 @@ def sum_2_Erlang_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
230231
return f
231232

232233
@staticmethod
233-
def sum_2_Gamma_flows(a1, a2, num_of_moments=4, num_of_sim=1000000):
234+
def sum_2_Gamma_flows(a1, a2, num_of_moments=4, num_of_sim=DEFAULT_NUM_JOBS):
234235
"""
235236
суммирование двух потоков c коэффициентами вариации > 1
236237
Аппроксимация H2-распределением

most_queue/sim/networks/base_network_sim.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from abc import ABC, abstractmethod
6+
from typing import Any
67

78
from most_queue.structs import NetworkResults, NetworkResultsPriority
89

@@ -22,24 +23,26 @@ def __init__(self):
2223
self.results: NetworkResults | NetworkResultsPriority | None = None
2324

2425
@abstractmethod
25-
def set_sources(self): # pylint: disable=arguments-differ
26+
def set_sources(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=arguments-differ
2627
"""
2728
Set sources for the queueing network. This method should be implemented by subclasses.
2829
:param args: arguments for setting sources
30+
:param kwargs: keyword arguments for setting sources
2931
3032
After setting the sources, self.is_sources_set should be True.
3133
"""
3234

3335
@abstractmethod
34-
def set_nodes(self): # pylint: disable=arguments-differ
36+
def set_nodes(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=arguments-differ
3537
"""
36-
Set servers for the queueing network. This method should be implemented by subclasses.
37-
:param args: arguments for setting servers
38+
Set nodes for the queueing network. This method should be implemented by subclasses.
39+
:param args: arguments for setting nodes
40+
:param kwargs: keyword arguments for setting nodes
3841
39-
After setting the servers, self.is_servers_set should be True.
42+
After setting the nodes, self.is_nodes_set should be True.
4043
"""
4144

42-
def _check_sources_and_nodes_is_set(self):
45+
def _check_sources_and_nodes_is_set(self) -> None:
4346
"""
4447
Check if sources and nodes are set.
4548
Raises:
@@ -54,7 +57,12 @@ def _check_sources_and_nodes_is_set(self):
5457
error_msg += "\nPlease use set_nodes() method."
5558
raise ValueError(error_msg)
5659

57-
def _check_if_results_calculated(self):
60+
def _check_if_results_calculated(self) -> None:
61+
"""
62+
Check if results have been calculated.
63+
Raises:
64+
ValueError: If results have not been calculated yet.
65+
"""
5866
if self.results is None:
5967
error_msg = "Results have not been calculated yet."
6068
error_msg += "For calculating results, use run() method."

most_queue/sim/priority.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from colorama import Fore, Style, init
1010
from tqdm import tqdm
1111

12+
from most_queue.constants import DEFAULT_NUM_STATES
1213
from most_queue.random.utils.create import create_distribution
1314
from most_queue.sim.utils.servers import ServerPriority
1415
from most_queue.sim.utils.tasks import TaskPriority
@@ -48,7 +49,7 @@ def __init__(self, num_of_channels, num_of_classes, prty_type="No", buffer=None)
4849
self.buffer = buffer
4950
self.prty_type = prty_type
5051
self.free_channels = self.n
51-
self.num_of_states = 100000
52+
self.num_of_states = DEFAULT_NUM_STATES
5253
self.load = 0 # load factor of the system
5354

5455
# for tracking the duration of continuous busy periods of channels:

0 commit comments

Comments
 (0)