Skip to content

Commit 27cc36e

Browse files
committed
refactor/build(distributions): remove distributions and their tests - instead, import sim-tools (with tests add to the sim-tools repository)
1 parent 54e7043 commit 27cc36e

File tree

4 files changed

+2
-342
lines changed

4 files changed

+2
-342
lines changed

environment.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ dependencies:
2020
- simpy=4.1.1
2121
- pip:
2222
- kaleido==0.2.1
23+
- sim-tools==0.8.0
2324
- -e .[dev]

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ pytest==8.3.4
1414
pytest-xdist==3.6.1
1515
rich==13.9.4
1616
simpy==4.1.1
17+
sim-tools==0.8.0
1718
-e .[dev]

simulation/distributions.py

Lines changed: 0 additions & 158 deletions
This file was deleted.

tests/test_unittest.py

Lines changed: 0 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,8 @@
88
from simulation.parameters import (
99
ASUArrivals, RehabArrivals, ASULOS, RehabLOS,
1010
ASURouting, RehabRouting, Param)
11-
from simulation.distributions import Exponential, LogNormal, Discrete
1211

1312

14-
# -----------------------------------------------------------------------------
15-
# Parameter classes
16-
# -----------------------------------------------------------------------------
17-
1813
@pytest.mark.parametrize('class_to_test', [
1914
ASUArrivals, RehabArrivals, ASULOS, RehabLOS,
2015
ASURouting, RehabRouting, Param])
@@ -33,182 +28,3 @@ def test_new_attribute(class_to_test):
3328
with pytest.raises(AttributeError,
3429
match='only possible to modify existing attributes'):
3530
setattr(instance, 'new_entry', 3)
36-
37-
38-
# -----------------------------------------------------------------------------
39-
# Distributions
40-
# -----------------------------------------------------------------------------
41-
42-
@pytest.mark.parametrize('dist_class, params, expected_mean, expected_type', [
43-
(Exponential, {'mean': 5}, 5, float),
44-
(LogNormal, {'mean': 1, 'stdev': 0.5}, 1, float),
45-
(Discrete, {'values': [1, 2, 3], 'freq': [0.2, 0.5, 0.3]}, 2.1, int)
46-
])
47-
def test_samples(dist_class, params, expected_mean, expected_type):
48-
"""
49-
Test that generated samples match the expected mean and requested size,
50-
and that the random seed is working.
51-
52-
Arguments:
53-
dist_class (class):
54-
Distribution class to test.
55-
params (dict):
56-
Parameters for initialising the distribution.
57-
expected_mean (float):
58-
Expected mean of the distribution.
59-
expected_type (type):
60-
Expected type of the sample (float for continuous distributions,
61-
int for discrete).
62-
"""
63-
# Initialise the distribution
64-
dist = dist_class(random_seed=42, **params)
65-
66-
# Check that sample is a float
67-
x = dist.sample()
68-
assert isinstance(x, expected_type), (
69-
f'Expected sample() to return a {expected_type} - instead: {type(x)}'
70-
)
71-
72-
# Check that the mean of generated samples is close to the expected mean
73-
samples = dist.sample(size=10000)
74-
assert np.isclose(np.mean(samples), expected_mean, rtol=0.1)
75-
76-
# Check that the sample size matches the requested size
77-
assert len(samples) == 10000
78-
79-
# Check that the same seed returns the same sample
80-
sample1 = dist_class(random_seed=5, **params).sample(size=5)
81-
sample2 = dist_class(random_seed=5, **params).sample(size=5)
82-
assert np.array_equal(sample1, sample2), (
83-
'Samples with the same random seeds should be equal.'
84-
)
85-
86-
# Check that different seeds return different samples
87-
sample3 = dist_class(random_seed=89, **params).sample(size=5)
88-
assert not np.array_equal(sample1, sample3), (
89-
'Samples with different random seeds should not be equal.'
90-
)
91-
92-
93-
def test_invalid_exponential():
94-
"""
95-
Ensure that Exponential distribution cannot be created with a negative
96-
or zero mean.
97-
"""
98-
# Negative mean
99-
with pytest.raises(ValueError):
100-
Exponential(mean=-5, random_seed=42)
101-
102-
# Zero mean
103-
with pytest.raises(ValueError):
104-
Exponential(mean=0, random_seed=42)
105-
106-
# Check that no negative values are sampled
107-
d = Exponential(mean=10, random_seed=42)
108-
bigsample = d.sample(size=100000)
109-
assert all(x > 0 for x in bigsample), (
110-
'Sample contains non-positive values.'
111-
)
112-
113-
114-
def test_lognormal_moments():
115-
"""
116-
Test the calculation of normal distribution parameters (mu, sigma) from
117-
lognormal parameters.
118-
119-
This test verifies that:
120-
1. The normal_moments_from_lognormal method correctly converts lognormal
121-
parameters (mean, variance) to normal distribution parameters (mu, sigma).
122-
2. The calculated values match the expected mathematical formulas.
123-
"""
124-
# Define lognormal parameters
125-
mean, stdev = 2.0, 0.5
126-
127-
# Initialise distribution and get calculated parameters
128-
dist = LogNormal(mean=mean, stdev=stdev, random_seed=42)
129-
calculated_mu, calculated_sigma = (
130-
dist.normal_moments_from_lognormal(mean, stdev**2))
131-
132-
# Verify calculated parameters match expected mathematical formulas
133-
# Formula for mu: ln(mean²/√(stdev² + mean²))
134-
assert np.isclose(calculated_mu,
135-
np.log(mean**2 / np.sqrt(stdev**2 + mean**2)),
136-
rtol=1e-5)
137-
# Formula for sigma: √ln(1 + stdev²/mean²)
138-
assert np.isclose(calculated_sigma,
139-
np.sqrt(np.log(1 + stdev**2 / mean**2)),
140-
rtol=1e-5)
141-
142-
143-
def test_discrete_probabilities():
144-
"""
145-
Test correct calculation of probabilities for Discrete distribution.
146-
147-
This test verifies that:
148-
1. The Discrete class correctly normalizes frequency values to
149-
probabilities.
150-
2. The sum of probabilities equals 1
151-
3. The relative proportions match the input frequencies
152-
"""
153-
# Define discrete distribution parameters
154-
values = [1, 2, 3]
155-
freq = [10, 20, 30]
156-
157-
# Initialise distribution
158-
dist = Discrete(values=values, freq=freq, random_seed=42)
159-
160-
# Calculate expected probabilities by normalising frequencies
161-
expected_probs = np.array(freq) / np.sum(freq)
162-
163-
# Verify calculated probabilities match expected values
164-
assert np.allclose(dist.probabilities, expected_probs, rtol=1e-5)
165-
166-
# Verify probabilities sum to 1
167-
assert np.isclose(np.sum(dist.probabilities), 1.0, rtol=1e-10)
168-
169-
170-
def test_discrete_value_error():
171-
"""
172-
Test if Discrete raises ValueError for mismatched inputs.
173-
174-
This test verifies that the Discrete class correctly validates that
175-
the values and frequencies arrays have the same length.
176-
"""
177-
# Attempt to initialise with mismatched array lengths
178-
with pytest.raises(ValueError):
179-
Discrete(values=[1, 2], freq=[0.5], random_seed=42)
180-
181-
182-
def test_invalid_input_types():
183-
"""
184-
Test error handling for invalid string input types.
185-
186-
This test verifies that appropriate errors are raised when
187-
string values are provided instead of numeric values for
188-
distribution parameters.
189-
"""
190-
with pytest.raises(TypeError):
191-
Exponential(mean='5')
192-
with pytest.raises(TypeError):
193-
LogNormal(mean='4', stdev=1)
194-
with pytest.raises(TypeError):
195-
Discrete(values=[1, 2, 3], freq=['0.2', '0.5', '0.3'])
196-
197-
198-
def test_discrete_uneven_probabilities():
199-
"""
200-
Test behavior of Discrete distribution with highly uneven probabilities.
201-
202-
This test verifies that when one probability is much larger than another,
203-
the sampling correctly reflects this imbalance by rarely selecting the
204-
low-probability value.
205-
"""
206-
# Create distribution with extremely uneven probabilities
207-
dist = Discrete(values=[1, 2], freq=[1, 1e-10], random_seed=42)
208-
209-
# Generate a large sample
210-
samples = dist.sample(size=10000)
211-
212-
# Verify that the low-probability value (2) appears very rarely
213-
# With p ≈ 1e-10, we expect virtually no occurrences of value 2
214-
assert np.sum(samples == 2) < 5

0 commit comments

Comments
 (0)