Skip to content

Commit 8c88118

Browse files
authored
Merge branch 'main' into nm_g_estimation
2 parents a6c15dc + 98eee73 commit 8c88118

35 files changed

+4162
-773
lines changed

jupiter_examples/nm_sigma_estimation_comparison.ipynb

Lines changed: 441 additions & 0 deletions
Large diffs are not rendered by default.

jupiter_examples/nmv_semiparametric_mixing_density_estimation_given_mu.ipynb

Lines changed: 1104 additions & 0 deletions
Large diffs are not rendered by default.

jupiter_examples/nv_mixing_density.ipynb

Lines changed: 842 additions & 0 deletions
Large diffs are not rendered by default.

requirements.dev.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
mypy~=1.10.0
22
black~=24.4.2
33
isort~=5.13.2
4-
mpmath~=1.3.0
54
pytest~=7.4.4
65
scikit-learn~=1.5.1

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
numpy~=1.26.4
22
scipy~=1.13.1
3+
sympy~=1.13.1
34
matplotlib~=3.8.4
45
numba~=0.59.0
56
mpmath~=1.3.0
7+
sympy~=1.13.1

src/algorithms/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
from src.algorithms.semiparam_algorithms.nm_semi_param_algorithms.g_estimation_convolution import (
22
NMSemiParametricGEstimation,
33
)
4+
from src.algorithms.semiparam_algorithms.nm_semi_param_algorithms.sigma_estimation_eigenvalue_based import (
5+
SemiParametricMeanSigmaEstimationEigenvalueBased,
6+
)
7+
from src.algorithms.semiparam_algorithms.nm_semi_param_algorithms.sigma_estimation_empirical import (
8+
SemiParametricMeanSigmaEstimationEmpirical,
9+
)
10+
from src.algorithms.semiparam_algorithms.nv_semi_param_algorithms.g_estimation_given_mu import (
11+
SemiParametricNVEstimation,
12+
)
13+
from src.algorithms.semiparam_algorithms.nv_semi_param_algorithms.g_estimation_post_widder import (
14+
NVSemiParametricGEstimationPostWidder,
15+
)
416
from src.algorithms.semiparam_algorithms.nvm_semi_param_algorithms.g_estimation_given_mu import (
517
SemiParametricGEstimationGivenMu,
618
)
19+
from src.algorithms.semiparam_algorithms.nvm_semi_param_algorithms.g_estimation_given_mu_rqmc_based import (
20+
SemiParametricGEstimationGivenMuRQMCBased,
21+
)
22+
from src.algorithms.semiparam_algorithms.nvm_semi_param_algorithms.g_estimation_post_widder import (
23+
SemiParametricGEstimationPostWidder,
24+
)
725
from src.algorithms.semiparam_algorithms.nvm_semi_param_algorithms.mu_estimation import SemiParametricMuEstimation
826
from src.register.algorithm_purpose import AlgorithmPurpose
927
from src.register.register import Registry
@@ -14,3 +32,15 @@
1432
SemiParametricGEstimationGivenMu
1533
)
1634
ALGORITHM_REGISTRY.register("g_estimation_convolution", AlgorithmPurpose.NM_SEMIPARAMETRIC)(NMSemiParametricGEstimation)
35+
ALGORITHM_REGISTRY.register("sigma_estimation_eigenvalue_based", AlgorithmPurpose.NM_SEMIPARAMETRIC)(
36+
SemiParametricMeanSigmaEstimationEigenvalueBased
37+
)
38+
ALGORITHM_REGISTRY.register("sigma_estimation_empirical", AlgorithmPurpose.NM_SEMIPARAMETRIC)(
39+
SemiParametricMeanSigmaEstimationEmpirical
40+
)
41+
ALGORITHM_REGISTRY.register("g_estimation_given_mu_rqmc_based", AlgorithmPurpose.NMV_SEMIPARAMETRIC)(
42+
SemiParametricGEstimationGivenMuRQMCBased
43+
)
44+
ALGORITHM_REGISTRY.register("g_estimation_post_widder", AlgorithmPurpose.NMV_SEMIPARAMETRIC)(
45+
SemiParametricGEstimationPostWidder
46+
)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import math
2+
from typing import Optional, Tuple, TypedDict
3+
4+
import numpy as np
5+
from scipy.linalg import eigh
6+
7+
from src.estimators.estimate_result import EstimateResult
8+
9+
L_DEFAULT_VALUE = 5
10+
K_DEFAULT_VALUE = 10
11+
EPS_DEFAULT_VALUE = 0.1
12+
SEARCH_AREA_DEFAULT_VALUE = 10
13+
SEARCH_DENSITY_DEFAULT_VALUE = 1000
14+
PARAMETER_KEYS = ["l", "k", "eps", "search_area", "search_density"]
15+
16+
17+
class SemiParametricMeanSigmaEstimationEigenvalueBased:
18+
"""Estimation of sigma parameter of NM mixture represented in canonical form Y = xi + sigma*N.
19+
20+
Args:
21+
sample: Sample of the analyzed distribution.
22+
params: Parameters of the algorithm.
23+
"""
24+
25+
class ParamsAnnotation(TypedDict, total=False):
26+
l: float
27+
k: float
28+
eps: float
29+
search_area: float
30+
search_density: int
31+
32+
def __init__(self, sample: Optional[np.ndarray] = None, **kwargs: ParamsAnnotation):
33+
self.sample: np.ndarray = np.array([]) if sample is None else np.asarray(sample)
34+
self.n: int = len(self.sample)
35+
(
36+
self.l,
37+
self.k,
38+
self.eps,
39+
self.search_area,
40+
self.search_density,
41+
) = self._validate_kwargs(**kwargs)
42+
43+
@staticmethod
44+
def _validate_kwargs(**kwargs: ParamsAnnotation) -> Tuple[float, float, float, float, int]:
45+
"""Validate and extract parameters.
46+
47+
Args:
48+
kwargs: Parameters of the algorithm.
49+
50+
Returns:
51+
Tuple[float, float, float, float, int]: Validated parameters.
52+
"""
53+
if any(key not in PARAMETER_KEYS for key in kwargs):
54+
raise ValueError("Got unexpected parameter.")
55+
l = kwargs.get("l", L_DEFAULT_VALUE)
56+
k = kwargs.get("k", K_DEFAULT_VALUE)
57+
eps = kwargs.get("eps", EPS_DEFAULT_VALUE)
58+
search_area = kwargs.get("search_area", SEARCH_AREA_DEFAULT_VALUE)
59+
search_density = kwargs.get("search_density", SEARCH_DENSITY_DEFAULT_VALUE)
60+
if not isinstance(l, float) or l <= 0:
61+
raise ValueError("Expected positive float as parameter 'l'.")
62+
if not isinstance(k, float) or k <= 0:
63+
raise ValueError("Expected positive float as parameter 'k'.")
64+
if not isinstance(eps, float) or eps <= 0:
65+
raise ValueError("Expected positive float as parameter 'eps'.")
66+
if not isinstance(search_area, float) or search_area <= 0:
67+
raise ValueError("Expected positive float as parameter 'search_area'.")
68+
if not isinstance(search_density, int) or search_density <= 0:
69+
raise ValueError("Expected positive integer as parameter 'search_density'.")
70+
return l, k, eps, search_area, search_density
71+
72+
def _alpha(self, zeta: float, tau: float) -> complex:
73+
return (1 / self.n) * np.sum(np.exp(1j * zeta * self.sample)) * math.exp(zeta**2 * tau**2 / 2)
74+
75+
def _generate_t(self) -> np.ndarray:
76+
k_values = np.arange(-self.l, self.l + 1)
77+
return k_values / self.k
78+
79+
def _build_matrix(self, tau: float) -> np.ndarray:
80+
"""Build the matrix for eigenvalue computation."""
81+
t = self._generate_t()
82+
t_len = len(t)
83+
matrix = np.zeros((t_len, t_len), dtype=np.complex128)
84+
for i in range(t_len):
85+
for j in range(t_len):
86+
matrix[i, j] = self._alpha(t[i] - t[j], tau)
87+
return matrix
88+
89+
def algorithm(self, sample: np.ndarray) -> EstimateResult:
90+
"""Estimate sigma.
91+
92+
Args:
93+
sample: Sample of the analyzed distribution.
94+
95+
Returns:
96+
EstimateResult: Object with estimated sigma value.
97+
"""
98+
if sample.size == 0:
99+
raise ValueError("Sample cannot be empty.")
100+
tau_values = np.linspace(0, self.search_area, self.search_density)
101+
for tau in tau_values:
102+
matrix = self._build_matrix(tau)
103+
eigenvalues = eigh(matrix, eigvals_only=True)
104+
lambda_min = np.min(eigenvalues)
105+
if lambda_min < -self.eps:
106+
return EstimateResult(value=tau, success=True)
107+
return EstimateResult(success=False)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import math
2+
from typing import Optional, TypedDict
3+
4+
import numpy as np
5+
6+
from src.estimators.estimate_result import EstimateResult
7+
8+
T_DEFAULT_VALUE = 7.5
9+
PARAMETER_KEYS = ["t"]
10+
11+
12+
class SemiParametricMeanSigmaEstimationEmpirical:
13+
"""Estimation of sigma parameter of NM mixture represented in canonical form Y = xi + sigma*N.
14+
15+
Args:
16+
sample (Optional[np.ndarray]): Sample of the analyzed distribution.
17+
params (Optional[dict]): Parameters of the algorithm.
18+
"""
19+
20+
class ParamsAnnotation(TypedDict, total=False):
21+
t: float
22+
23+
def __init__(
24+
self,
25+
sample: Optional[np.ndarray] = None,
26+
**kwargs: ParamsAnnotation,
27+
):
28+
self.sample: np.ndarray = np.array([]) if sample is None else sample
29+
self.n: int = len(self.sample)
30+
self.t: float = self._validate_kwargs(**kwargs)
31+
32+
@staticmethod
33+
def _validate_kwargs(**kwargs: ParamsAnnotation) -> float:
34+
"""Validate and extract parameters.
35+
36+
Args:
37+
kwargs (dict): Parameters of the algorithm.
38+
39+
Returns:
40+
float: Validated parameter `t`.
41+
"""
42+
if any(key not in PARAMETER_KEYS for key in kwargs):
43+
raise ValueError("Got unexpected parameter.")
44+
45+
t_value = kwargs.get("t", T_DEFAULT_VALUE)
46+
if not isinstance(t_value, (float, int)):
47+
raise ValueError("Expected a numeric value (float or int) as parameter `t`.")
48+
49+
t = float(t_value)
50+
if t <= 0:
51+
raise ValueError("Expected a positive float as parameter `t`.")
52+
return t
53+
54+
def algorithm(self, sample: np.ndarray) -> EstimateResult:
55+
"""Estimate the sigma parameter.
56+
57+
Args:
58+
sample (np.ndarray): Sample of the analyzed distribution.
59+
60+
Returns:
61+
EstimateResult: An object containing the estimated sigma value.
62+
"""
63+
if sample.size == 0:
64+
raise ValueError("Sample can't be empty.")
65+
sigma = ((2 / (self.t**2)) * math.log((1 / self.n) * sum([math.exp(self.t * x) for x in self.sample]))) ** 0.5
66+
return EstimateResult(value=sigma, success=True)

0 commit comments

Comments
 (0)