Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
8 changes: 4 additions & 4 deletions examples/big_mono_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import random

import numpy as np

from examples.config import MAX_WORKERS, TESTS_OPTIMIZERS
from examples.mono_test_generator import generate_mono_test
from examples.utils import Clicker, Test, init_solver, run_tests, save_results
from mpest.models import (
AModelWithGenerator,
ExponentialModel,
GaussianModel,
WeibullModelExp,
)

from examples.config import MAX_WORKERS, TESTS_OPTIMIZERS
from examples.mono_test_generator import generate_mono_test
from examples.utils import Clicker, Test, init_solver, run_tests, save_results

if __name__ == "__main__":
random.seed(42)
np.random.seed(42)
Expand Down
4 changes: 2 additions & 2 deletions examples/diff_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import random

import numpy as np
from mpest import Distribution, MixtureDistribution, Problem
from mpest.models import GaussianModel, WeibullModelExp

from examples.config import MAX_WORKERS, TESTS_OPTIMIZERS
from examples.mono_test_generator import Clicker
from examples.utils import Test, init_solver, run_tests, save_results
from mpest import Distribution, MixtureDistribution, Problem
from mpest.models import GaussianModel, WeibullModelExp

# Gaussian

Expand Down
4 changes: 2 additions & 2 deletions examples/mono_test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from collections.abc import Iterable

import numpy as np

from examples.utils import Clicker, Test
from mpest.core.distribution import Distribution
from mpest.core.mixture_distribution import MixtureDistribution
from mpest.core.problem import Problem
from mpest.em import EM
from mpest.models import AModel, AModelWithGenerator

from examples.utils import Clicker, Test


def generate_mono_test(
model_t: type[AModelWithGenerator],
Expand Down
4 changes: 2 additions & 2 deletions examples/prepare_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

import numpy as np
import pandas as pd
from mpest.annotations import Samples
from mpest.core.mixture_distribution import DistributionInMixture, MixtureDistribution
from tqdm.contrib.concurrent import process_map

from examples.config import MAX_WORKERS
from examples.mono_test_generator import Clicker
from examples.utils import SingleSolverResult, TestResult
from mpest.annotations import Samples
from mpest.core.mixture_distribution import DistributionInMixture, MixtureDistribution


def nll(samples: Samples, mixture: MixtureDistribution) -> float:
Expand Down
8 changes: 4 additions & 4 deletions examples/quick_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
import random

import numpy as np

from examples.config import MAX_WORKERS
from examples.mono_test_generator import generate_mono_test
from examples.utils import Clicker, Test, init_solver, run_tests, save_results
from mpest.models import (
AModelWithGenerator,
ExponentialModel,
Expand All @@ -15,6 +11,10 @@
)
from mpest.optimizers import ALL_OPTIMIZERS

from examples.config import MAX_WORKERS
from examples.mono_test_generator import generate_mono_test
from examples.utils import Clicker, Test, init_solver, run_tests, save_results


def run_test():
"""Runs the mixture distributions of single model quick test"""
Expand Down
1 change: 0 additions & 1 deletion examples/readme_example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

from mpest import Distribution, MixtureDistribution, Problem
from mpest.em import EM
from mpest.em.breakpointers import StepCountBreakpointer
Expand Down
9 changes: 4 additions & 5 deletions examples/readme_example/example_ml.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
import numpy as np
import pandas as pd
import seaborn as sns
from scipy.stats import entropy, wasserstein_distance
from sklearn.cluster import DBSCAN, AgglomerativeClustering, KMeans
from sklearn.metrics import calinski_harabasz_score, davies_bouldin_score, silhouette_score
from sklearn.neighbors import NearestNeighbors

from mpest import Distribution, MixtureDistribution, Problem
from mpest.em import EM
from mpest.em.breakpointers import StepCountBreakpointer
Expand All @@ -20,6 +15,10 @@
from mpest.em.methods.method import Method
from mpest.models import GaussianModel, WeibullModelExp
from mpest.optimizers import ScipyCG
from scipy.stats import entropy, wasserstein_distance
from sklearn.cluster import DBSCAN, AgglomerativeClustering, KMeans
from sklearn.metrics import calinski_harabasz_score, davies_bouldin_score, silhouette_score
from sklearn.neighbors import NearestNeighbors

os.makedirs("results", exist_ok=True)
os.makedirs("results/plots", exist_ok=True)
Expand Down
6 changes: 3 additions & 3 deletions examples/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
from typing import ClassVar, NamedTuple

import numpy as np
from tqdm.contrib.concurrent import process_map

from examples.config import RESULTS_FOLDER
from mpest.annotations import Samples
from mpest.core.mixture_distribution import MixtureDistribution
from mpest.core.problem import Problem, Result
Expand All @@ -21,6 +18,9 @@
)
from mpest.em.methods.likelihood_method import LikelihoodMethod
from mpest.optimizers import TOptimizer
from tqdm.contrib.concurrent import process_map

from examples.config import RESULTS_FOLDER

np.seterr(all="ignore")

