Source code for topsim.core.delay

# Copyright (C) 12/10/20 RW Bunney

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

from enum import Enum
import logging

from numpy.random import default_rng, seed

LOGGER = logging.getLogger(__name__)


[docs] class DelayModel: """ The delay model is the delay or failure model for tasks in a workflow. If we have a possibility for delay, then the timeout triggered by a process will be increased by a certain portion. A delay model object may be passed to any of the actors within the simulation that yield a timeout to the environment. Each actor will store their expected (current) timeouts and, if present, the actual timeout if a delay has been triggered. Attributes ---------- prob : float The probabilty a delay will occur dist : str String name of the distribution from which values will be picked to create a delay. Currently normal, poisson, and uniform distributions from numpy are supported. If degree : enumerate.Enum (float) The 'degree' to which we will be generating the delay. The higher degree, the larger the final delay. """ class DelayDegree(Enum): LOW = 0.25 MID = 0.5 HIGH = 0.75 NONE = 0 def __init__(self, prob, dist, degree=DelayDegree.LOW, seed=20): """ Parameters ---------- prob : float probability that a delay will occur dist : str String name of the distribution from which values will be picked to create a delay. degree : enumerate.Enum (float) The 'degree' to which we will be generating the delay. The higher degree, the larger the final delay. seed : int The input seed to ensure repeatable randomness """ _allowed_dist = ['normal', 'poisson', 'uniform'] if dist not in _allowed_dist: LOGGER.debug( "Distribution function not specified; defaulting " "to uniform distribution." ) dist = 'uniform' self.prob = prob self.dist = dist self.degree = degree self.seed = seed def __str__(self): return str(self.degree) def generate_delay(self, task_runtime, n=100): """ Produce a delay based on current DelayModel attributes. A delay is a unit of time to be passed to the timeout. Given a probability, return the new delay by a factor of 'degree' Returns ------- delay : int This is the runtime+delay value = essentially the new runtime value """ delay = task_runtime if self.degree.value == 0: return delay else: if default_rng(self.seed).random() < self.prob: rand_var = self._create_random_value_from_runtime(task_runtime, n) delay = int(rand_var) return delay def _create_random_value_from_runtime(self, runtime, n=100): """ Take a runtime value and generate a distribution from that value using the self.dist distrubution, based on the numpy distribution function. This distribution uses the runtime value as the mean (mu) value Returns ------- rand_var : int Random integer variable drawn from a distribution based on the runtime value """ s = None mu = runtime sigma = self.degree.value * mu if self.dist == "normal": s = default_rng(self.seed).normal(mu, sigma, n) elif self.dist == "poisson": s = default_rng().poisson(mu, int(runtime / self.degree)) else: s = default_rng().uniform() var = s[s > mu] rand_var = var[int(len(var)/2)] return rand_var