From 14424d76aa8c817fc9dcb5479a5a72b98d2f1867 Mon Sep 17 00:00:00 2001 From: jorenham Date: Thu, 24 Oct 2024 23:11:28 +0200 Subject: [PATCH 1/2] general typing simplifications --- lmo/contrib/scipy_stats.py | 42 ++-- lmo/diagnostic.py | 51 +++-- lmo/distributions/_genlambda.py | 6 +- lmo/distributions/_lm.py | 6 +- lmo/distributions/_nonparametric.py | 286 ++++++++-------------------- lmo/distributions/_wakeby.py | 13 +- lmo/inference/_l_gmm.py | 46 +++-- lmo/theoretical/_f_to_f.py | 14 +- lmo/theoretical/_f_to_h.py | 36 ++-- lmo/theoretical/_f_to_lcm.py | 29 +-- lmo/theoretical/_f_to_lm.py | 68 ++++--- lmo/theoretical/_f_to_lm_cov.py | 15 +- lmo/theoretical/_f_to_lm_eif.py | 58 ++---- lmo/theoretical/_lm_to_f.py | 54 ++---- lmo/theoretical/_utils.py | 37 ++-- lmo/typing/scipy.py | 6 +- 16 files changed, 325 insertions(+), 442 deletions(-) diff --git a/lmo/contrib/scipy_stats.py b/lmo/contrib/scipy_stats.py index d74719ad..2bdaedcb 100644 --- a/lmo/contrib/scipy_stats.py +++ b/lmo/contrib/scipy_stats.py @@ -7,8 +7,8 @@ from __future__ import annotations import contextlib -from collections.abc import Callable, Mapping, Sequence from typing import ( + TYPE_CHECKING, Any, ClassVar, Literal, @@ -23,10 +23,6 @@ import numpy as np import numpy.typing as npt -from scipy.stats import ( - fit as scipy_fit, - rv_discrete, -) from scipy.stats.distributions import rv_continuous, rv_frozen import lmo.typing as lmt @@ -51,11 +47,21 @@ l_stats_cov_from_cdf, ) +if TYPE_CHECKING: + from collections.abc import Callable, Mapping, Sequence + + from scipy.stats import rv_discrete + __all__ = "install", "l_rv_frozen", "l_rv_generic" _T = TypeVar("_T") -_T_x = TypeVar("_T_x", bound=float | npt.NDArray[np.float64]) +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + _Tuple2: TypeAlias = tuple[_T, _T] _Tuple4: TypeAlias = tuple[_T, _T, _T, _T] @@ -141,7 +147,7 @@ class l_rv_generic(PatchClass): cdf: lspt.RVFunction[...] fit: Callable[..., tuple[float, ...]] mean: Callable[..., float] - ppf: lspt.RVFunction[...] + ppf: _Fn1 std: Callable[..., float] def _get_xxf( @@ -149,15 +155,15 @@ def _get_xxf( *args: Any, loc: float = 0, scale: float = 1, - ) -> _Tuple2[Callable[[float], float]]: + ) -> _Tuple2[_Fn1]: assert scale > 0 _cdf, _ppf = self._cdf, self._ppf - def cdf(x: float, /) -> float: + def cdf(x: _T_x, /) -> _T_x: return _cdf(np.array([(x - loc) / scale], dtype=float), *args)[0] - def ppf(q: float, /) -> float: + def ppf(q: _T_x, /) -> _T_x: return _ppf(np.array([q], dtype=float), *args)[0] * scale + loc return cdf, ppf @@ -814,7 +820,7 @@ def l_moment_influence( quad_opts: lspt.QuadOptions | None = None, tol: float = 1e-8, **kwds: Any, - ) -> Callable[[_T_x], _T_x]: + ) -> _Fn1: r""" Returns the influence function (IF) of an L-moment. @@ -883,10 +889,7 @@ def l_moment_influence( lm = self.l_moment(r, *args, trim=trim, quad_opts=quad_opts, **kwds) args, loc, scale = self._parse_args(*args, **kwds) - cdf = cast( - Callable[[_ArrF8], _ArrF8], - self._get_xxf(*args, loc=loc, scale=scale)[0], - ) + cdf = self._get_xxf(*args, loc=loc, scale=scale)[0] return l_moment_influence_from_cdf( cdf, @@ -980,10 +983,7 @@ def l_ratio_influence( ) args, loc, scale = self._parse_args(*args, **kwds) - cdf = cast( - Callable[[_ArrF8], _ArrF8], - self._get_xxf(*args, loc=loc, scale=scale)[0], - ) + cdf = self._get_xxf(*args, loc=loc, scale=scale)[0] return l_ratio_influence_from_cdf( cdf, @@ -1207,7 +1207,9 @@ def l_fit( else: # almost never works without custom (finite and tight) bounds... # ... and otherwise it'll runs for +-17 exa-eons - rv = cast(rv_discrete, self) + from scipy.stats import fit as scipy_fit + + rv = cast("rv_discrete", self) bounds0 = [ (-np.inf if a is None else a, np.inf if b is None else b) for a, b in bounds diff --git a/lmo/diagnostic.py b/lmo/diagnostic.py index 8e7dd37d..9413ff6a 100644 --- a/lmo/diagnostic.py +++ b/lmo/diagnostic.py @@ -12,6 +12,7 @@ Any, Final, NamedTuple, + Protocol, TypeAlias, TypeVar, cast, @@ -20,13 +21,7 @@ import numpy as np import numpy.typing as npt -from scipy.integrate import quad -from scipy.optimize import OptimizeWarning, minimize -from scipy.special import chdtrc -from scipy.stats.distributions import rv_continuous, rv_frozen -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt from . import constants from ._lm import l_ratio from ._poly import extrema_jacobi @@ -41,9 +36,10 @@ if TYPE_CHECKING: + import lmo.typing.np as lnpt + import lmo.typing.scipy as lspt from .contrib.scipy_stats import l_rv_generic - __all__ = ( "error_sensitivity", "l_moment_bounds", @@ -57,6 +53,12 @@ _T = TypeVar("_T") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + _Tuple2: TypeAlias = tuple[_T, _T] _ArrF8: TypeAlias = npt.NDArray[np.float64] @@ -191,7 +193,7 @@ def _gof_stat_single(l_obs: _ArrF8, l_exp: _ArrF8, cov: _ArrF8) -> float: def l_moment_gof( - rv_or_cdf: lspt.RV | Callable[[float], float], + rv_or_cdf: lspt.RV | _Fn1, l_moments: _ArrF8, n_obs: int, /, @@ -270,28 +272,33 @@ def l_moment_gof( r = np.arange(1, 1 + n) - if isinstance(rv_or_cdf, rv_continuous.__base__ | rv_frozen): + if _is_rv(rv_or_cdf): rv = cast("l_rv_generic", rv_or_cdf) lambda_r = rv.l_moment(r, trim=trim, **kwargs) lambda_rr = rv.l_moments_cov(n, trim=trim, **kwargs) else: from .theoretical import l_moment_cov_from_cdf, l_moment_from_cdf - cdf = cast(Callable[[float], float], rv_or_cdf) + cdf = rv_or_cdf lambda_r = l_moment_from_cdf(cdf, r, trim, **kwargs) lambda_rr = l_moment_cov_from_cdf(cdf, n, trim, **kwargs) + from scipy.special import chdtrc + stat = n_obs * _gof_stat(l_r.T, lambda_r, lambda_rr).T[()] pval = chdtrc(n, stat) return HypothesisTestResult(stat, pval) def _is_rv(x: object) -> TypeIs[lspt.RV]: - return isinstance(x, lspt.RV) + from scipy.stats.distributions import rv_continuous, rv_discrete, rv_histogram + + # NOTE: this assumes that the (private) `rv_generic` class is a sealed type + return isinstance(x, rv_continuous | rv_discrete | rv_histogram) def l_stats_gof( - rv_or_cdf: lspt.RV | Callable[[float], float], + rv_or_cdf: lspt.RV | _Fn1, l_stats: _ArrF8, n_obs: int, /, @@ -319,8 +326,10 @@ def l_stats_gof( tau_rr = l_stats_cov_from_cdf(cdf, n, trim, **kwargs) tau_r = l_stats_from_cdf(cdf, n, trim, **kwargs) + from scipy.special import chdtrc + stat = n_obs * _gof_stat(t_r.T, tau_r, tau_rr).T[()] - pval = cast(float | _ArrF8, chdtrc(n, stat)) + pval = chdtrc(n, stat) return HypothesisTestResult(stat, pval) @@ -729,19 +738,17 @@ def rejection_point( if influence_fn(rho_max) != 0 or influence_fn(-rho_max) != 0: return np.nan + from scipy.integrate import quad + def integrand(x: float) -> float: return max(abs(influence_fn(-x)), abs(influence_fn(x))) def obj(r: _ArrF8) -> float: return quad(integrand, r[0], np.inf)[0] - # TO - res = minimize( - obj, - bounds=[(rho_min, rho_max)], - x0=[rho_min], - method="COBYLA", - ) + from scipy.optimize import minimize + + res = minimize(obj, bounds=[(rho_min, rho_max)], x0=[rho_min], method="COBYLA") rho = cast(float, res.x[0]) if rho <= _MIN_RHO or influence_fn(-rho) or influence_fn(rho): @@ -801,6 +808,8 @@ def obj(xs: _ArrF8) -> float: bounds = None if np.isneginf(a) and np.isposinf(b) else [(a, b)] + from scipy.optimize import OptimizeWarning, minimize + res = minimize(obj, bounds=bounds, x0=[min(max(0, a), b)], method="COBYLA") if not res.success: warnings.warn(res.message, OptimizeWarning, stacklevel=1) @@ -876,6 +885,8 @@ def obj(xs: _ArrF8) -> float: a, b = domain bounds = None if np.isneginf(a) and np.isposinf(b) else [(a, b)] + from scipy.optimize import OptimizeWarning, minimize + res = minimize( obj, bounds=bounds, diff --git a/lmo/distributions/_genlambda.py b/lmo/distributions/_genlambda.py index e405e586..43b373b3 100644 --- a/lmo/distributions/_genlambda.py +++ b/lmo/distributions/_genlambda.py @@ -7,7 +7,6 @@ import functools import math import sys -from collections.abc import Callable from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar, cast import numpy as np @@ -260,10 +259,7 @@ def _l_moment( lmbda_r = cast( float | _ArrF8, l_moment_from_ppf( - cast( - Callable[[float], float], - functools.partial(self._ppf, b=b, d=d, f=f), - ), + functools.partial(self._ppf, b=b, d=d, f=f), r, trim=trim, quad_opts=quad_opts, diff --git a/lmo/distributions/_lm.py b/lmo/distributions/_lm.py index 22587d30..e15272e7 100644 --- a/lmo/distributions/_lm.py +++ b/lmo/distributions/_lm.py @@ -322,7 +322,7 @@ def lm_genextreme(r: int, s: float, t: float, /, a: float) -> np.float64 | float # - conditionals within the function are avoided through multiple functions if a == 0: - def _ppf(q: float) -> float: + def _ppf(q: float, /) -> float: if q <= 0: return -float("inf") if q >= 1: @@ -331,7 +331,7 @@ def _ppf(q: float) -> float: elif a < 0: - def _ppf(q: float) -> float: + def _ppf(q: float, /) -> float: if q <= 0: return 1 / a if q >= 1: @@ -340,7 +340,7 @@ def _ppf(q: float) -> float: else: # a > 0 - def _ppf(q: float) -> float: + def _ppf(q: float, /) -> float: if q <= 0: return -float("inf") if q >= 1: diff --git a/lmo/distributions/_nonparametric.py b/lmo/distributions/_nonparametric.py index 4aa5402d..2a5801a0 100644 --- a/lmo/distributions/_nonparametric.py +++ b/lmo/distributions/_nonparametric.py @@ -1,21 +1,23 @@ from __future__ import annotations import functools -import sys +import math from typing import ( TYPE_CHECKING, Any, ClassVar, Final, Literal, + LiteralString, + Protocol, + Self, TypeAlias, - cast, + TypeVar, overload, ) import numpy as np import numpy.typing as npt -from scipy.integrate import quad import lmo.typing as lmt import lmo.typing.np as lnpt @@ -28,32 +30,28 @@ qdf_from_l_moments, ) -if sys.version_info >= (3, 13): - from typing import LiteralString, Protocol, Self, TypeVar -else: - from typing import LiteralString, Self - - from typing_extensions import Protocol, TypeVar - if TYPE_CHECKING: from collections.abc import Callable import optype.numpy as onpt +_T = TypeVar("_T") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + + _Trim: TypeAlias = tuple[int, int] | tuple[float, float] _MomentType: TypeAlias = Literal[0, 1] _LPolyParams: TypeAlias = ( tuple[lnpt.AnyVectorFloat] | tuple[lnpt.AnyVectorFloat, lmt.AnyTrim] ) - _AnyReal0D: TypeAlias = float | np.bool_ | lnpt.Int | lnpt.Float -_AnyReal1D: TypeAlias = lnpt.AnyVectorInt | lnpt.AnyVectorFloat -_AnyReal2D: TypeAlias = lnpt.AnyMatrixInt | lnpt.AnyMatrixFloat _AnyRealND: TypeAlias = lnpt.AnyArrayInt | lnpt.AnyArrayFloat -_AnyReal3D_: TypeAlias = lnpt.AnyTensorInt | lnpt.AnyTensorFloat -_AnyReal2D_: TypeAlias = _AnyReal2D | _AnyReal3D_ _Stats0: TypeAlias = Literal[""] _Stats1: TypeAlias = Literal["m", "v", "s", "k"] @@ -62,7 +60,7 @@ _Stats4: TypeAlias = Literal["mvsk"] _Stats: TypeAlias = Literal[_Stats0, _Stats1, _Stats2, _Stats3, _Stats4] -_T = TypeVar("_T") + _Tuple1: TypeAlias = tuple[_T] _Tuple2: TypeAlias = tuple[_T, _T] _Tuple3: TypeAlias = tuple[_T, _T, _T] @@ -70,16 +68,6 @@ _Tuple4m: TypeAlias = tuple[()] | _Tuple1[_T] | _Tuple2[_T] | _Tuple3[_T] | _Tuple4[_T] -NaN: Final[np.float64] = np.float64(np.nan) - - -class _VectorizedF(Protocol): - @overload - def __call__(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def __call__(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - - def _get_rng(seed: lnpt.Seed | None = None) -> np.random.Generator: if isinstance(seed, np.random.Generator): return seed @@ -101,9 +89,9 @@ class l_poly: # noqa: N801 _trim: Final[_Trim] _support: Final[tuple[np.float64, np.float64]] - _cdf: Final[_VectorizedF] - _ppf: Final[_VectorizedF] - _qdf: Final[_VectorizedF] + _cdf: Final[_Fn1] + _ppf: Final[_Fn1] + _qdf: Final[_Fn1] _random_state: np.random.Generator @@ -139,7 +127,7 @@ def __init__( self._ppf = ppf_from_l_moments(_lmbda, trim=_trim) self._qdf = qdf_from_l_moments(_lmbda, trim=_trim, validate=False) - a, b = self._ppf([0, 1]) + a, b = self._ppf(np.array([0, 1])) self._support = a, b self._cdf_single = cdf_from_ppf(self._ppf) @@ -231,15 +219,15 @@ def fit( def rvs( self, /, - size: Literal[1] | None = ..., - random_state: lnpt.Seed | None = ..., + size: Literal[1] | None = None, + random_state: lnpt.Seed | None = None, ) -> np.float64: ... @overload def rvs( self, /, size: int | tuple[int, ...], - random_state: lnpt.Seed | None = ..., + random_state: lnpt.Seed | None = None, ) -> npt.NDArray[np.float64]: ... def rvs( self, @@ -267,84 +255,45 @@ def rvs( return self._ppf(rng.uniform(size=size)) - @overload - def ppf(self, p: _AnyReal0D, /) -> np.float64: ... - @overload - def ppf(self, p: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def ppf( - self, - p: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def ppf(self, p: _T_x, /) -> _T_x: r""" [Percent point function](https://w.wiki/8cQU) \( Q(p) \) (inverse of [CDF][lmo.distributions.l_poly.cdf], a.k.a. the quantile function) at \( p \) of the given distribution. Args: - p: - Scalar or array-like of lower tail probability values in - \( [0, 1] \). + p: Scalar or array-like of lower tail probability values in \( [0, 1] \). See Also: - [`ppf_from_l_moments`][lmo.theoretical.ppf_from_l_moments] """ return self._ppf(p) - @overload - def isf(self, q: _AnyReal0D, /) -> np.float64: ... - @overload - def isf(self, q: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def isf( - self, - q: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def isf(self, q: _T_x, /) -> _T_x: r""" Inverse survival function \( \bar{Q}(q) = Q(1 - q) \) (inverse of [`sf`][lmo.distributions.l_poly.sf]) at \( q \). Args: - q: - Scalar or array-like of upper tail probability values in - \( [0, 1] \). + q: Scalar or array-like of upper tail probability values in \( [0, 1] \). """ - p = 1 - np.asarray(q) - return self._ppf(p[()] if np.isscalar(q) else p) + return self._ppf(1 - q) - @overload - def qdf(self, p: _AnyReal0D, /) -> np.float64: ... - @overload - def qdf(self, p: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def qdf( - self, - p: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def qdf(self, p: _T_x, /) -> _T_x: r""" Quantile density function \( q \equiv \frac{\dd{Q}}{\dd{p}} \) ( derivative of the [PPF][lmo.distributions.l_poly.ppf]) at \( p \) of the given distribution. Args: - p: - Scalar or array-like of lower tail probability values in - \( [0, 1] \). + p: Scalar or array-like of lower tail probability values in \( [0, 1] \). See Also: - [`qdf_from_l_moments`][lmo.theoretical.ppf_from_l_moments] """ return self._qdf(p) - @overload - def cdf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def cdf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def cdf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def cdf(self, x: _T_x, /) -> _T_x: r""" [Cumulative distribution function](https://w.wiki/3ota) \( F(x) = \mathrm{P}(X \le x) \) at \( x \) of the given distribution. @@ -358,15 +307,8 @@ def cdf( """ return self._cdf(x) - @overload - def logcdf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def logcdf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... @np.errstate(divide="ignore") - def logcdf( - self, - x: _AnyReal0D | _AnyRealND, - ) -> np.float64 | npt.NDArray[np.float64]: + def logcdf(self, x: _T_x, /) -> _T_x: r""" Logarithm of the cumulative distribution function (CDF) at \( x \), i.e. \( \ln F(x) \). @@ -374,18 +316,9 @@ def logcdf( Args: x: Scalar or array-like of quantiles. """ - return np.log(self._cdf(x)) - - @overload - def sf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def sf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... + return np.log(self._cdf(x)) # pyright: ignore[reportReturnType] - def sf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def sf(self, x: _T_x, /) -> _T_x: r""" Survival function \(S(x) = \mathrm{P}(X > x) = 1 - \mathrm{P}(X \le x) = 1 - F(x) \) (the complement of the @@ -396,16 +329,8 @@ def sf( """ return 1 - self._cdf(x) - @overload - def logsf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def logsf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... @np.errstate(divide="ignore") - def logsf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def logsf(self, x: _T_x, /) -> _T_x: r""" Logarithm of the survical function (SF) at \( x \), i.e. \( \ln \left( S(x) \right) \). @@ -413,17 +338,9 @@ def logsf( Args: x: Scalar or array-like of quantiles. """ - return np.log(self._cdf(x)) + return np.log(self._cdf(x)) # pyright: ignore[reportReturnType] - @overload - def pdf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def pdf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def pdf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def pdf(self, x: _T_x, /) -> _T_x: r""" Probability density function \( f \equiv \frac{\dd{F}}{\dd{x}} \) (derivative of the [CDF][lmo.distributions.l_poly.cdf]) at \( x \). @@ -437,27 +354,11 @@ def pdf( """ return 1 / self._qdf(self._cdf(x)) - @overload - def logpdf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def logpdf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def logpdf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def logpdf(self, x: _T_x, /) -> _T_x: """Logarithm of the PDF.""" - return -np.log(self._qdf(self._cdf(x))) + return -np.log(self._qdf(self._cdf(x))) # pyright: ignore[reportReturnType] - @overload - def hf(self, x: _AnyReal0D, /) -> np.float64: ... - @overload - def hf(self, x: _AnyRealND, /) -> npt.NDArray[np.float64]: ... - def hf( - self, - x: _AnyReal0D | _AnyRealND, - /, - ) -> np.float64 | npt.NDArray[np.float64]: + def hf(self, x: _T_x, /) -> _T_x: r""" [Hazard function ](https://w.wiki/8cWL#Failure_rate_in_the_continuous_sense) @@ -471,7 +372,7 @@ def hf( p = self._cdf(x) return 1 / (self._qdf(p) * (1 - p)) - def median(self, /) -> np.float64: + def median(self, /) -> float: r""" [Median](https://w.wiki/3oaw) (50th percentile) of the distribution. Alias for `ppf(1 / 2)`. @@ -482,54 +383,54 @@ def median(self, /) -> np.float64: return self._ppf(0.5) @functools.cached_property - def _mean(self, /) -> np.float64: + def _mean(self, /) -> float: """Mean; 1st raw product-moment.""" return self.moment(1) @functools.cached_property - def _var(self, /) -> np.float64: + def _var(self, /) -> float: """Variance; 2nd central product-moment.""" if not np.isfinite(m1 := self._mean): - return NaN + return np.nan m1_2 = m1 * m1 m2 = self.moment(2) if m2 <= m1_2 or np.isnan(m2): - return NaN + return np.nan return m2 - m1_2 @functools.cached_property - def _skew(self, /) -> np.float64: + def _skew(self, /) -> float: """Skewness; 3rd standardized central product-moment.""" if np.isnan(ss := self._var) or ss <= 0: - return NaN + return np.nan if np.isnan(m3 := self.moment(3)): - return NaN + return np.nan m = self._mean - s = np.sqrt(ss) + s = math.sqrt(ss) u = m / s return m3 / s**3 - u**3 - 3 * u @functools.cached_property - def _kurtosis(self, /) -> np.float64: + def _kurtosis(self, /) -> float: """Ex. kurtosis; 4th standardized central product-moment minus 3.""" if np.isnan(ss := self._var) or ss <= 0: - return NaN + return np.nan if np.isnan(m3 := self.moment(3)): - return NaN + return np.nan if np.isnan(m4 := self.moment(4)): - return NaN + return np.nan m1 = self._mean uu = m1**2 / ss return (m4 - 4 * m1 * m3) / ss**2 + 6 * uu + 3 * uu**2 - 3 - def mean(self, /) -> np.float64: + def mean(self, /) -> float: r""" The [mean](https://w.wiki/8cQe) \( \mu = \E[X] \) of random varianble \( X \) of the relevant distribution. @@ -542,7 +443,7 @@ def mean(self, /) -> np.float64: return self._mean - def var(self, /) -> np.float64: + def var(self, /) -> float: r""" The [variance](https://w.wiki/3jNh) \( \Var[X] = \E\bigl[(X - \E[X])^2\bigr] = @@ -554,7 +455,7 @@ def var(self, /) -> np.float64: """ return self._var - def std(self, /) -> np.float64: + def std(self, /) -> float: r""" The [standard deviation](https://w.wiki/3hwM) \( \Std[X] = \sqrt{\Var[X]} = \sigma \) of random varianble \( X \) of @@ -600,11 +501,7 @@ def support(self, /) -> tuple[np.float64, np.float64]: return self._support @overload - def interval( - self, - confidence: _AnyReal0D, - /, - ) -> tuple[np.float64, np.float64]: ... + def interval(self, confidence: _AnyReal0D, /) -> tuple[np.float64, np.float64]: ... @overload def interval( self, @@ -648,7 +545,7 @@ def interval( return self._ppf((1 - alpha) / 2), self._ppf((1 + alpha) / 2) - def moment(self, n: int | np.integer[npt.NBitBase], /) -> np.float64: + def moment(self, n: int | np.integer[Any], /) -> float: r""" Non-central product moment \( \E[X^n] \) of \( X \) of specified order \( n \). @@ -673,26 +570,28 @@ def moment(self, n: int | np.integer[npt.NBitBase], /) -> np.float64: msg = f"expected n >= 0, got {n}" raise ValueError(msg) if n == 0: - return np.float64(1) + return 1 - def _integrand(u: float | lnpt.Float, /) -> np.float64: - return cast(np.float64, self._ppf(u) ** n) + def _integrand(u: float, /) -> float: + return self._ppf(u) ** n - return cast(np.float64, quad(_integrand, 0, 1)[0]) + from scipy.integrate import quad + + return quad(_integrand, 0, 1)[0] @overload - def stats(self, /) -> _Tuple2[np.float64]: ... + def stats(self, /) -> _Tuple2[float]: ... @overload def stats(self, /, moments: _Stats0) -> tuple[()]: ... @overload - def stats(self, /, moments: _Stats1) -> _Tuple1[np.float64]: ... + def stats(self, /, moments: _Stats1) -> _Tuple1[float]: ... @overload - def stats(self, /, moments: _Stats2) -> _Tuple2[np.float64]: ... + def stats(self, /, moments: _Stats2) -> _Tuple2[float]: ... @overload - def stats(self, /, moments: _Stats3) -> _Tuple3[np.float64]: ... + def stats(self, /, moments: _Stats3) -> _Tuple3[float]: ... @overload - def stats(self, /, moments: _Stats4) -> _Tuple4[np.float64]: ... - def stats(self, /, moments: _Stats = "mv") -> _Tuple4m[np.float64]: + def stats(self, /, moments: _Stats4) -> _Tuple4[float]: ... + def stats(self, /, moments: _Stats = "mv") -> _Tuple4m[float]: r""" Some product-moment statistics of the given distribution. @@ -713,7 +612,7 @@ def stats(self, /, moments: _Stats = "mv") -> _Tuple4m[np.float64]: `'k'`: : Ex. Kurtosis \( \E[(X - \mu)^4] / \sigma^4 - 3 \) """ - out: list[np.float64] = [] + out: list[float] = [] _moments = set(moments) if "m" in _moments: @@ -727,11 +626,7 @@ def stats(self, /, moments: _Stats = "mv") -> _Tuple4m[np.float64]: return tuple(round0(np.array(out), 1e-15)) - def expect( - self, - g: Callable[[float], float | lnpt.Float], - /, - ) -> np.float64: + def expect(self, g: Callable[[float], float], /) -> float: r""" Calculate expected value of a function with respect to the distribution by numerical integration. @@ -755,32 +650,28 @@ def expect( """ ppf = self._ppf - def i(u: float, /) -> float | lnpt.Float: + def i(u: float, /) -> float: # the cast is safe, since `np.float64 <: float` (at runtime) - return g(cast(float, ppf(u))) + return g(ppf(u)) - a = 0 - b = 0.05 - c = 1 - b - d = 1 - return cast( - np.float64, - quad(i, a, b)[0] + quad(i, b, c)[0] + quad(i, c, d)[0], - ) + from scipy.integrate import quad + + a, b, c, d = 0, 0.05, 0.95, 1 + return quad(i, a, b)[0] + quad(i, b, c)[0] + quad(i, c, d)[0] @overload def l_moment( self, r: lmt.AnyOrder, /, - trim: lmt.AnyTrim | None = ..., + trim: lmt.AnyTrim | None = None, ) -> np.float64: ... @overload def l_moment( self, r: lmt.AnyOrderND, /, - trim: lmt.AnyTrim | None = ..., + trim: lmt.AnyTrim | None = None, ) -> npt.NDArray[np.float64]: ... def l_moment( self, @@ -808,7 +699,7 @@ def l_ratio( r: lmt.AnyOrder, k: lmt.AnyOrder, /, - trim: lmt.AnyTrim | None = ..., + trim: lmt.AnyTrim | None = None, ) -> np.float64: ... @overload def l_ratio( @@ -816,7 +707,7 @@ def l_ratio( r: lmt.AnyOrderND, k: lmt.AnyOrder | lmt.AnyOrderND, /, - trim: lmt.AnyTrim | None = ..., + trim: lmt.AnyTrim | None = None, ) -> npt.NDArray[np.float64]: ... @overload def l_ratio( @@ -824,7 +715,7 @@ def l_ratio( r: lmt.AnyOrder | lmt.AnyOrderND, k: lmt.AnyOrderND, /, - trim: lmt.AnyTrim | None = ..., + trim: lmt.AnyTrim | None = None, ) -> npt.NDArray[np.float64]: ... def l_ratio( self, @@ -937,24 +828,13 @@ def freeze( ) -> Self: return cls(lmbda, trim, **kwds) - @overload - @classmethod - def nnlf(cls, /, theta: _LPolyParams, x: _AnyReal1D) -> np.float64: ... - @overload @classmethod def nnlf( cls, /, theta: _LPolyParams, - x: _AnyReal2D_, - ) -> npt.NDArray[np.float64]: ... - @classmethod - def nnlf( - cls, - /, - theta: _LPolyParams, - x: _AnyRealND, - ) -> np.float64 | npt.NDArray[np.float64]: + x: npt.NDArray[np.float64], + ) -> float | npt.NDArray[np.float64]: """ Negative loglikelihood function. diff --git a/lmo/distributions/_wakeby.py b/lmo/distributions/_wakeby.py index f2230be5..59f41540 100644 --- a/lmo/distributions/_wakeby.py +++ b/lmo/distributions/_wakeby.py @@ -4,7 +4,6 @@ import math import sys import warnings -from collections.abc import Callable from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar, cast import numpy as np @@ -16,14 +15,14 @@ ) from scipy.stats.distributions import rv_continuous +from lmo.theoretical import l_moment_from_ppf +from ._lm import get_lm_func + if sys.version_info >= (3, 13): from typing import override else: from typing_extensions import override -from lmo.theoretical import l_moment_from_ppf -from ._lm import get_lm_func - if TYPE_CHECKING: import lmo.typing.scipy as lspt @@ -38,6 +37,7 @@ _XT = TypeVar("_XT", _F8, _ArrF8) + _MICRO: Final = 1e-6 _NaN: Final = float("nan") _INF: Final = float("inf") @@ -311,10 +311,7 @@ def _l_moment( if quad_opts is not None: # only do numerical integration when quad_opts is passed lmbda_r = l_moment_from_ppf( - cast( - Callable[[float], float], - functools.partial(self._ppf, b=b, d=d, f=f), - ), + functools.partial(self._ppf, b=b, d=d, f=f), r, trim=trim, quad_opts=quad_opts, diff --git a/lmo/inference/_l_gmm.py b/lmo/inference/_l_gmm.py index ed1dcdd9..44ca0bc3 100644 --- a/lmo/inference/_l_gmm.py +++ b/lmo/inference/_l_gmm.py @@ -1,10 +1,18 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias, cast +from typing import ( + TYPE_CHECKING, + Any, + Concatenate, + NamedTuple, + Protocol, + TypeAlias, + TypeVar, + cast, +) import numpy as np import numpy.typing as npt -from scipy import optimize, special from lmo._lm import l_moment as l_moment_est from lmo._lm_co import l_coscale as l_coscale_est @@ -20,14 +28,18 @@ import lmo.typing as lmt import lmo.typing.np as lnpt - import lmo.typing.scipy as lspt __all__ = "GMMResult", "fit" - _ArrF8: TypeAlias = npt.NDArray[np.float64] +_T_x = TypeVar("_T_x", float, _ArrF8) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + class GMMResult(NamedTuple): """ @@ -110,8 +122,10 @@ def j_test(self) -> HypothesisTestResult: msg = "The Sargan Hansen J-test requires `n_extra > 0`" raise ValueError(msg) + from scipy.special import chdtr + stat = self.statistic - pvalue = special.chdtr(df, stat) + pvalue = chdtr(df, stat) return HypothesisTestResult(stat, pvalue) @property @@ -173,14 +187,12 @@ def _loss_step( return np.sqrt(g_r.T @ w_rr @ g_r) # pyright: ignore[reportReturnType] -def _get_l_moment_fn(ppf: lspt.RVFunction[...]) -> Callable[..., _ArrF8]: - def l_moment_fn( - r: lmt.AnyOrderND, - /, - *args: Any, - trim: lmt.AnyTrim = 0, - ) -> _ArrF8: - return l_moment_from_ppf(lambda q: ppf(q, *args), r, trim=trim) +def _get_l_moment_fn(ppf: _Fn1) -> Callable[Concatenate[lmt.AnyOrderND, ...], _ArrF8]: + def l_moment_fn(r: lmt.AnyOrderND, /, *args: Any, trim: lmt.AnyTrim = 0) -> _ArrF8: + def _ppf(q: _T_x, /) -> _T_x: + return ppf(q, *args) + + return l_moment_from_ppf(_ppf, r, trim=trim) return l_moment_fn @@ -229,7 +241,7 @@ def _ensure_1d_f8( def fit( # noqa: C901 - ppf: lspt.RVFunction[...], + ppf: _Fn1, args0: lnpt.AnyVectorFloat, n_obs: int, l_moments: lnpt.AnyVectorFloat, @@ -414,15 +426,17 @@ def fit( # noqa: C901 success = False w_rr = np.eye(n_con) * scale_r + from scipy.optimize import minimize + for _k in range(1, k_max + 1): # calculate the weight matrix if n_con > n_par and qs is not None: w_rr = _get_weights_mc(ppf(qs, *theta), _r, trim=_trim) # run the optimizer - res = optimize.minimize( + res = minimize( _loss_step, - theta, # pyright: ignore[reportArgumentType] + theta, args=(_l_moment_fn, _r, l_r, _trim, w_rr), **kwds, ) diff --git a/lmo/theoretical/_f_to_f.py b/lmo/theoretical/_f_to_f.py index 795ed0ba..4ac1cba8 100644 --- a/lmo/theoretical/_f_to_f.py +++ b/lmo/theoretical/_f_to_f.py @@ -1,15 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Concatenate, ParamSpec, cast +from typing import TYPE_CHECKING, Concatenate, ParamSpec import numpy as np -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt - if TYPE_CHECKING: from collections.abc import Callable + import lmo.typing.np as lnpt + __all__: list[str] = ["cdf_from_ppf"] @@ -27,7 +26,7 @@ def cdf_from_ppf( Note: This function isn't vectorized. """ - from scipy.optimize import root_scalar # pyright: ignore[reportUnknownVariableType] + from scipy.optimize import root_scalar def cdf(x: float, /, *args: _Tss.args, **kwds: _Tss.kwargs) -> float: if np.isnan(x): @@ -40,10 +39,7 @@ def cdf(x: float, /, *args: _Tss.args, **kwds: _Tss.kwargs) -> float: def _ppf_to_solve(p: float) -> lnpt.Float | float: return ppf(p, *args, **kwds) - x - result = cast( - lspt.RootResult, - root_scalar(_ppf_to_solve, bracket=[0, 1], method="brentq"), - ) + result = root_scalar(_ppf_to_solve, bracket=[0, 1], method="brentq") return result.root return cdf diff --git a/lmo/theoretical/_f_to_h.py b/lmo/theoretical/_f_to_h.py index b65d7abe..c948c123 100644 --- a/lmo/theoretical/_f_to_h.py +++ b/lmo/theoretical/_f_to_h.py @@ -1,41 +1,30 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Concatenate, ParamSpec, overload +from collections.abc import Callable +from typing import Any, Concatenate, ParamSpec, TypeAlias, overload import numpy as np -import scipy.integrate as spi from ._utils import QUAD_LIMIT -if TYPE_CHECKING: - from collections.abc import Callable - - import lmo.typing.np as lnpt - - __all__ = ["entropy_from_qdf"] _Tss = ParamSpec("_Tss") +_QDF: TypeAlias = ( + Callable[Concatenate[float, _Tss], float] + | Callable[Concatenate[float, _Tss], np.floating[Any]] +) @overload -def entropy_from_qdf( - qdf: Callable[[float], float | lnpt.Float], - /, -) -> float: ... +def entropy_from_qdf(qdf: _QDF[[]], /) -> float: ... @overload def entropy_from_qdf( - qdf: Callable[Concatenate[float, _Tss], float | lnpt.Float], - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, + qdf: _QDF[_Tss], /, *args: _Tss.args, **kwds: _Tss.kwargs ) -> float: ... def entropy_from_qdf( - qdf: Callable[Concatenate[float, _Tss], float | lnpt.Float], - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, + qdf: _QDF[_Tss], /, *args: _Tss.args, **kwds: _Tss.kwargs ) -> float: r""" Evaluate the (differential / continuous) entropy \( H(X) \) of a @@ -54,11 +43,11 @@ def entropy_from_qdf( \] Args: - qdf ( (float, *Ts, **Ts) -> float): + qdf ( (float, *Tss.args, **Tss.kwargs) -> float): The quantile distribution function (QDF). - *args (*Ts): + *args (Tss.args): Optional additional positional arguments to pass to `qdf`. - **kwds (**Ts): + **kwds (Tss.kwargs): Optional keyword arguments to pass to `qdf`. Returns: @@ -69,6 +58,7 @@ def entropy_from_qdf( ](https://wikipedia.org/wiki/Differential_entropy) """ + import scipy.integrate as spi def ic(p: float, /) -> np.float64: return np.log(qdf(p, *args, **kwds)) diff --git a/lmo/theoretical/_f_to_lcm.py b/lmo/theoretical/_f_to_lcm.py index 77ea21aa..2c558442 100644 --- a/lmo/theoretical/_f_to_lcm.py +++ b/lmo/theoretical/_f_to_lcm.py @@ -3,11 +3,10 @@ from __future__ import annotations from functools import partial -from typing import TYPE_CHECKING, TypeAlias, TypeVar +from typing import TYPE_CHECKING, Protocol, TypeAlias, TypeVar import numpy as np import numpy.typing as npt -import scipy.integrate as spi from lmo._poly import eval_sh_jacobi from lmo._utils import clean_order, clean_trim, round0 @@ -24,14 +23,20 @@ __all__ = ["l_comoment_from_pdf", "l_coratio_from_pdf"] _T = TypeVar("_T") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + _Pair: TypeAlias = tuple[_T, _T] _ArrF8: TypeAlias = npt.NDArray[np.float64] def l_comoment_from_pdf( - pdf: Callable[[_ArrF8], float], - cdfs: Sequence[Callable[[float], float]], + pdf: Callable[[_ArrF8], float] | Callable[[_ArrF8], np.float64], + cdfs: Sequence[_Fn1], r: lmt.AnyOrder, /, trim: lmt.AnyTrim = 0, @@ -211,17 +216,19 @@ def l_comoment_from_pdf( _r = clean_order(int(r)) s, t = clean_trim(trim) - l_r = np.zeros((n, n)) - c = l_const(_r, s, t) # def integrand(*xs: float, i: int, j: int) -> float: def integrand(*xs: float, i: int, j: int) -> float: - x = np.asarray(xs) - q_j = cdfs[j](x[j]) + q_j = cdfs[j](xs[j]) p_j = eval_sh_jacobi(_r - 1, t, s, q_j) + x = np.asarray(xs, dtype=np.float64) return c * x[i] * q_j**s * (1 - q_j) ** t * p_j * pdf(x) + from scipy.integrate import nquad + + l_r = np.zeros((n, n)) + # TODO: parallelize for i, j in np.ndindex(l_r.shape): if i == j: @@ -234,14 +241,14 @@ def integrand(*xs: float, i: int, j: int) -> float: ) elif _r: fn = partial(integrand, i=i, j=j) - l_r[i, j] = spi.nquad(fn, limits, opts=quad_opts)[0] + l_r[i, j] = nquad(fn, limits, opts=quad_opts)[0] return round0(l_r) def l_coratio_from_pdf( - pdf: Callable[[_ArrF8], float], - cdfs: Sequence[Callable[[float], float]], + pdf: Callable[[_ArrF8], float] | Callable[[_ArrF8], np.float64], + cdfs: Sequence[_Fn1], r: lmt.AnyOrder, r0: lmt.AnyOrder = 2, /, diff --git a/lmo/theoretical/_f_to_lm.py b/lmo/theoretical/_f_to_lm.py index 193394a0..ca3680b2 100644 --- a/lmo/theoretical/_f_to_lm.py +++ b/lmo/theoretical/_f_to_lm.py @@ -1,14 +1,20 @@ from __future__ import annotations from collections.abc import Callable -from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar, Unpack, cast, overload +from typing import ( + TYPE_CHECKING, + Final, + Protocol, + TypeAlias, + TypeVar, + Unpack, + cast, + overload, +) import numpy as np import numpy.typing as npt -import scipy.integrate as spi -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt from lmo._poly import eval_sh_jacobi from lmo._utils import ( clean_orders, @@ -21,6 +27,7 @@ if TYPE_CHECKING: import lmo.typing as lmt + import lmo.typing.scipy as lspt __all__ = [ @@ -35,40 +42,55 @@ _T = TypeVar("_T") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... _Pair: TypeAlias = tuple[_T, _T] -_Fn1: TypeAlias = Callable[[float], float | lnpt.Float] _ArrF8: TypeAlias = npt.NDArray[np.float64] + ALPHA: Final[float] = 0.1 QUAD_LIMIT: Final[int] = 100 def _df_quad3( - f: Callable[[float, int], float | lnpt.Float], + f: Callable[[float, int], float], /, - a: float | lnpt.Float, - b: float | lnpt.Float, - c: float | lnpt.Float, - d: float | lnpt.Float, + a: float | np.float64, + b: float | np.float64, + c: float | np.float64, + d: float | np.float64, r: int, **kwds: Unpack[lspt.QuadOptions], ) -> float: + import scipy.integrate as spi + _integ = cast(Callable[..., tuple[float, float]], spi.quad) - out = _integ(f, b, c, (r,), **kwds)[0] + # TODO(jorenham): Remove this workaround (and pass `f` with `args` directly) after + # https://github.com/jorenham/scipy-stubs/issues/139 + + def _f(x: float, /) -> float: + return f(x, r) + + a, b, c, d = float(a), float(b), float(c), float(d) + + out = spi.quad(_f, b, c, **kwds)[0] if a < b: - out += _integ(f, a, b, (r,), **kwds)[0] + out += spi.quad(_f, a, b, **kwds)[0] if c < d: - out += _integ(f, c, d, (r,), **kwds)[0] + out += spi.quad(_f, c, d, **kwds)[0] return out @overload def l_moment_from_cdf( - cdf: _Fn1, + cdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrderND, /, trim: lmt.AnyTrim = ..., @@ -80,7 +102,7 @@ def l_moment_from_cdf( ) -> _ArrF8: ... @overload def l_moment_from_cdf( - cdf: _Fn1, + cdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder, /, trim: lmt.AnyTrim = ..., @@ -91,7 +113,7 @@ def l_moment_from_cdf( ppf: _Fn1 | None = ..., ) -> np.float64: ... def l_moment_from_cdf( - cdf: _Fn1, + cdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder | lmt.AnyOrderND, /, trim: lmt.AnyTrim = 0, @@ -267,7 +289,7 @@ def _l_moment_single(_r: int) -> float: @overload def l_moment_from_ppf( - ppf: _Fn1, + ppf: _Fn1 | Callable[[float], float], r: lmt.AnyOrderND, /, trim: lmt.AnyTrim = ..., @@ -278,7 +300,7 @@ def l_moment_from_ppf( ) -> _ArrF8: ... @overload def l_moment_from_ppf( - ppf: _Fn1, + ppf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder, /, trim: lmt.AnyTrim = ..., @@ -288,7 +310,7 @@ def l_moment_from_ppf( alpha: float = ..., ) -> np.float64: ... def l_moment_from_ppf( - ppf: _Fn1, + ppf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder | lmt.AnyOrderND, /, trim: lmt.AnyTrim = 0, @@ -392,7 +414,7 @@ def l_moment_from_ppf( rs = clean_orders(np.asanyarray(r)) s, t = clean_trim(trim) - def igf(p: float, _r: int, /) -> float | lnpt.Float: + def igf(p: float, _r: int, /) -> float: return p**s * (1 - p) ** t * eval_sh_jacobi(_r - 1, t, s, p) * ppf(p) kwds = quad_opts or {} @@ -419,7 +441,7 @@ def _l_moment_single(_r: int) -> float: @overload def l_moment_from_qdf( - qdf: _Fn1, + qdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrderND, /, trim: lmt.AnyTrim = ..., @@ -430,7 +452,7 @@ def l_moment_from_qdf( ) -> _ArrF8: ... @overload def l_moment_from_qdf( - qdf: _Fn1, + qdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder, /, trim: lmt.AnyTrim = ..., @@ -440,7 +462,7 @@ def l_moment_from_qdf( alpha: float = ..., ) -> np.float64: ... def l_moment_from_qdf( - qdf: _Fn1, + qdf: _Fn1 | Callable[[float], float], r: lmt.AnyOrder | lmt.AnyOrderND, /, trim: lmt.AnyTrim = 0, diff --git a/lmo/theoretical/_f_to_lm_cov.py b/lmo/theoretical/_f_to_lm_cov.py index 043ee6b2..02c8d603 100644 --- a/lmo/theoretical/_f_to_lm_cov.py +++ b/lmo/theoretical/_f_to_lm_cov.py @@ -1,14 +1,11 @@ from __future__ import annotations import functools -from collections.abc import Callable -from typing import TYPE_CHECKING, TypeAlias, TypeVar +from typing import TYPE_CHECKING, Protocol, TypeAlias, TypeVar, cast import numpy as np import numpy.typing as npt -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt from lmo._poly import eval_sh_jacobi from lmo._utils import clean_order, clean_trim, moments_to_stats_cov, round0 from ._f_to_lm import l_moment_from_cdf @@ -16,15 +13,21 @@ if TYPE_CHECKING: import lmo.typing as lmt + import lmo.typing.scipy as lspt __all__ = ["l_moment_cov_from_cdf", "l_stats_cov_from_cdf"] _T = TypeVar("_T") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + _Pair: TypeAlias = tuple[_T, _T] -_Fn1: TypeAlias = Callable[[float], float | lnpt.Float] _ArrF8: TypeAlias = npt.NDArray[np.float64] @@ -153,7 +156,7 @@ def l_moment_cov_from_cdf( s, t = clean_trim(trim) - _cdf = functools.cache(cdf) + _cdf = cast(_Fn1, functools.cache(cdf)) if support is None: a, b = tighten_cdf_support(_cdf, (-np.inf, np.inf)) diff --git a/lmo/theoretical/_f_to_lm_eif.py b/lmo/theoretical/_f_to_lm_eif.py index 5c9e3442..0e4a44c4 100644 --- a/lmo/theoretical/_f_to_lm_eif.py +++ b/lmo/theoretical/_f_to_lm_eif.py @@ -1,35 +1,38 @@ from __future__ import annotations -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar, cast +from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, TypeVar, cast import numpy as np import numpy.typing as npt -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt from lmo._poly import eval_sh_jacobi from lmo._utils import clean_order, clean_trim, round0 from ._f_to_lm import l_moment_from_cdf from ._utils import ALPHA, l_const, tighten_cdf_support if TYPE_CHECKING: + from collections.abc import Callable + import lmo.typing as lmt + import lmo.typing.scipy as lspt __all__ = ["l_moment_influence_from_cdf", "l_ratio_influence_from_cdf"] _T = TypeVar("_T") -_T_x = TypeVar("_T_x", bound=float | npt.NDArray[np.float64]) +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... + _Pair: TypeAlias = tuple[_T, _T] -_Fn1: TypeAlias = Callable[[float], float | lnpt.Float] -_ArrF8: TypeAlias = npt.NDArray[np.float64] def l_moment_influence_from_cdf( - cdf: Callable[[_ArrF8], _ArrF8], + cdf: _Fn1, r: lmt.AnyOrder, /, trim: lmt.AnyTrim = 0, @@ -110,8 +113,7 @@ def influence0(x: _T_x, /) -> _T_x: Returns: out """ - _x = np.asanyarray(x, np.float64)[()] - return cast(_T_x, _x * 0.0 + 0.0) # :+) + return np.asanyarray(x, np.float64)[()] * 0.0 + 0.0 # pyright: ignore[reportReturnType] return influence0 @@ -119,7 +121,7 @@ def influence0(x: _T_x, /) -> _T_x: if l_moment is None: lm = l_moment_from_cdf( - cast(Callable[[float], float], cdf), + cdf, _r, trim=(s, t), support=support, @@ -129,7 +131,7 @@ def influence0(x: _T_x, /) -> _T_x: else: lm = l_moment - a, b = support or tighten_cdf_support(cast(_Fn1, cdf), support) + a, b = support or tighten_cdf_support(cdf, support) c = l_const(_r, s, t) def influence(x: _T_x, /) -> _T_x: @@ -154,7 +156,7 @@ def influence(x: _T_x, /) -> _T_x: def l_ratio_influence_from_cdf( - cdf: Callable[[_ArrF8], _ArrF8], + cdf: _Fn1, r: lmt.AnyOrder, k: lmt.AnyOrder = 2, /, @@ -225,44 +227,24 @@ def l_ratio_influence_from_cdf( kwds: dict[str, Any] = {"support": support, "quad_opts": quad_opts} if l_moments is None: - l_r, l_k = l_moment_from_cdf( - cast(Callable[[float], float], cdf), - [_r, _k], - trim=trim, - alpha=alpha, - **kwds, - ) + l_r, l_k = l_moment_from_cdf(cdf, [_r, _k], trim=trim, alpha=alpha, **kwds) else: l_r, l_k = l_moments - if_r = l_moment_influence_from_cdf( - cdf, - _r, - trim, - l_moment=l_r, - tol=0, - **kwds, - ) - if_k = l_moment_influence_from_cdf( - cdf, - _k, - trim, - l_moment=l_k, - tol=0, - **kwds, - ) + if_r = l_moment_influence_from_cdf(cdf, _r, trim, l_moment=l_r, tol=0, **kwds) + if_k = l_moment_influence_from_cdf(cdf, _k, trim, l_moment=l_k, tol=0, **kwds) if abs(l_k) <= tol: msg = f"L-ratio ({r=}, {k=}) denominator is approximately zero." raise ZeroDivisionError(msg) + t_r = l_r / l_k def influence_function(x: _T_x, /) -> _T_x: psi_r = if_r(x) # cheat a bit to avoid `inf - inf = nan` situations psi_k = np.where(np.isinf(psi_r), 0, if_k(x)) - - return cast(_T_x, round0((psi_r - t_r * psi_k) / l_k, tol=tol)[()]) + return round0((psi_r - t_r * psi_k) / l_k, tol=tol)[()] # pyright: ignore[reportReturnType] influence_function.__doc__ = ( f"Theoretical influence function for L-moment ratio with r={_r}, " diff --git a/lmo/theoretical/_lm_to_f.py b/lmo/theoretical/_lm_to_f.py index 9e12ef44..f810a318 100644 --- a/lmo/theoretical/_lm_to_f.py +++ b/lmo/theoretical/_lm_to_f.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Protocol, TypeAlias, TypeVar, cast, overload +from typing import TYPE_CHECKING, Protocol, TypeAlias, TypeVar import numpy as np import numpy.typing as npt @@ -11,36 +11,21 @@ if TYPE_CHECKING: from collections.abc import Callable - import optype.numpy as onpt - import lmo.typing as lmt import lmo.typing.np as lnpt +__all__ = ["ppf_from_l_moments", "qdf_from_l_moments"] _T = TypeVar("_T") -_Pair: TypeAlias = tuple[_T, _T] -_ArrF8: TypeAlias = npt.NDArray[np.float64] +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) -__all__ = ["ppf_from_l_moments", "qdf_from_l_moments"] +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... -class _VectorizedPPF(Protocol): - @overload - def __call__( - self, - u: lnpt.AnyArrayInt | lnpt.AnyArrayFloat, - /, - *, - r_max: int = ..., - ) -> _ArrF8: ... - @overload - def __call__( - self, - u: lnpt.AnyScalarInt | lnpt.AnyScalarFloat, - /, - *, - r_max: int = ..., - ) -> np.float64: ... + +_Pair: TypeAlias = tuple[_T, _T] +_ArrF8: TypeAlias = npt.NDArray[np.float64] def _validate_l_bounds(l_r: _ArrF8, s: float, t: float) -> None: @@ -112,7 +97,7 @@ def ppf_from_l_moments( support: _Pair[float] = (-np.inf, np.inf), validate: bool = True, extrapolate: bool = False, -) -> _VectorizedPPF: +) -> _Fn1: r""" Return a PPF (quantile function, or inverse CDF), with the specified. L-moments \( \tlmoment{s, t}{1}, \tlmoment{s, t}{2}, \ldots, @@ -194,10 +179,10 @@ def ppf_from_l_moments( c = w * l_r def ppf( - u: npt.ArrayLike, + u: _T_x, *, r_max: int = -1, - ) -> np.float64 | _ArrF8: + ) -> _T_x: y = np.asarray(u) y = np.where((y < 0) | (y > 1), np.nan, 2 * y - 1) @@ -207,7 +192,7 @@ def ppf( if extrapolate and _n > 2: x = (x + fourier_jacobi(y, _c[:-1], t, s)) / 2 - return np.clip(x, *support)[()] + return np.clip(x, *support)[()] # pyright: ignore[reportReturnType] if validate and not _monotonic(ppf, 0, 1): msg = ( @@ -216,7 +201,7 @@ def ppf( ) raise ValueError(msg) - return cast(_VectorizedPPF, ppf) + return ppf def qdf_from_l_moments( @@ -226,7 +211,7 @@ def qdf_from_l_moments( *, validate: bool = True, extrapolate: bool = False, -) -> _VectorizedPPF: +) -> _Fn1: r""" Return the QDF (quantile density function, the derivative of the PPF), with the specified L-moments \( \tlmoment{s, t}{1}, \tlmoment{s, t}{2}, @@ -271,11 +256,8 @@ def qdf_from_l_moments( )[1:] alpha, beta = t + 1, s + 1 - def qdf( - u: onpt.AnyFloatingArray, - *, - r_max: int = -1, - ) -> float | _ArrF8: + def qdf(u: _T_x, *, r_max: int = -1) -> _T_x: + """Quantile Distribution Function (QDF) polynomial approximation.""" y = np.asanyarray(u, dtype=np.float64) # TODO: make this lazy y = np.where((y < 0) | (y > 1), np.nan, 2 * y - 1) @@ -286,10 +268,10 @@ def qdf( if extrapolate and _n > 2: x = (x + fourier_jacobi(y, _c[:-1], alpha, beta)) / 2 - return x[()] + return x[()] # pyright: ignore[reportReturnType] if validate and np.any(qdf(plotting_positions(100)) < 0): msg = "QDF is not positive; consider increasing the trim" raise ValueError(msg) - return cast(_VectorizedPPF, qdf) + return qdf diff --git a/lmo/theoretical/_utils.py b/lmo/theoretical/_utils.py index 8e61abd5..b7a8de4a 100644 --- a/lmo/theoretical/_utils.py +++ b/lmo/theoretical/_utils.py @@ -1,24 +1,30 @@ +from __future__ import annotations + import functools -from collections.abc import Callable, Sequence from math import exp, factorial, gamma, lgamma, log -from typing import Concatenate, Final, ParamSpec, TypeAlias +from typing import TYPE_CHECKING, Concatenate, Final, ParamSpec, Protocol, TypeVar import numpy as np import numpy.typing as npt -import scipy.integrate as spi -import scipy.special as sps -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt +if TYPE_CHECKING: + from collections.abc import Callable, Sequence + + import lmo.typing.scipy as lspt __all__ = ("ALPHA", "QUAD_LIMIT", "l_coef_factor", "l_const", "tighten_cdf_support") -_Fn1: TypeAlias = Callable[[float], float | lnpt.Float] _Tss = ParamSpec("_Tss") +_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) + + +class _Fn1(Protocol): + def __call__(self, x: _T_x, /) -> _T_x: ... -ALPHA: Final[float] = 0.1 -QUAD_LIMIT: Final[int] = 100 + +ALPHA: Final = 0.1 +QUAD_LIMIT: Final = 100 @functools.cache @@ -33,8 +39,7 @@ def l_const(r: int, s: float, t: float, k: int = 0) -> float: if k == 1: return 1 / (r - 1) - # math.lgamma is faster (and has better type annotations) than - # scipy.special.loggamma. + # math.lgamma is faster than scipy.special.loggamma. if r + s + t <= 20: v = gamma(r + s + t + 1) / (gamma(r + s) * gamma(r + t)) elif r + s + t <= 128: @@ -61,6 +66,8 @@ def l_coef_factor( assert s + t > -1 + import scipy.special as sps + _r = np.asarray(r) c = ( np.sqrt( @@ -75,16 +82,12 @@ def l_coef_factor( def tighten_cdf_support( - cdf: _Fn1, + cdf: _Fn1 | Callable[[float], float], support: tuple[float, float] | None = None, ) -> tuple[float, float]: """Attempt to tighten the support by checking some common bounds.""" a, b = (-np.inf, np.inf) if support is None else map(float, support) - # assert a < b, (a, b) - # assert (u_a := cdf(a)) == 0, (a, u_a) - # assert (u_b := cdf(b)) == 1, (b, u_b) - # attempt to tighten the default support by checking some common bounds if cdf(0) == 0: # left-bounded at 0 (e.g. weibull) @@ -107,6 +110,8 @@ def nquad( *args: _Tss.args, **kwds: _Tss.kwargs, ) -> float: + import scipy.integrate as spi + if kwds: integrand = functools.partial(integrand, *args, **kwds) args = () # pyright: ignore[reportAssignmentType] diff --git a/lmo/typing/scipy.py b/lmo/typing/scipy.py index d526c06b..a8c9d71e 100644 --- a/lmo/typing/scipy.py +++ b/lmo/typing/scipy.py @@ -78,14 +78,10 @@ class QuadOptions(TypedDict, total=False): epsabs: float epsrel: float limit: int - points: ( - CanGetitem[int, float | np.floating[Any] | np.integer[Any] | np.bool_] - | onpt.Array[tuple[int], np.floating[Any] | np.integer[Any] | np.bool_] - ) + points: CanGetitem[int, float | np.floating[Any] | np.integer[Any] | np.bool_] weight: QuadWeights wvar: float | tuple[float, float] wopts: tuple[int, npt.NDArray[np.float32 | np.float64]] - limlst: int maxp1: int From ff7a1e3c8552788fefbff4782d00f6502d2cb803 Mon Sep 17 00:00:00 2001 From: jorenham Date: Thu, 24 Oct 2024 23:11:36 +0200 Subject: [PATCH 2/2] update dependencies --- .pre-commit-config.yaml | 4 +- lmo/diagnostic.py | 23 ++-- poetry.lock | 295 ++++++++++++++++++++-------------------- pyproject.toml | 22 +-- 4 files changed, 179 insertions(+), 165 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a264cb9..6dfbaab8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,7 @@ repos: - id: markdownlint - repo: https://github.com/adamchainz/blacken-docs - rev: 1.19.0 + rev: 1.19.1 hooks: - id: blacken-docs additional_dependencies: [black==24.*] @@ -78,7 +78,7 @@ repos: - id: codespell - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.7.1 hooks: - id: ruff args: [--fix, --show-fixes] diff --git a/lmo/diagnostic.py b/lmo/diagnostic.py index 9413ff6a..06dd553d 100644 --- a/lmo/diagnostic.py +++ b/lmo/diagnostic.py @@ -192,8 +192,20 @@ def _gof_stat_single(l_obs: _ArrF8, l_exp: _ArrF8, cov: _ArrF8) -> float: ) +def _is_rv(x: object) -> TypeIs[lspt.RVFrozen | lspt.RV]: + from scipy.stats.distributions import ( + rv_continuous, + rv_discrete, + rv_frozen, + rv_histogram, + ) + + # NOTE: this assumes that the (private) `rv_generic` class is a sealed type + return isinstance(x, rv_frozen | rv_continuous | rv_discrete | rv_histogram) + + def l_moment_gof( - rv_or_cdf: lspt.RV | _Fn1, + rv_or_cdf: lspt.RV | lspt.RVFrozen | _Fn1, l_moments: _ArrF8, n_obs: int, /, @@ -290,15 +302,8 @@ def l_moment_gof( return HypothesisTestResult(stat, pval) -def _is_rv(x: object) -> TypeIs[lspt.RV]: - from scipy.stats.distributions import rv_continuous, rv_discrete, rv_histogram - - # NOTE: this assumes that the (private) `rv_generic` class is a sealed type - return isinstance(x, rv_continuous | rv_discrete | rv_histogram) - - def l_stats_gof( - rv_or_cdf: lspt.RV | _Fn1, + rv_or_cdf: lspt.RV | lspt.RVFrozen | _Fn1, l_stats: _ArrF8, n_obs: int, /, diff --git a/poetry.lock b/poetry.lock index bce69af0..75733104 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "appnope" @@ -64,13 +64,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "basedpyright" -version = "1.19.0" +version = "1.19.1" description = "static type checking for Python (but based)" optional = false python-versions = ">=3.8" files = [ - {file = "basedpyright-1.19.0-py3-none-any.whl", hash = "sha256:78f2fc2e5d3f2cc1c4dfae351f9d45aa52917bfb0e9a103579a4bb005f097bec"}, - {file = "basedpyright-1.19.0.tar.gz", hash = "sha256:847d9c75ca691c9ff0661c800e830c23b32c65793b97a4386db9cbf52c1a168a"}, + {file = "basedpyright-1.19.1-py3-none-any.whl", hash = "sha256:976744146fcecb7413a726fbb8e52f4606738e02820a7dfbe310199f8915311e"}, + {file = "basedpyright-1.19.1.tar.gz", hash = "sha256:2863e619296ddd7f9f566c44e4fa8f781d727a83fa2da6ef62951e0b657655f6"}, ] [package.dependencies] @@ -143,13 +143,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "blacken-docs" -version = "1.19.0" +version = "1.19.1" description = "Run Black on Python code blocks in documentation files." optional = false python-versions = ">=3.9" files = [ - {file = "blacken_docs-1.19.0-py3-none-any.whl", hash = "sha256:c9c8a65909e20d3af0680f5474f7d5ab80c3297c4a63cef8c50f9ce42ea597ed"}, - {file = "blacken_docs-1.19.0.tar.gz", hash = "sha256:a95176ff28f8d1a2cb7781fed21dc008b08270732d94b1e7336c26115dd92c4a"}, + {file = "blacken_docs-1.19.1-py3-none-any.whl", hash = "sha256:73c3dee042a28f2d4f7df6e2c340869d6ced9704f6174d035d9b6199567f890d"}, + {file = "blacken_docs-1.19.1.tar.gz", hash = "sha256:3cf7a10f5b87886683e3ab07a0dc17de41425f3d21e2948e59f1c6079c45b328"}, ] [package.dependencies] @@ -823,13 +823,13 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "griffe" -version = "1.4.1" +version = "1.5.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" files = [ - {file = "griffe-1.4.1-py3-none-any.whl", hash = "sha256:84295ee0b27743bd880aea75632830ef02ded65d16124025e4c263bb826ab645"}, - {file = "griffe-1.4.1.tar.gz", hash = "sha256:911a201b01dc92e08c0e84c38a301e9da5ec067f00e7d9f2e39bc24dbfa3c176"}, + {file = "griffe-1.5.1-py3-none-any.whl", hash = "sha256:ad6a7980f8c424c9102160aafa3bcdf799df0e75f7829d75af9ee5aef656f860"}, + {file = "griffe-1.5.1.tar.gz", hash = "sha256:72964f93e08c553257706d6cd2c42d1c172213feb48b2be386f243380b405d4b"}, ] [package.dependencies] @@ -847,13 +847,13 @@ files = [ [[package]] name = "hypothesis" -version = "6.115.3" +version = "6.115.5" description = "A library for property-based testing" optional = false python-versions = ">=3.9" files = [ - {file = "hypothesis-6.115.3-py3-none-any.whl", hash = "sha256:d2770b0db08ad666fe6ff36027910039ab681084d13bcf9c057449c2e27099c4"}, - {file = "hypothesis-6.115.3.tar.gz", hash = "sha256:d4efc8c7371bd4ec906d2777f1f18fee5539e47b3d7c7cdc93d1026ad35d9b33"}, + {file = "hypothesis-6.115.5-py3-none-any.whl", hash = "sha256:b7733459ae9a93020fac3b91b41473c9b85e975139a152a70d88f3a5caa3fa3f"}, + {file = "hypothesis-6.115.5.tar.gz", hash = "sha256:4768c5fb426b305462ed31032d6e216a31daaefb1dc3134fdf2795b7961d7cb3"}, ] [package.dependencies] @@ -1396,72 +1396,72 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "3.0.1" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" files = [ - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, - {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -1676,13 +1676,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-git-revision-date-localized-plugin" -version = "1.2.9" +version = "1.3.0" description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_git_revision_date_localized_plugin-1.2.9-py3-none-any.whl", hash = "sha256:dea5c8067c23df30275702a1708885500fadf0abfb595b60e698bffc79c7a423"}, - {file = "mkdocs_git_revision_date_localized_plugin-1.2.9.tar.gz", hash = "sha256:df9a50873fba3a42ce9123885f8c53d589e90ef6c2443fe3280ef1e8d33c8f65"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.3.0-py3-none-any.whl", hash = "sha256:c99377ee119372d57a9e47cff4e68f04cce634a74831c06bc89b33e456e840a1"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.3.0.tar.gz", hash = "sha256:439e2f14582204050a664c258861c325064d97cdc848c541e48bb034a6c4d0cb"}, ] [package.dependencies] @@ -1698,13 +1698,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd [[package]] name = "mkdocs-include-markdown-plugin" -version = "6.2.2" +version = "7.0.0" description = "Mkdocs Markdown includer plugin." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "mkdocs_include_markdown_plugin-6.2.2-py3-none-any.whl", hash = "sha256:d293950f6499d2944291ca7b9bc4a60e652bbfd3e3a42b564f6cceee268694e7"}, - {file = "mkdocs_include_markdown_plugin-6.2.2.tar.gz", hash = "sha256:f2bd5026650492a581d2fd44be6c22f90391910d76582b96a34c264f2d17875d"}, + {file = "mkdocs_include_markdown_plugin-7.0.0-py3-none-any.whl", hash = "sha256:bf8d19245ae3fb2eea395888e80c60bc91806a0d879279707d707896c24319c3"}, + {file = "mkdocs_include_markdown_plugin-7.0.0.tar.gz", hash = "sha256:a8eac8f2e6aa391d82d1d5e473b819b52393d91464060c02db5741834fe9008b"}, ] [package.dependencies] @@ -1819,13 +1819,13 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "1.12.1" +version = "1.12.2" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.9" files = [ - {file = "mkdocstrings_python-1.12.1-py3-none-any.whl", hash = "sha256:205244488199c9aa2a39787ad6a0c862d39b74078ea9aa2be817bc972399563f"}, - {file = "mkdocstrings_python-1.12.1.tar.gz", hash = "sha256:60d6a5ca912c9af4ad431db6d0111ce9f79c6c48d33377dde6a05a8f5f48d792"}, + {file = "mkdocstrings_python-1.12.2-py3-none-any.whl", hash = "sha256:7f7d40d6db3cb1f5d19dbcd80e3efe4d0ba32b073272c0c0de9de2e604eda62a"}, + {file = "mkdocstrings_python-1.12.2.tar.gz", hash = "sha256:7a1760941c0b52a2cd87b960a9e21112ffe52e7df9d0b9583d04d47ed2e186f3"}, ] [package.dependencies] @@ -2376,32 +2376,33 @@ wcwidth = "*" [[package]] name = "psutil" -version = "6.0.0" +version = "6.1.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, + {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, + {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, + {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, + {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, + {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, + {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, + {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "ptyprocess" @@ -3000,13 +3001,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.9.2" +version = "13.9.3" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, + {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, + {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, ] [package.dependencies] @@ -3150,29 +3151,29 @@ files = [ [[package]] name = "ruff" -version = "0.7.0" +version = "0.7.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, - {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, - {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, - {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, - {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, - {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, - {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, - {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, + {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, + {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, + {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, + {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, + {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, + {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, + {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, ] [[package]] @@ -3227,29 +3228,30 @@ test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.3 [[package]] name = "scipy-stubs" -version = "1.14.1.0" +version = "1.14.1.1" description = "Typing Stubs for SciPy" optional = false python-versions = "<4.0.0,>=3.10.1" files = [ - {file = "scipy_stubs-1.14.1.0-py3-none-any.whl", hash = "sha256:cdf3d1296c7da86f6e9ad89b056d45195c57f3f7e1b63005e04ad2afd997fcda"}, - {file = "scipy_stubs-1.14.1.0.tar.gz", hash = "sha256:50fc2e329bfc1c9986c5136c094c717a1b636c210ac49b99035bcc5efce9a593"}, + {file = "scipy_stubs-1.14.1.1-py3-none-any.whl", hash = "sha256:665298a8fed1bda309c214b4d76d3213badfd87d598b03f14005238feb145d11"}, + {file = "scipy_stubs-1.14.1.1.tar.gz", hash = "sha256:e67ba6d88ff0dc7512db1dd0d532b1ca11a1bcb7ffaeb8f69a0564fcf282fb08"}, ] [package.dependencies] -numpy = ">=1.25" optype = ">=0.6.1,<0.7.0" -scipy = ">=1.14.1" + +[package.extras] +scipy = ["scipy (>=1.10)"] [[package]] name = "setuptools" -version = "75.1.0" +version = "75.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, - {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [package.extras] @@ -3350,13 +3352,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "tinycss2" -version = "1.3.0" +version = "1.4.0" description = "A tiny CSS parser" optional = false python-versions = ">=3.8" files = [ - {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, - {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, + {file = "tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"}, + {file = "tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7"}, ] [package.dependencies] @@ -3388,13 +3390,13 @@ files = [ [[package]] name = "tox" -version = "4.23.0" +version = "4.23.2" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.23.0-py3-none-any.whl", hash = "sha256:46da40afb660e46238c251280eb910bdaf00b390c7557c8e4bb611f422e9db12"}, - {file = "tox-4.23.0.tar.gz", hash = "sha256:a6bd7d54231d755348d3c3a7b450b5bf6563833716d1299a1619587a1b77a3bf"}, + {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"}, + {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"}, ] [package.dependencies] @@ -3408,6 +3410,9 @@ pluggy = ">=1.5" pyproject-api = ">=1.8" virtualenv = ">=20.26.6" +[package.extras] +test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"] + [[package]] name = "traitlets" version = "5.14.3" @@ -3489,13 +3494,13 @@ crypto-eth-addresses = ["eth-hash[pycryptodome] (>=0.7.0)"] [[package]] name = "virtualenv" -version = "20.26.6" +version = "20.27.0" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, - {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, + {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, + {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] [package.dependencies] @@ -3592,4 +3597,4 @@ typing = ["scipy-stubs"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "0b49f082490d2f656d4b248f3cf8dba172582b3242cf05b0bd334056c4b968b9" +content-hash = "78c8a8fca23c3fc2f715b660528af977a5c872e3a3be6dde85e1fb0597a52f1f" diff --git a/pyproject.toml b/pyproject.toml index 2640101e..0ab111e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,10 @@ classifiers = [ "Intended Audience :: Science/Research", "Natural Language :: English", "Operating System :: OS Independent", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Office/Business :: Financial", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Hydrology", @@ -47,28 +51,28 @@ typing_extensions = {version = "^4.10", python = "<3.13"} numpy = ">=1.24" scipy = "^1.10" optype = {version = "^0.6.1", extras = ["numpy"]} -scipy-stubs = {version = "^1.14.1.0", optional = true} +scipy-stubs = {version = "^1.14.1.1", optional = true} pandas = {version = "^2.0", optional = true} [tool.poetry.group.dev.dependencies] -blacken-docs = "^1.19.0" +blacken-docs = "^1.19.1" codespell = "^2.3.0" -hypothesis = {version = "^6.115.2", extras = ["numpy"]} +hypothesis = {version = "^6.115.5", extras = ["numpy"]} pre-commit = "^4.0.1" -basedpyright = "^1.18.4" +basedpyright = "^1.19.1" pytest = "^8.3.3" pytest-doctestplus = "^1.2.1" -ruff = ">=0.6.9,<0.8.0" +ruff = ">=0.7.1" sp-repo-review = {version = "^2024.8.19", extras = ["cli"]} -tox = "^4.22.0" +tox = "^4.23.2" [tool.poetry.group.docs.dependencies] mkdocs = "^1.6.1" mkdocs-bibtex = "^2.16.2" -mkdocs-git-revision-date-localized-plugin = "^1.2.9" -mkdocs-include-markdown-plugin = "^6.2.2" +mkdocs-git-revision-date-localized-plugin = "^1.3.0" +mkdocs-include-markdown-plugin = "^7.0.0" mkdocs-jupyter = "^0.25.1" -mkdocs-material = "^9.5.41" +mkdocs-material = "^9.5.42" mkdocs-minify-plugin = "^0.8.0" mkdocstrings = {version = ">=0.26.2,<0.27.0", extras = ["python"]}