def test_tandem_with_different_services(): stime_limit = 20000 n = 3 services = [Exponential(5), Exponential(8), Exponential(4)] arrivals = [Exponential(10), None, None] simret = tandem_queue_network(arrivals, services, None, stime_limit) rhos = [services[i].mean() / arrivals[0].mean() for i in range(n)] sizes = [r / (1 - r) for r in rhos] delays = [sz * arrivals[0].mean() for sz in sizes] end_to_end_delay = sum(delays) assert_allclose(simret.nodes[0].delay.mean(), end_to_end_delay, rtol=0.25)
def test__as_ph__mixture_with_non_markovian_state_raise_runtime_error(): """ Validate that MixtureDistribution.as_ph() raises ValueError if one of the states is not Markovian (e.g., normal or uniform distribution). """ dist = MixtureDistribution( [Exponential(1.0), Exponential(8.0), Normal(1.0, 0.5)], weights=[0.495, 0.495, 0.01]) with pytest.raises(RuntimeError): dist.as_ph() # However, converting this distribution to PH with min_prob=0.1 is OK: ph = dist.as_ph(min_prob=0.1) assert_allclose(ph.s, [[-1, 0], [0, -8]]) assert_allclose(ph.p, [0.5, 0.5])
def __init__(self, rate: float, factory: RandomsFactory = None): """ Constructor. Parameters ---------- rate : float Rate (1 / mean) of exponential distribution. """ if rate <= 0.0: raise ValueError(f"positive rate expected, {rate} found") super().__init__(Exponential(rate), factory)
def test_tandem_with_fixed_service(): stime_limit = 20000 n = 3 service = Exponential(5) arrivals = [Exponential(10), None, None] simret = tandem_queue_network_with_fixed_service(arrivals, service, None, stime_limit) print(simret) service_means = [simret.nodes[i].service.mean() for i in range(n)] n_services = len(simret.nodes[-1].service.as_tuple()) assert_allclose( simret.nodes[0].service.as_tuple()[:n_services], simret.nodes[1].service.as_tuple()[:n_services], ) assert_allclose( simret.nodes[0].service.as_tuple()[:n_services], simret.nodes[2].service.as_tuple()[:n_services], ) assert_allclose(service_means[0], service.mean(), rtol=0.1)
import pytest from numpy.testing import assert_allclose from pydesim import simulate from pyqumo.random import Exponential, PhaseType from pyqumo.qsim import QueueingSystem, QueueingTandemNetwork, \ tandem_queue_network, tandem_queue_network_with_fixed_service @pytest.mark.parametrize('arrival,service,stime_limit', [ (Exponential(5), Exponential(1), 12000), (Exponential(3), Exponential(2), 12000), (PhaseType.exponential(10.0), PhaseType.exponential(48.0), 4000), ]) def test_mm1_model(arrival, service, stime_limit): ret = simulate(QueueingSystem, stime_limit=stime_limit, params={ 'arrival': arrival, 'service': service, 'queue_capacity': None, }) busy_rate = ret.data.server.busy_trace.timeavg() system_size = ret.data.system_size_trace.timeavg() est_arrival_mean = ret.data.source.intervals.statistic().mean() est_departure_mean = ret.data.sink.arrival_intervals.statistic().mean() est_service_mean = ret.data.server.service_intervals.mean() est_delay = ret.data.source.delays.mean() est_sys_wait = ret.data.system_wait_intervals.mean() est_queue_wait = ret.data.queue.wait_intervals.mean()
def test_exponential_fit(avg): dist = Exponential.fit(avg) assert_allclose(dist.mean, avg)
'dist, m1, m2, m3, m4, string, atol, rtol', [ # Constant distribution: (Const(2), 2, 4, 8, 16, '(Const: value=2)', 1e-2, 2e-2), (Const(3), 3, 9, 27, 81, '(Const: value=3)', 1e-2, 2e-2), # Uniform distribution: (Uniform(0, 1), 0.5, 1 / 3, 1 / 4, 1 / 5, '(Uniform: a=0, b=1)', 1e-2, 2e-2), (Uniform( 2, 10), 6, 124 / 3, 312, 2499.2, '(Uniform: a=2, b=10)', .01, .02), # Normal distribution: (Normal(0, 1), 0, 1, 0, 3, '(Normal: mean=0, std=1)', 1e-2, 2e-2), (Normal(1, 0.5), 1, 1.25, 1.75, 2.6875, '(Normal: mean=1, std=0.5)', 1e-2, 2e-2), # Exponential distribution: (Exponential(1.0), 1, 2, 6, 24, '(Exp: rate=1)', 1e-2, 2e-2), (Exponential(2.0), 1 / 2, 1 / 2, 6 / 8, 24 / 16, '(Exp: rate=2)', 1e-2, 2e-2), # Erlang distribution: (Erlang(1, 1), 1, 2, 6, 24, '(Erlang: shape=1, rate=1)', 1e-2, 2e-2), (Erlang(5, param=2.5), 2, 4.8, 13.44, 43.008, '(Erlang: shape=5, rate=2.5)', 1e-2, 2e-2), # Hyperexponential distribution (HyperExponential([1], [1]), 1, 2, 6, 24, '(HyperExponential: probs=[1], rates=[1])', 1e-2, 2e-2), (HyperExponential([2, 3, 4], probs=[0.5, 0.2, 0.3]), 0.39167, 0.33194, 0.4475694, 0.837384, '(HyperExponential: probs=[0.5, 0.2, 0.3], rates=[2, 3, 4])', 1e-2, 2e-2), # Hypererlang distribution (HyperErlang([1], [1], [1]), 1, 2, 6, 24,
@pytest.mark.parametrize('props', [ GG1Props(arrival=Poisson(2), service=Poisson(5), queue_capacity=4, system_size_avg=0.642, system_size_std=0.981, queue_size_avg=0.2444, queue_size_std=0.6545, loss_prob=0.0062, utilization=0.3975, departure_rate=1.9877, response_time_avg=0.323, wait_time_avg=0.123), GG1Props(arrival=Exponential(42), service=Exponential(34), queue_capacity=7, system_size_avg=5.3295, system_size_std=5.6015**0.5, queue_size_avg=4.3708, queue_size_std=5.2010**0.5, loss_prob=0.2239, utilization=0.9587, departure_rate=32.5959, response_time_avg=0.163, wait_time_avg=0.134, max_packets=int(1e5)), GG1Props(arrival=Poisson(1), service=Exponential(2), queue_capacity=np.inf,
busy_avg=[0.5, 0.5, 0.5], busy_std=[0.5, 0.5, 0.5], # Scalar probabilities and rates: drop_prob=[0, 0, 0], delivery_prob=[1, 1, 1], utilization=[.5, .5, .5], # Intervals: departure_avg=[1, 1, 1], arrival_avg=[1, 1, 1], response_time_avg=[1, 1, 1], wait_time_avg=[.5, .5, .5], delivery_delay_avg=[3, 0, 0]), TandemProps( arrival=[Poisson(1), Poisson(2), Poisson(7)], service=[Exponential(10), Exponential(6), Exponential(40)], queue_capacity=np.inf, num_stations=3, # System and queue sizes: system_size_avg=[1 / 9, 1, 1 / 3], system_size_std=[np.sqrt(10) / 9, np.sqrt(2), 2 / 3], queue_size_avg=[1 / 90, 1 / 2, 1 / 12], queue_size_std=[ np.sqrt(109) / 90, np.sqrt(5) / 2, np.sqrt(19) / 12 ], busy_avg=[1 / 10, 1 / 2, 1 / 4],
wait_time_avg: float # Test parameters: tol: float = 1e-1 max_packets: int = int(1e5) @pytest.mark.parametrize('props', [ GG1Props( arrival=Poisson(2), service=Poisson(5), queue_capacity=4, system_size_avg=0.642, system_size_std=0.981, queue_size_avg=0.2444, queue_size_std=0.6545, loss_prob=0.0062, utilization=0.3975, departure_rate=1.9877, response_time_avg=0.323, wait_time_avg=0.123), GG1Props( arrival=Exponential(42), service=Exponential(34), queue_capacity=7, system_size_avg=5.3295, system_size_std=5.6015**0.5, queue_size_avg=4.3708, queue_size_std=5.2010**0.5, loss_prob=0.2239, utilization=0.9587, departure_rate=32.5959, response_time_avg=0.163, wait_time_avg=0.134, max_packets=int(1e5) ), GG1Props( arrival=Poisson(1), service=Exponential(2), queue_capacity=np.inf, system_size_avg=1, system_size_std=2.0**0.5, queue_size_avg=0.5, queue_size_std=1.25**0.5, loss_prob=0, utilization=0.5, departure_rate=1.0, response_time_avg=1.0, wait_time_avg=0.5, max_packets=int(1e5) )
def __init__(self, d0: Union[np.ndarray, Sequence[Sequence[float]]], d1: Union[np.ndarray, Sequence[Sequence[float]]], safe: bool = False, tol: float = 1e-3, factory: RandomsFactory = None): """ Create MAP process with the given D0 and D1 matrices. If `safe = False` is set (this is default), we validate matrices D0 and D1. These matrices must comply three requirements: 1) Sum `D0 + D1` must be an infinitesimal matrix 2) All elements of D1 must be non-negative 3) All elements of D0 except the main diagonal must be non-negative To avoid problems with "1e-12 is not zero", constructor accepts parameters `tol` (tolerance). If matrices D0 and D1 fail the check, it tries to fix them using `fix_markovian_arrival()` call with this tolerance. Sometimes it is useful to avoid matrices validation (e.g., when MAP matrices are obtained as a result of another computation). To disable validation, set `safe = True`. Parameters ---------- d0 : array_like Matrix D0, it must be subinfinitesimal d1 : array_like Matrix D1, all its elements must be non-negative safe : bool, optional Flag indicating whether not to validate or try to fix matrices D0 and D1 if they don't satisfy requirements. By default, `False`. tol : float, optional If `safe = False` and matrices D0 and D1 violate requirements, try to fix errors those are less then `tol`. If `safe = True` this parameter is ignored. Default: 1e-3. """ super().__init__(factory) need_copy = [False, False] matrices = [d0, d1] for i in range(2): if not isinstance(matrices[i], np.ndarray): matrices[i] = np.asarray(matrices[i]) else: need_copy[i] = True if not safe and not check_markovian_arrival(matrices): matrices, _ = fix_markovian_arrival(matrices, tol=tol) # Since matrices are re-built, no need to copy them: for i in range(2): need_copy[i] = False # Copy matrices if needed: for i in range(2): if need_copy[i]: matrices[i] = matrices[i].copy() # Extract matrices: self._matrices = tuple(matrices) self._generator = sum(self._matrices) self._order = order_of(self._matrices[0]) self._inv_d0 = np.linalg.inv(self._matrices[0]) # Since we will use Mmap for data generation, we need to generate # special matrices for transitions probabilities and rates. # 1) We need to store rates (we will use them when choosing random # time we spend in the state): self._rates = -self._matrices[0].diagonal() # 2) Then, we need to store cumulative transition probabilities P. # We store them in a stochastic matrix of shape N x (K*N): # P[I, J] is a probability, that: # - new state will be J (mod N), and # - if J < N, then no packet is generated, or # - if J >= N, then packet of type J // N is generated. # self._trans_pmf = np.hstack(( # self._matrices[0] + np.diag(self._rates), # self._matrices[1] # )) / self._rates[:, None] # Build embedded DTMC and CTMC: self._ctmc = ContinuousTimeMarkovChain(self._generator, safe=True) self._dtmc = DiscreteTimeMarkovChain(-self._inv_d0.dot(self.d1), safe=True) self._states = [ Exponential(rate, factory=factory) for rate in self._rates ]