Expand Down
82 changes: 82 additions & 0 deletions mpest/em/methods/moments_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""The module in which the moments method is presented"""

import numpy as np

from mpest import Samples
from mpest.core.distribution import Distribution
from mpest.core.mixture_distribution import MixtureDistribution
from mpest.core.problem import Problem, Result
from mpest.em.methods.abstract_steps import AMaximization
from mpest.exceptions import MStepError
from mpest.utils import ResultWithError

EResult = tuple[Problem, np.ndarray] | ResultWithError[MixtureDistribution]


class MomentsMStep(AMaximization[EResult]):
"""
Class which calculate new params using matrix with indicator from E step.
"""

def calc_order_moment_of_index_element(self, order: int, i: int, samples: Samples, indicators: np.ndarray) -> float:
"""
A function that calculates the list of n-th moments of each distribution.

:param order: Order of Moment.
:param i: The number of the distribution for which we count the moment.
:param samples: Ndarray with samples.
:param indicators: Matrix with indicators

:return: order-Moment of index element.
"""

sum_j_row_probabilities = np.sum(indicators[i])

if sum_j_row_probabilities == 0:
return 0

moment_values = samples**order

numerator = np.sum(moment_values * indicators[i])

return numerator / sum_j_row_probabilities

def step(self, e_result: EResult) -> Result:
"""
A function that performs M step

:param e_result: Tuple with problem, new_priors and indicators.
"""

if isinstance(e_result, ResultWithError):
return e_result

problem, indicators = e_result

samples = problem.samples

mixture = problem.distributions

new_priors = np.sum(indicators, axis=1) / len(samples)

max_params_count = max(len(d.params) for d in mixture)
moments = np.zeros(shape=[len(mixture), max_params_count])

for j, d in enumerate(mixture):
for r in range(len(d.params)):
moments[j][r] = self.calc_order_moment_of_index_element(r + 1, j, samples, indicators)

for i, d in enumerate(mixture):
if d.model.name == "WeibullExp" and (moments[i][0] * moments[i][1] < 0):
error = MStepError("The weibul distribution degenerated in the first step.")
return ResultWithError(mixture.distributions, error)

new_distributions = []

for j, d in enumerate(mixture):
new_params = d.model.calc_moments_params(moments[j])
new_d = Distribution(d.model, d.model.params_convert_to_model(new_params))
new_distributions.append(new_d)

new_mixture = MixtureDistribution.from_distributions(new_distributions, new_priors)
return ResultWithError(new_mixture)
10 changes: 10 additions & 0 deletions mpest/models/exponential.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,13 @@ def calc_params(self, moments: list[float]):
lm = 1 / moments[0]

return np.array([lm])

def calc_moments_params(self, moments: list[float]):
"""
The function for calculating params using moments
"""

# Calculate lambda parameter
lm = 1 / moments[0]

return np.array([lm])
18 changes: 18 additions & 0 deletions mpest/models/gaussian.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,21 @@ def calc_params(self, moments: list[float]) -> np.ndarray:
variance = m2 * np.sqrt(np.pi)

return np.array([mean, variance])

def calc_moments_params(self, moments: list[float]) -> np.ndarray:
"""
The function for calculating params using moments
"""

m1 = moments[0]
m2 = moments[1]

# Calculate mean parameter
mu = m1

# Calculate variance parameter
variance = m2 - m1**2

sigma = np.sqrt(variance)

return np.array([mu, sigma])
24 changes: 24 additions & 0 deletions mpest/models/weibull.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import math

import numpy as np
from scipy.optimize import root_scalar
from scipy.special import gamma
from scipy.stats import weibull_min

from mpest.annotations import Params, Samples
Expand Down Expand Up @@ -86,3 +88,25 @@ def calc_params(self, moments: list[float]):
lm = m1 / math.gamma(1 + 1 / k)

return np.array([k, lm])

def calc_moments_params(self, moments: list[float]):
"""
The function for calculating params using moments
"""

m1, m2 = moments[0], moments[1]

moments_ratio = m2 / (m1**2)

def equation_for_k(k):
return gamma(1 + 2 / k) / (gamma(1 + 1 / k) ** 2) - moments_ratio

solution = root_scalar(equation_for_k, method="brentq", bracket=[0.02, 100])
if not solution.converged:
raise RuntimeError(f"Error in calculating the equation: m1={m1}, m2={m2}")

k = solution.root

lm = m1 / gamma(1 + 1 / k)

return np.array([k, lm])
21 changes: 21 additions & 0 deletions tests/tests_moments/moments_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from mpest.core.problem import Problem, Result
from mpest.em import EM
from mpest.em.breakpointers import ParamDifferBreakpointer, StepCountBreakpointer
from mpest.em.distribution_checkers import (
FiniteChecker,
PriorProbabilityThresholdChecker,
)
from mpest.em.methods.likelihood_method import BayesEStep
from mpest.em.methods.method import Method
from mpest.em.methods.moments_method import MomentsMStep


def run_test(problem: Problem, deviation: float) -> Result:
method = Method(BayesEStep(), MomentsMStep())
em_algo = EM(
StepCountBreakpointer() + ParamDifferBreakpointer(deviation=deviation),
FiniteChecker() + PriorProbabilityThresholdChecker(),
method,
)

return em_algo.solve(problem=problem)
Loading