Skip to content

Commit b35d129

Browse files
committed
fix: deterministic expenv, feature: uniform distribution
1 parent b412212 commit b35d129

File tree

18 files changed

+457
-50
lines changed

18 files changed

+457
-50
lines changed

experimental_env/analysis/analyze_summarizers/error_summarizer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ def calculate(self, results: list[ExperimentDescription]) -> tuple:
3838

3939
errors.append(error)
4040

41+
if not errors:
42+
return 0, 0, 0
43+
4144
mean = np.sum(errors) / len(errors)
4245
standart_deviation = np.sqrt(np.sum([(x - mean) ** 2 for x in errors]) / len(errors))
43-
44-
errors.sort()
4546
median = errors[len(errors) // 2]
4647

4748
return float(mean), float(standart_deviation), float(median)

experimental_env/experiment/estimators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(self, brkpointer: EM.ABreakpointer, dst_checker: EM.ADistributionCh
5050

5151
@property
5252
def name(self):
53-
return "MLE-EM"
53+
return "EM"
5454

5555
def _helper(self, problem: OrderedProblem):
5656
"""
@@ -90,7 +90,7 @@ def __init__(self, brkpointer, dst_checker):
9090

9191
@property
9292
def name(self):
93-
return "LM-EM"
93+
return "ELM"
9494

9595
def _helper(self, problem: OrderedProblem):
9696
"""

experimental_env/experiment/experiment_executors/abstract_executor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""A module that provides an abstract class for performing the 2nd stage of the experiment"""
22

3+
import random
34
import warnings
45
from abc import ABC, abstractmethod
56
from pathlib import Path
@@ -20,7 +21,7 @@ class AExecutor(ABC):
2021
as well as the implementation of the execute method, to implement the 2nd stage of the experiment.
2122
"""
2223

23-
def __init__(self, path: Path, cpu_count: int, seed):
24+
def __init__(self, path: Path, cpu_count: int, seed: int):
2425
"""
2526
Class constructor
2627
@@ -31,6 +32,8 @@ def __init__(self, path: Path, cpu_count: int, seed):
3132
self._out_dir = path
3233
self._cpu_count = cpu_count
3334
self._seed = seed
35+
36+
random.seed(self._seed)
3437
np.random.seed(self._seed)
3538

3639
@abstractmethod

experimental_env/experiment/experiment_executors/random_executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def init_problems(self, ds_descriptions, models):
1919
return [
2020
Problem(
2121
descr.samples,
22-
RandomMixtureGenerator(self._seed).create_mixture(models),
22+
RandomMixtureGenerator().create_mixture(models),
2323
)
2424
for i, descr in enumerate(ds_descriptions)
2525
]

experimental_env/experiment/experiment_executors/standart_executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def init_problems(self, ds_descriptions, models):
1919
return [
2020
Problem(
2121
descr.samples,
22-
StandartMixtureGenerator(self._seed).create_mixture(models),
22+
StandartMixtureGenerator().create_mixture(models),
2323
)
2424
for i, descr in enumerate(ds_descriptions)
2525
]

experimental_env/mixture_generators/abstract_generator.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""A module that provides an abstract class for generating a mixture."""
22

3-
import random
43
from abc import ABC, abstractmethod
54

65
from mpest import Distribution, MixtureDistribution
@@ -12,9 +11,6 @@ class AMixtureGenerator(ABC):
1211
An abstract class for generating mixtures.
1312
"""
1413

15-
def __init__(self, seed: int = 42):
16-
random.seed(seed)
17-
1814
@abstractmethod
1915
def generate_priors(self, models: list[type[AModel]]) -> list[float | None]:
2016
"""

experimental_env/mixture_generators/utils.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from random import uniform
44

55
from mpest import Distribution
6-
from mpest.models import AModel, ExponentialModel, GaussianModel
6+
from mpest.models import AModel, Beta, Cauchy, ExponentialModel, GaussianModel, Pareto
7+
from mpest.models.uniform import Uniform
78

89

910
def generate_standart_params(models: list[type[AModel]]) -> list[Distribution]:
@@ -14,10 +15,14 @@ def generate_standart_params(models: list[type[AModel]]) -> list[Distribution]:
1415
for m in models:
1516
if m == ExponentialModel:
1617
params = [1.0]
17-
elif m == GaussianModel:
18+
elif m in (GaussianModel, Uniform, Cauchy):
1819
params = [0.0, 1.0]
19-
else:
20-
params = [1.0, 1.5]
20+
elif m == Beta:
21+
params = [1.0, 1.0]
22+
elif m == Pareto:
23+
params = [1.0, 2.0]
24+
else: # Weibull
25+
params = [1.0, 1.0]
2126

2227
dists.append(Distribution.from_params(m, params))
2328

@@ -34,7 +39,15 @@ def generate_uniform_params(models: list[type[AModel]]) -> list[Distribution]:
3439
params = [uniform(0.1, 5.0)]
3540
elif m == GaussianModel:
3641
params = [uniform(-5.0, 5.0), uniform(0.1, 5.0)]
37-
else:
42+
elif m == Uniform:
43+
params = list(sorted([uniform(-5.0, 5.0), uniform(-5.0, 5.0)]))
44+
elif m == Cauchy:
45+
params = [uniform(-5.0, 5.0), uniform(0.1, 5.0)]
46+
elif m == Beta:
47+
params = [uniform(0.1, 5.0), uniform(0.1, 5.0)]
48+
elif m == Pareto:
49+
params = [uniform(0.1, 5.0), uniform(1.0, 5.0)]
50+
else: # Weibull
3851
params = [uniform(0.1, 5.0), uniform(0.1, 5.0)]
3952

4053
dists.append(Distribution.from_params(m, params))

experimental_env/preparation/dataset_generator.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ def __init__(self, seed: int = 42):
2626
"""
2727
Setting seed for determined result.
2828
"""
29-
random.seed(seed)
3029
self._seed = seed
3130

31+
random.seed(self._seed)
32+
np.random.seed(self._seed)
33+
3234
def generate(
3335
self,
3436
samples_size: int,
@@ -59,7 +61,6 @@ class ConcreteDatasetGenerator:
5961
"""
6062

6163
def __init__(self, seed: int = 42):
62-
np.random.seed(seed)
6364
self._dists: list[Distribution] = []
6465
self._priors: list[float | None] = []
6566

mpest/em/methods/likelihood_method.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
from mpest.core.mixture_distribution import MixtureDistribution
99
from mpest.core.problem import Problem, Result
1010
from mpest.em.methods.abstract_steps import AExpectation, AMaximization
11-
from mpest.exceptions import SampleError
12-
from mpest.models import AModel, AModelDifferentiable
11+
from mpest.models import AModel, AModelDifferentiable, Uniform
1312
from mpest.optimizers import AOptimizerJacobian, TOptimizer
1413
from mpest.utils import ResultWithError
1514

@@ -31,16 +30,8 @@ def step(self, problem: Problem) -> EResult:
3130
samples = problem.samples
3231
mixture = problem.distributions
3332
p_xij = []
34-
active_samples = []
3533
for x in samples:
36-
p = np.array([d.model.pdf(x, d.params) for d in mixture])
37-
if np.any(p):
38-
p_xij.append(p)
39-
active_samples.append(x)
40-
41-
if not active_samples:
42-
error = SampleError("None of the elements in the sample is correct for this mixture")
43-
return ResultWithError(mixture, error)
34+
p_xij.append(np.array([d.model.pdf(x, d.params) for d in mixture]))
4435

4536
# h[j, i] contains probability of X_i to be a part of distribution j
4637
m = len(p_xij)
@@ -56,7 +47,7 @@ def step(self, problem: Problem) -> EResult:
5647

5748
h[:, i] = wp / swp
5849

59-
return active_samples, h, problem
50+
return samples, h, problem
6051

6152

6253
# class ML(AExpectation[EResult]):
@@ -109,8 +100,30 @@ def step(self, e_result: EResult) -> Result:
109100
for j, ch in enumerate(h[:]):
110101
d = mixture[j]
111102

103+
if isinstance(d.model, Uniform):
104+
threshold = 1e-2
105+
curr_a, curr_b = d.params
106+
relevant_indices = np.where(ch > threshold)[0]
107+
if len(relevant_indices) == 0:
108+
new_params = d.params
109+
else:
110+
relevant_samples = np.array(samples)[relevant_indices]
111+
112+
new_a = np.min(relevant_samples)
113+
new_b = np.max(relevant_samples)
114+
115+
new_params = np.array([new_a, new_b])
116+
117+
new_distributions.append(Distribution(d.model, new_params))
118+
continue
119+
112120
def log_likelihood(params, ch, model: AModel):
113-
return -np.sum(ch * [model.lpdf(x, params) for x in samples])
121+
Y = np.array([model.lpdf(x, params) for x in samples])
122+
penalty = 1e20
123+
weighted_neg_lpdf = [-c * y if y != -np.inf else penalty * c for y, c in zip(Y, ch)]
124+
output = np.sum(weighted_neg_lpdf)
125+
126+
return output
114127

115128
def jacobian(params, ch, model: AModelDifferentiable):
116129
return -np.sum(

mpest/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
)
88
from mpest.models.exponential import ExponentialModel
99
from mpest.models.gaussian import GaussianModel
10+
from mpest.models.uniform import Uniform
1011
from mpest.models.weibull import WeibullModelExp
1112
from mpest.models.cauchy import Cauchy
1213
from mpest.models.pareto import Pareto
@@ -19,4 +20,5 @@
1920
Cauchy().name: Cauchy,
2021
Pareto().name: Pareto,
2122
Beta().name: Beta,
23+
Uniform().name: Uniform,
2224
}

0 commit comments

Comments
 (0)