Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impovements from PRs #245

Merged
merged 31 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
407c6a4
Node does need to explicitly inherit from object.
galenseilis Dec 3, 2023
13ea127
Mixtures and __repr__ enhancements.
galenseilis Dec 7, 2023
49a229c
Missing import
galenseilis Dec 7, 2023
e8589cd
More adjustments to the typing.
galenseilis Dec 7, 2023
1d81843
__repr__ tests
galenseilis Dec 7, 2023
54cd5b6
Sequential __repr__ test
galenseilis Dec 7, 2023
c3ed322
Update test_network.py
galenseilis Dec 7, 2023
3668413
Missing f-string in distributions.py
galenseilis Dec 7, 2023
9ff796c
More missing __repr__ updates in test_network.py
galenseilis Dec 7, 2023
0978e28
More __repr__ changes in test_network.py
galenseilis Dec 7, 2023
ad9da41
NumPy docstrings and type hints in disciplines.py
galenseilis Dec 9, 2023
3a19a8a
Docstrings and type hints for schedules.Schedule
galenseilis Dec 15, 2023
351c312
Update other.rst
galenseilis Dec 19, 2023
fc6e9a4
Change `!=` to `is not` in import_params.py
galenseilis Jan 7, 2024
232f95e
Remove inheriting from `object` state_tracker.py
galenseilis Jan 7, 2024
6525798
Merge pull request #224 from galenseilis/patch-4
geraintpalmer Jan 24, 2024
dd19e18
update sphinx requirement
geraintpalmer Jan 24, 2024
cd180fb
unpin shinx version
geraintpalmer Jan 24, 2024
6b99b2f
Update Sphinx version.
galenseilis Feb 7, 2024
d5d2275
Merge branch 'impovements_from_PRs' into patch-15
geraintpalmer Apr 3, 2024
524767f
Merge pull request #243 from galenseilis/patch-15
geraintpalmer Apr 3, 2024
ef169af
Merge pull request #241 from galenseilis/patch-13
geraintpalmer Apr 3, 2024
337c2e7
Merge pull request #236 from galenseilis/patch-11
geraintpalmer Apr 3, 2024
2d0ea67
Merge pull request #233 from galenseilis/patch-10
geraintpalmer Apr 3, 2024
ae4655d
update schedules docstring
geraintpalmer Apr 3, 2024
782075c
Merge pull request #229 from galenseilis/patch-7
geraintpalmer Apr 3, 2024
7747d8a
Merge pull request #227 from galenseilis/patch-6
geraintpalmer Apr 3, 2024
e85115d
add names to distribution reprs
geraintpalmer Apr 3, 2024
adf55ba
rename 'rhos' to 'probs' in Mixture distribution; write tests for Mix…
geraintpalmer Apr 3, 2024
a4eb4e2
add docs for Mixture distribution
geraintpalmer Apr 3, 2024
8626345
Merge pull request #240 from galenseilis/patch-12
geraintpalmer Apr 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ciw/arrival_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .individual import Individual


class ArrivalNode(object):
class ArrivalNode:
"""Class for the arrival node of the network.

See Also
Expand Down
44 changes: 34 additions & 10 deletions ciw/disciplines.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
from typing import List

from ciw.individual import Individual
from ciw.auxiliary import random_choice


def FIFO(individuals):
def FIFO(individuals: List[Individual]) -> Individual:
"""
FIFO: First in first out / First come first served
Returns the individual at the head of the queue
FIFO: First In, First Out (FIFO)

Returns the individual at the head of the queue.

Parameters:
- individuals (List[Individual]): List of individuals in the queue.

Returns:
- Individual: The individual at the head of the queue.
"""
return individuals[0]


def SIRO(individuals):
def SIRO(individuals: List[Individual]) -> Individual:
"""
SIRO: Service in random order
Returns a random individual from the queue
SIRO: Service In Random Order (SIRO)

Returns a random individual from the queue.

Parameters:
- individuals (List[Individual]): List of individuals in the queue.

Returns:
- Individual: A randomly selected individual from the queue.
"""
return random_choice(individuals)


def LIFO(individuals):
def LIFO(individuals: List[Individual]) -> Individual:
"""
LIFO: Last in first out / Last come first served
Returns the individual who joined the queue most recently
LIFO: Last In, First Out (LIFO)

