From 30c1761cb347718fc034f5332f3e71dc05e288bd Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 18:16:57 -0400 Subject: [PATCH 01/35] Started wip_types --- squigglepy/bayes.py | 62 ++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index cc30658..b771f7e 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -7,15 +7,16 @@ import pathos.multiprocessing as mp from datetime import datetime +from typing import Callable, List -from .distributions import BetaDistribution, NormalDistribution, norm, beta, mixture +from .distributions import BetaDistribution, NormalDistribution, MixtureDistribution, norm, beta, mixture from .utils import _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm _squigglepy_internal_bayesnet_caches = {} -def simple_bayes(likelihood_h, likelihood_not_h, prior): +def simple_bayes(likelihood_h: float, likelihood_not_h: float, prior: float) -> float: """ Calculate Bayes rule. @@ -51,21 +52,21 @@ def simple_bayes(likelihood_h, likelihood_not_h, prior): def bayesnet( - event_fn=None, - n=1, - find=None, - conditional_on=None, - reduce_fn=None, - raw=False, - memcache=True, - memcache_load=True, - memcache_save=True, - reload_cache=False, - dump_cache_file=None, - load_cache_file=None, - cache_file_primary=False, - verbose=False, - cores=1, + event_fn: Callable | None = None, + n: int = 1, + find: Callable | None = None, + conditional_on: Callable | None = None, + reduce_fn: Callable | None = None, + raw: bool = False, + memcache: bool = True, + memcache_load: bool = True, + memcache_save: bool = True, + reload_cache: bool = False, + dump_cache_file: str = "", + load_cache_file: str = "", + cache_file_primary: bool = False, + verbose: bool = False, + cores: int = 1, ): """ Calculate a Bayesian network. @@ -99,10 +100,10 @@ def bayesnet( is True. Cache will be matched based on the ``event_fn``. Default ``True``. reload_cache : bool If True, any existing cache will be ignored and recalculated. Default ``False``. - dump_cache_file : str or None + dump_cache_file : str If present, will write out the cache to a binary file with this path with ``.sqlcache`` appended to the file name. - load_cache_file : str or None + load_cache_file : str If present, will first attempt to load and use a cache from a file with this path with ``.sqlcache`` appended to the file name. cache_file_primary : bool @@ -149,8 +150,8 @@ def bayesnet( memcache_load = False memcache_save = False has_in_mem_cache = event_fn in _squigglepy_internal_bayesnet_caches - cache_path = load_cache_file + ".sqcache" if load_cache_file else None - has_file_cache = os.path.exists(cache_path) if load_cache_file else False + cache_path = load_cache_file + ".sqcache" if load_cache_file != "" else "" + has_file_cache = os.path.exists(cache_path) if load_cache_file != "" else False if load_cache_file or dump_cache_file or cores > 1: encoder = msgspec.msgpack.Encoder() @@ -296,7 +297,9 @@ def multicore_event_fn(core, total_cores=1, verbose=False): return events -def update(prior, evidence, evidence_weight=1): +def update( + prior: BaseDistribution, evidence: BaseDistribution, evidence_weight: float = 1 +) -> BaseDistribution: """ Update a distribution. @@ -354,7 +357,12 @@ def update(prior, evidence, evidence_weight=1): raise ValueError("type `{}` not supported.".format(prior.__class__.__name__)) -def average(prior, evidence, weights=[0.5, 0.5], relative_weights=None): +def average( + prior: BaseDistribution, + evidence: BaseDistribution, + weights: List | np.ndarray | float | None = [0.5, 0.5], + relative_weights: List | np.ndarray | float | None = None, +) -> MixtureDistribution: """ Average two distributions. @@ -374,14 +382,16 @@ def average(prior, evidence, weights=[0.5, 0.5], relative_weights=None): Returns ------- - Distribution + MixtureDistribution A mixture distribution that accords weights to ``prior`` and ``evidence``. Examples -------- - >> prior = sq.norm(1,5) - >> evidence = sq.norm(2,3) + >> prior = sq.norm(1, 5) + >> evidence = sq.norm(2, 3) >> bayes.average(prior, evidence) mixture + - 0.5 weight on norm(mean=3.0, sd=1.22) + - 0.5 weight on norm(mean=2.5, sd=0.3) """ return mixture(dists=[prior, evidence], weights=weights, relative_weights=relative_weights) From 016403aae0ceca7d4f84b2b173c74a134a632dc3 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:24:54 -0400 Subject: [PATCH 02/35] fix type issues --- squigglepy/bayes.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index b771f7e..b2e4bbd 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -142,7 +142,7 @@ def bayesnet( >> n=1*M) 0.07723995880535531 """ - events = None + events = {} if memcache is True: memcache_load = True memcache_save = True @@ -152,10 +152,8 @@ def bayesnet( has_in_mem_cache = event_fn in _squigglepy_internal_bayesnet_caches cache_path = load_cache_file + ".sqcache" if load_cache_file != "" else "" has_file_cache = os.path.exists(cache_path) if load_cache_file != "" else False - - if load_cache_file or dump_cache_file or cores > 1: - encoder = msgspec.msgpack.Encoder() - decoder = msgspec.msgpack.Decoder() + encoder = msgspec.msgpack.Encoder() + decoder = msgspec.msgpack.Decoder() if load_cache_file and not has_file_cache and verbose: print("Warning: cache file `{}.sqcache` not found.".format(load_cache_file)) @@ -173,14 +171,19 @@ def bayesnet( events = _squigglepy_internal_bayesnet_caches.get(event_fn) if events: - if events["metadata"]["n"] < n: - raise ValueError( - ("insufficient samples - {} results cached but " + "requested {}").format( - events["metadata"]["n"], n + n_ = events.get("metadata") + if n_ is not None: + n_ = n_.get("n") + if events["metadata"]["n"] < n: + raise ValueError( + ( + "insufficient samples - {} results cached but " + "requested {}" + ).format(events["metadata"]["n"], n) ) - ) + else: + raise ValueError("events is malformed") - events = events["events"] + events = events.get("events", []) if verbose: print("...Loaded") @@ -262,6 +265,7 @@ def multicore_event_fn(core, total_cores=1, verbose=False): if verbose: print("...Cached!") + assert events is not None if conditional_on is not None: if verbose: print("Filtering conditional...") @@ -332,8 +336,10 @@ def update( """ if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): prior_mean = prior.mean + assert prior.sd is not None prior_var = prior.sd**2 evidence_mean = evidence.mean + assert evidence.sd is not None evidence_var = evidence.sd**2 return norm( mean=( @@ -349,6 +355,10 @@ def update( prior_b = prior.b evidence_a = evidence.a evidence_b = evidence.b + assert prior_a is not None + assert evidence_a is not None + assert prior_b is not None + assert evidence_b is not None return beta(prior_a + evidence_a, prior_b + evidence_b) elif type(prior) != type(evidence): print(type(prior), type(evidence)) From 621fd0b21f60b6e75b2eb48559f0b09bb24bf83d Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:25:40 -0400 Subject: [PATCH 03/35] fix --- squigglepy/bayes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index b2e4bbd..0bdae30 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -183,7 +183,7 @@ def bayesnet( else: raise ValueError("events is malformed") - events = events.get("events", []) + events = events.get("events", {}) if verbose: print("...Loaded") From 35ea06f0eb1e760b38a221520951fa9ada49beb7 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:26:06 -0400 Subject: [PATCH 04/35] black --- squigglepy/bayes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 0bdae30..65f8ec2 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -177,7 +177,8 @@ def bayesnet( if events["metadata"]["n"] < n: raise ValueError( ( - "insufficient samples - {} results cached but " + "requested {}" + "insufficient samples - {} results cached but " + + "requested {}" ).format(events["metadata"]["n"], n) ) else: From 0edd49007d65295b9f3cc01f260e5544620298a1 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:35:31 -0400 Subject: [PATCH 05/35] fix bayes --- squigglepy/bayes.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 65f8ec2..dc91cc4 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -174,7 +174,9 @@ def bayesnet( n_ = events.get("metadata") if n_ is not None: n_ = n_.get("n") - if events["metadata"]["n"] < n: + if n_ is None: + raise ValueError("events is malformed") + elif n_ < n: raise ValueError( ( "insufficient samples - {} results cached but " @@ -184,14 +186,15 @@ def bayesnet( else: raise ValueError("events is malformed") - events = events.get("events", {}) + events = events.get("events", []) if verbose: print("...Loaded") elif verbose: print("Reloading cache...") - if events is None: + assert events is not None + if len(events) < 1: if event_fn is None: return None @@ -266,7 +269,6 @@ def multicore_event_fn(core, total_cores=1, verbose=False): if verbose: print("...Cached!") - assert events is not None if conditional_on is not None: if verbose: print("Filtering conditional...") From 4087d6cda28db15ecaae3d18cfe82bbd0deb85df Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:35:37 -0400 Subject: [PATCH 06/35] update README --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index e3d5011..a8e2919 100644 --- a/README.md +++ b/README.md @@ -438,9 +438,7 @@ You can see more examples of squigglepy in action [here](https://github.com/pete ## Run tests -Use `black .` for formatting. - -Run `ruff check . && pytest && pip3 install . && python3 tests/integration.py` +Run `black . && pyright . && ruff check . && pytest && pip3 install . && python3 tests/integration.py` ## Disclaimers From 9e8337a8f980e8f8bdbb4c4c2561e4185331448e Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 20:15:08 -0400 Subject: [PATCH 07/35] more fun with types --- squigglepy/bayes.py | 24 +++++++----------- squigglepy/distributions.py | 50 +++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index dc91cc4..419fb3b 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -7,9 +7,9 @@ import pathos.multiprocessing as mp from datetime import datetime -from typing import Callable, List +from typing import Callable, Optional, Union, List -from .distributions import BetaDistribution, NormalDistribution, MixtureDistribution, norm, beta, mixture +from .distributions import BaseDistribution, NormalDistribution, MixtureDistribution, BetaDistribution, NormalDistribution, norm, beta, mixture from .utils import _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm @@ -52,11 +52,11 @@ def simple_bayes(likelihood_h: float, likelihood_not_h: float, prior: float) -> def bayesnet( - event_fn: Callable | None = None, + event_fn: Optional[Callable] = None, n: int = 1, - find: Callable | None = None, - conditional_on: Callable | None = None, - reduce_fn: Callable | None = None, + find: Optional[Callable] = None, + conditional_on: Optional[Callable] = None, + reduce_fn: Optional[Callable] = None, raw: bool = False, memcache: bool = True, memcache_load: bool = True, @@ -305,7 +305,7 @@ def multicore_event_fn(core, total_cores=1, verbose=False): def update( - prior: BaseDistribution, evidence: BaseDistribution, evidence_weight: float = 1 + prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1 ) -> BaseDistribution: """ Update a distribution. @@ -339,10 +339,8 @@ def update( """ if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): prior_mean = prior.mean - assert prior.sd is not None prior_var = prior.sd**2 evidence_mean = evidence.mean - assert evidence.sd is not None evidence_var = evidence.sd**2 return norm( mean=( @@ -358,10 +356,6 @@ def update( prior_b = prior.b evidence_a = evidence.a evidence_b = evidence.b - assert prior_a is not None - assert evidence_a is not None - assert prior_b is not None - assert evidence_b is not None return beta(prior_a + evidence_a, prior_b + evidence_b) elif type(prior) != type(evidence): print(type(prior), type(evidence)) @@ -373,8 +367,8 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, - weights: List | np.ndarray | float | None = [0.5, 0.5], - relative_weights: List | np.ndarray | float | None = None, + weights: Union[List, np.ndarray, float, None] = [0.5, 0.5], + relative_weights: Union[List, np.ndarray, float, None] = None, ) -> MixtureDistribution: """ Average two distributions. diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 1862d5b..a7d317e 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -2,6 +2,7 @@ import math import numpy as np from scipy import stats +from types import Union, Optional from .utils import _process_weights_values, _is_numpy, is_dist, _round, _optional_import from .version import __version__ @@ -9,6 +10,8 @@ # We only import matplotlib.pyplot if we need it plt = _optional_import("matplotlib.pyplot") +Number = Union[int, float, np.floating, np.integer] + class BaseDistribution: def __init__(self): @@ -62,11 +65,15 @@ def plot(self, num_samples=None, bins=None): num_samples = 1000 if num_samples is None else num_samples bins = 200 if bins is None else bins - samples = self @ num_samples + from .samplers import sample +<<<<<<< HEAD if plt is None: raise ModuleNotFoundError("You must install matplotlib for plotting.") +======= + samples = sample(self, num_samples) +>>>>>>> 1b5a7d3 (more fun with types) plt.hist(samples, bins=bins) plt.show() @@ -639,8 +646,18 @@ def uniform(x, y): class NormalDistribution(OperableDistribution): - def __init__(self, x=None, y=None, mean=None, sd=None, credibility=90, lclip=None, rclip=None): + def __init__( + self, + x: Optional[Number] = None, + y: Optional[Number] = None + mean: Optional[Number] = None + sd: Optional[Number] = None, + credibility: Number = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None + ): super().__init__() +<<<<<<< HEAD self.x = x self.y = y self.credibility = credibility @@ -648,22 +665,33 @@ def __init__(self, x=None, y=None, mean=None, sd=None, credibility=90, lclip=Non self.sd = sd self.lclip = lclip self.rclip = rclip +======= +>>>>>>> 1b5a7d3 (more fun with types) - if self.x is not None and self.y is not None and self.x > self.y: + if x is not None and y is not None and x > y: raise ValueError("`high value` cannot be lower than `low value`") - if (self.x is None or self.y is None) and self.sd is None: + if (x is None or y is None) and sd is None: raise ValueError("must define either x/y or mean/sd") - elif (self.x is not None or self.y is not None) and self.sd is not None: + elif (x is not None or y is not None) and sd is not None: raise ValueError("must define either x/y or mean/sd -- cannot define both") - elif self.sd is not None and self.mean is None: - self.mean = 0 + elif sd is not None and mean is None: + mean = 0 - if self.mean is None and self.sd is None: - self.mean = (self.x + self.y) / 2 - cdf_value = 0.5 + 0.5 * (self.credibility / 100) + if sd is None or mean is None: + mean = (x + y) / 2 + cdf_value = 0.5 + 0.5 * (credibility / 100) normed_sigma = stats.norm.ppf(cdf_value) - self.sd = (self.y - self.mean) / normed_sigma + sd = (y - mean) / normed_sigma + + self.mean = mean: Number + self.sd = sd: Number + self.x = x: Number + self.y = y: Number + self.lclip = lclip: Optional[Number] + self.rclip = rclip: Optional[Number] + self.type = "norm" + def __str__(self): out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) From 81ad84a586c63c47a2766b9628f10d17324132a3 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 11:26:31 -0400 Subject: [PATCH 08/35] fix normal distribution --- squigglepy/bayes.py | 18 +++++++++++++++--- squigglepy/distributions.py | 33 +++++++++++++++++++++++++++++---- tests/test_distributions.py | 2 +- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 419fb3b..bf45153 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -9,7 +9,15 @@ from datetime import datetime from typing import Callable, Optional, Union, List -from .distributions import BaseDistribution, NormalDistribution, MixtureDistribution, BetaDistribution, NormalDistribution, norm, beta, mixture +from .distributions import ( + BaseDistribution, + MixtureDistribution, + BetaDistribution, + NormalDistribution, + norm, + beta, + mixture, +) from .utils import _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm @@ -305,7 +313,9 @@ def multicore_event_fn(core, total_cores=1, verbose=False): def update( - prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1 + prior: Union[NormalDistribution, BetaDistribution], + evidence: Union[NormalDistribution, BetaDistribution], + evidence_weight: float = 1, ) -> BaseDistribution: """ Update a distribution. @@ -337,7 +347,9 @@ def update( >> bayes.update(prior, evidence) norm(mean=2.53, sd=0.29) """ - if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): + if isinstance(prior, NormalDistribution) and isinstance( + evidence, NormalDistribution + ): prior_mean = prior.mean prior_var = prior.sd**2 evidence_mean = evidence.mean diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index a7d317e..7c61f30 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -2,7 +2,7 @@ import math import numpy as np from scipy import stats -from types import Union, Optional +from typing import Union, Optional from .utils import _process_weights_values, _is_numpy, is_dist, _round, _optional_import from .version import __version__ @@ -67,13 +67,10 @@ def plot(self, num_samples=None, bins=None): from .samplers import sample -<<<<<<< HEAD if plt is None: raise ModuleNotFoundError("You must install matplotlib for plotting.") -======= samples = sample(self, num_samples) ->>>>>>> 1b5a7d3 (more fun with types) plt.hist(samples, bins=bins) plt.show() @@ -657,6 +654,7 @@ def __init__( rclip: Optional[Number] = None ): super().__init__() +<<<<<<< HEAD <<<<<<< HEAD self.x = x self.y = y @@ -690,8 +688,35 @@ def __init__( self.y = y: Number self.lclip = lclip: Optional[Number] self.rclip = rclip: Optional[Number] +======= + self.credibility = credibility + self.lclip = lclip + self.rclip = rclip +>>>>>>> f457d15 (fix normal distribution) self.type = "norm" + self.x: Number + self.y: Number + self.mean: Number + self.sd: Number + + # Define the complementary set of parameters + # x/y => mean/sd, mean/sd => x/y + if mean is None and sd is None and x is not None and y is not None: + if x > y: + raise ValueError( + "`high value` (y) cannot be lower than `low value` (x)" + ) + self.x, self.y = x, y + self.mean = (self.x + self.y) / 2 + cdf_value = 0.5 + 0.5 * (self.credibility / 100) + normed_sigma = stats.norm.ppf(cdf_value) + self.sd = (self.y - self.mean) / normed_sigma + elif sd is not None and x is None and y is None: + self.sd = sd + self.mean = 0 if mean is None else mean + else: + raise ValueError("you must define either x/y or mean/sd") def __str__(self): out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) diff --git a/tests/test_distributions.py b/tests/test_distributions.py index a0bf8fc..409826e 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -169,7 +169,7 @@ def test_norm_overdefinition_value_error(): def test_norm_low_gt_high(): with pytest.raises(ValueError) as execinfo: norm(10, 5) - assert "`high value` cannot be lower than `low value`" in str(execinfo.value) + assert "`high value` (y) cannot be lower than `low value` (x)" in str(execinfo.value) def test_norm_passes_lclip_rclip(): From 1ef2de3255157b85af3046baac7788c80bcfde73 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 11:57:29 -0400 Subject: [PATCH 09/35] simplify get_percentiles return type --- CHANGES.md | 1 + squigglepy/distributions.py | 88 ++++++++++++++++++------------------- squigglepy/utils.py | 16 +++---- tests/test_utils.py | 8 ++-- 4 files changed, 55 insertions(+), 58 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6dd95d1..e388d85 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ * This package now only supports Python 3.9 and higher. * Package load time is now ~2x faster. * Pandas and matplotlib as removed as required dependencies, but their related features are lazily enabled when the modules are available. These packages are still available for install as extras, installable with pip install squigglepy[plots] (for plotting-related functionality, matplotlib for now), pip install squigglepy[ecosystem] (for pandas, and in the future other related packages), or pip install squigglepy[all] (for all extras). +* `get_percentiles` and `get_log_percentiles` now always return a dictionary, even if there's only one element. * Distribution objects now have the version of squigglepy they were created with, which can be accessed via `obj._version`. This should be helpful for debugging and noticing stale objects, especially when squigglepy distributions are stored in caches. * `.type` is now removed from distribution objects. * Using black now for formatting. diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 7c61f30..4615d84 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -2,7 +2,7 @@ import math import numpy as np from scipy import stats -from typing import Union, Optional +from typing import Callable, Union, Optional from .utils import _process_weights_values, _is_numpy, is_dist, _round, _optional_import from .version import __version__ @@ -15,31 +15,32 @@ class BaseDistribution: def __init__(self): - self.x = None - self.y = None - self.n = None - self.p = None - self.t = None - self.a = None - self.b = None - self.shape = None - self.scale = None - self.credibility = None - self.mean = None - self.sd = None - self.left = None - self.mode = None - self.right = None - self.fn = None - self.fn_str = None - self.lclip = None - self.rclip = None - self.lam = None - self.df = None - self.items = None - self.dists = None - self.weights = None - self._version = __version__ + self.x: Optional[Number] = None + self.y: Optional[Number] = None + self.n: Optional[Number] = None + self.p: Optional[Number] = None + self.t: Optional[Number] = None + self.a: Optional[Number] = None + self.b: Optional[Number] = None + self.shape: Optional[Number] = None + self.scale: Optional[Number] = None + self.credibility: Optional[Number] = None + self.mean: Optional[Number] = None + self.sd: Optional[Number] = None + self.left: Optional[Number] = None + self.mode: Optional[Number] = None + self.right: Optional[Number] = None + self.fn: Optional[Number] = None + self.fn_str: Optional[Number] = None + self.lclip: Optional[Number] = None + self.rclip: Optional[Number] = None + self.lam: Optional[Number] = None + self.df: Optional[Number] = None + self.items: Optional[Number] = None + self.dists: Optional[Number] = None + self.weights: Optional[Number] = None + self.type = "base" + self._version: str = __version__ def __str__(self): return " base" @@ -47,7 +48,7 @@ def __str__(self): def __repr__(self): return str(self) - def plot(self, num_samples=None, bins=None): + def plot(self, num_samples: int = 1000, bins: int = 200) -> None: """ Plot a histogram of the samples. @@ -62,9 +63,6 @@ def plot(self, num_samples=None, bins=None): -------- >>> sq.norm(5, 10).plot() """ - num_samples = 1000 if num_samples is None else num_samples - bins = 200 if bins is None else bins - from .samplers import sample if plt is None: @@ -84,7 +82,7 @@ def __invert__(self): return sample(self) - def __matmul__(self, n): + def __matmul__(self, n: int) -> np.ndarray: try: n = int(n) except ValueError: @@ -101,49 +99,49 @@ def __rshift__(self, fn): else: raise ValueError - def __rmatmul__(self, n): + def __rmatmul__(self, n: int) -> np.ndarray: return self.__matmul__(n) - def __gt__(self, dist): + def __gt__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.gt, ">") - def __ge__(self, dist): + def __ge__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.ge, ">=") - def __lt__(self, dist): + def __lt__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.lt, "<") - def __le__(self, dist): + def __le__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.le, "<=") - def __eq__(self, dist): + def __eq__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.le, "==") - def __ne__(self, dist): + def __ne__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.le, "!=") def __neg__(self): return ComplexDistribution(self, None, operator.neg, "-") - def __add__(self, dist): + def __add__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.add, "+") - def __radd__(self, dist): + def __radd__(self, dist: BaseDistribution): return ComplexDistribution(dist, self, operator.add, "+") - def __sub__(self, dist): + def __sub__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.sub, "-") - def __rsub__(self, dist): + def __rsub__(self, dist: BaseDistribution): return ComplexDistribution(dist, self, operator.sub, "-") - def __mul__(self, dist): + def __mul__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.mul, "*") - def __rmul__(self, dist): + def __rmul__(self, dist: BaseDistribution): return ComplexDistribution(dist, self, operator.mul, "*") - def __truediv__(self, dist): + def __truediv__(self, dist: BaseDistribution): return ComplexDistribution(self, dist, operator.truediv, "/") def __rtruediv__(self, dist): diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 986c07a..12d09a9 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -7,6 +7,7 @@ from datetime import datetime from collections import Counter from collections.abc import Iterable +from typing import List, Union import importlib import importlib.util @@ -408,11 +409,11 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( - data, - percentiles=[1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], - reverse=False, - digits=None, -): + data: Union[List, np.ndarray], + percentiles: List = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + reverse: bool = False, + digits: int = None, +) -> dict[str, float]: """ Print the percentiles of the data. @@ -442,10 +443,7 @@ def get_percentiles( percentile_labels = list(reversed(percentiles)) if reverse else percentiles percentiles = np.percentile(data, percentiles) percentiles = [_round(p, digits) for p in percentiles] - if len(percentile_labels) == 1: - return percentiles[0] - else: - return dict(list(zip(percentile_labels, percentiles))) + return dict(list(zip(percentile_labels, percentiles))) def get_log_percentiles( diff --git a/tests/test_utils.py b/tests/test_utils.py index f978f61..63cc91f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -280,9 +280,9 @@ def test_get_percentiles_digits(): def test_get_percentiles_length_one(): test = get_percentiles(range(1, 901), percentiles=[25], digits=1) - assert test == 225.8 + assert test == {25: 225.8} test = get_percentiles(range(1, 901), percentiles=25, digits=1) - assert test == 225.8 + assert test == {25: 225.8} def test_get_percentiles_zero_digits(): @@ -353,11 +353,11 @@ def test_get_log_percentiles_length_one(): test = get_log_percentiles( [10**x for x in range(1, 10)], percentiles=[20], display=False, digits=0 ) - assert test == 3 + assert test == {20: 3} test = get_log_percentiles( [10**x for x in range(1, 10)], percentiles=20, display=False, digits=0 ) - assert test == 3 + assert test == {20: 3} def test_get_mean_and_ci(): From 96958029112ca8025c2da520c160589dbace4f8a Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 12:19:11 -0400 Subject: [PATCH 10/35] fun with types III --- squigglepy/bayes.py | 12 +++++--- squigglepy/distributions.py | 26 ++---------------- squigglepy/utils.py | 55 +++++++++++++++++++++---------------- tests/test_distributions.py | 4 ++- 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index bf45153..a814f6d 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import time import math @@ -7,7 +9,8 @@ import pathos.multiprocessing as mp from datetime import datetime -from typing import Callable, Optional, Union, List +from typing import Callable, Optional, Union +from numpy.typing import NDArray from .distributions import ( BaseDistribution, @@ -18,7 +21,7 @@ beta, mixture, ) -from .utils import _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm +from .utils import OptionalListOfFloats, _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm _squigglepy_internal_bayesnet_caches = {} @@ -59,6 +62,7 @@ def simple_bayes(likelihood_h: float, likelihood_not_h: float, prior: float) -> return (likelihood_h * prior) / (likelihood_h * prior + likelihood_not_h * (1 - prior)) +# TODO: output type for bayesnet def bayesnet( event_fn: Optional[Callable] = None, n: int = 1, @@ -379,8 +383,8 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, - weights: Union[List, np.ndarray, float, None] = [0.5, 0.5], - relative_weights: Union[List, np.ndarray, float, None] = None, + weights: OptionalListOfFloats = [0.5, 0.5], + relative_weights: OptionalListOfFloats = None, ) -> MixtureDistribution: """ Average two distributions. diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 4615d84..5b1f942 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -15,30 +15,8 @@ class BaseDistribution: def __init__(self): - self.x: Optional[Number] = None - self.y: Optional[Number] = None - self.n: Optional[Number] = None - self.p: Optional[Number] = None - self.t: Optional[Number] = None - self.a: Optional[Number] = None - self.b: Optional[Number] = None - self.shape: Optional[Number] = None - self.scale: Optional[Number] = None - self.credibility: Optional[Number] = None - self.mean: Optional[Number] = None - self.sd: Optional[Number] = None - self.left: Optional[Number] = None - self.mode: Optional[Number] = None - self.right: Optional[Number] = None - self.fn: Optional[Number] = None - self.fn_str: Optional[Number] = None self.lclip: Optional[Number] = None self.rclip: Optional[Number] = None - self.lam: Optional[Number] = None - self.df: Optional[Number] = None - self.items: Optional[Number] = None - self.dists: Optional[Number] = None - self.weights: Optional[Number] = None self.type = "base" self._version: str = __version__ @@ -713,6 +691,8 @@ def __init__( elif sd is not None and x is None and y is None: self.sd = sd self.mean = 0 if mean is None else mean + self.x = None + self.y = None else: raise ValueError("you must define either x/y or mean/sd") @@ -993,7 +973,7 @@ def binomial(n, p): class BetaDistribution(OperableDistribution): - def __init__(self, a, b): + def __init__(self, a: Number, b: Number): super().__init__() self.a = a self.b = b diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 12d09a9..6a2bc1e 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math import types from typing import Optional @@ -7,7 +9,14 @@ from datetime import datetime from collections import Counter from collections.abc import Iterable -from typing import List, Union +from typing import Union +from numpy.typing import NDArray + + +Number = Union[int, float, np.floating, np.integer] +OptionalListOfFloats = Union[ + list[Union[Union[float, np.floating]]], NDArray[np.floating], float, None +] import importlib import importlib.util @@ -409,10 +418,10 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( - data: Union[List, np.ndarray], - percentiles: List = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], - reverse: bool = False, - digits: int = None, + data: Union[list[np.floating], NDArray[np.floating]], + percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + reverse: bool = False, + digits: int = None, ) -> dict[str, float]: """ Print the percentiles of the data. @@ -447,12 +456,12 @@ def get_percentiles( def get_log_percentiles( - data, - percentiles=[1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], - reverse=False, - display=True, - digits=1, -): + data: Union[list[np.floating], NDArray[np.floating]], + percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + reverse: bool = False, + display: bool = True, + digits: int = 1, +) -> dict[str, float]: """ Print the log (base 10) of the percentiles of the data. @@ -481,20 +490,18 @@ def get_log_percentiles( >>> get_percentiles(range(100), percentiles=[25, 50, 75]) {25: 24.75, 50: 49.5, 75: 74.25} """ - percentiles = get_percentiles(data, percentiles=percentiles, reverse=reverse, digits=digits) - if isinstance(percentiles, dict): - if display: - return dict( - [(k, ("{:." + str(digits) + "e}").format(v)) for k, v in percentiles.items()] - ) - else: - return dict([(k, _round(np.log10(v), digits)) for k, v in percentiles.items()]) + percentiles = get_percentiles( + data, percentiles=percentiles, reverse=reverse, digits=digits + ) + if display: + return dict( + [ + (k, ("{:." + str(digits) + "e}").format(v)) + for k, v in percentiles.items() + ] + ) else: - if display: - digit_str = "{:." + str(digits) + "e}" - digit_str.format(percentiles) - else: - return _round(np.log10(percentiles), digits) + return dict([(k, _round(np.log10(v), digits)) for k, v in percentiles.items()]) def get_mean_and_ci(data, credibility=90, digits=None): diff --git a/tests/test_distributions.py b/tests/test_distributions.py index 409826e..c36333d 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -169,7 +169,9 @@ def test_norm_overdefinition_value_error(): def test_norm_low_gt_high(): with pytest.raises(ValueError) as execinfo: norm(10, 5) - assert "`high value` (y) cannot be lower than `low value` (x)" in str(execinfo.value) + assert "`high value` (y) cannot be lower than `low value` (x)" in str( + execinfo.value + ) def test_norm_passes_lclip_rclip(): From 4b69d0f672b69baf430c285b7b41da7366a3a714 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 12:56:58 -0400 Subject: [PATCH 11/35] up Python support to 3.9 --- setup.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1e5cd26 --- /dev/null +++ b/setup.py @@ -0,0 +1,45 @@ +import sys +import setuptools + +from squigglepy.version import __version__ + + +with open("README.md", "r") as fh: + long_description = fh.read() + +if sys.version_info < (3, 9): + raise ValueError("Versions of Python before 3.9 are not supported") + +setuptools.setup( + name="squigglepy", + version=__version__, + author="Peter Wildeford", + author_email="peter@peterhurford.com", + description=( + "Squiggle programming language for intuitive probabilistic" + + " estimation features in Python" + ), + python_requires=">=3.9", + install_requires=[ + "ruff", + "msgspec", + "pytest", + "pytest-mock", + "numpy", + "pandas", + "pathos", + "scipy", + "tqdm", + "matplotlib", + ], + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/rethinkpriorities/squigglepy", + packages=setuptools.find_packages(), + classifiers=[ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], +) From 94f14294292416ad4802b0208c683bbdd321a46e Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 13:46:44 -0400 Subject: [PATCH 12/35] black reformat --- squigglepy/bayes.py | 11 ++++------- squigglepy/distributions.py | 4 +--- squigglepy/utils.py | 11 ++--------- tests/test_distributions.py | 4 +--- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index a814f6d..4479169 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -190,10 +190,9 @@ def bayesnet( raise ValueError("events is malformed") elif n_ < n: raise ValueError( - ( - "insufficient samples - {} results cached but " - + "requested {}" - ).format(events["metadata"]["n"], n) + ("insufficient samples - {} results cached but " + "requested {}").format( + events["metadata"]["n"], n + ) ) else: raise ValueError("events is malformed") @@ -351,9 +350,7 @@ def update( >> bayes.update(prior, evidence) norm(mean=2.53, sd=0.29) """ - if isinstance(prior, NormalDistribution) and isinstance( - evidence, NormalDistribution - ): + if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): prior_mean = prior.mean prior_var = prior.sd**2 evidence_mean = evidence.mean diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 5b1f942..48075d9 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -680,9 +680,7 @@ def __init__( # x/y => mean/sd, mean/sd => x/y if mean is None and sd is None and x is not None and y is not None: if x > y: - raise ValueError( - "`high value` (y) cannot be lower than `low value` (x)" - ) + raise ValueError("`high value` (y) cannot be lower than `low value` (x)") self.x, self.y = x, y self.mean = (self.x + self.y) / 2 cdf_value = 0.5 + 0.5 * (self.credibility / 100) diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 6a2bc1e..7a1547b 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -490,16 +490,9 @@ def get_log_percentiles( >>> get_percentiles(range(100), percentiles=[25, 50, 75]) {25: 24.75, 50: 49.5, 75: 74.25} """ - percentiles = get_percentiles( - data, percentiles=percentiles, reverse=reverse, digits=digits - ) + percentiles = get_percentiles(data, percentiles=percentiles, reverse=reverse, digits=digits) if display: - return dict( - [ - (k, ("{:." + str(digits) + "e}").format(v)) - for k, v in percentiles.items() - ] - ) + return dict([(k, ("{:." + str(digits) + "e}").format(v)) for k, v in percentiles.items()]) else: return dict([(k, _round(np.log10(v), digits)) for k, v in percentiles.items()]) diff --git a/tests/test_distributions.py b/tests/test_distributions.py index c36333d..409826e 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -169,9 +169,7 @@ def test_norm_overdefinition_value_error(): def test_norm_low_gt_high(): with pytest.raises(ValueError) as execinfo: norm(10, 5) - assert "`high value` (y) cannot be lower than `low value` (x)" in str( - execinfo.value - ) + assert "`high value` (y) cannot be lower than `low value` (x)" in str(execinfo.value) def test_norm_passes_lclip_rclip(): From 0c07539e377369e3b062a72ff1605f96663521a5 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 18:16:57 -0400 Subject: [PATCH 13/35] Started wip_types --- squigglepy/bayes.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 4479169..f69e1ce 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -64,11 +64,19 @@ def simple_bayes(likelihood_h: float, likelihood_not_h: float, prior: float) -> # TODO: output type for bayesnet def bayesnet( +<<<<<<< HEAD event_fn: Optional[Callable] = None, n: int = 1, find: Optional[Callable] = None, conditional_on: Optional[Callable] = None, reduce_fn: Optional[Callable] = None, +======= + event_fn: Callable | None = None, + n: int = 1, + find: Callable | None = None, + conditional_on: Callable | None = None, + reduce_fn: Callable | None = None, +>>>>>>> 087260e (Started wip_types) raw: bool = False, memcache: bool = True, memcache_load: bool = True, @@ -164,8 +172,15 @@ def bayesnet( has_in_mem_cache = event_fn in _squigglepy_internal_bayesnet_caches cache_path = load_cache_file + ".sqcache" if load_cache_file != "" else "" has_file_cache = os.path.exists(cache_path) if load_cache_file != "" else False +<<<<<<< HEAD encoder = msgspec.msgpack.Encoder() decoder = msgspec.msgpack.Decoder() +======= + + if load_cache_file or dump_cache_file or cores > 1: + encoder = msgspec.msgpack.Encoder() + decoder = msgspec.msgpack.Decoder() +>>>>>>> 087260e (Started wip_types) if load_cache_file and not has_file_cache and verbose: print("Warning: cache file `{}.sqcache` not found.".format(load_cache_file)) @@ -316,9 +331,13 @@ def multicore_event_fn(core, total_cores=1, verbose=False): def update( +<<<<<<< HEAD prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1, +======= + prior: BaseDistribution, evidence: BaseDistribution, evidence_weight: float = 1 +>>>>>>> 087260e (Started wip_types) ) -> BaseDistribution: """ Update a distribution. @@ -380,8 +399,13 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, +<<<<<<< HEAD weights: OptionalListOfFloats = [0.5, 0.5], relative_weights: OptionalListOfFloats = None, +======= + weights: List | np.ndarray | float | None = [0.5, 0.5], + relative_weights: List | np.ndarray | float | None = None, +>>>>>>> 087260e (Started wip_types) ) -> MixtureDistribution: """ Average two distributions. From 8de7c50a9dad9adaeb1468480bf2640613e72067 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 19:24:54 -0400 Subject: [PATCH 14/35] fix type issues --- squigglepy/bayes.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index f69e1ce..dffc5c0 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -64,19 +64,11 @@ def simple_bayes(likelihood_h: float, likelihood_not_h: float, prior: float) -> # TODO: output type for bayesnet def bayesnet( -<<<<<<< HEAD event_fn: Optional[Callable] = None, n: int = 1, find: Optional[Callable] = None, conditional_on: Optional[Callable] = None, reduce_fn: Optional[Callable] = None, -======= - event_fn: Callable | None = None, - n: int = 1, - find: Callable | None = None, - conditional_on: Callable | None = None, - reduce_fn: Callable | None = None, ->>>>>>> 087260e (Started wip_types) raw: bool = False, memcache: bool = True, memcache_load: bool = True, @@ -172,6 +164,7 @@ def bayesnet( has_in_mem_cache = event_fn in _squigglepy_internal_bayesnet_caches cache_path = load_cache_file + ".sqcache" if load_cache_file != "" else "" has_file_cache = os.path.exists(cache_path) if load_cache_file != "" else False +<<<<<<< HEAD <<<<<<< HEAD encoder = msgspec.msgpack.Encoder() decoder = msgspec.msgpack.Decoder() @@ -181,6 +174,10 @@ def bayesnet( encoder = msgspec.msgpack.Encoder() decoder = msgspec.msgpack.Decoder() >>>>>>> 087260e (Started wip_types) +======= + encoder = msgspec.msgpack.Encoder() + decoder = msgspec.msgpack.Decoder() +>>>>>>> 10e4ded (fix type issues) if load_cache_file and not has_file_cache and verbose: print("Warning: cache file `{}.sqcache` not found.".format(load_cache_file)) @@ -201,6 +198,7 @@ def bayesnet( n_ = events.get("metadata") if n_ is not None: n_ = n_.get("n") +<<<<<<< HEAD if n_ is None: raise ValueError("events is malformed") elif n_ < n: @@ -208,6 +206,13 @@ def bayesnet( ("insufficient samples - {} results cached but " + "requested {}").format( events["metadata"]["n"], n ) +======= + if events["metadata"]["n"] < n: + raise ValueError( + ( + "insufficient samples - {} results cached but " + "requested {}" + ).format(events["metadata"]["n"], n) +>>>>>>> 10e4ded (fix type issues) ) else: raise ValueError("events is malformed") @@ -295,6 +300,7 @@ def multicore_event_fn(core, total_cores=1, verbose=False): if verbose: print("...Cached!") + assert events is not None if conditional_on is not None: if verbose: print("Filtering conditional...") @@ -371,8 +377,10 @@ def update( """ if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): prior_mean = prior.mean + assert prior.sd is not None prior_var = prior.sd**2 evidence_mean = evidence.mean + assert evidence.sd is not None evidence_var = evidence.sd**2 return norm( mean=( @@ -388,6 +396,10 @@ def update( prior_b = prior.b evidence_a = evidence.a evidence_b = evidence.b + assert prior_a is not None + assert evidence_a is not None + assert prior_b is not None + assert evidence_b is not None return beta(prior_a + evidence_a, prior_b + evidence_b) elif type(prior) != type(evidence): print(type(prior), type(evidence)) From 2e6d818bb25117e1489d51f7f9fa6f36c414ad3c Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sat, 10 Jun 2023 20:15:08 -0400 Subject: [PATCH 15/35] more fun with types --- squigglepy/bayes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index dffc5c0..7708213 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -337,6 +337,7 @@ def multicore_event_fn(core, total_cores=1, verbose=False): def update( +<<<<<<< HEAD <<<<<<< HEAD prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], @@ -344,6 +345,9 @@ def update( ======= prior: BaseDistribution, evidence: BaseDistribution, evidence_weight: float = 1 >>>>>>> 087260e (Started wip_types) +======= + prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1 +>>>>>>> 827297d (more fun with types) ) -> BaseDistribution: """ Update a distribution. @@ -377,10 +381,8 @@ def update( """ if isinstance(prior, NormalDistribution) and isinstance(evidence, NormalDistribution): prior_mean = prior.mean - assert prior.sd is not None prior_var = prior.sd**2 evidence_mean = evidence.mean - assert evidence.sd is not None evidence_var = evidence.sd**2 return norm( mean=( @@ -396,10 +398,6 @@ def update( prior_b = prior.b evidence_a = evidence.a evidence_b = evidence.b - assert prior_a is not None - assert evidence_a is not None - assert prior_b is not None - assert evidence_b is not None return beta(prior_a + evidence_a, prior_b + evidence_b) elif type(prior) != type(evidence): print(type(prior), type(evidence)) @@ -411,6 +409,7 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, +<<<<<<< HEAD <<<<<<< HEAD weights: OptionalListOfFloats = [0.5, 0.5], relative_weights: OptionalListOfFloats = None, @@ -418,6 +417,10 @@ def average( weights: List | np.ndarray | float | None = [0.5, 0.5], relative_weights: List | np.ndarray | float | None = None, >>>>>>> 087260e (Started wip_types) +======= + weights: Union[List, np.ndarray, float, None] = [0.5, 0.5], + relative_weights: Union[List, np.ndarray, float, None] = None, +>>>>>>> 827297d (more fun with types) ) -> MixtureDistribution: """ Average two distributions. From feae1158a4611afb4b82f3c1e42107a92828fa57 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 11:57:29 -0400 Subject: [PATCH 16/35] simplify get_percentiles return type --- squigglepy/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 7a1547b..524e4d4 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -418,10 +418,17 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( +<<<<<<< HEAD data: Union[list[np.floating], NDArray[np.floating]], percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], reverse: bool = False, digits: int = None, +======= + data: Union[List, np.ndarray], + percentiles: List = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + reverse: bool = False, + digits: int = None, +>>>>>>> ec8d11a (simplify get_percentiles return type) ) -> dict[str, float]: """ Print the percentiles of the data. From d174e9ee677f25d1380f91bc7c727f1d663e4d55 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 12:19:11 -0400 Subject: [PATCH 17/35] fun with types III --- squigglepy/bayes.py | 37 ------------------------------------- squigglepy/utils.py | 7 ------- tests/test_distributions.py | 4 +++- 3 files changed, 3 insertions(+), 45 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 7708213..30917e8 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -164,20 +164,8 @@ def bayesnet( has_in_mem_cache = event_fn in _squigglepy_internal_bayesnet_caches cache_path = load_cache_file + ".sqcache" if load_cache_file != "" else "" has_file_cache = os.path.exists(cache_path) if load_cache_file != "" else False -<<<<<<< HEAD -<<<<<<< HEAD encoder = msgspec.msgpack.Encoder() decoder = msgspec.msgpack.Decoder() -======= - - if load_cache_file or dump_cache_file or cores > 1: - encoder = msgspec.msgpack.Encoder() - decoder = msgspec.msgpack.Decoder() ->>>>>>> 087260e (Started wip_types) -======= - encoder = msgspec.msgpack.Encoder() - decoder = msgspec.msgpack.Decoder() ->>>>>>> 10e4ded (fix type issues) if load_cache_file and not has_file_cache and verbose: print("Warning: cache file `{}.sqcache` not found.".format(load_cache_file)) @@ -198,7 +186,6 @@ def bayesnet( n_ = events.get("metadata") if n_ is not None: n_ = n_.get("n") -<<<<<<< HEAD if n_ is None: raise ValueError("events is malformed") elif n_ < n: @@ -206,13 +193,6 @@ def bayesnet( ("insufficient samples - {} results cached but " + "requested {}").format( events["metadata"]["n"], n ) -======= - if events["metadata"]["n"] < n: - raise ValueError( - ( - "insufficient samples - {} results cached but " + "requested {}" - ).format(events["metadata"]["n"], n) ->>>>>>> 10e4ded (fix type issues) ) else: raise ValueError("events is malformed") @@ -337,17 +317,10 @@ def multicore_event_fn(core, total_cores=1, verbose=False): def update( -<<<<<<< HEAD -<<<<<<< HEAD prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1, -======= - prior: BaseDistribution, evidence: BaseDistribution, evidence_weight: float = 1 ->>>>>>> 087260e (Started wip_types) -======= prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1 ->>>>>>> 827297d (more fun with types) ) -> BaseDistribution: """ Update a distribution. @@ -409,18 +382,8 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, -<<<<<<< HEAD -<<<<<<< HEAD weights: OptionalListOfFloats = [0.5, 0.5], relative_weights: OptionalListOfFloats = None, -======= - weights: List | np.ndarray | float | None = [0.5, 0.5], - relative_weights: List | np.ndarray | float | None = None, ->>>>>>> 087260e (Started wip_types) -======= - weights: Union[List, np.ndarray, float, None] = [0.5, 0.5], - relative_weights: Union[List, np.ndarray, float, None] = None, ->>>>>>> 827297d (more fun with types) ) -> MixtureDistribution: """ Average two distributions. diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 524e4d4..7a1547b 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -418,17 +418,10 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( -<<<<<<< HEAD data: Union[list[np.floating], NDArray[np.floating]], percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], reverse: bool = False, digits: int = None, -======= - data: Union[List, np.ndarray], - percentiles: List = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], - reverse: bool = False, - digits: int = None, ->>>>>>> ec8d11a (simplify get_percentiles return type) ) -> dict[str, float]: """ Print the percentiles of the data. diff --git a/tests/test_distributions.py b/tests/test_distributions.py index 409826e..c36333d 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -169,7 +169,9 @@ def test_norm_overdefinition_value_error(): def test_norm_low_gt_high(): with pytest.raises(ValueError) as execinfo: norm(10, 5) - assert "`high value` (y) cannot be lower than `low value` (x)" in str(execinfo.value) + assert "`high value` (y) cannot be lower than `low value` (x)" in str( + execinfo.value + ) def test_norm_passes_lclip_rclip(): From 9091f1f614f147dc5cd455d7b32b318bd17e9fad Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 14:09:04 -0400 Subject: [PATCH 18/35] fix --- squigglepy/bayes.py | 1 - tests/test_distributions.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 30917e8..919e2ff 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -320,7 +320,6 @@ def update( prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1, - prior: Union[NormalDistribution, BetaDistribution], evidence: Union[NormalDistribution, BetaDistribution], evidence_weight: float = 1 ) -> BaseDistribution: """ Update a distribution. diff --git a/tests/test_distributions.py b/tests/test_distributions.py index c36333d..409826e 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -169,9 +169,7 @@ def test_norm_overdefinition_value_error(): def test_norm_low_gt_high(): with pytest.raises(ValueError) as execinfo: norm(10, 5) - assert "`high value` (y) cannot be lower than `low value` (x)" in str( - execinfo.value - ) + assert "`high value` (y) cannot be lower than `low value` (x)" in str(execinfo.value) def test_norm_passes_lclip_rclip(): From 7c0a4a906707cb1750b2769e160282e2299ad8ec Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 14:18:25 -0400 Subject: [PATCH 19/35] fix III --- squigglepy/distributions.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 48075d9..07c8e38 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -620,14 +620,14 @@ def uniform(x, y): class NormalDistribution(OperableDistribution): def __init__( - self, - x: Optional[Number] = None, - y: Optional[Number] = None - mean: Optional[Number] = None - sd: Optional[Number] = None, - credibility: Number = 90, - lclip: Optional[Number] = None, - rclip: Optional[Number] = None + self, + x: Optional[Number] = None, + y: Optional[Number] = None, + mean: Optional[Number] = None, + sd: Optional[Number] = None, + credibility: Number = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, ): super().__init__() <<<<<<< HEAD From cac2c6982fb3bcbfff750ed36a467a3f54549bfb Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Sun, 11 Jun 2023 14:21:15 -0400 Subject: [PATCH 20/35] ruff check fix --- squigglepy/bayes.py | 1 - squigglepy/distributions.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 919e2ff..d39e422 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -10,7 +10,6 @@ from datetime import datetime from typing import Callable, Optional, Union -from numpy.typing import NDArray from .distributions import ( BaseDistribution, diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 07c8e38..16a0f37 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -2,7 +2,7 @@ import math import numpy as np from scipy import stats -from typing import Callable, Union, Optional +from typing import Optional from .utils import _process_weights_values, _is_numpy, is_dist, _round, _optional_import from .version import __version__ From 11edb85a2f5fbd3a3eabc4d404c4e5fb8ee1c062 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Tue, 13 Jun 2023 20:53:40 -0400 Subject: [PATCH 21/35] I'm too lazy to write a commit message. --- squigglepy/distributions.py | 26 +------------------------- squigglepy/utils.py | 8 ++++---- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 16a0f37..cfaa410 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -2,7 +2,7 @@ import math import numpy as np from scipy import stats -from typing import Optional +from typing import Optional, Union from .utils import _process_weights_values, _is_numpy, is_dist, _round, _optional_import from .version import __version__ @@ -630,17 +630,6 @@ def __init__( rclip: Optional[Number] = None, ): super().__init__() -<<<<<<< HEAD -<<<<<<< HEAD - self.x = x - self.y = y - self.credibility = credibility - self.mean = mean - self.sd = sd - self.lclip = lclip - self.rclip = rclip -======= ->>>>>>> 1b5a7d3 (more fun with types) if x is not None and y is not None and x > y: raise ValueError("`high value` cannot be lower than `low value`") @@ -658,19 +647,6 @@ def __init__( normed_sigma = stats.norm.ppf(cdf_value) sd = (y - mean) / normed_sigma - self.mean = mean: Number - self.sd = sd: Number - self.x = x: Number - self.y = y: Number - self.lclip = lclip: Optional[Number] - self.rclip = rclip: Optional[Number] -======= - self.credibility = credibility - self.lclip = lclip - self.rclip = rclip ->>>>>>> f457d15 (fix normal distribution) - self.type = "norm" - self.x: Number self.y: Number self.mean: Number diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 7a1547b..439c69e 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -12,16 +12,16 @@ from typing import Union from numpy.typing import NDArray +import importlib +import importlib.util +import sys + Number = Union[int, float, np.floating, np.integer] OptionalListOfFloats = Union[ list[Union[Union[float, np.floating]]], NDArray[np.floating], float, None ] -import importlib -import importlib.util -import sys - def _optional_import(name) -> Optional[types.ModuleType]: """ From 446fec5e6a2001f0ce38a9d02c99ac6a236be52e Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Tue, 13 Jun 2023 20:56:09 -0400 Subject: [PATCH 22/35] I'm too lazy to write a commit message. --- setup.py | 45 ------------------------------------- squigglepy/distributions.py | 1 - 2 files changed, 46 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 1e5cd26..0000000 --- a/setup.py +++ /dev/null @@ -1,45 +0,0 @@ -import sys -import setuptools - -from squigglepy.version import __version__ - - -with open("README.md", "r") as fh: - long_description = fh.read() - -if sys.version_info < (3, 9): - raise ValueError("Versions of Python before 3.9 are not supported") - -setuptools.setup( - name="squigglepy", - version=__version__, - author="Peter Wildeford", - author_email="peter@peterhurford.com", - description=( - "Squiggle programming language for intuitive probabilistic" - + " estimation features in Python" - ), - python_requires=">=3.9", - install_requires=[ - "ruff", - "msgspec", - "pytest", - "pytest-mock", - "numpy", - "pandas", - "pathos", - "scipy", - "tqdm", - "matplotlib", - ], - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/rethinkpriorities/squigglepy", - packages=setuptools.find_packages(), - classifiers=[ - "Development Status :: 3 - Alpha", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], -) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index cfaa410..88640fb 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -17,7 +17,6 @@ class BaseDistribution: def __init__(self): self.lclip: Optional[Number] = None self.rclip: Optional[Number] = None - self.type = "base" self._version: str = __version__ def __str__(self): From 77621164161e00f7ed1c422be50a1c84abaac241 Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Tue, 13 Jun 2023 20:59:29 -0400 Subject: [PATCH 23/35] bugfixes --- squigglepy/distributions.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 88640fb..733d946 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -629,22 +629,9 @@ def __init__( rclip: Optional[Number] = None, ): super().__init__() - - if x is not None and y is not None and x > y: - raise ValueError("`high value` cannot be lower than `low value`") - - if (x is None or y is None) and sd is None: - raise ValueError("must define either x/y or mean/sd") - elif (x is not None or y is not None) and sd is not None: - raise ValueError("must define either x/y or mean/sd -- cannot define both") - elif sd is not None and mean is None: - mean = 0 - - if sd is None or mean is None: - mean = (x + y) / 2 - cdf_value = 0.5 + 0.5 * (credibility / 100) - normed_sigma = stats.norm.ppf(cdf_value) - sd = (y - mean) / normed_sigma + self.credibility = credibility + self.lclip = lclip + self.rclip = rclip self.x: Number self.y: Number @@ -669,6 +656,7 @@ def __init__( else: raise ValueError("you must define either x/y or mean/sd") + def __str__(self): out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) if self.lclip is not None: From b883ff7b0f8307edc8d88597cc73a49e185b9e2e Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Tue, 13 Jun 2023 21:00:06 -0400 Subject: [PATCH 24/35] black reformat --- squigglepy/distributions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 733d946..2cbadda 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -656,7 +656,6 @@ def __init__( else: raise ValueError("you must define either x/y or mean/sd") - def __str__(self): out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) if self.lclip is not None: From 7e8e4ed6dfda74a5cab4b4332040d6ddf6d9f4cf Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Thu, 15 Jun 2023 11:41:42 -0400 Subject: [PATCH 25/35] label breaking changes in CHANGES --- CHANGES.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e388d85..695a5f3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,11 @@ ## v0.27 -* This package now only supports Python 3.9 and higher. +* **[Breaking change]** This package now only supports Python 3.9 and higher. +* **[Breaking change]** `get_percentiles` and `get_log_percentiles` now always return a dictionary, even if there's only one element. +* **[Breaking change]** `.type` is now removed from distribution objects. * Package load time is now ~2x faster. * Pandas and matplotlib as removed as required dependencies, but their related features are lazily enabled when the modules are available. These packages are still available for install as extras, installable with pip install squigglepy[plots] (for plotting-related functionality, matplotlib for now), pip install squigglepy[ecosystem] (for pandas, and in the future other related packages), or pip install squigglepy[all] (for all extras). -* `get_percentiles` and `get_log_percentiles` now always return a dictionary, even if there's only one element. * Distribution objects now have the version of squigglepy they were created with, which can be accessed via `obj._version`. This should be helpful for debugging and noticing stale objects, especially when squigglepy distributions are stored in caches. -* `.type` is now removed from distribution objects. * Using black now for formatting. * Switched from `flake8` to `ruff`. @@ -283,4 +283,3 @@ ## v0.1 * Initial library - From 02783bc2600606562d58d208eea2903ad758dd7a Mon Sep 17 00:00:00 2001 From: Peter Wildeford Date: Thu, 15 Jun 2023 11:44:01 -0400 Subject: [PATCH 26/35] label breaking changes in CHANGES II --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 695a5f3..154c0d3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ ## v0.26 -* `lognorm` can now be defined either referencing the mean and sd of the underlying normal distribution via `norm_mean` / `norm_sd` or via the mean and sd of the lognormal distribution itself via `lognorm_mean` / `lognorm_sd`. To further disambiguate, `mean` and `sd` are no longer variables that can be passed to `lognorm`. +* **[Breaking change]** `lognorm` can now be defined either referencing the mean and sd of the underlying normal distribution via `norm_mean` / `norm_sd` or via the mean and sd of the lognormal distribution itself via `lognorm_mean` / `lognorm_sd`. To further disambiguate, `mean` and `sd` are no longer variables that can be passed to `lognorm`. ## v0.25 From 865ffb393040e8143fe61d86f235d4f38a883b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 13:29:28 -0400 Subject: [PATCH 27/35] ci: add pyright to checks --- .github/workflows/ci.yml | 26 +- poetry.lock | 992 +++++++++++++++++++++------------------ pyproject.toml | 1 + 3 files changed, 551 insertions(+), 468 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 027f902..ca4893b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,4 +52,28 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: psf/black@stable \ No newline at end of file + - uses: psf/black@stable + + typecheck-python: + name: "Typecheck (pyright)" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + name: Checkout repository + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: "3.11" + cache: 'poetry' + + - name: Install dependencies + run: | + poetry install + + - run: echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + + - uses: jakebailey/pyright-action@v1 \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 8ca1d79..2a7dc1c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,36 +2,33 @@ [[package]] name = "black" -version = "23.3.0" +version = "23.7.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, + {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, + {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, + {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, + {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, + {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, + {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, + {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, + {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, + {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, + {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, ] [package.dependencies] @@ -51,13 +48,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "click" -version = "8.1.3" +version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -76,77 +73,61 @@ files = [ [[package]] name = "contourpy" -version = "1.0.7" +version = "1.1.0" description = "Python library for calculating contours of 2D quadrilateral grids" optional = true python-versions = ">=3.8" files = [ - {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"}, - {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"}, - {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"}, - {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"}, - {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"}, - {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"}, - {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"}, - {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"}, - {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"}, - {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"}, - {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"}, - {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"}, - {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"}, - {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"}, - {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"}, - {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"}, - {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"}, - {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"}, - {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"}, - {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"}, - {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"}, - {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"}, - {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"}, - {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"}, - {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, + {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, + {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, + {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, + {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, + {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, ] [package.dependencies] numpy = ">=1.16" [package.extras] -bokeh = ["bokeh", "chromedriver", "selenium"] +bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx-copybutton"] -mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"] -test = ["Pillow", "matplotlib", "pytest"] -test-no-images = ["pytest"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] [[package]] name = "cycler" @@ -161,13 +142,13 @@ files = [ [[package]] name = "dill" -version = "0.3.6" -description = "serialize all of python" +version = "0.3.7" +description = "serialize all of Python" optional = false python-versions = ">=3.7" files = [ - {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, - {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, + {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, + {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, ] [package.extras] @@ -175,13 +156,13 @@ graph = ["objgraph (>=1.7.2)"] [[package]] name = "exceptiongroup" -version = "1.1.1" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -189,45 +170,45 @@ test = ["pytest (>=6)"] [[package]] name = "fonttools" -version = "4.40.0" +version = "4.42.1" description = "Tools to manipulate font files" optional = true python-versions = ">=3.8" files = [ - {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b802dcbf9bcff74672f292b2466f6589ab8736ce4dcf36f48eb994c2847c4b30"}, - {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f6e3fa3da923063c286320e728ba2270e49c73386e3a711aa680f4b0747d692"}, - {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdf60f8a5c6bcce7d024a33f7e4bc7921f5b74e8ea13bccd204f2c8b86f3470"}, - {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91784e21a1a085fac07c6a407564f4a77feb471b5954c9ee55a4f9165151f6c1"}, - {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05171f3c546f64d78569f10adc0de72561882352cac39ec7439af12304d8d8c0"}, - {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7449e5e306f3a930a8944c85d0cbc8429cba13503372a1a40f23124d6fb09b58"}, - {file = "fonttools-4.40.0-cp310-cp310-win32.whl", hash = "sha256:bae8c13abbc2511e9a855d2142c0ab01178dd66b1a665798f357da0d06253e0d"}, - {file = "fonttools-4.40.0-cp310-cp310-win_amd64.whl", hash = "sha256:425b74a608427499b0e45e433c34ddc350820b6f25b7c8761963a08145157a66"}, - {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:00ab569b2a3e591e00425023ade87e8fef90380c1dde61be7691cb524ca5f743"}, - {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18ea64ac43e94c9e0c23d7a9475f1026be0e25b10dda8f236fc956188761df97"}, - {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:022c4a16b412293e7f1ce21b8bab7a6f9d12c4ffdf171fdc67122baddb973069"}, - {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530c5d35109f3e0cea2535742d6a3bc99c0786cf0cbd7bb2dc9212387f0d908c"}, - {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5e00334c66f4e83535384cb5339526d01d02d77f142c23b2f97bd6a4f585497a"}, - {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb52c10fda31159c22c7ed85074e05f8b97da8773ea461706c273e31bcbea836"}, - {file = "fonttools-4.40.0-cp311-cp311-win32.whl", hash = "sha256:6a8d71b9a5c884c72741868e845c0e563c5d83dcaf10bb0ceeec3b4b2eb14c67"}, - {file = "fonttools-4.40.0-cp311-cp311-win_amd64.whl", hash = "sha256:15abb3d055c1b2dff9ce376b6c3db10777cb74b37b52b78f61657634fd348a0d"}, - {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14037c31138fbd21847ad5e5441dfdde003e0a8f3feb5812a1a21fd1c255ffbd"}, - {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:94c915f6716589f78bc00fbc14c5b8de65cfd11ee335d32504f1ef234524cb24"}, - {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37467cee0f32cada2ec08bc16c9c31f9b53ea54b2f5604bf25a1246b5f50593a"}, - {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56d4d85f5374b45b08d2f928517d1e313ea71b4847240398decd0ab3ebbca885"}, - {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8c4305b171b61040b1ee75d18f9baafe58bd3b798d1670078efe2c92436bfb63"}, - {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a954b90d1473c85a22ecf305761d9fd89da93bbd31dae86e7dea436ad2cb5dc9"}, - {file = "fonttools-4.40.0-cp38-cp38-win32.whl", hash = "sha256:1bc4c5b147be8dbc5df9cc8ac5e93ee914ad030fe2a201cc8f02f499db71011d"}, - {file = "fonttools-4.40.0-cp38-cp38-win_amd64.whl", hash = "sha256:8a917828dbfdb1cbe50cf40eeae6fbf9c41aef9e535649ed8f4982b2ef65c091"}, - {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:882983279bf39afe4e945109772c2ffad2be2c90983d6559af8b75c19845a80a"}, - {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c55f1b4109dbc3aeb496677b3e636d55ef46dc078c2a5e3f3db4e90f1c6d2907"}, - {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec468c022d09f1817c691cf884feb1030ef6f1e93e3ea6831b0d8144c06480d1"}, - {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d5adf4ba114f028fc3f5317a221fd8b0f4ef7a2e5524a2b1e0fd891b093791a"}, - {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa83b3f151bc63970f39b2b42a06097c5a22fd7ed9f7ba008e618de4503d3895"}, - {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97d95b8301b62bdece1af943b88bcb3680fd385f88346a4a899ee145913b414a"}, - {file = "fonttools-4.40.0-cp39-cp39-win32.whl", hash = "sha256:1a003608400dd1cca3e089e8c94973c6b51a4fb1ef00ff6d7641617b9242e637"}, - {file = "fonttools-4.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:7961575221e3da0841c75da53833272c520000d76f7f71274dbf43370f8a1065"}, - {file = "fonttools-4.40.0-py3-none-any.whl", hash = "sha256:200729d12461e2038700d31f0d49ad5a7b55855dec7525074979a06b46f88505"}, - {file = "fonttools-4.40.0.tar.gz", hash = "sha256:337b6e83d7ee73c40ea62407f2ce03b07c3459e213b6f332b94a69923b9e1cb9"}, + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ed1a13a27f59d1fc1920394a7f596792e9d546c9ca5a044419dca70c37815d7c"}, + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9b1ce7a45978b821a06d375b83763b27a3a5e8a2e4570b3065abad240a18760"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f720fa82a11c0f9042376fd509b5ed88dab7e3cd602eee63a1af08883b37342b"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db55cbaea02a20b49fefbd8e9d62bd481aaabe1f2301dabc575acc6b358874fa"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a35981d90feebeaef05e46e33e6b9e5b5e618504672ca9cd0ff96b171e4bfff"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:68a02bbe020dc22ee0540e040117535f06df9358106d3775e8817d826047f3fd"}, + {file = "fonttools-4.42.1-cp310-cp310-win32.whl", hash = "sha256:12a7c247d1b946829bfa2f331107a629ea77dc5391dfd34fdcd78efa61f354ca"}, + {file = "fonttools-4.42.1-cp310-cp310-win_amd64.whl", hash = "sha256:a398bdadb055f8de69f62b0fc70625f7cbdab436bbb31eef5816e28cab083ee8"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:689508b918332fb40ce117131633647731d098b1b10d092234aa959b4251add5"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e36344e48af3e3bde867a1ca54f97c308735dd8697005c2d24a86054a114a71"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19b7db825c8adee96fac0692e6e1ecd858cae9affb3b4812cdb9d934a898b29e"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113337c2d29665839b7d90b39f99b3cac731f72a0eda9306165a305c7c31d341"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37983b6bdab42c501202500a2be3a572f50d4efe3237e0686ee9d5f794d76b35"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6ed2662a3d9c832afa36405f8748c250be94ae5dfc5283d668308391f2102861"}, + {file = "fonttools-4.42.1-cp311-cp311-win32.whl", hash = "sha256:179737095eb98332a2744e8f12037b2977f22948cf23ff96656928923ddf560a"}, + {file = "fonttools-4.42.1-cp311-cp311-win_amd64.whl", hash = "sha256:f2b82f46917d8722e6b5eafeefb4fb585d23babd15d8246c664cd88a5bddd19c"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:62f481ac772fd68901573956231aea3e4b1ad87b9b1089a61613a91e2b50bb9b"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2f806990160d1ce42d287aa419df3ffc42dfefe60d473695fb048355fe0c6a0"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db372213d39fa33af667c2aa586a0c1235e88e9c850f5dd5c8e1f17515861868"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d18fc642fd0ac29236ff88ecfccff229ec0386090a839dd3f1162e9a7944a40"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8708b98c278012ad267ee8a7433baeb809948855e81922878118464b274c909d"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c95b0724a6deea2c8c5d3222191783ced0a2f09bd6d33f93e563f6f1a4b3b3a4"}, + {file = "fonttools-4.42.1-cp38-cp38-win32.whl", hash = "sha256:4aa79366e442dbca6e2c8595645a3a605d9eeabdb7a094d745ed6106816bef5d"}, + {file = "fonttools-4.42.1-cp38-cp38-win_amd64.whl", hash = "sha256:acb47f6f8680de24c1ab65ebde39dd035768e2a9b571a07c7b8da95f6c8815fd"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb289b7a815638a7613d46bcf324c9106804725b2bb8ad913c12b6958ffc4ec"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:53eb5091ddc8b1199330bb7b4a8a2e7995ad5d43376cadce84523d8223ef3136"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46a0ec8adbc6ff13494eb0c9c2e643b6f009ce7320cf640de106fb614e4d4360"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cc7d685b8eeca7ae69dc6416833fbfea61660684b7089bca666067cb2937dcf"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:be24fcb80493b2c94eae21df70017351851652a37de514de553435b256b2f249"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:515607ec756d7865f23070682622c49d922901943697871fc292277cf1e71967"}, + {file = "fonttools-4.42.1-cp39-cp39-win32.whl", hash = "sha256:0eb79a2da5eb6457a6f8ab904838454accc7d4cccdaff1fd2bd3a0679ea33d64"}, + {file = "fonttools-4.42.1-cp39-cp39-win_amd64.whl", hash = "sha256:7286aed4ea271df9eab8d7a9b29e507094b51397812f7ce051ecd77915a6e26b"}, + {file = "fonttools-4.42.1-py3-none-any.whl", hash = "sha256:9398f244e28e0596e2ee6024f808b06060109e33ed38dcc9bded452fd9bbb853"}, + {file = "fonttools-4.42.1.tar.gz", hash = "sha256:c391cd5af88aacaf41dd7cfb96eeedfad297b5899a39e12f4c2c3706d0a3329d"}, ] [package.extras] @@ -246,21 +227,21 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "importlib-resources" -version = "5.12.0" +version = "6.0.1" description = "Read resources from Python packages" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, + {file = "importlib_resources-6.0.1-py3-none-any.whl", hash = "sha256:134832a506243891221b88b4ae1213327eea96ceb4e407a00d790bb0626f45cf"}, + {file = "importlib_resources-6.0.1.tar.gz", hash = "sha256:4359457e42708462b9626a04657c6208ad799ceb41e5c58c57ffa0e6a098a5d4"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [[package]] name = "iniconfig" @@ -275,129 +256,165 @@ files = [ [[package]] name = "kiwisolver" -version = "1.4.4" +version = "1.4.5" description = "A fast implementation of the Cassowary constraint solver" optional = true python-versions = ">=3.7" files = [ - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, - {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, - {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, - {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, - {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, - {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, - {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, - {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, - {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, - {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, - {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, - {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, - {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, - {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, - {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, - {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, - {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, - {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, - {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, - {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, - {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, - {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, - {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, ] [[package]] name = "matplotlib" -version = "3.7.1" +version = "3.7.2" description = "Python plotting package" optional = true python-versions = ">=3.8" files = [ - {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"}, - {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"}, - {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"}, - {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"}, - {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"}, - {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"}, - {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"}, - {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"}, - {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"}, - {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"}, - {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"}, - {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"}, - {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"}, - {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"}, - {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"}, - {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"}, - {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"}, - {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"}, - {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"}, - {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"}, - {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8"}, + {file = "matplotlib-3.7.2-cp310-cp310-win32.whl", hash = "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a"}, + {file = "matplotlib-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4"}, + {file = "matplotlib-3.7.2-cp311-cp311-win32.whl", hash = "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc"}, + {file = "matplotlib-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117"}, + {file = "matplotlib-3.7.2-cp38-cp38-win32.whl", hash = "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13"}, + {file = "matplotlib-3.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f"}, + {file = "matplotlib-3.7.2-cp39-cp39-win32.whl", hash = "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20"}, + {file = "matplotlib-3.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11"}, + {file = "matplotlib-3.7.2.tar.gz", hash = "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b"}, ] [package.dependencies] @@ -409,7 +426,7 @@ kiwisolver = ">=1.0.1" numpy = ">=1.20" packaging = ">=20.0" pillow = ">=6.2.0" -pyparsing = ">=2.3.1" +pyparsing = ">=2.3.1,<3.1" python-dateutil = ">=2.7" [[package]] @@ -459,29 +476,31 @@ yaml = ["pyyaml"] [[package]] name = "multiprocess" -version = "0.70.14" -description = "better multiprocessing and multithreading in python" +version = "0.70.15" +description = "better multiprocessing and multithreading in Python" optional = false python-versions = ">=3.7" files = [ - {file = "multiprocess-0.70.14-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:560a27540daef4ce8b24ed3cc2496a3c670df66c96d02461a4da67473685adf3"}, - {file = "multiprocess-0.70.14-pp37-pypy37_pp73-manylinux_2_24_i686.whl", hash = "sha256:bfbbfa36f400b81d1978c940616bc77776424e5e34cb0c94974b178d727cfcd5"}, - {file = "multiprocess-0.70.14-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:89fed99553a04ec4f9067031f83a886d7fdec5952005551a896a4b6a59575bb9"}, - {file = "multiprocess-0.70.14-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:40a5e3685462079e5fdee7c6789e3ef270595e1755199f0d50685e72523e1d2a"}, - {file = "multiprocess-0.70.14-pp38-pypy38_pp73-manylinux_2_24_i686.whl", hash = "sha256:44936b2978d3f2648727b3eaeab6d7fa0bedf072dc5207bf35a96d5ee7c004cf"}, - {file = "multiprocess-0.70.14-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e628503187b5d494bf29ffc52d3e1e57bb770ce7ce05d67c4bbdb3a0c7d3b05f"}, - {file = "multiprocess-0.70.14-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d5da0fc84aacb0e4bd69c41b31edbf71b39fe2fb32a54eaedcaea241050855c"}, - {file = "multiprocess-0.70.14-pp39-pypy39_pp73-manylinux_2_24_i686.whl", hash = "sha256:6a7b03a5b98e911a7785b9116805bd782815c5e2bd6c91c6a320f26fd3e7b7ad"}, - {file = "multiprocess-0.70.14-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:cea5bdedd10aace3c660fedeac8b087136b4366d4ee49a30f1ebf7409bce00ae"}, - {file = "multiprocess-0.70.14-py310-none-any.whl", hash = "sha256:7dc1f2f6a1d34894c8a9a013fbc807971e336e7cc3f3ff233e61b9dc679b3b5c"}, - {file = "multiprocess-0.70.14-py37-none-any.whl", hash = "sha256:93a8208ca0926d05cdbb5b9250a604c401bed677579e96c14da3090beb798193"}, - {file = "multiprocess-0.70.14-py38-none-any.whl", hash = "sha256:6725bc79666bbd29a73ca148a0fb5f4ea22eed4a8f22fce58296492a02d18a7b"}, - {file = "multiprocess-0.70.14-py39-none-any.whl", hash = "sha256:63cee628b74a2c0631ef15da5534c8aedbc10c38910b9c8b18dcd327528d1ec7"}, - {file = "multiprocess-0.70.14.tar.gz", hash = "sha256:3eddafc12f2260d27ae03fe6069b12570ab4764ab59a75e81624fac453fbf46a"}, + {file = "multiprocess-0.70.15-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aa36c7ed16f508091438687fe9baa393a7a8e206731d321e443745e743a0d4e5"}, + {file = "multiprocess-0.70.15-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:20e024018c46d0d1602024c613007ac948f9754659e3853b0aa705e83f6931d8"}, + {file = "multiprocess-0.70.15-pp37-pypy37_pp73-manylinux_2_24_i686.whl", hash = "sha256:e576062981c91f0fe8a463c3d52506e598dfc51320a8dd8d78b987dfca91c5db"}, + {file = "multiprocess-0.70.15-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e73f497e6696a0f5433ada2b3d599ae733b87a6e8b008e387c62ac9127add177"}, + {file = "multiprocess-0.70.15-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73db2e7b32dcc7f9b0f075c2ffa45c90b6729d3f1805f27e88534c8d321a1be5"}, + {file = "multiprocess-0.70.15-pp38-pypy38_pp73-manylinux_2_24_i686.whl", hash = "sha256:4271647bd8a49c28ecd6eb56a7fdbd3c212c45529ad5303b40b3c65fc6928e5f"}, + {file = "multiprocess-0.70.15-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:cf981fb998d6ec3208cb14f0cf2e9e80216e834f5d51fd09ebc937c32b960902"}, + {file = "multiprocess-0.70.15-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:18f9f2c7063346d1617bd1684fdcae8d33380ae96b99427260f562e1a1228b67"}, + {file = "multiprocess-0.70.15-pp39-pypy39_pp73-manylinux_2_24_i686.whl", hash = "sha256:0eac53214d664c49a34695e5824872db4006b1a465edd7459a251809c3773370"}, + {file = "multiprocess-0.70.15-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1a51dd34096db47fb21fa2b839e615b051d51b97af9a67afbcdaa67186b44883"}, + {file = "multiprocess-0.70.15-py310-none-any.whl", hash = "sha256:7dd58e33235e83cf09d625e55cffd7b0f0eede7ee9223cdd666a87624f60c21a"}, + {file = "multiprocess-0.70.15-py311-none-any.whl", hash = "sha256:134f89053d82c9ed3b73edd3a2531eb791e602d4f4156fc92a79259590bd9670"}, + {file = "multiprocess-0.70.15-py37-none-any.whl", hash = "sha256:f7d4a1629bccb433114c3b4885f69eccc200994323c80f6feee73b0edc9199c5"}, + {file = "multiprocess-0.70.15-py38-none-any.whl", hash = "sha256:bee9afba476c91f9ebee7beeee0601face9eff67d822e893f9a893725fbd6316"}, + {file = "multiprocess-0.70.15-py39-none-any.whl", hash = "sha256:3e0953f5d52b4c76f1c973eaf8214554d146f2be5decb48e928e55c7a2d19338"}, + {file = "multiprocess-0.70.15.tar.gz", hash = "sha256:f20eed3036c0ef477b07a4177cf7c1ba520d9a2677870a4f47fe026f0cd6787e"}, ] [package.dependencies] -dill = ">=0.3.6" +dill = ">=0.3.7" [[package]] name = "mypy-extensions" @@ -494,41 +513,52 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "numpy" -version = "1.24.3" +version = "1.25.2" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"}, - {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"}, - {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"}, - {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"}, - {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"}, - {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"}, - {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"}, - {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"}, - {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"}, - {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"}, - {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"}, - {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"}, - {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"}, - {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"}, - {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"}, - {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"}, - {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"}, - {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"}, - {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"}, - {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"}, - {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"}, - {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"}, - {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"}, - {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"}, - {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"}, - {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, + {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, + {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, + {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, + {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, + {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, + {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, + {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, + {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, + {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, + {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, ] [[package]] @@ -544,36 +574,36 @@ files = [ [[package]] name = "pandas" -version = "2.0.2" +version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = true python-versions = ">=3.8" files = [ - {file = "pandas-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ebb9f1c22ddb828e7fd017ea265a59d80461d5a79154b49a4207bd17514d122"}, - {file = "pandas-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eb09a242184092f424b2edd06eb2b99d06dc07eeddff9929e8667d4ed44e181"}, - {file = "pandas-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7319b6e68de14e6209460f72a8d1ef13c09fb3d3ef6c37c1e65b35d50b5c145"}, - {file = "pandas-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd46bde7309088481b1cf9c58e3f0e204b9ff9e3244f441accd220dd3365ce7c"}, - {file = "pandas-2.0.2-cp310-cp310-win32.whl", hash = "sha256:51a93d422fbb1bd04b67639ba4b5368dffc26923f3ea32a275d2cc450f1d1c86"}, - {file = "pandas-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:66d00300f188fa5de73f92d5725ced162488f6dc6ad4cecfe4144ca29debe3b8"}, - {file = "pandas-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02755de164da6827764ceb3bbc5f64b35cb12394b1024fdf88704d0fa06e0e2f"}, - {file = "pandas-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0a1e0576611641acde15c2322228d138258f236d14b749ad9af498ab69089e2d"}, - {file = "pandas-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6b5f14cd24a2ed06e14255ff40fe2ea0cfaef79a8dd68069b7ace74bd6acbba"}, - {file = "pandas-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50e451932b3011b61d2961b4185382c92cc8c6ee4658dcd4f320687bb2d000ee"}, - {file = "pandas-2.0.2-cp311-cp311-win32.whl", hash = "sha256:7b21cb72958fc49ad757685db1919021d99650d7aaba676576c9e88d3889d456"}, - {file = "pandas-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:c4af689352c4fe3d75b2834933ee9d0ccdbf5d7a8a7264f0ce9524e877820c08"}, - {file = "pandas-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69167693cb8f9b3fc060956a5d0a0a8dbfed5f980d9fd2c306fb5b9c855c814c"}, - {file = "pandas-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30a89d0fec4263ccbf96f68592fd668939481854d2ff9da709d32a047689393b"}, - {file = "pandas-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a18e5c72b989ff0f7197707ceddc99828320d0ca22ab50dd1b9e37db45b010c0"}, - {file = "pandas-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7376e13d28eb16752c398ca1d36ccfe52bf7e887067af9a0474de6331dd948d2"}, - {file = "pandas-2.0.2-cp38-cp38-win32.whl", hash = "sha256:6d6d10c2142d11d40d6e6c0a190b1f89f525bcf85564707e31b0a39e3b398e08"}, - {file = "pandas-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:e69140bc2d29a8556f55445c15f5794490852af3de0f609a24003ef174528b79"}, - {file = "pandas-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b42b120458636a981077cfcfa8568c031b3e8709701315e2bfa866324a83efa8"}, - {file = "pandas-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f908a77cbeef9bbd646bd4b81214cbef9ac3dda4181d5092a4aa9797d1bc7774"}, - {file = "pandas-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713f2f70abcdade1ddd68fc91577cb090b3544b07ceba78a12f799355a13ee44"}, - {file = "pandas-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf3f0c361a4270185baa89ec7ab92ecaa355fe783791457077473f974f654df5"}, - {file = "pandas-2.0.2-cp39-cp39-win32.whl", hash = "sha256:598e9020d85a8cdbaa1815eb325a91cfff2bb2b23c1442549b8a3668e36f0f77"}, - {file = "pandas-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:77550c8909ebc23e56a89f91b40ad01b50c42cfbfab49b3393694a50549295ea"}, - {file = "pandas-2.0.2.tar.gz", hash = "sha256:dd5476b6c3fe410ee95926873f377b856dbc4e81a9c605a0dc05aaccc6a7c6c6"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, ] [package.dependencies] @@ -587,7 +617,7 @@ pytz = ">=2020.1" tzdata = ">=2022.1" [package.extras] -all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] aws = ["s3fs (>=2021.08.0)"] clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] @@ -606,110 +636,100 @@ plot = ["matplotlib (>=3.6.1)"] postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] spss = ["pyreadstat (>=1.1.2)"] sql-other = ["SQLAlchemy (>=1.4.16)"] -test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.6.3)"] [[package]] name = "pathos" -version = "0.3.0" +version = "0.3.1" description = "parallel graph management and execution in heterogeneous computing" optional = false python-versions = ">=3.7" files = [ - {file = "pathos-0.3.0-py3-none-any.whl", hash = "sha256:b1f5a79b1c79a594330d451832642ee5bb61dd77dc75ba9e5c72087c77e8994c"}, - {file = "pathos-0.3.0.tar.gz", hash = "sha256:24fa8db51fbd9284da8e191794097c4bb2aa3fce411090e57af6385e61b97e09"}, + {file = "pathos-0.3.1-py3-none-any.whl", hash = "sha256:b1c7145e2adcc19c7e9cac48f110ea5a63e300c1cc10c2947d4857dc97a47b46"}, + {file = "pathos-0.3.1.tar.gz", hash = "sha256:c9a088021493c5cb627d4459bba6c0533c684199e271a5dc297d62be23d74019"}, ] [package.dependencies] -dill = ">=0.3.6" -multiprocess = ">=0.70.14" -pox = ">=0.3.2" -ppft = ">=1.7.6.6" +dill = ">=0.3.7" +multiprocess = ">=0.70.15" +pox = ">=0.3.3" +ppft = ">=1.7.6.7" [[package]] name = "pathspec" -version = "0.11.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "pillow" -version = "9.5.0" +version = "10.0.0" description = "Python Imaging Library (Fork)" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, - {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, - {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, - {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, - {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, - {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, - {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, - {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, - {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, - {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, - {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, - {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, - {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, - {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, - {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, - {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, - {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, + {file = "Pillow-10.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891"}, + {file = "Pillow-10.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992"}, + {file = "Pillow-10.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, + {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, + {file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, + {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, + {file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7"}, + {file = "Pillow-10.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017"}, + {file = "Pillow-10.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3"}, + {file = "Pillow-10.0.0.tar.gz", hash = "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396"}, ] [package.extras] @@ -718,28 +738,28 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa [[package]] name = "platformdirs" -version = "3.5.3" +version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.3-py3-none-any.whl", hash = "sha256:0ade98a4895e87dc51d47151f7d2ec290365a585151d97b4d8d6312ed6132fed"}, - {file = "platformdirs-3.5.3.tar.gz", hash = "sha256:e48fabd87db8f3a7df7150a4a5ea22c546ee8bc39bc2473244730d4b56d2cc4e"}, + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] @@ -748,28 +768,28 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pox" -version = "0.3.2" +version = "0.3.3" description = "utilities for filesystem exploration and automated builds" optional = false python-versions = ">=3.7" files = [ - {file = "pox-0.3.2-py3-none-any.whl", hash = "sha256:56fe2f099ecd8a557b8948082504492de90e8598c34733c9b1fdeca8f7b6de61"}, - {file = "pox-0.3.2.tar.gz", hash = "sha256:e825225297638d6e3d49415f8cfb65407a5d15e56f2fb7fe9d9b9e3050c65ee1"}, + {file = "pox-0.3.3-py3-none-any.whl", hash = "sha256:e95febf7401918478a3c1441a3630656d9a2049803889b4f589821372889d0ce"}, + {file = "pox-0.3.3.tar.gz", hash = "sha256:e1ced66f2a0c92a58cf3646bc7ccb8b4773d40884b76f85eeda0670474871667"}, ] [[package]] name = "ppft" -version = "1.7.6.6" -description = "distributed and parallel python" +version = "1.7.6.7" +description = "distributed and parallel Python" optional = false python-versions = ">=3.7" files = [ - {file = "ppft-1.7.6.6-py3-none-any.whl", hash = "sha256:f355d2caeed8bd7c9e4a860c471f31f7e66d1ada2791ab5458ea7dca15a51e41"}, - {file = "ppft-1.7.6.6.tar.gz", hash = "sha256:f933f0404f3e808bc860745acb3b79cd4fe31ea19a20889a645f900415be60f1"}, + {file = "ppft-1.7.6.7-py3-none-any.whl", hash = "sha256:fedb1b1253729d62483f2e1f36547fd50a5fc873ffbf9b78b48cfdc727d4180c"}, + {file = "ppft-1.7.6.7.tar.gz", hash = "sha256:ab34436814e2f18238f35688fd869b2641b2d2d8dca22b8d246f6701dfc954c8"}, ] [package.extras] -dill = ["dill (>=0.3.6)"] +dill = ["dill (>=0.3.7)"] [[package]] name = "pyparsing" @@ -785,15 +805,33 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyright" +version = "1.1.324" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.324-py3-none-any.whl", hash = "sha256:0edb712afbbad474e347de12ca1bd9368aa85d3365a1c7b795012e48e6a65111"}, + {file = "pyright-1.1.324.tar.gz", hash = "sha256:0c48e3bca3d081bba0dddd0c1f075aaa965c59bba691f7b9bd9d73a98e44e0cf"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" + +[package.extras] +all = ["twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] + [[package]] name = "pytest" -version = "7.3.2" +version = "7.4.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.3.2-py3-none-any.whl", hash = "sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295"}, - {file = "pytest-7.3.2.tar.gz", hash = "sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b"}, + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, ] [package.dependencies] @@ -809,13 +847,13 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-mock" -version = "3.10.0" +version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, - {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, + {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, + {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, ] [package.dependencies] @@ -877,42 +915,62 @@ files = [ [[package]] name = "scipy" -version = "1.10.1" +version = "1.11.2" description = "Fundamental algorithms for scientific computing in Python" optional = false -python-versions = "<3.12,>=3.8" -files = [ - {file = "scipy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7354fd7527a4b0377ce55f286805b34e8c54b91be865bac273f527e1b839019"}, - {file = "scipy-1.10.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4b3f429188c66603a1a5c549fb414e4d3bdc2a24792e061ffbd607d3d75fd84e"}, - {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1553b5dcddd64ba9a0d95355e63fe6c3fc303a8fd77c7bc91e77d61363f7433f"}, - {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c0ff64b06b10e35215abce517252b375e580a6125fd5fdf6421b98efbefb2d2"}, - {file = "scipy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:fae8a7b898c42dffe3f7361c40d5952b6bf32d10c4569098d276b4c547905ee1"}, - {file = "scipy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f1564ea217e82c1bbe75ddf7285ba0709ecd503f048cb1236ae9995f64217bd"}, - {file = "scipy-1.10.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d925fa1c81b772882aa55bcc10bf88324dadb66ff85d548c71515f6689c6dac5"}, - {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaea0a6be54462ec027de54fca511540980d1e9eea68b2d5c1dbfe084797be35"}, - {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a35c4242ec5f292c3dd364a7c71a61be87a3d4ddcc693372813c0b73c9af1d"}, - {file = "scipy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:43b8e0bcb877faf0abfb613d51026cd5cc78918e9530e375727bf0625c82788f"}, - {file = "scipy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5678f88c68ea866ed9ebe3a989091088553ba12c6090244fdae3e467b1139c35"}, - {file = "scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:39becb03541f9e58243f4197584286e339029e8908c46f7221abeea4b749fa88"}, - {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bce5869c8d68cf383ce240e44c1d9ae7c06078a9396df68ce88a1230f93a30c1"}, - {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07c3457ce0b3ad5124f98a86533106b643dd811dd61b548e78cf4c8786652f6f"}, - {file = "scipy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:049a8bbf0ad95277ffba9b3b7d23e5369cc39e66406d60422c8cfef40ccc8415"}, - {file = "scipy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd9f1027ff30d90618914a64ca9b1a77a431159df0e2a195d8a9e8a04c78abf9"}, - {file = "scipy-1.10.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:79c8e5a6c6ffaf3a2262ef1be1e108a035cf4f05c14df56057b64acc5bebffb6"}, - {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51af417a000d2dbe1ec6c372dfe688e041a7084da4fdd350aeb139bd3fb55353"}, - {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b4735d6c28aad3cdcf52117e0e91d6b39acd4272f3f5cd9907c24ee931ad601"}, - {file = "scipy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ff7f37b1bf4417baca958d254e8e2875d0cc23aaadbe65b3d5b3077b0eb23ea"}, - {file = "scipy-1.10.1.tar.gz", hash = "sha256:2cf9dfb80a7b4589ba4c40ce7588986d6d5cebc5457cad2c2880f6bc2d42f3a5"}, +python-versions = "<3.13,>=3.9" +files = [ + {file = "scipy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b997a5369e2d30c97995dcb29d638701f8000d04df01b8e947f206e5d0ac788"}, + {file = "scipy-1.11.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:95763fbda1206bec41157582bea482f50eb3702c85fffcf6d24394b071c0e87a"}, + {file = "scipy-1.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e367904a0fec76433bf3fbf3e85bf60dae8e9e585ffd21898ab1085a29a04d16"}, + {file = "scipy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d690e1ca993c8f7ede6d22e5637541217fc6a4d3f78b3672a6fe454dbb7eb9a7"}, + {file = "scipy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d2b813bfbe8dec6a75164523de650bad41f4405d35b0fa24c2c28ae07fcefb20"}, + {file = "scipy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:afdb0d983f6135d50770dd979df50bf1c7f58b5b33e0eb8cf5c73c70600eae1d"}, + {file = "scipy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d9886f44ef8c9e776cb7527fb01455bf4f4a46c455c4682edc2c2cc8cd78562"}, + {file = "scipy-1.11.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1342ca385c673208f32472830c10110a9dcd053cf0c4b7d4cd7026d0335a6c1d"}, + {file = "scipy-1.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b133f237bd8ba73bad51bc12eb4f2d84cbec999753bf25ba58235e9fc2096d80"}, + {file = "scipy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aeb87661de987f8ec56fa6950863994cd427209158255a389fc5aea51fa7055"}, + {file = "scipy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90d3b1364e751d8214e325c371f0ee0dd38419268bf4888b2ae1040a6b266b2a"}, + {file = "scipy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:f73102f769ee06041a3aa26b5841359b1a93cc364ce45609657751795e8f4a4a"}, + {file = "scipy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa4909c6c20c3d91480533cddbc0e7c6d849e7d9ded692918c76ce5964997898"}, + {file = "scipy-1.11.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac74b1512d38718fb6a491c439aa7b3605b96b1ed3be6599c17d49d6c60fca18"}, + {file = "scipy-1.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8425fa963a32936c9773ee3ce44a765d8ff67eed5f4ac81dc1e4a819a238ee9"}, + {file = "scipy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:542a757e2a6ec409e71df3d8fd20127afbbacb1c07990cb23c5870c13953d899"}, + {file = "scipy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea932570b1c2a30edafca922345854ff2cd20d43cd9123b6dacfdecebfc1a80b"}, + {file = "scipy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:4447ad057d7597476f9862ecbd9285bbf13ba9d73ce25acfa4e4b11c6801b4c9"}, + {file = "scipy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b0620240ef445b5ddde52460e6bc3483b7c9c750275369379e5f609a1050911c"}, + {file = "scipy-1.11.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:f28f1f6cfeb48339c192efc6275749b2a25a7e49c4d8369a28b6591da02fbc9a"}, + {file = "scipy-1.11.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:214cdf04bbae7a54784f8431f976704ed607c4bc69ba0d5d5d6a9df84374df76"}, + {file = "scipy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10eb6af2f751aa3424762948e5352f707b0dece77288206f227864ddf675aca0"}, + {file = "scipy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0f3261f14b767b316d7137c66cc4f33a80ea05841b9c87ad83a726205b901423"}, + {file = "scipy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:2c91cf049ffb5575917f2a01da1da082fd24ed48120d08a6e7297dfcac771dcd"}, + {file = "scipy-1.11.2.tar.gz", hash = "sha256:b29318a5e39bd200ca4381d80b065cdf3076c7d7281c5e36569e99273867f61d"}, ] [package.dependencies] -numpy = ">=1.19.5,<1.27.0" +numpy = ">=1.21.6,<1.28.0" [package.extras] -dev = ["click", "doit (>=0.36.0)", "flake8", "mypy", "pycodestyle", "pydevtool", "rich-click", "typing_extensions"] -doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +[[package]] +name = "setuptools" +version = "68.1.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, + {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -937,33 +995,33 @@ files = [ [[package]] name = "tqdm" -version = "4.65.0" +version = "4.66.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, - {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] [[package]] name = "typing-extensions" -version = "4.6.3" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] @@ -979,18 +1037,18 @@ files = [ [[package]] name = "zipp" -version = "3.15.0" +version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, + {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] all = [] @@ -1000,4 +1058,4 @@ plots = ["matplotlib"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "c08e85917917d22c840e121db13bdcdf32be455720eec35eee6299d378d645e0" +content-hash = "2aee5b08b431613a7a4b5fbee9ee4f50af7694c90b5eb2176942b5eecb196a38" diff --git a/pyproject.toml b/pyproject.toml index 5552dcc..c8b0ed1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ ruff = "^0.0.272" pytest = "^7.3.2" pytest-mock = "^3.10.0" black = "^23.3.0" +pyright = "^1.1.324" [tool.poetry.extras] plots = ["matplotlib"] From 25368462e9cd98c0dc3460bb901b5b19a7d4d00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 13:48:21 -0400 Subject: [PATCH 28/35] chore: use standard python package structure --- __init__.py | 0 tests/test_bayes.py | 56 ++++++++++++++++++------------------- tests/test_distributions.py | 4 +-- tests/test_numbers.py | 2 +- tests/test_rng.py | 6 ++-- tests/test_samplers.py | 38 ++++++++++++------------- tests/test_utils.py | 6 ++-- 7 files changed, 56 insertions(+), 56 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_bayes.py b/tests/test_bayes.py index 09c6323..a5783b9 100644 --- a/tests/test_bayes.py +++ b/tests/test_bayes.py @@ -1,11 +1,11 @@ import os import pytest -from ..squigglepy.bayes import simple_bayes, bayesnet, update, average -from ..squigglepy.samplers import sample -from ..squigglepy.distributions import discrete, norm, beta, gamma -from ..squigglepy.rng import set_seed -from ..squigglepy.distributions import BetaDistribution, MixtureDistribution, NormalDistribution +from squigglepy.bayes import simple_bayes, bayesnet, update, average +from squigglepy.samplers import sample +from squigglepy.distributions import discrete, norm, beta, gamma +from squigglepy.rng import set_seed +from squigglepy.distributions import BetaDistribution, MixtureDistribution, NormalDistribution def test_simple_bayes(): @@ -75,7 +75,7 @@ def test_bayesnet_raw(): def test_bayesnet_cache(): - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -83,19 +83,19 @@ def define_event(): return {"a": 1, "b": 2} bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches < n_caches2 bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches3 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches3 bayesnet(define_event, find=lambda e: e["b"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches4 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches4 @@ -103,7 +103,7 @@ def define_event(): def test_bayesnet_cache_multiple(): - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -111,13 +111,13 @@ def define_event(): return {"a": 1, "b": 2} bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches < n_caches2 bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches3 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches3 @@ -126,7 +126,7 @@ def define_event2(): return {"a": 4, "b": 6} bayesnet(define_event2, find=lambda e: e["b"], n=1000) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches4 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 < n_caches4 @@ -134,20 +134,20 @@ def define_event2(): assert _squigglepy_internal_bayesnet_caches.get(define_event2)["metadata"]["n"] == 1000 bayesnet(define_event2, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches5 = len(_squigglepy_internal_bayesnet_caches) assert n_caches4 == n_caches5 bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches6 = len(_squigglepy_internal_bayesnet_caches) assert n_caches4 == n_caches6 def test_bayesnet_reload_cache(): - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -155,19 +155,19 @@ def define_event(): return {"a": 1, "b": 2} bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches < n_caches2 bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches3 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches3 bayesnet(define_event, find=lambda e: e["b"], n=100, reload_cache=True) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches4 = len(_squigglepy_internal_bayesnet_caches) assert n_caches3 == n_caches4 @@ -175,7 +175,7 @@ def define_event(): def test_bayesnet_dont_use_cache(): - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -183,7 +183,7 @@ def define_event(): return {"a": 1, "b": 2} bayesnet(define_event, find=lambda e: e["a"], memcache=False, n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches == n_caches2 @@ -259,7 +259,7 @@ def define_event(): def test_bayesnet_cachefile_primary(cachefile): assert not os.path.exists(cachefile + ".sqcache") - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -268,7 +268,7 @@ def define_event(): bayesnet(define_event, find=lambda e: e["a"], n=100) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches + 1 @@ -285,7 +285,7 @@ def define_event2(): n=100, ) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches3 = len(_squigglepy_internal_bayesnet_caches) assert n_caches3 == n_caches2 @@ -314,7 +314,7 @@ def define_event2(): ) assert set(out) == set([2]) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches4 = len(_squigglepy_internal_bayesnet_caches) assert n_caches4 == n_caches2 @@ -323,7 +323,7 @@ def define_event2(): def test_bayesnet_cachefile_will_also_memcache(cachefile): assert not os.path.exists(cachefile + ".sqcache") - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches = len(_squigglepy_internal_bayesnet_caches) @@ -341,7 +341,7 @@ def define_event(): assert os.path.exists(cachefile + ".sqcache") assert set(out) == set([1]) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches2 = len(_squigglepy_internal_bayesnet_caches) assert n_caches2 == n_caches @@ -357,7 +357,7 @@ def define_event(): assert os.path.exists(cachefile + ".sqcache") assert set(out) == set([1]) - from ..squigglepy.bayes import _squigglepy_internal_bayesnet_caches + from squigglepy.bayes import _squigglepy_internal_bayesnet_caches n_caches3 = len(_squigglepy_internal_bayesnet_caches) assert n_caches3 == n_caches + 1 diff --git a/tests/test_distributions.py b/tests/test_distributions.py index 409826e..2059d65 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -1,6 +1,6 @@ import pytest import numpy as np -from ..squigglepy.distributions import ( +from squigglepy.distributions import ( to, const, uniform, @@ -51,7 +51,7 @@ ParetoDistribution, UniformDistribution, ) -from ..squigglepy.version import __version__ +from squigglepy.version import __version__ def _mirror(x): diff --git a/tests/test_numbers.py b/tests/test_numbers.py index 9d52027..fc3b5ad 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -1,4 +1,4 @@ -from ..squigglepy.numbers import K, M, B, T +from squigglepy.numbers import K, M, B, T def test_thousand(): diff --git a/tests/test_rng.py b/tests/test_rng.py index d2eb3cf..aef3a90 100644 --- a/tests/test_rng.py +++ b/tests/test_rng.py @@ -1,6 +1,6 @@ -from ..squigglepy.rng import set_seed -from ..squigglepy.samplers import sample -from ..squigglepy.distributions import norm +from squigglepy.rng import set_seed +from squigglepy.samplers import sample +from squigglepy.distributions import norm def test_seed(): diff --git a/tests/test_samplers.py b/tests/test_samplers.py index 8d884dd..d8ec18c 100644 --- a/tests/test_samplers.py +++ b/tests/test_samplers.py @@ -3,7 +3,7 @@ import numpy as np from unittest.mock import patch, Mock -from ..squigglepy.distributions import ( +from squigglepy.distributions import ( const, uniform, norm, @@ -33,9 +33,9 @@ clip, dist_fn, ) -from ..squigglepy import samplers -from ..squigglepy.utils import _is_numpy -from ..squigglepy.samplers import ( +from squigglepy import samplers +from squigglepy.utils import _is_numpy +from squigglepy.samplers import ( normal_sample, lognormal_sample, mixture_sample, @@ -44,7 +44,7 @@ t_sample, sample, ) -from ..squigglepy.distributions import NormalDistribution +from squigglepy.distributions import NormalDistribution class FakeRNG: @@ -934,61 +934,61 @@ def test_clip(): def test_sample_cache(): - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches = len(_squigglepy_internal_sample_caches) sample(norm(1, 2), memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches2 = len(_squigglepy_internal_sample_caches) assert n_caches < n_caches2 sample(norm(1, 2), memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches3 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches3 sample(norm(1, 2)) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches4 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches4 sample(norm(3, 4)) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches5 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches5 sample(norm(3, 4), memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches6 = len(_squigglepy_internal_sample_caches) assert n_caches6 > n_caches5 def test_sample_reload_cache(): - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches = len(_squigglepy_internal_sample_caches) out1 = sample(norm(5, 6), memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches2 = len(_squigglepy_internal_sample_caches) assert n_caches < n_caches2 out2 = sample(norm(5, 6), memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches3 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches3 assert out1 == out2 out3 = sample(norm(5, 6), memcache=True, reload_cache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches4 = len(_squigglepy_internal_sample_caches) assert n_caches3 == n_caches4 @@ -1014,13 +1014,13 @@ def test_sample_cachefile(cachefile): def test_sample_cachefile_primary(cachefile): assert not os.path.exists(cachefile + ".sqcache.npy") - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches = len(_squigglepy_internal_sample_caches) sample(norm(10, 20), dump_cache_file=cachefile, memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches2 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches + 1 @@ -1034,13 +1034,13 @@ def test_sample_cachefile_primary(cachefile): def test_sample_load_noop_cachefile(cachefile): assert not os.path.exists(cachefile + ".sqcache.npy") - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches = len(_squigglepy_internal_sample_caches) o1 = sample(norm(100, 200), dump_cache_file=cachefile, memcache=True) - from ..squigglepy.samplers import _squigglepy_internal_sample_caches + from squigglepy.samplers import _squigglepy_internal_sample_caches n_caches2 = len(_squigglepy_internal_sample_caches) assert n_caches2 == n_caches + 1 diff --git a/tests/test_utils.py b/tests/test_utils.py index 63cc91f..b59bd95 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,7 +2,7 @@ import numpy as np from datetime import datetime, timedelta -from ..squigglepy.utils import ( +from squigglepy.utils import ( _process_weights_values, _process_discrete_weights_values, event_occurs, @@ -29,8 +29,8 @@ extremize, normalize, ) -from ..squigglepy.rng import set_seed -from ..squigglepy.distributions import bernoulli, beta, norm, dist_round, const +from squigglepy.rng import set_seed +from squigglepy.distributions import bernoulli, beta, norm, dist_round, const def test_process_weights_values_simple_case(): From 6013dcbeecc1c8c8fe50e0747fb687f34e1f6a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 13:48:32 -0400 Subject: [PATCH 29/35] chore: update gitignore for python/vscode --- .gitignore | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 6e8f457..e21e41e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,164 @@ -build/* -dist/* -*.egg-info -__pycache__ -.ruff_cache -.pytest-runtimes +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +.ruff_cache/ + +.vscode/ \ No newline at end of file From 4cf5360cbf378dad7fecd4b7aaf1701084c3e534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 14:18:29 -0400 Subject: [PATCH 30/35] fix: make plot part of OperableDistribution --- .gitignore | 3 +- squigglepy/distributions.py | 58 ++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index e21e41e..29225aa 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,5 @@ cython_debug/ .ruff_cache/ -.vscode/ \ No newline at end of file +.vscode/ +monkeytype.sqlite3 diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 662f353..14801ea 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -1,13 +1,13 @@ -import operator import math +import operator +from typing import Optional, Union + import numpy as np from scipy import stats -from typing import Optional, Union -from .utils import _process_weights_values, _is_numpy, is_dist, _round +from .utils import _is_numpy, _process_weights_values, _round, is_dist from .version import __version__ - Number = Union[int, float, np.floating, np.integer] @@ -23,31 +23,6 @@ def __str__(self): def __repr__(self): return str(self) - def plot(self, num_samples: int = 1000, bins: int = 200) -> None: - """ - Plot a histogram of the samples. - - Parameters - ---------- - num_samples : int - The number of samples to draw for plotting. Defaults to 1000 if not set. - bins : int - The number of bins to plot. Defaults to 200 if not set. - - Examples - -------- - >>> sq.norm(5, 10).plot() - """ - from matplotlib import pyplot as plt - - num_samples = 1000 if num_samples is None else num_samples - bins = 200 if bins is None else bins - - samples = self @ num_samples - - plt.hist(samples, bins=bins) - plt.show() - class OperableDistribution(BaseDistribution): def __init__(self): @@ -134,6 +109,31 @@ def __pow__(self, dist): def __rpow__(self, dist): return ComplexDistribution(dist, self, operator.pow, "**") + + def plot(self, num_samples: int = 1000, bins: int = 200) -> None: + """ + Plot a histogram of the samples. + + Parameters + ---------- + num_samples : int + The number of samples to draw for plotting. Defaults to 1000 if not set. + bins : int + The number of bins to plot. Defaults to 200 if not set. + + Examples + -------- + >>> sq.norm(5, 10).plot() + """ + from matplotlib import pyplot as plt + + num_samples = 1000 if num_samples is None else num_samples + bins = 200 if bins is None else bins + + samples = self @ num_samples + + plt.hist(samples, bins=bins) + plt.show() class ComplexDistribution(OperableDistribution): From bbbbed5fd0c55b39d97842fb2ee96c43297ed847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 14:36:41 -0400 Subject: [PATCH 31/35] fix: minor type fixes on distributions --- squigglepy/distributions.py | 57 +++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 14801ea..ff34d9b 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -616,6 +616,7 @@ def uniform(x, y): return UniformDistribution(x=x, y=y) + class NormalDistribution(OperableDistribution): def __init__( self, @@ -632,10 +633,10 @@ def __init__( self.lclip = lclip self.rclip = rclip - self.x: Number - self.y: Number - self.mean: Number - self.sd: Number + self.x: Optional[Number] + self.y: Optional[Number] + self.mean: Optional[Number] + self.sd: Optional[Number] # Define the complementary set of parameters # x/y => mean/sd, mean/sd => x/y @@ -644,8 +645,9 @@ def __init__( raise ValueError("`high value` (y) cannot be lower than `low value` (x)") self.x, self.y = x, y self.mean = (self.x + self.y) / 2 - cdf_value = 0.5 + 0.5 * (self.credibility / 100) - normed_sigma = stats.norm.ppf(cdf_value) + cdf_value: float | np.float64 = 0.5 + 0.5 * (self.credibility / 100) + normed_sigma: np.float64 = stats.norm.ppf(cdf_value) # type: ignore + assert self.mean is not None self.sd = (self.y - self.mean) / normed_sigma elif sd is not None and x is None and y is None: self.sd = sd @@ -656,6 +658,7 @@ def __init__( raise ValueError("you must define either x/y or mean/sd") def __str__(self): + assert self.mean is not None and self.sd is not None out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) if self.lclip is not None: out += ", lclip={}".format(self.lclip) @@ -709,26 +712,26 @@ def norm(x=None, y=None, credibility=90, mean=None, sd=None, lclip=None, rclip=N class LognormalDistribution(OperableDistribution): def __init__( self, - x=None, - y=None, - norm_mean=None, - norm_sd=None, - lognorm_mean=None, - lognorm_sd=None, - credibility=90, - lclip=None, - rclip=None, + x: Optional[Number]=None, + y: Optional[Number]=None, + norm_mean: Optional[Number]=None, + norm_sd: Optional[Number]=None, + lognorm_mean: Optional[Number]=None, + lognorm_sd: Optional[Number]=None, + credibility: int=90, + lclip: Optional[Number]=None, + rclip: Optional[Number]=None, ): super().__init__() - self.x = x - self.y = y - self.credibility = credibility - self.norm_mean = norm_mean - self.norm_sd = norm_sd - self.lognorm_mean = lognorm_mean - self.lognorm_sd = lognorm_sd - self.lclip = lclip - self.rclip = rclip + self.x: Optional[Number] = x + self.y: Optional[Number] = y + self.credibility: int = credibility + self.norm_mean: Optional[Number] = norm_mean + self.norm_sd: Optional[Number] = norm_sd + self.lognorm_mean: Optional[Number] = lognorm_mean + self.lognorm_sd: Optional[Number] = lognorm_sd + self.lclip: Optional[Number] = lclip + self.rclip: Optional[Number] = rclip if self.x is not None and self.y is not None and self.x > self.y: raise ValueError("`high value` cannot be lower than `low value`") @@ -756,24 +759,28 @@ def __init__( elif self.lognorm_sd is not None and self.lognorm_mean is None: self.lognorm_mean = 1 - if self.x is not None: + if self.x is not None and self.y is not None: self.norm_mean = (np.log(self.x) + np.log(self.y)) / 2 cdf_value = 0.5 + 0.5 * (self.credibility / 100) normed_sigma = stats.norm.ppf(cdf_value) self.norm_sd = (np.log(self.y) - self.norm_mean) / normed_sigma if self.lognorm_sd is None: + assert self.norm_sd is not None and self.norm_mean is not None self.lognorm_mean = np.exp(self.norm_mean + self.norm_sd**2 / 2) self.lognorm_sd = ( (np.exp(self.norm_sd**2) - 1) * np.exp(2 * self.norm_mean + self.norm_sd**2) ) ** 0.5 elif self.norm_sd is None: + assert self.lognorm_sd is not None and self.lognorm_mean is not None self.norm_mean = np.log( (self.lognorm_mean**2 / np.sqrt(self.lognorm_sd**2 + self.lognorm_mean**2)) ) self.norm_sd = np.sqrt(np.log(1 + self.lognorm_sd**2 / self.lognorm_mean**2)) def __str__(self): + assert self.lognorm_mean is not None and self.lognorm_sd is not None + assert self.norm_mean is not None and self.norm_sd is not None out = " lognorm(lognorm_mean={}, lognorm_sd={}, norm_mean={}, norm_sd={}" out = out.format( round(self.lognorm_mean, 2), From d085bc2cd20937fdf4c603659a35894fdad1dbca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 18:56:19 -0400 Subject: [PATCH 32/35] feat(types): fully type distributions, reimplement dist_fn --- CHANGES.md | 304 +++++++------- squigglepy/bayes.py | 6 +- squigglepy/distributions.py | 785 +++++++++++++++++++++++++++--------- squigglepy/samplers.py | 10 +- squigglepy/utils.py | 53 ++- tests/test_distributions.py | 20 +- 6 files changed, 803 insertions(+), 375 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9697481..1e61b73 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,286 +1,264 @@ ## v0.27 -* **[Breaking change]** This package now only supports Python 3.9 and higher. -* **[Breaking change]** `get_percentiles` and `get_log_percentiles` now always return a dictionary, even if there's only one element. -* **[Breaking change]** `.type` is now removed from distribution objects. -* Package load time is now ~2x faster. -* Pandas and matplotlib as removed as required dependencies, but their related features are lazily enabled when the modules are available. These packages are still available for install as extras, installable with `pip install squigglepy[plots]` (for plotting-related functionality, matplotlib for now), `pip install squigglepy[ecosystem]` (for pandas, and in the future other related packages), or `pip install squigglepy[all]` (for all extras). -* Multicore distribution now does extra checks to avoid crashing from race conditions. -* Distribution objects now have the version of squigglepy they were created with, which can be accessed via `obj._version`. This should be helpful for debugging and noticing stale objects, especially when squigglepy distributions are stored in caches. -* Using black now for formatting. -* Switched from `flake8` to `ruff`. +- **[Breaking change]** This package now only supports Python 3.9 and higher. +- **[Breaking change]** `get_percentiles` and `get_log_percentiles` now always return a dictionary, even if there's only one element. +- **[Breaking change]** `.type` is now removed from distribution objects. +- **[Breaking change]** The signature of `dist_fn` has changed to make it simpler: + - `dist_fn` no longer takes `None` positional arguments. (i.e. `dist_fn(dist1, None, np.ceil)` is now `dist_fn(dist1, np.ceil)`). + - `dist_fn` no longer supports passing arbitrary arguments as if they were distributions. Use partials instead (i.e. `dist_fn(d1, 2, round)` is now `dist_fn(d1, partial(round, digits=2))`). +- Package load time is now ~2x faster. +- Pandas and matplotlib as removed as required dependencies, but their related features are lazily enabled when the modules are available. These packages are still available for install as extras, installable with `pip install squigglepy[plots]` (for plotting-related functionality, matplotlib for now), `pip install squigglepy[ecosystem]` (for pandas, and in the future other related packages), or `pip install squigglepy[all]` (for all extras). +- Multicore distribution now does extra checks to avoid crashing from race conditions. +- Distribution objects now have the version of squigglepy they were created with, which can be accessed via `obj._version`. This should be helpful for debugging and noticing stale objects, especially when squigglepy distributions are stored in caches. +- Using black now for formatting. +- Switched from `flake8` to `ruff`. ## v0.26 -* **[Breaking change]** `lognorm` can now be defined either referencing the mean and sd of the underlying normal distribution via `norm_mean` / `norm_sd` or via the mean and sd of the lognormal distribution itself via `lognorm_mean` / `lognorm_sd`. To further disambiguate, `mean` and `sd` are no longer variables that can be passed to `lognorm`. - +- **[Breaking change]** `lognorm` can now be defined either referencing the mean and sd of the underlying normal distribution via `norm_mean` / `norm_sd` or via the mean and sd of the lognormal distribution itself via `lognorm_mean` / `lognorm_sd`. To further disambiguate, `mean` and `sd` are no longer variables that can be passed to `lognorm`. ## v0.25 -* Added `plot` as a method to more easily plot distributions. -* Added `dist_log` and `dist_exp` operators on distributions. -* Added `growth_rate_to_doubling_time` and `doubling_time_to_growth_rate` convenience functions. These take numbers, numpy arrays or distributions. -* Mixture distributions now print with weights in addition to distributions. -* Changes `get_log_percentiles` to report in scientific notation. -* `bayes` now supports separate arguments for `memcache_load` and `memcache_save` to better customize how memcache behavior works. `memcache` remains a parameter that sets both `memcache_load` and `memcache_save` to True. - +- Added `plot` as a method to more easily plot distributions. +- Added `dist_log` and `dist_exp` operators on distributions. +- Added `growth_rate_to_doubling_time` and `doubling_time_to_growth_rate` convenience functions. These take numbers, numpy arrays or distributions. +- Mixture distributions now print with weights in addition to distributions. +- Changes `get_log_percentiles` to report in scientific notation. +- `bayes` now supports separate arguments for `memcache_load` and `memcache_save` to better customize how memcache behavior works. `memcache` remains a parameter that sets both `memcache_load` and `memcache_save` to True. ## v0.24 -* Distributions can now be negated with `-` (e.g., `-lognorm(0.1, 1)`). -* Numpy ints and floats can now be used for determining the number of samples. -* Fixed some typos in the documentation. - +- Distributions can now be negated with `-` (e.g., `-lognorm(0.1, 1)`). +- Numpy ints and floats can now be used for determining the number of samples. +- Fixed some typos in the documentation. ## v0.23 -* Added `pareto` distribution. -* Added `get_median_and_ci` to return the median and a given confidence interval for data. -* `discrete` and `mixture` distributions now give more detail when printed. -* Fixed some typos in the documentation. - +- Added `pareto` distribution. +- Added `get_median_and_ci` to return the median and a given confidence interval for data. +- `discrete` and `mixture` distributions now give more detail when printed. +- Fixed some typos in the documentation. ## v0.22 -* Added `extremize` to extremize predictions. -* Added `normalize` to normalize a list of numbers to sum to 1. -* Added `get_mean_and_ci` to return the mean and a given confidence interval for data. -* Added `is_dist` to determine if an object is a Squigglepy distribution. -* Added `is_sampleable` to determine if an object can be sampled using `sample`. -* Support for working within Pandas is now explicitly added. `pandas` has been added as a requirement. -* `discrete` sampling now will compress a large array if possible for more efficient sampling. -* `clip`, `lclip`, and `rclip` can now be used without needing distributions. -* Some functions (e.g, `geomean`) previously only supported lists, dictionaries, and numpy arrays. They have been expanded to support all iterables. -* `dist_max` and `dist_min` now support pipes (`>>`) -* `get_percentiles` now coerces output to integer if `digits` is less than or equal to 0, instead of just exactly 0. - +- Added `extremize` to extremize predictions. +- Added `normalize` to normalize a list of numbers to sum to 1. +- Added `get_mean_and_ci` to return the mean and a given confidence interval for data. +- Added `is_dist` to determine if an object is a Squigglepy distribution. +- Added `is_sampleable` to determine if an object can be sampled using `sample`. +- Support for working within Pandas is now explicitly added. `pandas` has been added as a requirement. +- `discrete` sampling now will compress a large array if possible for more efficient sampling. +- `clip`, `lclip`, and `rclip` can now be used without needing distributions. +- Some functions (e.g, `geomean`) previously only supported lists, dictionaries, and numpy arrays. They have been expanded to support all iterables. +- `dist_max` and `dist_min` now support pipes (`>>`) +- `get_percentiles` now coerces output to integer if `digits` is less than or equal to 0, instead of just exactly 0. ## v0.21 -* Mixture sampling is now 4-23x faster. -* You can now get the version of squigglepy via `sq.__version__`. -* Fixes a bug where the tqdm was displayed with the incorrect count when collecting cores during a multicore `sample`. - +- Mixture sampling is now 4-23x faster. +- You can now get the version of squigglepy via `sq.__version__`. +- Fixes a bug where the tqdm was displayed with the incorrect count when collecting cores during a multicore `sample`. ## v0.20 -* Fixes how package dependencies are handled in `setup.py` an specifies Python >= 3.7 must be used. This should fix install errors. - +- Fixes how package dependencies are handled in `setup.py` an specifies Python >= 3.7 must be used. This should fix install errors. ## v0.19 #### Bugfixes -* Fixes a bug where `lclip` and/or `rclip` on `mixture` distribution were not working correctly. -* Fixes a bug where `dist_fn` did not work with `np.vectorize` functions. -* Fixes a bug where in-memory caching was invoked for `bayesnet` when not desired. +- Fixes a bug where `lclip` and/or `rclip` on `mixture` distribution were not working correctly. +- Fixes a bug where `dist_fn` did not work with `np.vectorize` functions. +- Fixes a bug where in-memory caching was invoked for `bayesnet` when not desired. #### Caching and Multicore -* **[Breaking change]** `bayesnet` caching is now based on binary files instead of pickle files (uses `msgspec` as the underlying library). -* **[Breaking change]** `sample` caching is now based on numpy files instead of pickle files. -* A cache can now be loaded via `sample(load_cache=cachefile)` or `bayesnet(load_cache=cachefile)`, without needing to pass the distribution / function. -* `bayesnet` and `sample` now take an argument `cores` (default 1). If greater than 1, will run the calculations on multiple cores using the pathos package. +- **[Breaking change]** `bayesnet` caching is now based on binary files instead of pickle files (uses `msgspec` as the underlying library). +- **[Breaking change]** `sample` caching is now based on numpy files instead of pickle files. +- A cache can now be loaded via `sample(load_cache=cachefile)` or `bayesnet(load_cache=cachefile)`, without needing to pass the distribution / function. +- `bayesnet` and `sample` now take an argument `cores` (default 1). If greater than 1, will run the calculations on multiple cores using the pathos package. #### Other -* Functions that take `weights` now can instead take a parameter `relative_weights` where waits are automatically normalized to sum to 1 (instead of erroring, which is still the behavior if using `weights`). -* Verbose output for `bayesnet` and `sample` is now clearer (and slightly more verbose). - +- Functions that take `weights` now can instead take a parameter `relative_weights` where waits are automatically normalized to sum to 1 (instead of erroring, which is still the behavior if using `weights`). +- Verbose output for `bayesnet` and `sample` is now clearer (and slightly more verbose). ## v0.18 -* **[Breaking change]** The default `t` for t-distributions has changed from 1 to 20. -* `sample` results can now be cached in-memory using `memcache=True`. They can also be cached to a file -- use `dump_cache_file` to write the file and `load_cache_file` to load from the file. -* _(Non-visible backend change)_ Weights that are set to 0 are now dropped entirely. - +- **[Breaking change]** The default `t` for t-distributions has changed from 1 to 20. +- `sample` results can now be cached in-memory using `memcache=True`. They can also be cached to a file -- use `dump_cache_file` to write the file and `load_cache_file` to load from the file. +- _(Non-visible backend change)_ Weights that are set to 0 are now dropped entirely. ## v0.17 -* When `verbose=True` is used in `sample`, the progress bar now pops up in more relevant places and is much more likely to get triggered when relevant. -* `discrete_sample` and `mixture_sample` now can take `verbose` parameter. - +- When `verbose=True` is used in `sample`, the progress bar now pops up in more relevant places and is much more likely to get triggered when relevant. +- `discrete_sample` and `mixture_sample` now can take `verbose` parameter. ## v0.16 -* `zero_inflated` can create an arbitrary zero-inflated distribution. -* Individual sampling functions (`normal_sample`, `lognormal_sample`, etc.) can now take an argument `samples` to generate multiple samples. -* A large speedup has been achieved to sampling from the same distribution multiple times. -* `requirements.txt` has been updated. - +- `zero_inflated` can create an arbitrary zero-inflated distribution. +- Individual sampling functions (`normal_sample`, `lognormal_sample`, etc.) can now take an argument `samples` to generate multiple samples. +- A large speedup has been achieved to sampling from the same distribution multiple times. +- `requirements.txt` has been updated. ## v0.15 -* **[Breaking change]** `bayesnet` function now refers to parameter `memcache` where previously this parameter was called `cache`. -* **[Breaking change]** If `get_percentiles` or `get_log_percentiles` is called with just one elemement for `percentiles`, it will return that value instead of a dict. -* Fixed a bug where `get_percentiles` would not round correctly. -* `bayesnet` results can now be cached to a file. Use `dump_cache_file` to write the file and `load_cache_file` to load from the file. -* `discrete` now works with numpy arrays in addition to lists. -* Added `one_in` as a shorthand to convert percentages into "1 in X" notation. -* Distributions can now be compared with `==` and `!=`. - +- **[Breaking change]** `bayesnet` function now refers to parameter `memcache` where previously this parameter was called `cache`. +- **[Breaking change]** If `get_percentiles` or `get_log_percentiles` is called with just one elemement for `percentiles`, it will return that value instead of a dict. +- Fixed a bug where `get_percentiles` would not round correctly. +- `bayesnet` results can now be cached to a file. Use `dump_cache_file` to write the file and `load_cache_file` to load from the file. +- `discrete` now works with numpy arrays in addition to lists. +- Added `one_in` as a shorthand to convert percentages into "1 in X" notation. +- Distributions can now be compared with `==` and `!=`. ## v0.14 -* Nested sampling now works as intended. -* You can now use `>>` for pipes for distributions. For example, `sq.norm(1, 2) >> dist_ceil` -* Distributions can now be compared with `>`, `<`, `>=`, and `<=`. -* `dist_max` can be used to get the maximum value between two distributions. This family of functions are not evaluated until the distribution is sampled and they work with pipes. -* `dist_min` can be used to get the minimum value between two distributions. -* `dist_round` can be used to round the final output of a distribution. This makes the distribution discrete. -* `dist_ceil` can be used to ceiling round the final output of a distribution. This makes the distribution discrete. -* `dist_floor` can be used to floor round the final output of a distribution. This makes the distribution discrete. -* `lclip` can be used to clip a distribution to a lower bound. This is the same functionality that is available within the distribution and the `sample` method. -* `rclip` can be used to clip a distribution to an upper bound. This is the same functionality that is available within the distribution and the `sample` method. -* `clip` can be used to clip a distribution to both an upper bound and a lower bound. This is the same functionality that is available within the distribution and the `sample` method. -* `sample` can now be used directly on numbers. This makes `const` functionally obsolete, but `const` is maintained for backwards compatibility and in case it is useful. -* `sample(None)` now returns `None` instead of an error. - +- Nested sampling now works as intended. +- You can now use `>>` for pipes for distributions. For example, `sq.norm(1, 2) >> dist_ceil` +- Distributions can now be compared with `>`, `<`, `>=`, and `<=`. +- `dist_max` can be used to get the maximum value between two distributions. This family of functions are not evaluated until the distribution is sampled and they work with pipes. +- `dist_min` can be used to get the minimum value between two distributions. +- `dist_round` can be used to round the final output of a distribution. This makes the distribution discrete. +- `dist_ceil` can be used to ceiling round the final output of a distribution. This makes the distribution discrete. +- `dist_floor` can be used to floor round the final output of a distribution. This makes the distribution discrete. +- `lclip` can be used to clip a distribution to a lower bound. This is the same functionality that is available within the distribution and the `sample` method. +- `rclip` can be used to clip a distribution to an upper bound. This is the same functionality that is available within the distribution and the `sample` method. +- `clip` can be used to clip a distribution to both an upper bound and a lower bound. This is the same functionality that is available within the distribution and the `sample` method. +- `sample` can now be used directly on numbers. This makes `const` functionally obsolete, but `const` is maintained for backwards compatibility and in case it is useful. +- `sample(None)` now returns `None` instead of an error. ## v0.13 -* Sample shorthand notation can go in either order. That is, `100 @ sq.norm(1, 2)` now works and is the same as `sq.norm(1, 2) @ 100`, which is the same as `sq.sample(sq.norm(1, 2), n=100)`. - +- Sample shorthand notation can go in either order. That is, `100 @ sq.norm(1, 2)` now works and is the same as `sq.norm(1, 2) @ 100`, which is the same as `sq.sample(sq.norm(1, 2), n=100)`. ## v0.12 -* Distributions now implement math directly. That is, you can do things like `sq.norm(2, 3) + sq.norm(4, 5)`, whereas previously this would not work. Thanks to Dawn Drescher for helping me implement this. -* `~sq.norm(1, 2)` is now a shorthand for `sq.sample(sq.norm(1, 2))`. Thanks to Dawn Drescher for helping me implement this shorthand. -* `sq.norm(1, 2) @ 100` is now a shorthand for `sq.sample(sq.norm(1, 2), n=100)` - +- Distributions now implement math directly. That is, you can do things like `sq.norm(2, 3) + sq.norm(4, 5)`, whereas previously this would not work. Thanks to Dawn Drescher for helping me implement this. +- `~sq.norm(1, 2)` is now a shorthand for `sq.sample(sq.norm(1, 2))`. Thanks to Dawn Drescher for helping me implement this shorthand. +- `sq.norm(1, 2) @ 100` is now a shorthand for `sq.sample(sq.norm(1, 2), n=100)` ## v0.11 #### Distributions -* **[Breaking change]** `tdist` and `log_tdist` have been modified to better approximate the desired credible intervals. -* `tdist` now can be defined by just `t`, producing a classic t-distribution. -* `tdist` now has a default value for `t`: 1. -* Added `chisquare` distribution. -* `lognormal` now returns an error if it is defined with a zero or negative value. +- **[Breaking change]** `tdist` and `log_tdist` have been modified to better approximate the desired credible intervals. +- `tdist` now can be defined by just `t`, producing a classic t-distribution. +- `tdist` now has a default value for `t`: 1. +- Added `chisquare` distribution. +- `lognormal` now returns an error if it is defined with a zero or negative value. #### Other -* All functions now have docstrings. -* Added `kelly` to calculate Kelly criterion for bet sizing with probabilities. -* Added `full_kelly`, `half_kelly`, `quarter_kelly` as helpful aliases. - +- All functions now have docstrings. +- Added `kelly` to calculate Kelly criterion for bet sizing with probabilities. +- Added `full_kelly`, `half_kelly`, `quarter_kelly` as helpful aliases. ## v0.10 -* **[Breaking change]** `credibility` is now defined using a number out of 100 (e.g., `credibility=80` to define an 80% CI) rather than a decimal out of 1 (e.g., `credibility=0.8` to define an 80% CI). -* Distribution objects now print their parameters. - +- **[Breaking change]** `credibility` is now defined using a number out of 100 (e.g., `credibility=80` to define an 80% CI) rather than a decimal out of 1 (e.g., `credibility=0.8` to define an 80% CI). +- Distribution objects now print their parameters. ## v0.9 -* `goemean` and `geomean_odds` now can take the nested-list-based and dictionary-based formats for passing weights. - +- `goemean` and `geomean_odds` now can take the nested-list-based and dictionary-based formats for passing weights. ## v0.8 #### Bayesian library updates -* **[Breaking change]** `bayes.update` now updates normal distributions from the distribution rather than from samples. -* **[Breaking change]** `bayes.update` no longer takes a `type` parameter but can now infer the type from the passed distribution. -* **[Breaking change]** Corrected a bug in how `bayes.update` implemented `evidence_weight` when updating normal distributions. +- **[Breaking change]** `bayes.update` now updates normal distributions from the distribution rather than from samples. +- **[Breaking change]** `bayes.update` no longer takes a `type` parameter but can now infer the type from the passed distribution. +- **[Breaking change]** Corrected a bug in how `bayes.update` implemented `evidence_weight` when updating normal distributions. #### Non-visible backend changes -* Distributions are now implemented as classes (rather than lists). - +- Distributions are now implemented as classes (rather than lists). ## v0.7 #### Bugfixes -* Fixes an issue with sampling from the `bernoulli` distribution. -* Fixes a bug with the implementation of `lclip` and `rclip`. +- Fixes an issue with sampling from the `bernoulli` distribution. +- Fixes a bug with the implementation of `lclip` and `rclip`. #### New distributions -* Adds `discrete` to calculate a discrete distribution. Example: `discrete({'A': 0.3, 'B': 0.3, 'C': 0.4})` will return A 30% of the time, B 30% of the time, and C 40% of the time. -* Adds `poisson(lam)` to calculate a poisson distribution. -* Adds `gamma(size, scale)` to calculate a gamma distribution. +- Adds `discrete` to calculate a discrete distribution. Example: `discrete({'A': 0.3, 'B': 0.3, 'C': 0.4})` will return A 30% of the time, B 30% of the time, and C 40% of the time. +- Adds `poisson(lam)` to calculate a poisson distribution. +- Adds `gamma(size, scale)` to calculate a gamma distribution. #### Bayesian library updates -* Adds `bayes.bayesnet` to do bayesian inferece (see README). -* `bayes.update` now can take an `evidence_weight` parameter. Typically this would be equal to the number of samples. -* **[Breaking change]** `bayes.bayes` has been renamed `bayes.simple_bayes`. +- Adds `bayes.bayesnet` to do bayesian inferece (see README). +- `bayes.update` now can take an `evidence_weight` parameter. Typically this would be equal to the number of samples. +- **[Breaking change]** `bayes.bayes` has been renamed `bayes.simple_bayes`. #### Other -* **[Breaking change]** `credibility`, which defines the size of the interval (e.g., `credibility=0.8` for an 80% CI), is now a property of the distribution rather than the sampler. That is, you should now call `sample(norm(1, 3, credibility=0.8))` whereas previously it was `sample(norm(1, 3), credibility=0.8)`. This will allow mixing of distributions with different credible ranges. -* **[Breaking change]** Numbers have been changed from functions to global variables. Use `thousand` or `K` instead of `thousand()` (old/deprecated). -* `sample` now has a nice progress reporter if `verbose=True`. -* The `exponential` distribution now implements `lclip` and `rclip`. -* The `mixture` distribution can infer equal weights if no weights are given. -* The `mixture` distribution can infer the last weight if the last weight is not given. -* `geomean` and `geomean_odds` can infer the last weight if the last weight is not given. -* You can use `flip_coin` and `roll_die(sides)` to flip a coin or roll a die. -* `event_happens` and `event` are aliases for `event_occurs`. -* `get_percentiles` will now cast output to `int` if `digits=0`. -* `get_log_percentiles` now has a default value for `percentiles`. -* You can now set the seed for the RNG using `sq.set_seed`. +- **[Breaking change]** `credibility`, which defines the size of the interval (e.g., `credibility=0.8` for an 80% CI), is now a property of the distribution rather than the sampler. That is, you should now call `sample(norm(1, 3, credibility=0.8))` whereas previously it was `sample(norm(1, 3), credibility=0.8)`. This will allow mixing of distributions with different credible ranges. +- **[Breaking change]** Numbers have been changed from functions to global variables. Use `thousand` or `K` instead of `thousand()` (old/deprecated). +- `sample` now has a nice progress reporter if `verbose=True`. +- The `exponential` distribution now implements `lclip` and `rclip`. +- The `mixture` distribution can infer equal weights if no weights are given. +- The `mixture` distribution can infer the last weight if the last weight is not given. +- `geomean` and `geomean_odds` can infer the last weight if the last weight is not given. +- You can use `flip_coin` and `roll_die(sides)` to flip a coin or roll a die. +- `event_happens` and `event` are aliases for `event_occurs`. +- `get_percentiles` will now cast output to `int` if `digits=0`. +- `get_log_percentiles` now has a default value for `percentiles`. +- You can now set the seed for the RNG using `sq.set_seed`. #### Non-visible backend changes -* Now has tests via pytest. -* The random numbers now come from a numpy generator as opposed to the previous deprecated `np.random` methods. -* The `sample` module (containing the `sample` function) has been renamed `samplers`. - +- Now has tests via pytest. +- The random numbers now come from a numpy generator as opposed to the previous deprecated `np.random` methods. +- The `sample` module (containing the `sample` function) has been renamed `samplers`. ## v0.6 #### New distributions -* Add `triangular(left, mode, right)` to calculate a triangular distribution. -* Add `binomial(n, p)` to calculate a binomial distribution. -* Add `beta(a, b)` to calculate a beta distribution. -* Add `bernoulli(p)` to calculate a bernoulli distribution. -* Add `exponential(scale)` to calculate an exponential distribution. +- Add `triangular(left, mode, right)` to calculate a triangular distribution. +- Add `binomial(n, p)` to calculate a binomial distribution. +- Add `beta(a, b)` to calculate a beta distribution. +- Add `bernoulli(p)` to calculate a bernoulli distribution. +- Add `exponential(scale)` to calculate an exponential distribution. #### New Bayesian library -* Add `bayes.update` to get a posterior distribution from a prior distribution and an evidence distribution. -* Add `bayes.average` to average distributions (via a mixture). +- Add `bayes.update` to get a posterior distribution from a prior distribution and an evidence distribution. +- Add `bayes.average` to average distributions (via a mixture). #### New utility functions -* Add `laplace` to calculate Laplace's Law of Succession. If `s` and `n` are passed, it will calculate `(s+1)/(n+2)`. If `s`, `time_passed`, and `time_remaining` are passed, it will use the [time invariant version](https://www.lesswrong.com/posts/wE7SK8w8AixqknArs/a-time-invariant-version-of-laplace-s-rule). Use `time_fixed=True` for fixed time periods and `time_fixed=False` (default) otherwise. -* Add `geomean` to calculate the geometric mean. -* Add `p_to_odds` to convert probability to odds. Also `odds_to_p` to convert odds to probability. -* Add `geomean_odds` to calculate the geometric mean of odds, converted to and from probabilities. +- Add `laplace` to calculate Laplace's Law of Succession. If `s` and `n` are passed, it will calculate `(s+1)/(n+2)`. If `s`, `time_passed`, and `time_remaining` are passed, it will use the [time invariant version](https://www.lesswrong.com/posts/wE7SK8w8AixqknArs/a-time-invariant-version-of-laplace-s-rule). Use `time_fixed=True` for fixed time periods and `time_fixed=False` (default) otherwise. +- Add `geomean` to calculate the geometric mean. +- Add `p_to_odds` to convert probability to odds. Also `odds_to_p` to convert odds to probability. +- Add `geomean_odds` to calculate the geometric mean of odds, converted to and from probabilities. #### Other -* If a distribution is defined with `sd` but not `mean`, `mean` will be inferred to be 0. -* `sample` can now take `lclip` and `rclip` directly, in addition to defining `lclip` and `rclip` on the distribution itself. If both are defined, the most restrictive of the two bounds will be used. - +- If a distribution is defined with `sd` but not `mean`, `mean` will be inferred to be 0. +- `sample` can now take `lclip` and `rclip` directly, in addition to defining `lclip` and `rclip` on the distribution itself. If both are defined, the most restrictive of the two bounds will be used. ## v0.5 -* Fix critical bug to `tdist` and `log_tdist` introduced in v0.3. - +- Fix critical bug to `tdist` and `log_tdist` introduced in v0.3. ## v0.4 -* Fix critical bug introduced in v0.3. - +- Fix critical bug introduced in v0.3. ## v0.3 -* Be able to define distributions using `mean` and `sd` instead of defining the interval. - +- Be able to define distributions using `mean` and `sd` instead of defining the interval. ## v0.2 -* **[Breaking change]** Change `distributed_log` to `mixture` (to follow Squiggle) and allow it to implement any sub-distribution. -* **[Breaking change]** Changed library to single import. -* **[Breaking change]** Remove `weighted_log` as a distribution. - +- **[Breaking change]** Change `distributed_log` to `mixture` (to follow Squiggle) and allow it to implement any sub-distribution. +- **[Breaking change]** Changed library to single import. +- **[Breaking change]** Remove `weighted_log` as a distribution. ## v0.1 -* Initial library +- Initial library diff --git a/squigglepy/bayes.py b/squigglepy/bayes.py index 5b28741..33aade2 100644 --- a/squigglepy/bayes.py +++ b/squigglepy/bayes.py @@ -20,7 +20,7 @@ beta, mixture, ) -from .utils import OptionalListOfFloats, _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm +from .utils import Weights, _core_cuts, _init_tqdm, _tick_tqdm, _flush_tqdm _squigglepy_internal_bayesnet_caches = {} @@ -384,8 +384,8 @@ def update( def average( prior: BaseDistribution, evidence: BaseDistribution, - weights: OptionalListOfFloats = [0.5, 0.5], - relative_weights: OptionalListOfFloats = None, + weights: Optional[Weights] = [0.5, 0.5], + relative_weights: Optional[Weights] = None, ) -> MixtureDistribution: """ Average two distributions. diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index ff34d9b..a995991 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -1,18 +1,33 @@ +from __future__ import annotations +from functools import partial +import functools + import math import operator -from typing import Optional, Union +from typing import Any, Callable, Optional, Self, TypeVar, Union import numpy as np from scipy import stats -from .utils import _is_numpy, _process_weights_values, _round, is_dist +from .utils import ( + Integer, + Weights, + _is_numpy, + _process_weights_values, + _round, + is_dist, + Number, + Float, +) from .version import __version__ -Number = Union[int, float, np.floating, np.integer] +from typing import overload + +from numpy import ufunc, vectorize class BaseDistribution: - def __init__(self): + def __init__(self) -> None: self.lclip: Optional[Number] = None self.rclip: Optional[Number] = None self._version: str = __version__ @@ -20,12 +35,12 @@ def __init__(self): def __str__(self): return " base" - def __repr__(self): + def __repr__(self) -> str: return str(self) class OperableDistribution(BaseDistribution): - def __init__(self): + def __init__(self) -> None: super().__init__() def __invert__(self): @@ -33,7 +48,7 @@ def __invert__(self): return sample(self) - def __matmul__(self, n: int) -> np.ndarray: + def __matmul__(self, n: int): try: n = int(n) except ValueError: @@ -42,7 +57,9 @@ def __matmul__(self, n: int) -> np.ndarray: return sample(self, n=n) - def __rshift__(self, fn): + def __rshift__( + self, fn: Union[Callable[[Self], ComplexDistribution], ComplexDistribution] + ) -> Union[ComplexDistribution, int]: if callable(fn): return fn(self) elif isinstance(fn, ComplexDistribution): @@ -50,66 +67,66 @@ def __rshift__(self, fn): else: raise ValueError - def __rmatmul__(self, n: int) -> np.ndarray: + def __rmatmul__(self, n: int): return self.__matmul__(n) - def __gt__(self, dist: BaseDistribution): + def __gt__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.gt, ">") - def __ge__(self, dist: BaseDistribution): + def __ge__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.ge, ">=") - def __lt__(self, dist: BaseDistribution): + def __lt__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.lt, "<") - def __le__(self, dist: BaseDistribution): + def __le__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "<=") - def __eq__(self, dist: BaseDistribution): + def __eq__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "==") - def __ne__(self, dist: BaseDistribution): + def __ne__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "!=") - def __neg__(self): + def __neg__(self) -> "ComplexDistribution": return ComplexDistribution(self, None, operator.neg, "-") - def __add__(self, dist: BaseDistribution): + def __add__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.add, "+") - def __radd__(self, dist: BaseDistribution): + def __radd__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.add, "+") - def __sub__(self, dist: BaseDistribution): + def __sub__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.sub, "-") - def __rsub__(self, dist: BaseDistribution): + def __rsub__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.sub, "-") - def __mul__(self, dist: BaseDistribution): + def __mul__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.mul, "*") - def __rmul__(self, dist: BaseDistribution): + def __rmul__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.mul, "*") - def __truediv__(self, dist: BaseDistribution): + def __truediv__(self, dist: BaseDistribution) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.truediv, "/") - def __rtruediv__(self, dist): + def __rtruediv__(self, dist: Number) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.truediv, "/") - def __floordiv__(self, dist): + def __floordiv__(self, dist: int) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.floordiv, "//") - def __rfloordiv__(self, dist): + def __rfloordiv__(self, dist: int) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.floordiv, "//") - def __pow__(self, dist): + def __pow__(self, dist: int) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.pow, "**") - def __rpow__(self, dist): + def __rpow__(self, dist: int) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.pow, "**") - + def plot(self, num_samples: int = 1000, bins: int = 200) -> None: """ Plot a histogram of the samples. @@ -137,7 +154,14 @@ def plot(self, num_samples: int = 1000, bins: int = 200) -> None: class ComplexDistribution(OperableDistribution): - def __init__(self, left, right=None, fn=operator.add, fn_str="+", infix=True): + def __init__( + self, + left: Any, + right: Optional[Any] = None, + fn: UFunction = operator.add, + fn_str: str = "+", + infix: bool = True, + ) -> None: super().__init__() self.left = left self.right = right @@ -145,36 +169,46 @@ def __init__(self, left, right=None, fn=operator.add, fn_str="+", infix=True): self.fn_str = fn_str self.infix = infix - def __str__(self): - if self.right is None and self.infix: + def __str__(self) -> str: + left, right, infix = self.left, self.right, self.infix + if isinstance(self.fn, functools.partial) and right is None and not infix: + # This prints the arguments when a partial function is being used + # this is useful for things like round, lclip, rclip, etc. + right = "".join(str(arg) for arg in self.fn.args) + for i, (k, v) in enumerate(self.fn.keywords.items()): + if i == 0 and right: + right += ", " + right += "{}={}".format(k, v) + + if right is None and infix: if self.fn_str == "-": out = " {}{}" else: out = " {} {}" - out = out.format(self.fn_str, str(self.left).replace(" ", "")) - elif self.right is None and not self.infix: + out = out.format(self.fn_str, str(left).replace(" ", "")) + elif right is None and not infix: out = " {}({})".format( - self.fn_str, str(self.left).replace(" ", "") + self.fn_str, str(left).replace(" ", "") ) - elif self.right is not None and self.infix: + elif right is not None and infix: out = " {} {} {}".format( - str(self.left).replace(" ", ""), + str(left).replace(" ", ""), self.fn_str, - str(self.right).replace(" ", ""), + str(right).replace(" ", ""), ) - elif self.right is not None and not self.infix: + elif right is not None and not infix: out = " {}({}, {})" out = out.format( self.fn_str, - str(self.left).replace(" ", ""), - str(self.right).replace(" ", ""), + str(left).replace(" ", ""), + str(right).replace(" ", ""), ) else: - raise ValueError + raise ValueError("The complex distribution is not properly defined") return out -def _get_fname(f, name): +def _get_fname(f: UFunction, name: Optional[str]) -> str: if name is None: if isinstance(f, np.vectorize): name = f.pyfunc.__name__ @@ -183,7 +217,21 @@ def _get_fname(f, name): return name -def dist_fn(dist1, dist2=None, fn=None, name=None): +# These are the types of functions that can be used in the dist_fn function +# We allow booleans for the case of the comparison operators +UScalar = TypeVar("UScalar", bound=Union[Number, bool]) +UFunction = Union[ + Callable[[UScalar], UScalar], + Callable[[UScalar, UScalar], UScalar], + ufunc, + vectorize, +] + + +@overload +def dist_fn( + dist: OperableDistribution, fn: Union[UFunction, list[UFunction]], name: Optional[str] = None +) -> ComplexDistribution: """ Initialize a distribution that has a custom function applied to the result. @@ -191,24 +239,19 @@ def dist_fn(dist1, dist2=None, fn=None, name=None): Parameters ---------- - dist1 : Distribution or function or list - Typically, the distribution to apply the function to. Could also be a function - or list of functions if ``dist_fn`` is being used in a pipe. - dist2 : Distribution or function or list or None - Typically, the second distribution to apply the function to if the function takes - two arguments. Could also be a function or list of functions if ``dist_fn`` is - being used in a pipe. - fn : function or None - The function to apply to the distribution(s). + dist : Distribution or function or list + The distribution to apply the function to. + fn : function or list of functions + The function(s) to apply to the distribution. name : str or None By default, ``fn.__name__`` will be used to name the function. But you can pass a custom name. Returns ------- - ComplexDistribution or function - This will be a lazy evaluation of the desired function that will then be calculated - when it is sampled. + ComplexDistribution + This distribution performs a lazy evaluation of the desired function that + will be calculated when it is sampled. Examples -------- @@ -216,42 +259,309 @@ def dist_fn(dist1, dist2=None, fn=None, name=None): >>> return x * 2 >>> dist_fn(norm(0, 1), double) double(norm(mean=0.5, sd=0.3)) + """ + # A unary function (or functions) was passed in + # The function is applied to each sample + ... + + +@overload +def dist_fn( + dist1: OperableDistribution, + dist2: OperableDistribution, + fn: Union[UFunction, list[UFunction]], + name: Optional[str] = None, +) -> ComplexDistribution: + """ + Apply a binary function to a pair of distributions. + + The function won't be applied until the returned distribution is sampled. + + Parameters + ---------- + dist1 : Distribution + The first distribution to apply the function to. + dist2 : Distribution + The second distribution to apply the function to. + fn : function or list of functions + The function(s) to apply to the distributions. Must take two arguments and + return a single value. + name : str or None + By default, ``fn.__name__`` will be used to name the function. But you can pass + a custom name. + + Returns + ------- + ComplexDistribution + This distribution performs a lazy evaluation of the desired function that + will be calculated when it is sampled. + + Examples + -------- + >>> def add(x, y): + >>> return x + y + >>> dist_fn(norm(0, 1), norm(1, 2), add) + add(norm(mean=0.5, sd=0.3), norm(mean=1.5, sd=0.3)) + """ + # A binary function (or function) passed was passed in + # The function is applied to pairs of samples, and returns the combined sample + ... + + +@overload +def dist_fn( + fn: Union[UFunction, list[UFunction]], + name: Optional[str] = None, +) -> Callable[[OperableDistribution], ComplexDistribution]: + """ + Lazily applies the given function to the previous distribution in a pipe. (Or, equivalently, + produces a partial of the function with the previous distribution as the first argument.) + + Parameters + ---------- + fn : function or list of functions + The function(s) to apply to the distributions. Each must take one argument and + return a single value. + name : str or None + By default, ``fn.__name__`` will be used to name the function. But you can pass + a custom name. + + Returns + ------- + function + A function that takes a distribution and returns a distribution. The returned distribution + performs a lazy evaluation of the desired function whenever it is sampled. + + Examples + -------- + >>> def double(x): + >>> return x * 2 >>> norm(0, 1) >> dist_fn(double) double(norm(mean=0.5, sd=0.3)) """ - if isinstance(dist1, list) and callable(dist1[0]) and dist2 is None and fn is None: - fn = dist1 + # Unary case of the pipe version + ... + + +@overload +def dist_fn( + dist: OperableDistribution, + fn: Union[UFunction, list[UFunction]], + name: Optional[str] = None, +) -> Callable[[OperableDistribution], ComplexDistribution]: + """ + Lazily applies the given binary function to the previous distribution in a pipe, with the given + distribution as the second argument. (Or, equivalently, produces a partial of the function with + the previous distribution passed as the second argument.) + + Parameters + ---------- + dist : Distribution + The distribution to apply the function to. + fn : function or list of functions + The function(s) to apply to the distributions. Each must take two arguments and + return a single value. + name : str or None + By default, ``fn.__name__`` will be used to name the function. But you can pass + a custom name. + + Returns + ------- + function + A function that takes a distribution and returns a distribution. The returned distribution + performs a lazy evaluation of the desired function whenever it is sampled. + + Examples + -------- + >>> def add(x, y): + >>> return x + y + >>> norm(0, 1) >> dist_fn(norm(1, 2), add) + add(norm(mean=0.5, sd=0.3), norm(mean=1.5, sd=0.3)) + """ + # Binary case of the pipe version + ... + - def out_fn(d): - out = d - for f in fn: - out = ComplexDistribution(out, None, fn=f, fn_str=_get_fname(f, name), infix=False) - return out +def dist_fn( + *args, + **kwargs, +) -> Union[ComplexDistribution, Callable[[OperableDistribution], ComplexDistribution]]: + """ + This is the dispatcher for the `dist_fn` function. It handles the different cases + of the function, which are then passed to the `_dist_fn` function to actually + create the respective distribution or partial function. - return out_fn + This handles both unary and binary functions, as well as the pipe versions of each. + The pipe version simply creates a partial of the inner `_dist_fn` function, and returns + that partial. + """ + # Get the arguments + p1: Union[OperableDistribution, Union[UFunction, list[UFunction]]] = ( + args[0] if len(args) > 0 else kwargs.get("dist1", None) + ) + p2: Optional[Union[OperableDistribution, Union[UFunction, list[UFunction]]]] = ( + args[1] if len(args) > 1 else kwargs.get("dist2", None) + ) + p3: Union[UFunction, list[UFunction]] = args[2] if len(args) > 2 else kwargs.get("fn", None) + name: str = kwargs.get("name", None) - if callable(dist1) and dist2 is None and fn is None: - return lambda d: dist_fn(d, fn=dist1) + # Dispatch to the proper form of the function + if ( + (isinstance(p1, OperableDistribution)) + and (not isinstance(p2, OperableDistribution) and p2 is not None) + and (p3 is None) + ): + # Simple case, unary function + p2 = p2 if isinstance(p2, list) else [p2] + return _dist_fn(dist1=p1, fn_list=p2, name=name) + elif ( + isinstance(p1, OperableDistribution) + and isinstance(p2, OperableDistribution) + and (p3 is not None) + ): + # Simple case, binary function + p3 = p3 if isinstance(p3, list) else [p3] + return _dist_fn(dist1=p1, dist2=p2, fn_list=p3, name=name) + elif p1 is not None and not isinstance(p1, OperableDistribution) and p2 is None and p3 is None: + # Pipe case, unary function + p1 = p1 if isinstance(p1, list) else [p1] + return lambda d: _dist_fn(dist1=d, fn_list=p1, name=name) + elif ( + p1 is not None + and not isinstance(p1, OperableDistribution) + and isinstance(p2, OperableDistribution) + and p3 is None + ): + p1 = p1 if isinstance(p1, list) else [p1] + # Pipe case, binary function + return lambda d: _dist_fn(dist1=d, dist2=p2, fn_list=p1, name=name) + else: + raise ValueError("Invalid arguments to dist_fn") - if isinstance(dist2, list) and callable(dist2[0]) and fn is None: - fn = dist2 - dist2 = None - if callable(dist2) and fn is None: - fn = dist2 - dist2 = None +def _dist_fn( + dist1: Optional[OperableDistribution] = None, + dist2: Optional[OperableDistribution] = None, + fn_list: Optional[list[UFunction]] = None, + name: Optional[str] = None, +) -> ComplexDistribution: + """ + This is the actual function that creates the complex distribution + whenever `dist_fn` is used. It handles the simple one function case, + as well as the case where multiple functions are being composed together. + """ + assert dist1 is not None and fn_list is not None + assert all(callable(f) for f in fn_list), "All functions provided must be callable" + assert len(fn_list) > 0, "Must provide at least one function to compose" - if not isinstance(fn, list): - fn = [fn] + if len(fn_list) == 1: + return ComplexDistribution( + dist1, dist2, fn=fn_list[0], fn_str=_get_fname(fn_list[0], name), infix=False + ) + else: + assert ( + dist2 is None + ), "Cannot provide a second distribution when composing multiple functions" - out = dist1 - for f in fn: - out = ComplexDistribution(out, dist2, fn=f, fn_str=_get_fname(f, name), infix=False) + out = ComplexDistribution( + dist1, None, fn=fn_list[0], fn_str=_get_fname(fn_list[0], name), infix=False + ) + for f in fn_list[1:]: + out = ComplexDistribution(out, None, fn=f, fn_str=_get_fname(f, name), infix=False) - return out + return out -def dist_max(dist1, dist2=None): +# def _dist_fn( +# dist1: Union[OperableDistribution, Callable, list[Callable]], +# dist2: Optional[Union[OperableDistribution, Callable, list[Callable]]] = None, +# fn: Optional[UFunction] = None, +# name: Optional[str] = None, +# ) -> Union[ComplexDistribution, Callable]: +# """ +# Initialize a distribution that has a custom function applied to the result. + +# The function won't be applied until the distribution is sampled. + +# Parameters +# ---------- +# dist1 : Distribution or function or list +# Typically, the distribution to apply the function to. Could also be a function +# or list of functions if ``dist_fn`` is being used in a pipe. +# dist2 : Distribution or function or list or None +# Typically, the second distribution to apply the function to if the function takes +# two arguments. Could also be a function or list of functions if ``dist_fn`` is +# being used in a pipe. +# fn : function or None +# The function to apply to the distribution(s). +# name : str or None +# By default, ``fn.__name__`` will be used to name the function. But you can pass +# a custom name. + +# Returns +# ------- +# ComplexDistribution or function +# This will be a lazy evaluation of the desired function that will then be calculated +# when it is sampled. + +# Examples +# -------- +# >>> def double(x): +# >>> return x * 2 +# >>> dist_fn(norm(0, 1), double) +# double(norm(mean=0.5, sd=0.3)) +# >>> norm(0, 1) >> dist_fn(double) +# double(norm(mean=0.5, sd=0.3)) +# """ + +# p1 = dist1 if isinstance(dist1, list) else [dist1] +# assert (p1 is None) or all(callable(f) for f in p1) +# p2 = dist2 if isinstance(dist2, list) else [dist2] +# assert (p2 is None) or all(callable(f) for f in p2) +# p3 = fn if isinstance(fn, list) else [fn] +# assert (p3 is None) or all(callable(f) for f in p3) + + +# if (p1 is not None and p2 is None and p3 is None) and (not ): +# # This is a simple pipe, +# # we compose the functions and return a partial of the composition + +# def out_fn(d): +# out = d +# for fn in p1: +# out = ComplexDistribution( +# out, None, fn=fn, fn_str=_get_fname(fn, name), infix=False +# ) +# return out + +# return out_fn + + +# if callable(dist1) and dist2 is None and fn is None: +# return lambda d: dist_fn(d, fn=dist1) + +# # These are cases where we're just applying function(s) to a distribution +# if isinstance(dist2, list) and callable(dist2[0]) and fn is None: +# # The user provided the functions through dist2 +# assert all(callable(f) for f in dist2) +# fn_list = dist2 + +# if callable(dist2) and fn is None: +# fn_list = [dist2] + +# if fn is not None and callable(fn): +# fn_list = [fn] + +# out = dist1 +# for f in fn_list: +# out = ComplexDistribution(out, dist2, fn=f, fn_str=_get_fname(f, name), infix=False) + +# return out + + +def dist_max( + dist1: OperableDistribution, dist2: Optional[OperableDistribution] = None +) -> Union[Callable, ComplexDistribution]: """ Initialize the calculation of the maximum value of two distributions. @@ -277,11 +587,16 @@ def dist_max(dist1, dist2=None): """ if is_dist(dist1) and dist2 is None: return lambda d: dist_fn(d, dist1, np.maximum, name="max") - else: + elif dist2 is not None: return dist_fn(dist1, dist2, np.maximum, name="max") + else: + raise ValueError("Invalid arguments to dist_max") -def dist_min(dist1, dist2=None): +def dist_min( + dist1: OperableDistribution, + dist2: Optional[OperableDistribution] = None, +) -> Union[Callable, ComplexDistribution]: """ Initialize the calculation of the minimum value of two distributions. @@ -306,11 +621,15 @@ def dist_min(dist1, dist2=None): """ if is_dist(dist1) and dist2 is None: return lambda d: dist_fn(d, dist1, np.minimum, name="min") - else: + elif dist2 is not None: return dist_fn(dist1, dist2, np.minimum, name="min") + else: + raise ValueError("Invalid arguments to dist_min") -def dist_round(dist1, digits=0): +def dist_round( + dist: OperableDistribution, digits: int = 0 +) -> Union[Callable, ComplexDistribution]: """ Initialize the rounding of the output of the distribution. @@ -333,13 +652,15 @@ def dist_round(dist1, digits=0): >>> dist_round(norm(0, 1)) round(norm(mean=0.5, sd=0.3), 0) """ - if isinstance(dist1, int) and digits == 0: - return lambda d: dist_round(d, digits=dist1) + if isinstance(dist, int) and digits == 0: + return lambda d: dist_round(d, digits=dist) else: - return dist_fn(dist1, digits, _round, name="round") + return dist_fn(dist, partial(_round, digits=digits), name="round") -def dist_ceil(dist1): +def dist_ceil( + dist1: OperableDistribution, +) -> Union[ComplexDistribution, Callable]: """ Initialize the ceiling rounding of the output of the distribution. @@ -352,7 +673,7 @@ def dist_ceil(dist1): Returns ------- - ComplexDistribution or function + ComplexDistribution This will be a lazy evaluation of the desired function that will then be calculated Examples @@ -360,10 +681,10 @@ def dist_ceil(dist1): >>> dist_ceil(norm(0, 1)) ceil(norm(mean=0.5, sd=0.3)) """ - return dist_fn(dist1, None, np.ceil) + return dist_fn(dist1, np.ceil) -def dist_floor(dist1): +def dist_floor(dist1: OperableDistribution): """ Initialize the floor rounding of the output of the distribution. @@ -376,7 +697,7 @@ def dist_floor(dist1): Returns ------- - ComplexDistribution or function + ComplexDistribution This will be a lazy evaluation of the desired function that will then be calculated Examples @@ -384,10 +705,12 @@ def dist_floor(dist1): >>> dist_floor(norm(0, 1)) floor(norm(mean=0.5, sd=0.3)) """ - return dist_fn(dist1, None, np.floor) + return dist_fn(dist1, np.floor) -def dist_log(dist1, base=math.e): +def dist_log( + dist1: OperableDistribution, base: Number = math.e +) -> Union[ComplexDistribution, Callable]: """ Initialize the log of the output of the distribution. @@ -411,7 +734,7 @@ def dist_log(dist1, base=math.e): return dist_fn(dist1, const(base), math.log) -def dist_exp(dist1): +def dist_exp(dist1: OperableDistribution): """ Initialize the exp of the output of the distribution. @@ -424,7 +747,7 @@ def dist_exp(dist1): Returns ------- - ComplexDistribution or function + ComplexDistribution This will be a lazy evaluation of the desired function that will then be calculated Examples @@ -432,18 +755,21 @@ def dist_exp(dist1): >>> dist_exp(norm(0, 1)) exp(norm(mean=0.5, sd=0.3)) """ - return dist_fn(dist1, None, math.exp) + return dist_fn(dist1, math.exp) @np.vectorize -def _lclip(n, val=None): +def _lclip(n: Number, val: Number) -> Number: if val is None: return n else: return val if n < val else n -def lclip(dist1, val=None): +def lclip( + dist1: Union[OperableDistribution, Callable], + val: Optional[Number] = None, +) -> Union[ComplexDistribution, Callable]: """ Initialize the clipping/bounding of the output of the distribution by the lower value. @@ -469,21 +795,24 @@ def lclip(dist1, val=None): """ if (isinstance(dist1, int) or isinstance(dist1, float)) and val is None: return lambda d: lclip(d, dist1) - elif is_dist(dist1): - return dist_fn(dist1, val, _lclip, name="lclip") + elif isinstance(dist1, OperableDistribution): + return dist_fn(dist1, partial(_lclip, val), name="lclip") else: return _lclip(dist1, val) @np.vectorize -def _rclip(n, val=None): +def _rclip(n: Number, val: Number) -> Number: if val is None: return n else: return val if n > val else n -def rclip(dist1, val=None): +def rclip( + dist1: Union[OperableDistribution, Callable], + val: Optional[Number] = None, +) -> Union[ComplexDistribution, Callable]: """ Initialize the clipping/bounding of the output of the distribution by the upper value. @@ -509,13 +838,17 @@ def rclip(dist1, val=None): """ if (isinstance(dist1, int) or isinstance(dist1, float)) and val is None: return lambda d: rclip(d, dist1) - elif is_dist(dist1): - return dist_fn(dist1, val, _rclip, name="rclip") + elif isinstance(dist1, OperableDistribution): + return dist_fn(dist1, partial(_rclip, val), name="rclip") else: return _rclip(dist1, val) -def clip(dist1, left, right=None): +def clip( + dist1: Union[OperableDistribution, Callable], + left: Optional[Number], + right: Optional[Number] = None, +) -> Union[ComplexDistribution, Callable]: """ Initialize the clipping/bounding of the output of the distribution. @@ -552,15 +885,15 @@ def clip(dist1, left, right=None): class ConstantDistribution(OperableDistribution): - def __init__(self, x): + def __init__(self, x: Any) -> None: super().__init__() self.x = x - def __str__(self): + def __str__(self) -> str: return " const({})".format(self.x) -def const(x): +def const(x: Any) -> ConstantDistribution: """ Initialize a constant distribution. @@ -584,16 +917,16 @@ def const(x): class UniformDistribution(OperableDistribution): - def __init__(self, x, y): + def __init__(self, x: Number, y: Number) -> None: super().__init__() self.x = x self.y = y - def __str__(self): + def __str__(self) -> str: return " uniform({}, {})".format(self.x, self.y) -def uniform(x, y): +def uniform(x: Number, y: Number) -> UniformDistribution: """ Initialize a uniform random distribution. @@ -616,7 +949,6 @@ def uniform(x, y): return UniformDistribution(x=x, y=y) - class NormalDistribution(OperableDistribution): def __init__( self, @@ -627,7 +959,7 @@ def __init__( credibility: Number = 90, lclip: Optional[Number] = None, rclip: Optional[Number] = None, - ): + ) -> None: super().__init__() self.credibility = credibility self.lclip = lclip @@ -645,8 +977,8 @@ def __init__( raise ValueError("`high value` (y) cannot be lower than `low value` (x)") self.x, self.y = x, y self.mean = (self.x + self.y) / 2 - cdf_value: float | np.float64 = 0.5 + 0.5 * (self.credibility / 100) - normed_sigma: np.float64 = stats.norm.ppf(cdf_value) # type: ignore + cdf_value: Float = 0.5 + 0.5 * (self.credibility / 100) + normed_sigma: np.float64 = stats.norm.ppf(cdf_value) # type: ignore assert self.mean is not None self.sd = (self.y - self.mean) / normed_sigma elif sd is not None and x is None and y is None: @@ -657,7 +989,7 @@ def __init__( else: raise ValueError("you must define either x/y or mean/sd") - def __str__(self): + def __str__(self) -> str: assert self.mean is not None and self.sd is not None out = " norm(mean={}, sd={}".format(round(self.mean, 2), round(self.sd, 2)) if self.lclip is not None: @@ -668,7 +1000,15 @@ def __str__(self): return out -def norm(x=None, y=None, credibility=90, mean=None, sd=None, lclip=None, rclip=None): +def norm( + x: Optional[Number] = None, + y: Optional[Number] = None, + credibility: Integer = 90, + mean: Optional[Number] = None, + sd: Optional[Number] = None, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, +) -> NormalDistribution: """ Initialize a normal distribution. @@ -712,20 +1052,20 @@ def norm(x=None, y=None, credibility=90, mean=None, sd=None, lclip=None, rclip=N class LognormalDistribution(OperableDistribution): def __init__( self, - x: Optional[Number]=None, - y: Optional[Number]=None, - norm_mean: Optional[Number]=None, - norm_sd: Optional[Number]=None, - lognorm_mean: Optional[Number]=None, - lognorm_sd: Optional[Number]=None, - credibility: int=90, - lclip: Optional[Number]=None, - rclip: Optional[Number]=None, - ): + x: Optional[Number] = None, + y: Optional[Number] = None, + norm_mean: Optional[Number] = None, + norm_sd: Optional[Number] = None, + lognorm_mean: Optional[Number] = None, + lognorm_sd: Optional[Number] = None, + credibility: Integer = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, + ) -> None: super().__init__() self.x: Optional[Number] = x self.y: Optional[Number] = y - self.credibility: int = credibility + self.credibility: Integer = credibility self.norm_mean: Optional[Number] = norm_mean self.norm_sd: Optional[Number] = norm_sd self.lognorm_mean: Optional[Number] = lognorm_mean @@ -778,7 +1118,7 @@ def __init__( ) self.norm_sd = np.sqrt(np.log(1 + self.lognorm_sd**2 / self.lognorm_mean**2)) - def __str__(self): + def __str__(self) -> str: assert self.lognorm_mean is not None and self.lognorm_sd is not None assert self.norm_mean is not None and self.norm_sd is not None out = " lognorm(lognorm_mean={}, lognorm_sd={}, norm_mean={}, norm_sd={}" @@ -797,16 +1137,16 @@ def __str__(self): def lognorm( - x=None, - y=None, - credibility=90, - norm_mean=None, - norm_sd=None, - lognorm_mean=None, - lognorm_sd=None, - lclip=None, - rclip=None, -): + x: Optional[Number] = None, + y: Optional[Number] = None, + credibility: Integer = 90, + norm_mean: Optional[Number] = None, + norm_sd: Optional[Number] = None, + lognorm_mean: Optional[Number] = None, + lognorm_sd: Optional[Number] = None, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, +) -> LognormalDistribution: """ Initialize a lognormal distribution. @@ -863,7 +1203,13 @@ def lognorm( ) -def to(x, y, credibility=90, lclip=None, rclip=None): +def to( + x: Number, + y: Number, + credibility: Integer = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, +) -> Union[LognormalDistribution, NormalDistribution]: """ Initialize a distribution from ``x`` to ``y``. @@ -904,18 +1250,18 @@ def to(x, y, credibility=90, lclip=None, rclip=None): class BinomialDistribution(OperableDistribution): - def __init__(self, n, p): + def __init__(self, n: int, p: Number) -> None: super().__init__() self.n = n self.p = p if self.p < 0 or self.p > 1: raise ValueError("p must be between 0 and 1") - def __str__(self): + def __str__(self) -> str: return " binomial(n={}, p={})".format(self.n, self.p) -def binomial(n, p): +def binomial(n: int, p: Number) -> BinomialDistribution: """ Initialize a binomial distribution. @@ -939,16 +1285,16 @@ def binomial(n, p): class BetaDistribution(OperableDistribution): - def __init__(self, a: Number, b: Number): + def __init__(self, a: Number, b: Number) -> None: super().__init__() self.a = a self.b = b - def __str__(self): + def __str__(self) -> str: return " beta(a={}, b={})".format(self.a, self.b) -def beta(a, b): +def beta(a: Number, b: Number) -> BetaDistribution: """ Initialize a beta distribution. @@ -974,7 +1320,7 @@ def beta(a, b): class BernoulliDistribution(OperableDistribution): - def __init__(self, p): + def __init__(self, p: Number) -> None: super().__init__() if not isinstance(p, float) or isinstance(p, int): raise ValueError("bernoulli p must be a float or int") @@ -982,11 +1328,11 @@ def __init__(self, p): raise ValueError("bernoulli p must be 0-1") self.p = p - def __str__(self): + def __str__(self) -> str: return " bernoulli(p={})".format(self.p) -def bernoulli(p): +def bernoulli(p: Number) -> BernoulliDistribution: """ Initialize a Bernoulli distribution. @@ -1008,17 +1354,17 @@ def bernoulli(p): class DiscreteDistribution(OperableDistribution): - def __init__(self, items): + def __init__(self, items: Any) -> None: super().__init__() if not isinstance(items, dict) and not isinstance(items, list) and not _is_numpy(items): raise ValueError("inputs to discrete must be a dict or list") self.items = list(items) if _is_numpy(items) else items - def __str__(self): + def __str__(self) -> str: return " discrete({})".format(self.items) -def discrete(items): +def discrete(items: Union[dict[Any, Number], list[list[Number]]]) -> DiscreteDistribution: """ Initialize a discrete distribution (aka categorical distribution). @@ -1047,7 +1393,15 @@ def discrete(items): class TDistribution(OperableDistribution): - def __init__(self, x=None, y=None, t=20, credibility=90, lclip=None, rclip=None): + def __init__( + self, + x: Optional[Number] = None, + y: Optional[Number] = None, + t: Integer = 20, + credibility: Integer = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, + ) -> None: super().__init__() self.x = x self.y = y @@ -1065,7 +1419,7 @@ def __init__(self, x=None, y=None, t=20, credibility=90, lclip=None, rclip=None) if self.x is None: self.credibility = None - def __str__(self): + def __str__(self) -> str: if self.x is not None: out = " tdist(x={}, y={}, t={}".format(self.x, self.y, self.t) else: @@ -1080,7 +1434,14 @@ def __str__(self): return out -def tdist(x=None, y=None, t=20, credibility=90, lclip=None, rclip=None): +def tdist( + x: Optional[Number] = None, + y: Optional[Number] = None, + t: Integer = 20, + credibility: Integer = 90, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, +) -> TDistribution: """ Initialize a t-distribution. @@ -1097,7 +1458,7 @@ def tdist(x=None, y=None, t=20, credibility=90, lclip=None, rclip=None): The low value of a credible interval defined by ``credibility``. Defaults to a 90% CI. y : float or None The high value of a credible interval defined by ``credibility``. Defaults to a 90% CI. - t : float + t : integer The number of degrees of freedom of the t-distribution. Defaults to 20. credibility : float The range of the credibility interval. Defaults to 90. @@ -1121,7 +1482,15 @@ def tdist(x=None, y=None, t=20, credibility=90, lclip=None, rclip=None): class LogTDistribution(OperableDistribution): - def __init__(self, x=None, y=None, t=1, credibility=90, lclip=None, rclip=None): + def __init__( + self, + x: Optional[Integer] = None, + y: Optional[Integer] = None, + t: int = 1, + credibility: int = 90, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, + ) -> None: super().__init__() self.x = x self.y = y @@ -1139,7 +1508,7 @@ def __init__(self, x=None, y=None, t=1, credibility=90, lclip=None, rclip=None): if self.x is None: self.credibility = None - def __str__(self): + def __str__(self) -> str: if self.x is not None: out = " log_tdist(x={}, y={}, t={}".format(self.x, self.y, self.t) else: @@ -1154,7 +1523,14 @@ def __str__(self): return out -def log_tdist(x=None, y=None, t=1, credibility=90, lclip=None, rclip=None): +def log_tdist( + x: Optional[Integer] = None, + y: Optional[Integer] = None, + t: int = 1, + credibility: int = 90, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, +) -> LogTDistribution: """ Initialize a log t-distribution, which is a t-distribution in log-space. @@ -1195,7 +1571,14 @@ def log_tdist(x=None, y=None, t=1, credibility=90, lclip=None, rclip=None): class TriangularDistribution(OperableDistribution): - def __init__(self, left, mode, right, lclip=None, rclip=None): + def __init__( + self, + left: int, + mode: int, + right: int, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, + ) -> None: super().__init__() self.left = left self.mode = mode @@ -1203,7 +1586,7 @@ def __init__(self, left, mode, right, lclip=None, rclip=None): self.lclip = lclip self.rclip = rclip - def __str__(self): + def __str__(self) -> str: out = " triangular({}, {}, {}".format(self.left, self.mode, self.right) if self.lclip is not None: out += ", lclip={}".format(self.lclip) @@ -1213,7 +1596,13 @@ def __str__(self): return out -def triangular(left, mode, right, lclip=None, rclip=None): +def triangular( + left: int, + mode: int, + right: int, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, +) -> TriangularDistribution: """ Initialize a triangular distribution. @@ -1243,13 +1632,15 @@ def triangular(left, mode, right, lclip=None, rclip=None): class PoissonDistribution(OperableDistribution): - def __init__(self, lam, lclip=None, rclip=None): + def __init__( + self, lam: Number, lclip: Optional[Integer] = None, rclip: Optional[Integer] = None + ) -> None: super().__init__() self.lam = lam self.lclip = lclip self.rclip = rclip - def __str__(self): + def __str__(self) -> str: out = " poisson({}".format(self.lam) if self.lclip is not None: out += ", lclip={}".format(self.lclip) @@ -1259,7 +1650,9 @@ def __str__(self): return out -def poisson(lam, lclip=None, rclip=None): +def poisson( + lam: Number, lclip: Optional[Integer] = None, rclip: Optional[Integer] = None +) -> PoissonDistribution: """ Initialize a poisson distribution. @@ -1285,17 +1678,17 @@ def poisson(lam, lclip=None, rclip=None): class ChiSquareDistribution(OperableDistribution): - def __init__(self, df): + def __init__(self, df: int) -> None: super().__init__() self.df = df if self.df <= 0: raise ValueError("df must be positive") - def __str__(self): + def __str__(self) -> str: return " chisquare({})".format(self.df) -def chisquare(df): +def chisquare(df: int) -> ChiSquareDistribution: """ Initialize a chi-square distribution. @@ -1317,13 +1710,15 @@ def chisquare(df): class ExponentialDistribution(OperableDistribution): - def __init__(self, scale, lclip=None, rclip=None): + def __init__( + self, scale: int, lclip: Optional[Integer] = None, rclip: Optional[Integer] = None + ) -> None: super().__init__() self.scale = scale self.lclip = lclip self.rclip = rclip - def __str__(self): + def __str__(self) -> str: out = " exponential({}".format(self.scale) if self.lclip is not None: out += ", lclip={}".format(self.lclip) @@ -1333,7 +1728,9 @@ def __str__(self): return out -def exponential(scale, lclip=None, rclip=None): +def exponential( + scale: int, lclip: Optional[Integer] = None, rclip: Optional[Integer] = None +) -> ExponentialDistribution: """ Initialize an exponential distribution. @@ -1359,14 +1756,20 @@ def exponential(scale, lclip=None, rclip=None): class GammaDistribution(OperableDistribution): - def __init__(self, shape, scale=1, lclip=None, rclip=None): + def __init__( + self, + shape: int, + scale: int = 1, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, + ) -> None: super().__init__() self.shape = shape self.scale = scale self.lclip = lclip self.rclip = rclip - def __str__(self): + def __str__(self) -> str: out = " gamma(shape={}, scale={}".format(self.shape, self.scale) if self.lclip is not None: out += ", lclip={}".format(self.lclip) @@ -1376,7 +1779,9 @@ def __str__(self): return out -def gamma(shape, scale=1, lclip=None, rclip=None): +def gamma( + shape: int, scale: int = 1, lclip: Optional[Integer] = None, rclip: Optional[Integer] = None +) -> GammaDistribution: """ Initialize a gamma distribution. @@ -1404,15 +1809,15 @@ def gamma(shape, scale=1, lclip=None, rclip=None): class ParetoDistribution(OperableDistribution): - def __init__(self, shape): + def __init__(self, shape: int) -> None: super().__init__() self.shape = shape - def __str__(self): + def __str__(self) -> str: return " pareto({})".format(self.shape) -def pareto(shape): +def pareto(shape: int) -> ParetoDistribution: """ Initialize a pareto distribution. @@ -1434,7 +1839,14 @@ def pareto(shape): class MixtureDistribution(OperableDistribution): - def __init__(self, dists, weights=None, relative_weights=None, lclip=None, rclip=None): + def __init__( + self, + dists: Any, + weights: Optional[Weights] = None, + relative_weights: Optional[Weights] = None, + lclip: Optional[Number] = None, + rclip: Optional[Number] = None, + ) -> None: super().__init__() weights, dists = _process_weights_values(weights, relative_weights, dists) self.dists = dists @@ -1442,14 +1854,21 @@ def __init__(self, dists, weights=None, relative_weights=None, lclip=None, rclip self.lclip = lclip self.rclip = rclip - def __str__(self): + def __str__(self) -> str: out = " mixture" + assert self.weights is not None for i in range(len(self.dists)): out += "\n - {} weight on {}".format(self.weights[i], self.dists[i]) return out -def mixture(dists, weights=None, relative_weights=None, lclip=None, rclip=None): +def mixture( + dists: Any, + weights: Optional[Weights] = None, + relative_weights: Optional[Weights] = None, + lclip: Optional[Integer] = None, + rclip: Optional[Integer] = None, +) -> MixtureDistribution: """ Initialize a mixture distribution, which is a combination of different distributions. @@ -1496,7 +1915,7 @@ def mixture(dists, weights=None, relative_weights=None, lclip=None, rclip=None): ) -def zero_inflated(p_zero, dist): +def zero_inflated(p_zero: float, dist: NormalDistribution) -> MixtureDistribution: """ Initialize an arbitrary zero-inflated distribution. @@ -1520,10 +1939,10 @@ def zero_inflated(p_zero, dist): """ if p_zero > 1 or p_zero < 0 or not isinstance(p_zero, float): raise ValueError("`p_zero` must be between 0 and 1") - return MixtureDistribution(dists=[0, dist], weights=p_zero) + return MixtureDistribution(dists=[0, dist], weights=[p_zero]) -def inf0(p_zero, dist): +def inf0(p_zero: float, dist: NormalDistribution) -> MixtureDistribution: """ Initialize an arbitrary zero-inflated distribution. diff --git a/squigglepy/samplers.py b/squigglepy/samplers.py index 4bb40a2..ff41ea7 100644 --- a/squigglepy/samplers.py +++ b/squigglepy/samplers.py @@ -1,5 +1,6 @@ import os import time +from typing import Optional import numpy as np import pathos.multiprocessing as mp @@ -7,6 +8,8 @@ from scipy import stats from .utils import ( + Integer, + Number, _process_weights_values, _process_discrete_weights_values, is_dist, @@ -21,6 +24,7 @@ ) from .distributions import ( + BaseDistribution, BernoulliDistribution, BetaDistribution, BinomialDistribution, @@ -107,7 +111,7 @@ def lognormal_sample(mean, sd, samples=1): return _simplify(_get_rng().lognormal(mean, sd, samples)) -def t_sample(low=None, high=None, t=20, samples=1, credibility=90): +def t_sample(low=None, high=None, t: Integer = 20, samples=1, credibility=90): """ Sample a random number according to a t-distribution. @@ -124,7 +128,7 @@ def t_sample(low=None, high=None, t=20, samples=1, credibility=90): The low value of a credible interval defined by ``credibility``. Defaults to a 90% CI. high : float or None The high value of a credible interval defined by ``credibility``. Defaults to a 90% CI. - t : float + t : integer The number of degrees of freedom of the t-distribution. Defaults to 20. samples : int The number of samples to return. @@ -692,7 +696,7 @@ def mixture_sample( def sample( - dist=None, + dist: Optional[BaseDistribution | Number | str | bool] = None, n=1, lclip=None, rclip=None, diff --git a/squigglepy/utils.py b/squigglepy/utils.py index d282440..248a52c 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -7,18 +7,18 @@ from datetime import datetime from collections import Counter from collections.abc import Iterable -from typing import Union +from typing import Optional, Union, overload from numpy.typing import NDArray import importlib import importlib.util import sys - -Number = Union[int, float, np.floating, np.integer] -OptionalListOfFloats = Union[ - list[Union[Union[float, np.floating]]], NDArray[np.floating], float, None -] +Float = Union[float, np.floating] +Integer = Union[int, np.integer] +Number = Union[Float, Integer] +FloatArray = Union[list[Float], NDArray[np.floating]] +Weights = Union[list[Float], NDArray[np.floating], np.floating, float] def _check_pandas_series(values): @@ -30,7 +30,12 @@ def _check_pandas_series(values): return isinstance(values, pd.core.series.Series) -def _process_weights_values(weights=None, relative_weights=None, values=None, drop_na=False): +def _process_weights_values( + weights: Optional[Weights] = None, + relative_weights: Optional[Weights] = None, + values=None, + drop_na: bool = False, +) -> tuple[list[Float], list[list]]: if weights is not None and relative_weights is not None: raise ValueError("can only pass either `weights` or `relative_weights`, not both.") if values is None or _safe_len(values) == 0: @@ -41,7 +46,7 @@ def _process_weights_values(weights=None, relative_weights=None, values=None, dr weights = relative_weights relative = True - if isinstance(weights, float): + if isinstance(weights, float) or isinstance(weights, np.floating): weights = [weights] elif isinstance(weights, np.ndarray): weights = list(weights) @@ -77,7 +82,7 @@ def _process_weights_values(weights=None, relative_weights=None, values=None, dr if any([_is_na_like(w) for w in weights]): raise ValueError("cannot handle NA-like values in weights") - sum_weights = sum(weights) + sum_weights = sum(weights) # type: ignore if relative: weights = normalize(weights) @@ -129,7 +134,22 @@ def _is_na_like(a): return a is None or np.isnan(a) -def _round(x, digits=0): +@overload +def _round(x: NDArray[np.floating], digits: Optional[int] = 0) -> NDArray[np.floating]: + ... + + +@overload +def _round(x: NDArray[np.integer], digits: Optional[int] = 0) -> NDArray[np.integer]: + ... + + +@overload +def _round(x: Number, digits: Optional[int] = 0) -> Number: + ... + + +def _round(x: Union[Number, np.ndarray], digits: Optional[int] = 0) -> Union[Number, np.ndarray]: if digits is None: return x @@ -395,9 +415,9 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( data: Union[list[np.floating], NDArray[np.floating]], - percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + percentiles: list[Integer] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], reverse: bool = False, - digits: int = None, + digits: Optional[int] = None, ) -> dict[str, float]: """ Print the percentiles of the data. @@ -863,7 +883,14 @@ def flip_coin(n=1): return flips[0] if len(flips) == 1 else flips -def kelly(my_price, market_price, deference=0, bankroll=1, resolve_date=None, current=0): +def kelly( + my_price: Number, + market_price: Number, + deference: Number = 0, + bankroll: Number = 1, + resolve_date: Optional[str] = None, + current: Number = 0, +): """ Calculate the Kelly criterion. diff --git a/tests/test_distributions.py b/tests/test_distributions.py index 2059d65..fe898c3 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -118,7 +118,7 @@ def test_norm(): assert norm(1, 2).x == 1 assert norm(1, 2).y == 2 assert norm(1, 2).mean == 1.5 - assert round(norm(1, 2).sd, 2) == 0.3 + assert round(norm(1, 2).sd, 2) == 0.3 # type: ignore assert norm(1, 2).credibility == 90 assert norm(1, 2).lclip is None assert norm(1, 2).rclip is None @@ -210,10 +210,10 @@ def test_lognorm(): assert isinstance(lognorm(1, 2), LognormalDistribution) assert lognorm(1, 2).x == 1 assert lognorm(1, 2).y == 2 - assert round(lognorm(1, 2).norm_mean, 2) == 0.35 - assert round(lognorm(1, 2).norm_sd, 2) == 0.21 - assert round(lognorm(1, 2).lognorm_mean, 2) == 1.45 - assert round(lognorm(1, 2).lognorm_sd, 2) == 0.31 + assert round(lognorm(1, 2).norm_mean, 2) == 0.35 # type: ignore + assert round(lognorm(1, 2).norm_sd, 2) == 0.21 # type: ignore + assert round(lognorm(1, 2).lognorm_mean, 2) == 1.45 # type: ignore + assert round(lognorm(1, 2).lognorm_sd, 2) == 0.31 # type: ignore assert lognorm(1, 2).credibility == 90 assert lognorm(1, 2).lclip is None assert lognorm(1, 2).rclip is None @@ -1089,13 +1089,13 @@ def test_min(): def test_round(): obj = dist_round(norm(0, 1)) assert isinstance(obj, ComplexDistribution) - assert str(obj) == " round(norm(mean=0.5, sd=0.3), 0)" + assert str(obj) == " round(norm(mean=0.5, sd=0.3), digits=0)" def test_round_two_digits(): obj = dist_round(norm(0, 1), digits=2) assert isinstance(obj, ComplexDistribution) - assert str(obj) == " round(norm(mean=0.5, sd=0.3), 2)" + assert str(obj) == " round(norm(mean=0.5, sd=0.3), digits=2)" def test_ceil(): @@ -1198,10 +1198,10 @@ def test_min_pipe(): def test_round_pipe(): obj = norm(0, 1) >> dist_round assert isinstance(obj, ComplexDistribution) - assert str(obj) == " round(norm(mean=0.5, sd=0.3), 0)" + assert str(obj) == " round(norm(mean=0.5, sd=0.3), digits=0)" obj = norm(0, 1) >> dist_round(2) assert isinstance(obj, ComplexDistribution) - assert str(obj) == " round(norm(mean=0.5, sd=0.3), 2)" + assert str(obj) == " round(norm(mean=0.5, sd=0.3), digits=2)" def test_floor_pipe(): @@ -1219,7 +1219,7 @@ def test_ceil_pipe(): def test_two_pipes(): obj = norm(0, 1) >> lclip(2) >> dist_round assert isinstance(obj, ComplexDistribution) - assert str(obj) == " round(lclip(norm(mean=0.5, sd=0.3), 2), 0)" + assert str(obj) == " round(lclip(norm(mean=0.5, sd=0.3), 2), digits=0)" def test_dist_fn_list_pipe(): From 1b55b9b9a805d5d108f1eb5a461f0f867581aabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Mon, 28 Aug 2023 19:12:37 -0400 Subject: [PATCH 33/35] fix: remove dependency on Self --- squigglepy/distributions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index a995991..c8018a1 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -4,7 +4,7 @@ import math import operator -from typing import Any, Callable, Optional, Self, TypeVar, Union +from typing import Any, Callable, Optional, TypeVar, Union import numpy as np from scipy import stats @@ -58,7 +58,7 @@ def __matmul__(self, n: int): return sample(self, n=n) def __rshift__( - self, fn: Union[Callable[[Self], ComplexDistribution], ComplexDistribution] + self, fn: Union[Callable[[OperableDistribution], ComplexDistribution], ComplexDistribution] ) -> Union[ComplexDistribution, int]: if callable(fn): return fn(self) From 4a272899aa189fde2df462bfa4866a295c7f8586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Tue, 29 Aug 2023 16:56:54 -0400 Subject: [PATCH 34/35] fix: more manual type fixes --- squigglepy/distributions.py | 47 +++++++++++++++++++++---------------- squigglepy/samplers.py | 17 +++++++------- squigglepy/utils.py | 22 ++++++++++------- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index c8018a1..502f15b 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -4,12 +4,14 @@ import math import operator -from typing import Any, Callable, Optional, TypeVar, Union +from typing import Any, Callable, Optional, TypeAlias, TypeVar, Union import numpy as np from scipy import stats +from numpy.typing import NDArray from .utils import ( + FloatArray, Integer, Weights, _is_numpy, @@ -39,6 +41,9 @@ def __repr__(self) -> str: return str(self) +OperationTarget: TypeAlias = Union[BaseDistribution, Number] + + class OperableDistribution(BaseDistribution): def __init__(self) -> None: super().__init__() @@ -70,61 +75,61 @@ def __rshift__( def __rmatmul__(self, n: int): return self.__matmul__(n) - def __gt__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __gt__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.gt, ">") - def __ge__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __ge__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.ge, ">=") - def __lt__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __lt__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.lt, "<") - def __le__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __le__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "<=") - def __eq__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __eq__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "==") - def __ne__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __ne__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.le, "!=") def __neg__(self) -> "ComplexDistribution": return ComplexDistribution(self, None, operator.neg, "-") - def __add__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __add__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.add, "+") - def __radd__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __radd__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.add, "+") - def __sub__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __sub__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.sub, "-") - def __rsub__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __rsub__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.sub, "-") - def __mul__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __mul__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.mul, "*") - def __rmul__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __rmul__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.mul, "*") - def __truediv__(self, dist: BaseDistribution) -> "ComplexDistribution": + def __truediv__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.truediv, "/") - def __rtruediv__(self, dist: Number) -> "ComplexDistribution": + def __rtruediv__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.truediv, "/") - def __floordiv__(self, dist: int) -> "ComplexDistribution": + def __floordiv__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.floordiv, "//") - def __rfloordiv__(self, dist: int) -> "ComplexDistribution": + def __rfloordiv__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.floordiv, "//") - def __pow__(self, dist: int) -> "ComplexDistribution": + def __pow__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(self, dist, operator.pow, "**") - def __rpow__(self, dist: int) -> "ComplexDistribution": + def __rpow__(self, dist: OperationTarget) -> "ComplexDistribution": return ComplexDistribution(dist, self, operator.pow, "**") def plot(self, num_samples: int = 1000, bins: int = 200) -> None: @@ -1364,7 +1369,9 @@ def __str__(self) -> str: return " discrete({})".format(self.items) -def discrete(items: Union[dict[Any, Number], list[list[Number]]]) -> DiscreteDistribution: +def discrete( + items: Union[dict[Any, Number], list[list[Number]], FloatArray] +) -> DiscreteDistribution: """ Initialize a discrete distribution (aka categorical distribution). diff --git a/squigglepy/samplers.py b/squigglepy/samplers.py index ff41ea7..9ee53d1 100644 --- a/squigglepy/samplers.py +++ b/squigglepy/samplers.py @@ -1,6 +1,6 @@ import os import time -from typing import Optional +from typing import Optional, Union import numpy as np import pathos.multiprocessing as mp @@ -10,6 +10,7 @@ from .utils import ( Integer, Number, + Weights, _process_weights_values, _process_discrete_weights_values, is_dist, @@ -621,13 +622,13 @@ def _run_mixture(values, weights, pbar=None, tick=1): def mixture_sample( - values, - weights=None, - relative_weights=None, - samples=1, - verbose=False, - _multicore_tqdm_n=1, - _multicore_tqdm_cores=1, + values: Union[dict, list], + weights: Optional[Weights] = None, + relative_weights: Optional[Weights] = None, + samples: int = 1, + verbose: bool = False, + _multicore_tqdm_n: int = 1, + _multicore_tqdm_cores: int = 1, ): """ Sample a ranom number from a mixture distribution. diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 248a52c..948c929 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -415,10 +415,10 @@ def one_in(p, digits=0, verbose=True): def get_percentiles( data: Union[list[np.floating], NDArray[np.floating]], - percentiles: list[Integer] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], + percentiles: list[Number] = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99], reverse: bool = False, digits: Optional[int] = None, -) -> dict[str, float]: +) -> dict[Number, Number]: """ Print the percentiles of the data. @@ -446,9 +446,9 @@ def get_percentiles( """ percentiles = percentiles if isinstance(percentiles, list) else [percentiles] percentile_labels = list(reversed(percentiles)) if reverse else percentiles - percentiles = np.percentile(data, percentiles) - percentiles = [_round(p, digits) for p in percentiles] - return dict(list(zip(percentile_labels, percentiles))) + computed_percentiles: NDArray[np.float64] = np.percentile(data, percentiles) # type: ignore + rounded_percentiles: list[Number] = [_round(p, digits) for p in computed_percentiles] + return dict(list(zip(percentile_labels, rounded_percentiles, strict=True))) def get_log_percentiles( @@ -457,7 +457,7 @@ def get_log_percentiles( reverse: bool = False, display: bool = True, digits: int = 1, -) -> dict[str, float]: +): """ Print the log (base 10) of the percentiles of the data. @@ -486,11 +486,15 @@ def get_log_percentiles( >>> get_percentiles(range(100), percentiles=[25, 50, 75]) {25: 24.75, 50: 49.5, 75: 74.25} """ - percentiles = get_percentiles(data, percentiles=percentiles, reverse=reverse, digits=digits) + computed_percentiles = get_percentiles( + data, percentiles=percentiles, reverse=reverse, digits=digits + ) if display: - return dict([(k, ("{:." + str(digits) + "e}").format(v)) for k, v in percentiles.items()]) + return dict( + [(k, ("{:." + str(digits) + "e}").format(v)) for k, v in computed_percentiles.items()] + ) else: - return dict([(k, _round(np.log10(v), digits)) for k, v in percentiles.items()]) + return dict([(k, _round(np.log10(v), digits)) for k, v in computed_percentiles.items()]) def get_mean_and_ci(data, credibility=90, digits=None): From 1f4072c2b73682b423b58b03d2112add61d8c177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Covarrubias?= Date: Tue, 29 Aug 2023 18:13:19 -0400 Subject: [PATCH 35/35] fix: simplify and other dynamic types --- squigglepy/distributions.py | 1 - squigglepy/rng.py | 3 +- squigglepy/samplers.py | 202 ++++++++++++++++++++---------------- squigglepy/utils.py | 34 ++++-- 4 files changed, 139 insertions(+), 101 deletions(-) diff --git a/squigglepy/distributions.py b/squigglepy/distributions.py index 502f15b..7846467 100644 --- a/squigglepy/distributions.py +++ b/squigglepy/distributions.py @@ -8,7 +8,6 @@ import numpy as np from scipy import stats -from numpy.typing import NDArray from .utils import ( FloatArray, diff --git a/squigglepy/rng.py b/squigglepy/rng.py index 2a6737a..82356a4 100644 --- a/squigglepy/rng.py +++ b/squigglepy/rng.py @@ -1,9 +1,10 @@ import numpy as np +from numpy.random import Generator _squigglepy_internal_rng = np.random.default_rng() -def set_seed(seed): +def set_seed(seed: int) -> Generator: """ Set the seed of the random number generator used by Squigglepy. diff --git a/squigglepy/samplers.py b/squigglepy/samplers.py index 9ee53d1..133b7a1 100644 --- a/squigglepy/samplers.py +++ b/squigglepy/samplers.py @@ -1,29 +1,14 @@ import os import time -from typing import Optional, Union +from typing import Any, List, Optional, Tuple, TypeAlias, Union import numpy as np import pathos.multiprocessing as mp - +from numpy import float64 +from numpy.typing import NDArray +from numpy.random import Generator from scipy import stats -from .utils import ( - Integer, - Number, - Weights, - _process_weights_values, - _process_discrete_weights_values, - is_dist, - is_sampleable, - _simplify, - _enlist, - _safe_len, - _core_cuts, - _init_tqdm, - _tick_tqdm, - _flush_tqdm, -) - from .distributions import ( BaseDistribution, BernoulliDistribution, @@ -35,8 +20,8 @@ DiscreteDistribution, ExponentialDistribution, GammaDistribution, - LogTDistribution, LognormalDistribution, + LogTDistribution, MixtureDistribution, NormalDistribution, ParetoDistribution, @@ -46,17 +31,40 @@ UniformDistribution, const, ) +from .utils import ( + Float, + Integer, + Number, + Weights, + _core_cuts, + _enlist, + _flush_tqdm, + _init_tqdm, + _process_discrete_weights_values, + _process_weights_values, + _safe_len, + _simplify_numpy, + _simplify_list, + _tick_tqdm, + is_dist, + is_sampleable, +) _squigglepy_internal_sample_caches = {} -def _get_rng(): +def _get_rng() -> Generator: from .rng import _squigglepy_internal_rng return _squigglepy_internal_rng -def normal_sample(mean, sd, samples=1): +SampleFloatOutput: TypeAlias = NDArray[np.float64] | List[np.float64] | np.float64 +SampleIntOutput: TypeAlias = NDArray[np.integer] | List[np.integer] | np.integer +SampleNumberOutput: TypeAlias = SampleFloatOutput | SampleIntOutput + + +def normal_sample(mean: Number, sd: Number, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a normal distribution. @@ -81,10 +89,10 @@ def normal_sample(mean, sd, samples=1): >>> normal_sample(0, 1) 0.30471707975443135 """ - return _simplify(_get_rng().normal(mean, sd, samples)) + return _simplify_numpy(_get_rng().normal(mean, sd, samples)) -def lognormal_sample(mean, sd, samples=1): +def lognormal_sample(mean: Number, sd: Number, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a lognormal distribution. @@ -109,10 +117,16 @@ def lognormal_sample(mean, sd, samples=1): >>> lognormal_sample(0, 1) 1.3562412406168636 """ - return _simplify(_get_rng().lognormal(mean, sd, samples)) + return _simplify_numpy(_get_rng().lognormal(mean, sd, samples)) -def t_sample(low=None, high=None, t: Integer = 20, samples=1, credibility=90): +def t_sample( + low: Optional[Number] = None, + high: Optional[Number] = None, + t: Integer = 20, + samples: int = 1, + credibility: int = 90, +) -> SampleFloatOutput: """ Sample a random number according to a t-distribution. @@ -161,12 +175,18 @@ def t_sample(low=None, high=None, t: Integer = 20, samples=1, credibility=90): cdf_value = 0.5 + 0.5 * (credibility / 100) normed_sigma = stats.norm.ppf(cdf_value) sigma = (high - mu) / normed_sigma - return _simplify( + return _simplify_numpy( normal_sample(mu, sigma, samples) / ((chi_square_sample(t, samples) / t) ** 0.5) ) -def log_t_sample(low=None, high=None, t=20, samples=1, credibility=90): +def log_t_sample( + low: Optional[int] = None, + high: Optional[int] = None, + t: int = 20, + samples: int = 1, + credibility: int = 90, +) -> SampleFloatOutput: """ Sample a random number according to a log-t-distribution. @@ -217,14 +237,14 @@ def log_t_sample(low=None, high=None, t=20, samples=1, credibility=90): cdf_value = 0.5 + 0.5 * (credibility / 100) normed_sigma = stats.norm.ppf(cdf_value) sigma = (log_high - mu) / normed_sigma - return _simplify( + return _simplify_numpy( np.exp( normal_sample(mu, sigma, samples) / ((chi_square_sample(t, samples) / t) ** 0.5) ) ) -def binomial_sample(n, p, samples=1): +def binomial_sample(n: int, p: float, samples: int = 1) -> SampleIntOutput: """ Sample a random number according to a binomial distribution. @@ -249,10 +269,10 @@ def binomial_sample(n, p, samples=1): >>> binomial_sample(10, 0.1) 2 """ - return _simplify(_get_rng().binomial(n, p, samples)) + return _simplify_numpy(_get_rng().binomial(n, p, samples)) -def beta_sample(a, b, samples=1): +def beta_sample(a: int, b: int, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a beta distribution. @@ -279,10 +299,10 @@ def beta_sample(a, b, samples=1): >>> beta_sample(1, 1) 0.22145847498048798 """ - return _simplify(_get_rng().beta(a, b, samples)) + return _simplify_numpy(_get_rng().beta(a, b, samples)) -def bernoulli_sample(p, samples=1): +def bernoulli_sample(p: Float, samples: int = 1) -> SampleIntOutput: """ Sample 1 with probability ``p`` and 0 otherwise. @@ -305,13 +325,13 @@ def bernoulli_sample(p, samples=1): 0 """ a = uniform_sample(0, 1, samples) - if _safe_len(a) == 1: - return int(a < p) + if isinstance(a, Number): + return np.int64(a < p) else: - return (a < p).astype(int) + return (a < p).astype(np.int64) -def triangular_sample(left, mode, right, samples=1): +def triangular_sample(left: int, mode: int, right: int, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a triangular distribution. @@ -337,10 +357,10 @@ def triangular_sample(left, mode, right, samples=1): >>> triangular_sample(1, 2, 3) 2.327625176788963 """ - return _simplify(_get_rng().triangular(left, mode, right, samples)) + return _simplify_numpy(_get_rng().triangular(left, mode, right, samples)) -def poisson_sample(lam, samples=1): +def poisson_sample(lam: Union[int, float], samples: int = 1) -> SampleIntOutput: """ Sample a random number according to a poisson distribution. @@ -362,10 +382,10 @@ def poisson_sample(lam, samples=1): >>> poisson_sample(10) 13 """ - return _simplify(_get_rng().poisson(lam, samples)) + return _simplify_numpy(_get_rng().poisson(lam, samples)) -def exponential_sample(scale, samples=1): +def exponential_sample(scale: int, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to an exponential distribution. @@ -387,10 +407,10 @@ def exponential_sample(scale, samples=1): >>> exponential_sample(10) 24.042086039659946 """ - return _simplify(_get_rng().exponential(scale, samples)) + return _simplify_numpy(_get_rng().exponential(scale, samples)) -def gamma_sample(shape, scale, samples=1): +def gamma_sample(shape: int, scale: int, samples: int = 1) -> SampleNumberOutput: """ Sample a random number according to a gamma distribution. @@ -414,10 +434,10 @@ def gamma_sample(shape, scale, samples=1): >>> gamma_sample(10, 2) 21.290716894247602 """ - return _simplify(_get_rng().gamma(shape, scale, samples)) + return _simplify_numpy(_get_rng().gamma(shape, scale, samples)) -def pareto_sample(shape, samples=1): +def pareto_sample(shape: int, samples: int = 1) -> SampleNumberOutput: """ Sample a random number according to a pareto distribution. @@ -437,10 +457,10 @@ def pareto_sample(shape, samples=1): >>> pareto_sample(1) 10.069666324736094 """ - return _simplify(_get_rng().pareto(shape, samples)) + return _simplify_numpy(_get_rng().pareto(shape, samples)) -def uniform_sample(low, high, samples=1): +def uniform_sample(low: int, high: int, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a uniform distribution. @@ -465,10 +485,10 @@ def uniform_sample(low, high, samples=1): >>> uniform_sample(0, 1) 0.7739560485559633 """ - return _simplify(_get_rng().uniform(low, high, samples)) + return _simplify_numpy(_get_rng().uniform(low, high, samples)) -def chi_square_sample(df, samples=1): +def chi_square_sample(df: int, samples: int = 1) -> SampleFloatOutput: """ Sample a random number according to a chi-square distribution. @@ -490,10 +510,16 @@ def chi_square_sample(df, samples=1): >>> chi_square_sample(2) 4.808417207931989 """ - return _simplify(_get_rng().chisquare(df, samples)) + return _simplify_numpy(_get_rng().chisquare(df, samples)) -def discrete_sample(items, samples=1, verbose=False, _multicore_tqdm_n=1, _multicore_tqdm_cores=1): +def discrete_sample( + items: Any, + samples: int = 1, + verbose: bool = False, + _multicore_tqdm_n: int = 1, + _multicore_tqdm_cores: int = 1, +) -> Union[List[float], str, List[int], NormalDistribution, int]: """ Sample a random value from a discrete distribution (aka categorical distribution). @@ -548,14 +574,14 @@ def discrete_sample(items, samples=1, verbose=False, _multicore_tqdm_n=1, _multi def _mixture_sample_for_large_n( - values, - weights=None, - relative_weights=None, - samples=1, - verbose=False, - _multicore_tqdm_n=1, - _multicore_tqdm_cores=1, -): + values: List[Union[NormalDistribution, ConstantDistribution, float]], + weights: Optional[List[float]] = None, + relative_weights: None = None, + samples: int = 1, + verbose: bool = False, + _multicore_tqdm_n: int = 1, + _multicore_tqdm_cores: int = 1, +) -> Number | List[Number]: def _run_presample(dist, pbar): if is_dist(dist) and isinstance(dist, MixtureDistribution): raise ValueError( @@ -583,22 +609,22 @@ def _run_mixture(picker, i, pbar): picker = uniform_sample(0, 1, samples=samples) tqdm_samples = samples if _multicore_tqdm_cores == 1 else _multicore_tqdm_n - pbar = _init_tqdm(verbose=verbose, total=tqdm_samples) - out = _simplify([_run_mixture(p, i, pbar) for i, p in enumerate(_enlist(picker))]) + pbar: tqdm[NoReturn] | None = _init_tqdm(verbose=verbose, total=tqdm_samples) + out = _simplify_list([_run_mixture(p, i, pbar) for i, p in enumerate(_enlist(picker))]) _flush_tqdm(pbar) return out def _mixture_sample_for_small_n( - values, - weights=None, - relative_weights=None, - samples=1, - verbose=False, - _multicore_tqdm_n=1, - _multicore_tqdm_cores=1, -): + values: List[Any], + weights: Optional[List[float]] = None, + relative_weights: None = None, + samples: int = 1, + verbose: bool = False, + _multicore_tqdm_n: int = 1, + _multicore_tqdm_cores: int = 1, +) -> Number | List[Number]: def _run_mixture(values, weights, pbar=None, tick=1): r_ = uniform_sample(0, 1) _tick_tqdm(pbar, tick) @@ -611,7 +637,7 @@ def _run_mixture(values, weights, pbar=None, tick=1): weights = np.cumsum(weights) tqdm_samples = samples if _multicore_tqdm_cores == 1 else _multicore_tqdm_n pbar = _init_tqdm(verbose=verbose, total=tqdm_samples) - out = _simplify( + out = _simplify_list( [ _run_mixture(values=values, weights=weights, pbar=pbar, tick=_multicore_tqdm_cores) for _ in range(samples) @@ -629,7 +655,7 @@ def mixture_sample( verbose: bool = False, _multicore_tqdm_n: int = 1, _multicore_tqdm_cores: int = 1, -): +) -> Any: """ Sample a ranom number from a mixture distribution. @@ -698,19 +724,19 @@ def mixture_sample( def sample( dist: Optional[BaseDistribution | Number | str | bool] = None, - n=1, - lclip=None, - rclip=None, - memcache=False, - reload_cache=False, - dump_cache_file=None, - load_cache_file=None, - cache_file_primary=False, - verbose=None, - cores=1, - _multicore_tqdm_n=1, - _multicore_tqdm_cores=1, -): + n: int = 1, + lclip: Optional[int] = None, + rclip: Optional[int] = None, + memcache: bool = False, + reload_cache: bool = False, + dump_cache_file: Optional[str] = None, + load_cache_file: Optional[str] = None, + cache_file_primary: bool = False, + verbose: Optional[bool] = None, + cores: int = 1, + _multicore_tqdm_n: int = 1, + _multicore_tqdm_cores: int = 1, +) -> Any: """ Sample random numbers from a given distribution. @@ -892,7 +918,7 @@ def run_dist(dist, pbar=None, tick=1): return samp pbar = _init_tqdm(verbose=verbose, total=len(out) * _multicore_tqdm_cores) - samples = _simplify( + samples = _simplify_numpy( np.array([run_dist(dist=o, pbar=pbar, tick=_multicore_tqdm_cores) for o in out]) ) _flush_tqdm(pbar) @@ -903,10 +929,10 @@ def run_dist(dist, pbar=None, tick=1): or isinstance(dist, str) or dist is None ): - samples = _simplify(np.array([dist for _ in range(n)])) + samples = _simplify_numpy(np.array([dist for _ in range(n)])) elif isinstance(dist, ConstantDistribution): - samples = _simplify(np.array([dist.x for _ in range(n)])) + samples = _simplify_numpy(np.array([dist.x for _ in range(n)])) elif isinstance(dist, UniformDistribution): samples = uniform_sample(dist.x, dist.y, samples=n) diff --git a/squigglepy/utils.py b/squigglepy/utils.py index 948c929..11d2d79 100644 --- a/squigglepy/utils.py +++ b/squigglepy/utils.py @@ -7,8 +7,9 @@ from datetime import datetime from collections import Counter from collections.abc import Iterable -from typing import Optional, Union, overload +from typing import Any, Optional, Union, overload from numpy.typing import NDArray +from typing import TypeVar, List import importlib import importlib.util @@ -161,13 +162,22 @@ def _round(x: Union[Number, np.ndarray], digits: Optional[int] = 0) -> Union[Num return int(x) if digits <= 0 else x -def _simplify(a): - if _is_numpy(a): - a = a.tolist() if a.size == 1 else a - if isinstance(a, list): - a = a[0] if len(a) == 1 else a +NumpyNumberVar = TypeVar("NumpyNumberVar", np.floating, np.integer) + +def _simplify_numpy( + a: Union[NumpyNumberVar, NDArray[NumpyNumberVar]] +) -> Union[NumpyNumberVar, NDArray[NumpyNumberVar]]: + if isinstance(a, np.ndarray): + a = a[0] if a.size == 1 and a.ndim == 1 else a + return a return a +NumberVar = TypeVar("NumberVar", bound=Number) + +def _simplify_list(a: list[NumberVar]) -> Union[NumberVar, list[NumberVar]]: + if len(a) == 1: + return a[0] + return a def _enlist(a): if _is_numpy(a) and isinstance(a, np.ndarray): @@ -620,7 +630,7 @@ def _convert(p): raise ValueError("p must be between 0 and 1") return p / (1 - p) - return _simplify(np.array([_convert(p) for p in _enlist(p)])) + return _simplify_numpy(np.array([_convert(p) for p in _enlist(p)])) def odds_to_p(odds): @@ -650,7 +660,7 @@ def _convert(o): raise ValueError("odds must be greater than 0") return o / (1 + o) - return _simplify(np.array([_convert(o) for o in _enlist(odds)])) + return _simplify_numpy(np.array([_convert(o) for o in _enlist(odds)])) def geomean_odds(a, weights=None, relative_weights=None, drop_na=True): @@ -821,7 +831,7 @@ def doubling_time_to_growth_rate(doubling_time): return math.exp(math.log(2) / doubling_time) - 1 -def roll_die(sides, n=1): +def roll_die(sides: int, n: int=1) -> Union[int, list[int], None]: """ Roll a die. @@ -853,6 +863,8 @@ def roll_die(sides, n=1): raise ValueError("cannot roll less than a 2-sided die.") elif not isinstance(sides, int): raise ValueError("can only roll an integer number of sides") + elif n <= 0: + raise ValueError("cannot roll less than once") else: from .samplers import sample from .distributions import discrete @@ -881,8 +893,8 @@ def flip_coin(n=1): 'heads' """ rolls = roll_die(2, n=n) - if isinstance(rolls, int): - rolls = [rolls] + if isinstance(rolls, np.integer): + rolls: list[np.integer] = [rolls] flips = ["heads" if d == 2 else "tails" for d in rolls] return flips[0] if len(flips) == 1 else flips