most-queue 1.53__tar.gz → 1.54__tar.gz
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.
Potentially problematic release.
This version of most-queue might be problematic. Click here for more details.
- {most_queue-1.53 → most_queue-1.54}/PKG-INFO +7 -2
- {most_queue-1.53 → most_queue-1.54}/README.md +6 -1
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/conv.py +12 -4
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/tables.py +2 -2
- {most_queue-1.53 → most_queue-1.54}/most_queue/rand_distribution.py +5 -5
- most_queue-1.54/most_queue/sim/queueing_systems/negative.py +482 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/distribution_utils.py +30 -30
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/qs_queue.py +24 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/mgn_takahasi.py +1 -9
- most_queue-1.54/most_queue/theory/queueing_systems/negative/mgn_rcs.py +579 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/vacations/m_h2_h2warm.py +3 -11
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/vacations/mgn_with_h2_delay_cold_warm.py +5 -16
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/vacations/mmn_with_h2_cold_and_h2_warmup.py +1 -12
- {most_queue-1.53 → most_queue-1.54}/pyproject.toml +1 -1
- {most_queue-1.53 → most_queue-1.54}/setup.py +1 -1
- {most_queue-1.53 → most_queue-1.54}/tests/test_m_h2_h2warm.py +1 -1
- most_queue-1.54/tests/test_mgn_rcs.py +72 -0
- {most_queue-1.53 → most_queue-1.54}/.gitignore +0 -0
- {most_queue-1.53 → most_queue-1.54}/LICENSE +0 -0
- {most_queue-1.53 → most_queue-1.54}/assets/queue_long.png +0 -0
- {most_queue-1.53 → most_queue-1.54}/deploy.txt +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/Makefile +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/make.bat +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/conf.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/index.rst +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/modules.rst +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/most_queue.rst +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/most_queue.sim.rst +0 -0
- {most_queue-1.53 → most_queue-1.54}/docs/source/most_queue.theory.rst +0 -0
- {most_queue-1.53 → most_queue-1.54}/googleb31d3dccb40f969f.html +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/approx_cdf_make.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/distribution_params.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/interfaces.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/general/vacations_paper_utils.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/misc/compare_fj_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/misc/flow_sum.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/misc/test_weibull.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/flow_sum.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/networks/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/networks/priority_network.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/batch.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/fifo.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/finite_source.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/fork_join.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/fork_join_delta.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/impatient.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/queueing_systems/priority.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/exceptions.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/phase.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/qs.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/servers.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/stats_update.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/sim/utils/tasks.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/networks/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/networks/network_viewer.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/networks/open_network.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/batch/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/batch/mm1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/closed/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/closed/engset.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/ek_d_n.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/gi_m_1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/gi_m_n.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/m_d_n.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/mg1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fifo/mmnr.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fork_join/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fork_join/m_m_n.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/fork_join/split_join.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/impatience/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/impatience/mm1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/mgn_invar_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/non_preemptive/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/non_preemptive/mg1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/preemptive/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/preemptive/m_ph_n_busy_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/preemptive/mg1.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/preemptive/mmn_2cls_pr_busy_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/priority/preemptive/mmn_3cls_busy_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/vacations/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/queueing_systems/vacations/mg1_warm_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/r_example.gexf +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/binom_probs.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/busy_periods.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/diff5dots.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/flow_sum.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/passage_time.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/q_poisson_arrival_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/student_stat.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/transforms.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/most_queue/theory/utils/weibull.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/requirements.txt +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/__init__.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/n_test/ro_0.700.json +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/ro_test/n_3.json +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_batch.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_ek_d_n.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_engset.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_fj_delta.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_fj_im.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_gi_m_1_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_gi_m_n_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_impatience.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_m_d_n_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_m_ph_n_prty.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mg1_calc.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mgn_tt.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mgn_with_h2_delay_cold_warm.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mmn3_pnz_cox_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mmn_h2cold_h2warm.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_mmn_prty_pnz_approx.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_network_im_prty.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_qs_sim_prty.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_qs_sim_test.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tests/test_queue_types.py +0 -0
- {most_queue-1.53 → most_queue-1.54}/tutorials/m_h2_h2warm.ipynb +0 -0
- {most_queue-1.53 → most_queue-1.54}/tutorials/mgn_takahasi_takami.ipynb +0 -0
- {most_queue-1.53 → most_queue-1.54}/tutorials/priority_queue.ipynb +0 -0
- {most_queue-1.53 → most_queue-1.54}/tutorials/queue_sim.ipynb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: most_queue
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.54
|
|
4
4
|
Summary: Software package for calculation and simulation of queuing systems
|
|
5
5
|
Project-URL: Homepage, https://github.com/xabarov/most-queue
|
|
6
6
|
Author-email: Xabarov Roman <xabarov1985@gmail.com>
|
|
@@ -68,11 +68,16 @@ Contributions are welcome! If you find any issues or have suggestions, please op
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
70
|
## Installation
|
|
71
|
-
|
|
71
|
+
- install most-queue with pip
|
|
72
72
|
```bash
|
|
73
73
|
pip install most-queue
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
- install most-queue from repository
|
|
77
|
+
```bash
|
|
78
|
+
pip install -e .
|
|
79
|
+
```
|
|
80
|
+
|
|
76
81
|
---
|
|
77
82
|
|
|
78
83
|
## Description of the Project
|
|
@@ -28,11 +28,16 @@ Contributions are welcome! If you find any issues or have suggestions, please op
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
30
|
## Installation
|
|
31
|
-
|
|
31
|
+
- install most-queue with pip
|
|
32
32
|
```bash
|
|
33
33
|
pip install most-queue
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
- install most-queue from repository
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
36
41
|
---
|
|
37
42
|
|
|
38
43
|
## Description of the Project
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Functions for calculating convolutions of distributions.
|
|
3
3
|
"""
|
|
4
|
-
|
|
4
|
+
import numpy as np
|
|
5
5
|
|
|
6
6
|
def conv_moments(a: list[float], b: list[float], num: int = 3) -> list[float]:
|
|
7
7
|
"""
|
|
@@ -18,7 +18,7 @@ def conv_moments(a: list[float], b: list[float], num: int = 3) -> list[float]:
|
|
|
18
18
|
res[1] = a[1] + b[1] + 2 * a[0] * b[0]
|
|
19
19
|
if num > 2:
|
|
20
20
|
res[2] = a[2] + b[2] + 3 * a[1] * b[0] + 3 * a[0] * b[1]
|
|
21
|
-
return res
|
|
21
|
+
return np.array(res)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def get_self_conv_moments(b: list[float], n_times, num: int = 3) -> list[float]:
|
|
@@ -48,6 +48,14 @@ def conv_moments_minus(a: list[float], b: list[float], num: int = 3) -> list[flo
|
|
|
48
48
|
num = min(num, 3)
|
|
49
49
|
res = [0] * num
|
|
50
50
|
res[0] = a[0] - b[0]
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if num > 1:
|
|
52
|
+
res[1] = a[1] - b[1] - 2 * a[0] * b[0]
|
|
53
|
+
if num > 2:
|
|
54
|
+
res[2] = a[2] - b[2] - 3 * a[1] * b[0] - 3 * a[0] * b[1]
|
|
53
55
|
return res
|
|
56
|
+
|
|
57
|
+
def get_residual_distr_moments(a):
|
|
58
|
+
f = [0.0] * (len(a) - 1)
|
|
59
|
+
for i in range(len(f)):
|
|
60
|
+
f[i] = a[i + 1] / ((i + 2) * a[0])
|
|
61
|
+
return f
|
|
@@ -226,11 +226,11 @@ def print_mrx(mrx, is_short=False):
|
|
|
226
226
|
color = Fore.RED
|
|
227
227
|
else:
|
|
228
228
|
color = Fore.GREEN
|
|
229
|
-
if is_short:
|
|
229
|
+
if is_short or mrx[i, j].imag == 0.0:
|
|
230
230
|
print(f"{color}{mrx[i, j].real:^4.2f} | ", end="")
|
|
231
231
|
else:
|
|
232
232
|
print(f"{color}{mrx[i, j]:^5.3f} | ", end="")
|
|
233
|
-
print("\n" + "-------" * col if is_short else "\n" + "---------------" * col)
|
|
233
|
+
print("\n" + "-------" * col if is_short or mrx[i, j].imag == 0.0 else "\n" + "---------------" * col)
|
|
234
234
|
Fore.RESET # Reset colors after each row
|
|
235
235
|
|
|
236
236
|
|
|
@@ -572,21 +572,21 @@ class DeterministicDistribution(Distribution):
|
|
|
572
572
|
return DeterministicDistribution.generate_static(self.b)
|
|
573
573
|
|
|
574
574
|
@staticmethod
|
|
575
|
-
def generate_static(
|
|
575
|
+
def generate_static(params, generator=None):
|
|
576
576
|
"""
|
|
577
577
|
Generate a constant value b. Static method for use without creating an instance of the class.
|
|
578
578
|
"""
|
|
579
|
-
return
|
|
579
|
+
return params
|
|
580
580
|
|
|
581
581
|
@staticmethod
|
|
582
|
-
def calc_theory_moments(
|
|
582
|
+
def calc_theory_moments(params, num: int) -> list[float]:
|
|
583
583
|
"""
|
|
584
584
|
Calculates theoretical moments of the distribution up to the specified order.
|
|
585
|
-
:param b: constant value.
|
|
585
|
+
:param params = b: constant value.
|
|
586
586
|
:param num: number of moments to calculate.
|
|
587
587
|
:return: list[float]
|
|
588
588
|
"""
|
|
589
|
-
moments = [
|
|
589
|
+
moments = [params**i for i in range(1, num+1)]
|
|
590
590
|
return moments
|
|
591
591
|
|
|
592
592
|
@staticmethod
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simulation model of QS GI/G/n/r and GI/G/n with negative jobs
|
|
3
|
+
"""
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from colorama import Fore, Style, init
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
|
|
12
|
+
from most_queue.sim.utils.distribution_utils import calc_qs_load, create_distribution
|
|
13
|
+
from most_queue.sim.utils.exceptions import QsWrongQueueTypeException
|
|
14
|
+
from most_queue.sim.utils.qs_queue import QsQueueDeque, QsQueueList
|
|
15
|
+
from most_queue.sim.utils.servers import Server
|
|
16
|
+
from most_queue.sim.utils.stats_update import refresh_moments_stat
|
|
17
|
+
from most_queue.sim.utils.tasks import Task
|
|
18
|
+
|
|
19
|
+
init()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NegativeServiceType(Enum):
|
|
23
|
+
"""
|
|
24
|
+
Negative service types
|
|
25
|
+
"""
|
|
26
|
+
DISASTER = 1 # remove all customers
|
|
27
|
+
RCS = 2 # remove customer in service
|
|
28
|
+
RCH = 3 # remove customer at the Head
|
|
29
|
+
RCE = 4 # remove customer at the End
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class QueueingSystemSimulatorWithNegatives:
|
|
33
|
+
"""
|
|
34
|
+
Simulation model of QS GI/G/n/r and GI/G/n with negative jobs
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, num_of_channels: int,
|
|
38
|
+
type_of_negatives: NegativeServiceType = NegativeServiceType.DISASTER,
|
|
39
|
+
buffer: int | None = None,
|
|
40
|
+
verbose: bool = True,
|
|
41
|
+
buffer_type="list"):
|
|
42
|
+
"""
|
|
43
|
+
Initialize the queueing system with GI/G/n/r or GI/G/n model.
|
|
44
|
+
:param num_of_channels: int : number of channels in the system
|
|
45
|
+
:param buffer: Optional(int, None) : maximum length of the queue, None if infinite
|
|
46
|
+
:param verbose: bool : whether to print detailed information during simulation
|
|
47
|
+
:param buffer_type: str : type of the buffer, "list" or "deque"
|
|
48
|
+
"""
|
|
49
|
+
self.n = num_of_channels
|
|
50
|
+
self.buffer = buffer
|
|
51
|
+
self.verbose = verbose
|
|
52
|
+
self.type_of_negatives = type_of_negatives
|
|
53
|
+
|
|
54
|
+
self.generator = np.random.default_rng()
|
|
55
|
+
|
|
56
|
+
self.free_channels = self.n
|
|
57
|
+
self.num_of_states = 100000
|
|
58
|
+
self.load = 0 # utilization factor
|
|
59
|
+
|
|
60
|
+
self.ttek = 0 # current simulation time
|
|
61
|
+
|
|
62
|
+
self.w = [0, 0, 0] # initial moments of waiting time in the QS
|
|
63
|
+
self.v_served = [0, 0, 0] # initial moments of sojourn time of served job in the QS
|
|
64
|
+
self.v_breaked = [0, 0, 0] # initial moments of sojourn time of breaked job in the QS
|
|
65
|
+
|
|
66
|
+
# probabilities of the QS states:
|
|
67
|
+
self.p = [0.0] * self.num_of_states
|
|
68
|
+
|
|
69
|
+
self.taked = 0 # number of job accepted for service
|
|
70
|
+
self.served = 0 # number of job serviced by the system
|
|
71
|
+
self.breaked = 0 # number of job breaked by negative arrivals in the system
|
|
72
|
+
self.in_sys = 0 # number of job in the system
|
|
73
|
+
|
|
74
|
+
# Positive arrivals
|
|
75
|
+
self.positive_arrived = 0 # number of job received
|
|
76
|
+
self.positive_dropped = 0 # number of job denied service
|
|
77
|
+
self.positive_arrival_time = 0 # time of arrival of the next job
|
|
78
|
+
self.positive_source = None
|
|
79
|
+
self.positive_source_params = None
|
|
80
|
+
self.positive_source_types = None
|
|
81
|
+
self.is_set_positive_source_params = False
|
|
82
|
+
|
|
83
|
+
# Negative arrivals
|
|
84
|
+
self.negative_arrived = 0 # number of job received
|
|
85
|
+
self.negative_arrival_time = 0 # time of arrival of the next job
|
|
86
|
+
self.negative_source = None
|
|
87
|
+
self.negative_source_params = None
|
|
88
|
+
self.negative_source_types = None
|
|
89
|
+
self.is_set_negative_source_params = False
|
|
90
|
+
|
|
91
|
+
# queue of jobs: class - Task
|
|
92
|
+
if buffer_type == "list":
|
|
93
|
+
self.queue = QsQueueList()
|
|
94
|
+
elif buffer_type == "deque":
|
|
95
|
+
self.queue = QsQueueDeque()
|
|
96
|
+
else:
|
|
97
|
+
raise QsWrongQueueTypeException("Unknown queue type")
|
|
98
|
+
|
|
99
|
+
self.servers = [] # service channels, list of Server's
|
|
100
|
+
|
|
101
|
+
self.server_params = None
|
|
102
|
+
self.server_types = None
|
|
103
|
+
|
|
104
|
+
self.is_set_server_params = False
|
|
105
|
+
|
|
106
|
+
self.time_spent = 0
|
|
107
|
+
|
|
108
|
+
def set_positive_sources(self, params, types):
|
|
109
|
+
"""
|
|
110
|
+
Specifies the type and parameters of positives source time distribution.
|
|
111
|
+
:param params: list : parameters for the positives source time distribution
|
|
112
|
+
:param types: list : types of positives source time distribution
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
self.positive_source_params = params
|
|
116
|
+
self.positive_source_types = types
|
|
117
|
+
|
|
118
|
+
self.is_set_positive_source_params = True
|
|
119
|
+
|
|
120
|
+
self.positive_source = create_distribution(
|
|
121
|
+
params, types, self.generator)
|
|
122
|
+
|
|
123
|
+
self.positive_arrival_time = self.positive_source.generate()
|
|
124
|
+
|
|
125
|
+
def set_negative_sources(self, params, types):
|
|
126
|
+
"""
|
|
127
|
+
Specifies the type and parameters of negative source time distribution.
|
|
128
|
+
:param params: list : parameters for the negative source time distribution
|
|
129
|
+
:param types: list : types of negative source time distribution
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
self.negative_source_params = params
|
|
133
|
+
self.negative_source_types = types
|
|
134
|
+
|
|
135
|
+
self.is_set_negative_source_params = True
|
|
136
|
+
|
|
137
|
+
self.negative_source = create_distribution(
|
|
138
|
+
params, types, self.generator)
|
|
139
|
+
|
|
140
|
+
self.negative_arrival_time = self.negative_source.generate()
|
|
141
|
+
|
|
142
|
+
def set_servers(self, params, types):
|
|
143
|
+
"""
|
|
144
|
+
Specifies the type and parameters of service time distribution.
|
|
145
|
+
:param params: list : parameters for the service time distribution
|
|
146
|
+
:param types: list : types of service time distribution
|
|
147
|
+
"""
|
|
148
|
+
self.server_params = params
|
|
149
|
+
self.server_types = types
|
|
150
|
+
|
|
151
|
+
self.is_set_server_params = True
|
|
152
|
+
|
|
153
|
+
self.servers = [Server(self.server_params, self.server_types,
|
|
154
|
+
generator=self.generator) for i in range(self.n)]
|
|
155
|
+
|
|
156
|
+
def calc_positive_load(self):
|
|
157
|
+
"""
|
|
158
|
+
Calculates the load factor of the QS if has no disatsers
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
return calc_qs_load(self.positive_source_types,
|
|
162
|
+
self.positive_source_params,
|
|
163
|
+
self.server_types,
|
|
164
|
+
self.server_params, self.n)
|
|
165
|
+
|
|
166
|
+
def send_task_to_channel(self):
|
|
167
|
+
"""
|
|
168
|
+
Sends a job to the service channel
|
|
169
|
+
"""
|
|
170
|
+
for s in self.servers:
|
|
171
|
+
if s.is_free:
|
|
172
|
+
tsk = Task(self.ttek)
|
|
173
|
+
tsk.wait_time = 0
|
|
174
|
+
self.taked += 1
|
|
175
|
+
self.refresh_w_stat(tsk.wait_time)
|
|
176
|
+
|
|
177
|
+
s.start_service(tsk, self.ttek, False)
|
|
178
|
+
self.free_channels -= 1
|
|
179
|
+
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
def send_task_to_queue(self):
|
|
183
|
+
"""
|
|
184
|
+
Send Task to Queue
|
|
185
|
+
"""
|
|
186
|
+
if self.buffer is None: # queue length is not specified, i.e. infinite queue
|
|
187
|
+
new_tsk = Task(self.ttek)
|
|
188
|
+
new_tsk.start_waiting_time = self.ttek
|
|
189
|
+
self.queue.append(new_tsk)
|
|
190
|
+
else:
|
|
191
|
+
if self.queue.size() < self.buffer:
|
|
192
|
+
new_tsk = Task(self.ttek)
|
|
193
|
+
new_tsk.start_waiting_time = self.ttek
|
|
194
|
+
self.queue.append(new_tsk)
|
|
195
|
+
else:
|
|
196
|
+
self.positive_dropped += 1
|
|
197
|
+
self.in_sys -= 1
|
|
198
|
+
|
|
199
|
+
def positive_arrival(self):
|
|
200
|
+
"""
|
|
201
|
+
Actions upon arrival of positive job by the QS.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
self.positive_arrived += 1
|
|
205
|
+
self.p[self.in_sys] += self.positive_arrival_time - self.ttek
|
|
206
|
+
|
|
207
|
+
self.in_sys += 1
|
|
208
|
+
self.ttek = self.positive_arrival_time
|
|
209
|
+
self.positive_arrival_time = self.ttek + self.positive_source.generate()
|
|
210
|
+
|
|
211
|
+
if self.free_channels == 0:
|
|
212
|
+
self.send_task_to_queue()
|
|
213
|
+
else:
|
|
214
|
+
self.send_task_to_channel()
|
|
215
|
+
|
|
216
|
+
def negative_arrival(self):
|
|
217
|
+
"""
|
|
218
|
+
Actions upon arrival of negative job by the QS.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
self.negative_arrived += 1
|
|
222
|
+
self.p[self.in_sys] += self.negative_arrival_time - self.ttek
|
|
223
|
+
self.ttek = self.negative_arrival_time
|
|
224
|
+
self.negative_arrival_time = self.ttek + self.negative_source.generate()
|
|
225
|
+
|
|
226
|
+
if self.in_sys == 0:
|
|
227
|
+
# If no jobs in system, negatives has no effect
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
if self.type_of_negatives == NegativeServiceType.DISASTER:
|
|
231
|
+
self.in_sys = 0
|
|
232
|
+
self.free_channels = self.n
|
|
233
|
+
while self.queue:
|
|
234
|
+
ts = self.queue.pop()
|
|
235
|
+
ts.wait_time += self.ttek - ts.start_waiting_time
|
|
236
|
+
self.taked += 1
|
|
237
|
+
self.breaked += 1
|
|
238
|
+
self.refresh_w_stat(ts.wait_time)
|
|
239
|
+
self.refresh_v_stat_breaked(self.ttek - ts.arr_time)
|
|
240
|
+
|
|
241
|
+
elif self.type_of_negatives == NegativeServiceType.RCE:
|
|
242
|
+
self.in_sys -= 1
|
|
243
|
+
ts = self.queue.tail()
|
|
244
|
+
ts.wait_time += self.ttek - ts.start_waiting_time
|
|
245
|
+
self.taked += 1
|
|
246
|
+
self.breaked += 1
|
|
247
|
+
self.refresh_w_stat(ts.wait_time)
|
|
248
|
+
self.refresh_v_stat_breaked(self.ttek - ts.arr_time)
|
|
249
|
+
|
|
250
|
+
elif self.type_of_negatives == NegativeServiceType.RCH:
|
|
251
|
+
self.in_sys -= 1
|
|
252
|
+
ts = self.queue.pop()
|
|
253
|
+
ts.wait_time += self.ttek - ts.start_waiting_time
|
|
254
|
+
self.taked += 1
|
|
255
|
+
self.breaked += 1
|
|
256
|
+
self.refresh_w_stat(ts.wait_time)
|
|
257
|
+
self.refresh_v_stat_breaked(self.ttek - ts.arr_time)
|
|
258
|
+
|
|
259
|
+
elif self.type_of_negatives == NegativeServiceType.RCS:
|
|
260
|
+
|
|
261
|
+
not_free_servers = [c for c in range(self.n) if not self.servers[c].is_free]
|
|
262
|
+
c = random.choice(not_free_servers)
|
|
263
|
+
|
|
264
|
+
end_ts = self.servers[c].end_service()
|
|
265
|
+
self.breaked += 1
|
|
266
|
+
self.free_channels += 1
|
|
267
|
+
self.refresh_v_stat_breaked(self.ttek - end_ts.arr_time)
|
|
268
|
+
|
|
269
|
+
self.in_sys -= 1
|
|
270
|
+
|
|
271
|
+
if self.queue.size() != 0:
|
|
272
|
+
self.send_head_of_queue_to_channel(c)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def serving(self, c):
|
|
276
|
+
"""
|
|
277
|
+
Actions upon receipt of a service job with - channel number
|
|
278
|
+
"""
|
|
279
|
+
time_to_end = self.servers[c].time_to_end_service
|
|
280
|
+
end_ts = self.servers[c].end_service()
|
|
281
|
+
|
|
282
|
+
self.p[self.in_sys] += time_to_end - self.ttek
|
|
283
|
+
|
|
284
|
+
self.ttek = time_to_end
|
|
285
|
+
self.served += 1
|
|
286
|
+
self.free_channels += 1
|
|
287
|
+
self.refresh_v_stat_served(self.ttek - end_ts.arr_time)
|
|
288
|
+
|
|
289
|
+
self.in_sys -= 1
|
|
290
|
+
|
|
291
|
+
if self.queue.size() != 0:
|
|
292
|
+
self.send_head_of_queue_to_channel(c)
|
|
293
|
+
|
|
294
|
+
def send_head_of_queue_to_channel(self, channel_num):
|
|
295
|
+
"""
|
|
296
|
+
Send first Task (head of queue) to Channel
|
|
297
|
+
"""
|
|
298
|
+
que_ts = self.queue.pop()
|
|
299
|
+
|
|
300
|
+
self.taked += 1
|
|
301
|
+
que_ts.wait_time += self.ttek - que_ts.start_waiting_time
|
|
302
|
+
self.refresh_w_stat(que_ts.wait_time)
|
|
303
|
+
|
|
304
|
+
self.servers[channel_num].start_service(que_ts, self.ttek)
|
|
305
|
+
self.free_channels -= 1
|
|
306
|
+
|
|
307
|
+
def run_one_step(self):
|
|
308
|
+
"""
|
|
309
|
+
Run Open step of simulation
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
num_of_server_earlier = -1
|
|
313
|
+
serv_earl = 1e16
|
|
314
|
+
|
|
315
|
+
for c in range(self.n):
|
|
316
|
+
if self.servers[c].time_to_end_service < serv_earl:
|
|
317
|
+
serv_earl = self.servers[c].time_to_end_service
|
|
318
|
+
num_of_server_earlier = c
|
|
319
|
+
|
|
320
|
+
# Global warm-up is set. Need to track
|
|
321
|
+
# including the moment of warm-up end
|
|
322
|
+
times = [serv_earl, self.positive_arrival_time,
|
|
323
|
+
self.negative_arrival_time]
|
|
324
|
+
min_time_num = np.argmin(times)
|
|
325
|
+
if min_time_num == 0:
|
|
326
|
+
# Serving
|
|
327
|
+
self.serving(num_of_server_earlier)
|
|
328
|
+
elif min_time_num == 1:
|
|
329
|
+
# Arrival positive
|
|
330
|
+
self.positive_arrival()
|
|
331
|
+
else:
|
|
332
|
+
# Arrival negative
|
|
333
|
+
self.negative_arrival()
|
|
334
|
+
|
|
335
|
+
def run(self, total_served, is_real_served=True):
|
|
336
|
+
"""
|
|
337
|
+
Run simulation process
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
# Check if is_set_server_params is set
|
|
341
|
+
if not self.is_set_server_params:
|
|
342
|
+
raise ValueError('Server parameters are not set. Please call set_servers() first.')
|
|
343
|
+
# Check if is_set_positive_source_params
|
|
344
|
+
if not self.is_set_positive_source_params:
|
|
345
|
+
raise ValueError('Positive source parameters are not set. Please call set_positive_sources() first.')
|
|
346
|
+
# Check if is_set_negative_source_params
|
|
347
|
+
if not self.is_set_negative_source_params:
|
|
348
|
+
raise ValueError('Negative source parameters are not set. Please call set_negative_sources() first.')
|
|
349
|
+
|
|
350
|
+
start = time.process_time()
|
|
351
|
+
|
|
352
|
+
print(Fore.GREEN + '\rStart simulation')
|
|
353
|
+
|
|
354
|
+
if is_real_served:
|
|
355
|
+
|
|
356
|
+
last_percent = 0
|
|
357
|
+
|
|
358
|
+
with tqdm(total=100) as pbar:
|
|
359
|
+
while self.served < total_served:
|
|
360
|
+
self.run_one_step()
|
|
361
|
+
percent = int(100*(self.served/total_served))
|
|
362
|
+
if last_percent != percent:
|
|
363
|
+
last_percent = percent
|
|
364
|
+
pbar.update(1)
|
|
365
|
+
pbar.set_description(Fore.MAGENTA + '\rJob served: ' +
|
|
366
|
+
Fore.YELLOW + f'{self.served}/{total_served}' +
|
|
367
|
+
Fore.LIGHTGREEN_EX)
|
|
368
|
+
|
|
369
|
+
else:
|
|
370
|
+
for _ in tqdm(range(total_served)):
|
|
371
|
+
self.run_one_step()
|
|
372
|
+
|
|
373
|
+
print(Fore.GREEN + '\rSimulation is finished')
|
|
374
|
+
print(Style.RESET_ALL)
|
|
375
|
+
|
|
376
|
+
self.time_spent = time.process_time() - start
|
|
377
|
+
|
|
378
|
+
def refresh_v_stat_served(self, new_a):
|
|
379
|
+
"""
|
|
380
|
+
Updating statistics of sojourn times of served jobs
|
|
381
|
+
"""
|
|
382
|
+
self.v_served = refresh_moments_stat(self.v_served, new_a, self.served)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def refresh_v_stat_breaked(self, new_a):
|
|
386
|
+
"""
|
|
387
|
+
Updating statistics of sojourn times of breaked jobs
|
|
388
|
+
"""
|
|
389
|
+
self.v_breaked = refresh_moments_stat(self.v_breaked, new_a, self.breaked)
|
|
390
|
+
|
|
391
|
+
def refresh_w_stat(self, new_a):
|
|
392
|
+
"""
|
|
393
|
+
Updating statistics of wait times
|
|
394
|
+
"""
|
|
395
|
+
self.w = refresh_moments_stat(self.w, new_a, self.taked)
|
|
396
|
+
|
|
397
|
+
def get_p(self):
|
|
398
|
+
"""
|
|
399
|
+
Returns a list with probabilities of QS states
|
|
400
|
+
p[j] - the probability that there will be exactly j jobs
|
|
401
|
+
in the QS at a random moment in time
|
|
402
|
+
"""
|
|
403
|
+
res = [0.0] * len(self.p)
|
|
404
|
+
for j in range(0, self.num_of_states):
|
|
405
|
+
res[j] = self.p[j] / self.ttek
|
|
406
|
+
return res
|
|
407
|
+
|
|
408
|
+
def get_w(self):
|
|
409
|
+
"""
|
|
410
|
+
Returns waiting time initial moments
|
|
411
|
+
"""
|
|
412
|
+
return self.w
|
|
413
|
+
|
|
414
|
+
def get_v_served(self):
|
|
415
|
+
"""
|
|
416
|
+
Returns initial moments of soujourn time served jobs
|
|
417
|
+
"""
|
|
418
|
+
return self.v_served
|
|
419
|
+
|
|
420
|
+
def get_v_breaked(self):
|
|
421
|
+
"""
|
|
422
|
+
Returns initial moments of soujourn time breaked jobs
|
|
423
|
+
"""
|
|
424
|
+
return self.v_breaked
|
|
425
|
+
|
|
426
|
+
def __str__(self, is_short=False):
|
|
427
|
+
|
|
428
|
+
type_of_neg_str = ''
|
|
429
|
+
if self.type_of_negatives == NegativeServiceType.DISASTER:
|
|
430
|
+
type_of_neg_str = 'Disaster'
|
|
431
|
+
elif self.type_of_negatives == NegativeServiceType.RCE:
|
|
432
|
+
type_of_neg_str = 'Remove customer from the End of the Queue'
|
|
433
|
+
elif self.type_of_negatives == NegativeServiceType.RCH:
|
|
434
|
+
type_of_neg_str = 'Remove customer from the Head of the Queue'
|
|
435
|
+
elif self.type_of_negatives == NegativeServiceType.RCS:
|
|
436
|
+
type_of_neg_str = 'Remove customer from the Service'
|
|
437
|
+
else:
|
|
438
|
+
type_of_neg_str = 'Unknown'
|
|
439
|
+
|
|
440
|
+
res = "Queueing system " + self.positive_source_types + \
|
|
441
|
+
"/" + self.server_types + "/" + str(self.n) + type_of_neg_str
|
|
442
|
+
if self.buffer is not None:
|
|
443
|
+
res += "/" + str(self.buffer)
|
|
444
|
+
res += f"\nLoad: {self.calc_positive_load():4.3f}\n"
|
|
445
|
+
res += f"Current Time {self.ttek:8.3f}\n"
|
|
446
|
+
res += f"Positive arrival Time: {self.positive_arrival_time:8.3f}\n"
|
|
447
|
+
res += f"Negative arrival Time: {self.negative_arrival_time:8.3f}\n"
|
|
448
|
+
|
|
449
|
+
res += "Sojourn moments of served jobs:\n"
|
|
450
|
+
for i in range(3):
|
|
451
|
+
res += f"\t{self.v_served[i]:8.4f}"
|
|
452
|
+
res += "\n"
|
|
453
|
+
|
|
454
|
+
res += "Sojourn moments of breaked jobs:\n"
|
|
455
|
+
for i in range(3):
|
|
456
|
+
res += f"\t{self.v_breaked[i]:8.4f}"
|
|
457
|
+
res += "\n"
|
|
458
|
+
|
|
459
|
+
res += "Wait moments:\n"
|
|
460
|
+
for i in range(3):
|
|
461
|
+
res += f"\t{self.w[i]:8.4f}"
|
|
462
|
+
res += "\n"
|
|
463
|
+
|
|
464
|
+
if not is_short:
|
|
465
|
+
res += "Stationary prob:\n"
|
|
466
|
+
res += "\t"
|
|
467
|
+
for i in range(10):
|
|
468
|
+
res += f"{self.p[i] / self.ttek:6.5f}\t"
|
|
469
|
+
res += "\n"
|
|
470
|
+
res += f"Positive arrived: {self.positive_arrived}\n"
|
|
471
|
+
if self.buffer is not None:
|
|
472
|
+
res += f"Positive dropped: {self.positive_dropped}\n"
|
|
473
|
+
res += f"Negative arrived: {self.negative_arrived}\n"
|
|
474
|
+
res += f"Taken: {self.taked}\n"
|
|
475
|
+
res += f"Served: {self.served}\n"
|
|
476
|
+
res += f"In System: {self.in_sys}\n"
|
|
477
|
+
res += "\n"
|
|
478
|
+
for c in range(self.n):
|
|
479
|
+
res += str(self.servers[c])
|
|
480
|
+
res += f"\nQueue Count {self.queue.size()}\n"
|
|
481
|
+
|
|
482
|
+
return res
|