Returns the individual who joined the queue most recently.

Parameters:
- individuals (List[Individual]): List of individuals in the queue.

Returns:
- Individual: The individual who joined the queue most recently.
"""
return individuals[-1]
return individuals[-1]
114 changes: 97 additions & 17 deletions ciw/dists/distributions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ciw.auxiliary import *
from itertools import cycle
import numpy as np
'''Distributions available in Ciw.'''

import copy
from itertools import cycle
from operator import add, mul, sub, truediv
from random import (
expovariate,
Expand All @@ -11,7 +11,12 @@
lognormvariate,
weibullvariate,
)
from typing import List, NoReturn

import numpy as np

from ciw.auxiliary import *
from ciw.individual import Individual

class Distribution(object):
"""
Expand Down Expand Up @@ -99,7 +104,7 @@ def __init__(self, lower, upper):
self.upper = upper

def __repr__(self):
return "Uniform: {0}, {1}".format(self.lower, self.upper)
return f"Uniform(lower={self.lower}, upper={self.upper})"

def sample(self, t=None, ind=None):
return uniform(self.lower, self.upper)
Expand All @@ -121,7 +126,7 @@ def __init__(self, value):
self.value = value

def __repr__(self):
return "Deterministic: {0}".format(self.value)
return f"Deterministic(value={self.value})"

def sample(self, t=None, ind=None):
return self.value
Expand Down Expand Up @@ -151,7 +156,7 @@ def __init__(self, lower, mode, upper):
self.upper = upper

def __repr__(self):
return "Triangular: {0}, {1}, {2}".format(self.lower, self.mode, self.upper)
return f"Triangular(lower={self.lower}, mode={self.mode}, upper={self.upper})"

def sample(self, t=None, ind=None):
return triangular(self.lower, self.upper, self.mode)
Expand All @@ -173,7 +178,7 @@ def __init__(self, rate):
self.rate = rate

def __repr__(self):
return "Exponential: {0}".format(self.rate)
return f"Exponential(rate={self.rate})"

def sample(self, t=None, ind=None):
return expovariate(self.rate)
Expand All @@ -193,7 +198,7 @@ def __init__(self, shape, scale):
self.scale = scale

def __repr__(self):
return "Gamma: {0}, {1}".format(self.shape, self.scale)
return f"Gamma(shape={self.shape}, scale={self.scale})"

def sample(self, t=None, ind=None):
return gammavariate(self.shape, self.scale)
Expand All @@ -213,7 +218,7 @@ def __init__(self, mean, sd):
self.sd = sd

def __repr__(self):
return "Normal: {0}, {1}".format(self.mean, self.sd)
return f"Normal(mean={self.mean}, sd={self.sd})"

def sample(self, t=None, ind=None):
return truncated_normal(self.mean, self.sd)
Expand All @@ -233,7 +238,7 @@ def __init__(self, mean, sd):
self.sd = sd

def __repr__(self):
return "Lognormal: {0}, {1}".format(self.mean, self.sd)
return f"Lognormal(mean={self.mean}, sd={self.sd})"

def sample(self, t=None, ind=None):
return lognormvariate(self.mean, self.sd)
Expand All @@ -253,7 +258,7 @@ def __init__(self, scale, shape):
self.shape = shape

def __repr__(self):
return "Weibull: {0}, {1}".format(self.scale, self.shape)
return f"Weibull(shape={self.scale}, scale={self.shape})"

def sample(self, t=None, ind=None):
return weibullvariate(self.scale, self.shape)
Expand Down Expand Up @@ -298,7 +303,10 @@ def __init__(self, sequence):
self.generator = cycle(self.sequence)

def __repr__(self):
return "Sequential"
if len(self.sequence) <= 3:
return f"Sequential({self.sequence})"
else:
return f"Sequential([{self.sequence[0]}, ..., {self.sequence[-1]}])"

def sample(self, t=None, ind=None):
return next(self.generator)
Expand All @@ -324,7 +332,7 @@ def __init__(self, values, probs):
self.probs = probs

def __repr__(self):
return "Pmf"
return f"Pmf(values={self.values}, probs={self.probs})"

def sample(self, t=None, ind=None):
return random_choice(self.values, self.probs)
Expand Down Expand Up @@ -420,7 +428,7 @@ def __init__(self, rate, num_phases):
super().__init__(initial_state, absorbing_matrix)

def __repr__(self):
return f"Erlang: {self.rate}, {self.num_phases}"
return f"Erlang(rate={self.rate}, k={self.num_phases})"


class HyperExponential(PhaseType):
Expand Down Expand Up @@ -611,7 +619,7 @@ def sample(self, t=None, ind=None):
return ciw.rng.poisson(lam=self.rate)

def __repr__(self):
return f"Poisson: {self.rate}"
return f"Poisson(rate={self.rate})"


class Geometric(Distribution):
Expand All @@ -634,7 +642,7 @@ def sample(self, t=None, ind=None):
return ciw.rng.geometric(p=self.prob)

def __repr__(self):
return f"Geometric: {self.prob}"
return f"Geometric(prob={self.prob})"


class Binomial(Distribution):
Expand Down Expand Up @@ -663,4 +671,76 @@ def sample(self, t=None, ind=None):
return ciw.rng.binomial(n=self.n, p=self.prob)

def __repr__(self):
return f"Binomial: {self.n}, {self.prob}"
return f"Binomial(n={self.n}, prob={self.prob})"


class MixtureDistribution(Distribution):
"""
A mixture distribution combining multiple probability distributions.

Parameters
----------
dists : List[Distribution]
A list of probability distributions to be combined in the mixture.
probs : List[float]
A list of weights corresponding to the importance of each distribution in the mixture.
The weights must sum to 1.

Attributes
----------
probs : List[float]
List of weights assigned to each distribution in the mixture.
dists : List[Distribution]
List of probability distributions in the mixture.

Methods
-------
sample(t: float, inds: List[Individual] = None) -> float:
Generate a random sample from the mixture distribution.

Notes
-----
The weights in `probs` should sum to 1, indicating the relative importance of each distribution
in the mixture. The distributions in `dists` should be instances of `ciw.dists.Distribution`.
"""

def __init__(self, dists: List[Distribution], probs: List[float]) -> NoReturn:
"""
Initialize the MixtureDistribution.

Parameters
----------
dists : List[Distribution]
A list of probability distributions to be combined in the mixture.
probs : List[float]
A list of weights corresponding to the importance of each distribution in the mixture.
The weights must sum to 1.
"""
self.probs = probs
self.dists = dists

def sample(self, t: float = None, inds: List[Individual] = None) -> float:
"""
Generate a random sample from the mixture distribution.

Parameters
----------
t : float
The time parameter for the sample generation.
inds : List[Individual], optional
List of individuals associated with the sample, if applicable.

Returns
-------
float
A random sample from the mixture distribution.
"""
chosen_dist = random.choices(
population=self.dists,
weights=self.probs,
k=1)[0]

return chosen_dist.sample(t, inds)

def __repr__(self):
return "MixtureDistribution"
16 changes: 8 additions & 8 deletions ciw/import_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,23 @@ def create_network(
"service_distributions": service_distributions,
}

if baulking_functions != None:
if baulking_functions is not None:
params["baulking_functions"] = baulking_functions
if class_change_matrices != None:
if class_change_matrices is not None:
params["class_change_matrices"] = class_change_matrices
if class_change_time_distributions is not None:
params["class_change_time_distributions"] = class_change_time_distributions
if priority_classes != None:
if priority_classes is not None:
params["priority_classes"] = priority_classes
if queue_capacities != None:
if queue_capacities is not None:
params["queue_capacities"] = queue_capacities
if routing != None:
if routing is not None:
params["routing"] = routing
if batching_distributions != None:
if batching_distributions is not None:
params["batching_distributions"] = batching_distributions
if ps_thresholds != None:
if ps_thresholds is not None:
params["ps_thresholds"] = ps_thresholds
if server_priority_functions != None:
if server_priority_functions is not None:
params["server_priority_functions"] = server_priority_functions
if reneging_time_distributions is not None:
params["reneging_time_distributions"] = reneging_time_distributions
Expand Down
Loading
Loading