distparser 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {distparser-0.3.0/src/distparser.egg-info → distparser-0.3.2}/PKG-INFO +32 -33
- distparser-0.3.2/README.md +62 -0
- {distparser-0.3.0 → distparser-0.3.2}/pyproject.toml +1 -1
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser/core.py +6 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser/graph.py +54 -16
- distparser-0.3.2/src/distparser/version.py +1 -0
- {distparser-0.3.0 → distparser-0.3.2/src/distparser.egg-info}/PKG-INFO +32 -33
- {distparser-0.3.0 → distparser-0.3.2}/tests/test_core.py +11 -0
- {distparser-0.3.0 → distparser-0.3.2}/tests/test_graph.py +29 -29
- distparser-0.3.0/README.md +0 -63
- distparser-0.3.0/src/distparser/version.py +0 -1
- {distparser-0.3.0 → distparser-0.3.2}/LICENSE +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/setup.cfg +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser/__init__.py +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser/mapping.py +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser.egg-info/SOURCES.txt +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser.egg-info/dependency_links.txt +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser.egg-info/requires.txt +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/src/distparser.egg-info/top_level.txt +0 -0
- {distparser-0.3.0 → distparser-0.3.2}/tests/test_mapping.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: distparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Parse function-call strings into frozen scipy.stats distributions
|
|
5
5
|
Author-email: Juyoung Choi <njuyoung35@gmail.com>
|
|
6
6
|
Maintainer-email: Juyoung Choi <njuyoung35@gmail.com>
|
|
@@ -33,19 +33,13 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# distparser
|
|
35
35
|
|
|
36
|
-
**Parse function‑call
|
|
36
|
+
**Parse function‑call strings into frozen `scipy.stats` distributions —**
|
|
37
|
+
**with dependency resolution, arithmetic expressions, and seed management.**
|
|
37
38
|
|
|
38
39
|
[](https://badge.fury.io/py/distparser)
|
|
39
40
|
[](https://www.python.org/downloads/)
|
|
40
41
|
[](https://opensource.org/licenses/MIT)
|
|
41
42
|
|
|
42
|
-
## Why `distparser`?
|
|
43
|
-
|
|
44
|
-
When you have configuration files or user input that specifies a probability
|
|
45
|
-
distribution (e.g. `"norm(loc=0, scale=1)"`), you want to turn that string
|
|
46
|
-
into a ready‑to‑sample object. `distparser` does exactly that, with a tiny
|
|
47
|
-
API and zero surprises.
|
|
48
|
-
|
|
49
43
|
## Installation
|
|
50
44
|
|
|
51
45
|
```bash
|
|
@@ -54,42 +48,47 @@ pip install distparser
|
|
|
54
48
|
|
|
55
49
|
## Quick Start
|
|
56
50
|
|
|
57
|
-
```
|
|
58
|
-
|
|
51
|
+
```python
|
|
52
|
+
import distparser as lib
|
|
59
53
|
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
# Parse a single distribution string
|
|
55
|
+
d = lib.parse_dist("norm(loc=0, scale=1)")
|
|
56
|
+
print(d.rvs(size=3)) # array([...])
|
|
57
|
+
```
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
dist = parse_dist("norm(loc=0, scale=1)")
|
|
66
|
-
samples = dist.rvs(size=5) # array([-0.12, 1.03, ...])
|
|
59
|
+
## DistGraph — dependency-aware evaluation
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```
|
|
61
|
+
```python
|
|
62
|
+
from distparser import DistGraph
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
config = {
|
|
65
|
+
"mean": 5.0,
|
|
66
|
+
"noise": "norm(0, 0.1)",
|
|
67
|
+
"measurement": "mean + noise",
|
|
68
|
+
}
|
|
73
69
|
|
|
74
|
-
|
|
70
|
+
graph = DistGraph(config, seed=42)
|
|
71
|
+
result = graph.resolve_all()
|
|
72
|
+
print(result["measurement"]) # 5.0 + sample from N(0, 0.1)
|
|
73
|
+
```
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
- `norm(loc, scale)`
|
|
78
|
-
- `expon(loc, scale)`
|
|
79
|
-
- … and many more (see [docs](https://distparser.readthedocs.io)).
|
|
75
|
+
## Features
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
- **15 built-in distributions** — `uniform`, `norm`, `expon`, `gamma`, `beta`, `lognorm`, `weibull_min`, `t`, `chi2`, `f`, `pareto`, `cauchy`, `laplace`, `logistic`, `rayleigh`
|
|
78
|
+
- **Distribution aliases** — `normal` → `norm`, `gaussian` → `norm`, `unif` → `uniform`
|
|
79
|
+
- **DistGraph** — automatic dependency resolution via topological sort
|
|
80
|
+
- **Arithmetic expressions** — `"60 + 30 * uniform(0, 1)"` with `sin`, `cos`, `exp`, `sqrt`, `pi` and more
|
|
81
|
+
- **Seed management** — global `seed()`, context manager `seed_context()`, per-instance `DistGraph(seed=...)`
|
|
82
|
+
- **Bounds clipping** — clamp sampled values with `min`/`max` or `lbound`/`rbound`
|
|
83
|
+
- **Extensible registry** — register custom distributions at runtime
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
from distparser import register_distribution
|
|
85
|
-
from scipy.stats import gumbel_r
|
|
85
|
+
## Docs
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
```
|
|
87
|
+
Full documentation at [distparser.readthedocs.io](https://distparser.readthedocs.io).
|
|
89
88
|
|
|
90
89
|
## Development
|
|
91
90
|
|
|
92
|
-
See [AGENTS.md](AGENTS.md) for
|
|
91
|
+
See [AGENTS.md](https://github.com/njuyoung35/distparser/blob/main/AGENTS.md) for setup and guidelines.
|
|
93
92
|
|
|
94
93
|
## License
|
|
95
94
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# distparser
|
|
2
|
+
|
|
3
|
+
**Parse function‑call strings into frozen `scipy.stats` distributions —**
|
|
4
|
+
**with dependency resolution, arithmetic expressions, and seed management.**
|
|
5
|
+
|
|
6
|
+
[](https://badge.fury.io/py/distparser)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install distparser
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import distparser as lib
|
|
20
|
+
|
|
21
|
+
# Parse a single distribution string
|
|
22
|
+
d = lib.parse_dist("norm(loc=0, scale=1)")
|
|
23
|
+
print(d.rvs(size=3)) # array([...])
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## DistGraph — dependency-aware evaluation
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from distparser import DistGraph
|
|
30
|
+
|
|
31
|
+
config = {
|
|
32
|
+
"mean": 5.0,
|
|
33
|
+
"noise": "norm(0, 0.1)",
|
|
34
|
+
"measurement": "mean + noise",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
graph = DistGraph(config, seed=42)
|
|
38
|
+
result = graph.resolve_all()
|
|
39
|
+
print(result["measurement"]) # 5.0 + sample from N(0, 0.1)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- **15 built-in distributions** — `uniform`, `norm`, `expon`, `gamma`, `beta`, `lognorm`, `weibull_min`, `t`, `chi2`, `f`, `pareto`, `cauchy`, `laplace`, `logistic`, `rayleigh`
|
|
45
|
+
- **Distribution aliases** — `normal` → `norm`, `gaussian` → `norm`, `unif` → `uniform`
|
|
46
|
+
- **DistGraph** — automatic dependency resolution via topological sort
|
|
47
|
+
- **Arithmetic expressions** — `"60 + 30 * uniform(0, 1)"` with `sin`, `cos`, `exp`, `sqrt`, `pi` and more
|
|
48
|
+
- **Seed management** — global `seed()`, context manager `seed_context()`, per-instance `DistGraph(seed=...)`
|
|
49
|
+
- **Bounds clipping** — clamp sampled values with `min`/`max` or `lbound`/`rbound`
|
|
50
|
+
- **Extensible registry** — register custom distributions at runtime
|
|
51
|
+
|
|
52
|
+
## Docs
|
|
53
|
+
|
|
54
|
+
Full documentation at [distparser.readthedocs.io](https://distparser.readthedocs.io).
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
See [AGENTS.md](https://github.com/njuyoung35/distparser/blob/main/AGENTS.md) for setup and guidelines.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -26,6 +26,8 @@ from scipy.stats import (
|
|
|
26
26
|
|
|
27
27
|
from distparser.mapping import normalize_dist_name
|
|
28
28
|
|
|
29
|
+
_BOUND_KEYS = frozenset({"min", "max", "lbound", "rbound"})
|
|
30
|
+
|
|
29
31
|
__all__ = [
|
|
30
32
|
"DistParserError",
|
|
31
33
|
"ParseError",
|
|
@@ -195,6 +197,10 @@ def parse_dist(s: str) -> Any:
|
|
|
195
197
|
|
|
196
198
|
params.update(keyword)
|
|
197
199
|
|
|
200
|
+
# Strip bound keys before passing to scipy
|
|
201
|
+
for bk in _BOUND_KEYS:
|
|
202
|
+
params.pop(bk, None)
|
|
203
|
+
|
|
198
204
|
return dist_class(**params)
|
|
199
205
|
|
|
200
206
|
|
|
@@ -4,13 +4,14 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import ast as _ast
|
|
6
6
|
import contextlib
|
|
7
|
+
import re as _re
|
|
7
8
|
from typing import Any, Iterator
|
|
8
9
|
|
|
9
10
|
import graphlib
|
|
10
11
|
import numpy as np
|
|
11
12
|
from asteval import Interpreter
|
|
12
13
|
|
|
13
|
-
from distparser.core import REGISTRY, ParseError
|
|
14
|
+
from distparser.core import REGISTRY, ParseError, _split_args
|
|
14
15
|
|
|
15
16
|
# ---------------------------------------------------------------------------
|
|
16
17
|
# Seed management
|
|
@@ -57,6 +58,9 @@ def _get_current_rng() -> np.random.Generator:
|
|
|
57
58
|
# Distribution sampler factory
|
|
58
59
|
# ---------------------------------------------------------------------------
|
|
59
60
|
|
|
61
|
+
|
|
62
|
+
_BOUND_KEYS = frozenset({"min", "max", "lbound", "rbound"})
|
|
63
|
+
_NAME_RE = _re.compile(r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*)\)$")
|
|
60
64
|
# Math constants and functions exposed in expressions
|
|
61
65
|
_MATH_SYMBOLS: dict[str, Any] = {
|
|
62
66
|
"sin": np.sin,
|
|
@@ -74,7 +78,7 @@ _MATH_SYMBOLS: dict[str, Any] = {
|
|
|
74
78
|
|
|
75
79
|
def _make_dist_samplers(rng: np.random.Generator) -> dict[str, Any]:
|
|
76
80
|
"""Build a dict mapping each registered distribution name to a callable
|
|
77
|
-
sampler that returns a single ``.rvs()`` sample."""
|
|
81
|
+
sampler that returns a single ``.rvs()`` sample, clipped to bounds."""
|
|
78
82
|
samplers: dict[str, Any] = {}
|
|
79
83
|
for name, entry in REGISTRY.items():
|
|
80
84
|
dist_class = entry["class"]
|
|
@@ -87,12 +91,27 @@ def _make_dist_samplers(rng: np.random.Generator) -> dict[str, Any]:
|
|
|
87
91
|
_rng: np.random.Generator = rng,
|
|
88
92
|
**kwargs: Any,
|
|
89
93
|
) -> Any:
|
|
94
|
+
# Separate bound kwargs from distribution kwargs
|
|
95
|
+
bounds: dict[str, float] = {}
|
|
96
|
+
dist_kwargs: dict[str, Any] = {}
|
|
97
|
+
for k, v in kwargs.items():
|
|
98
|
+
if k in _BOUND_KEYS:
|
|
99
|
+
bounds[k] = float(v)
|
|
100
|
+
else:
|
|
101
|
+
dist_kwargs[k] = v
|
|
102
|
+
|
|
90
103
|
params: dict[str, Any] = {}
|
|
91
104
|
for i, val in enumerate(args):
|
|
92
105
|
if i < len(_po):
|
|
93
106
|
params[_po[i]] = val
|
|
94
|
-
params.update(
|
|
95
|
-
|
|
107
|
+
params.update(dist_kwargs)
|
|
108
|
+
|
|
109
|
+
value = float(_dc(**params).rvs(random_state=_rng))
|
|
110
|
+
|
|
111
|
+
# Clip to bounds
|
|
112
|
+
lb = bounds.get("min", bounds.get("lbound", -float("inf")))
|
|
113
|
+
ub = bounds.get("max", bounds.get("rbound", float("inf")))
|
|
114
|
+
return float(np.clip(value, lb, ub))
|
|
96
115
|
|
|
97
116
|
samplers[name] = _sampler
|
|
98
117
|
return samplers
|
|
@@ -124,7 +143,7 @@ class DistGraph:
|
|
|
124
143
|
self._raw_config = config
|
|
125
144
|
self._rng = np.random.default_rng(seed) if seed is not None else None
|
|
126
145
|
self._resolved: dict[str, Any] = {}
|
|
127
|
-
self._bounds: dict[str, dict[str,
|
|
146
|
+
self._bounds: dict[str, dict[str, float]] = {}
|
|
128
147
|
|
|
129
148
|
# Parse config into dist expressions + bounds
|
|
130
149
|
self._dists: dict[str, Any] = {}
|
|
@@ -139,18 +158,36 @@ class DistGraph:
|
|
|
139
158
|
|
|
140
159
|
def _parse_config(self) -> None:
|
|
141
160
|
for key, value in self._raw_config.items():
|
|
142
|
-
if isinstance(value,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
f"Config key {key!r} is a dict but missing required "
|
|
146
|
-
f"'dist' key. Use 'dist' for the expression string."
|
|
147
|
-
)
|
|
148
|
-
self._dists[key] = value["dist"]
|
|
149
|
-
if "bounds" in value:
|
|
150
|
-
self._bounds[key] = value["bounds"]
|
|
161
|
+
if isinstance(value, str):
|
|
162
|
+
self._dists[key] = value
|
|
163
|
+
self._bounds[key] = self._extract_bounds(value)
|
|
151
164
|
else:
|
|
152
165
|
self._dists[key] = value
|
|
153
166
|
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
# Bound extraction
|
|
169
|
+
# ------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def _extract_bounds(expr: str) -> dict[str, float]:
|
|
173
|
+
"""Extract bound keyword args (min/max/lbound/rbound) from an expression."""
|
|
174
|
+
m = _NAME_RE.match(expr.strip())
|
|
175
|
+
if not m:
|
|
176
|
+
return {}
|
|
177
|
+
args_str = m.group(2)
|
|
178
|
+
bounds: dict[str, float] = {}
|
|
179
|
+
for arg in _split_args(args_str):
|
|
180
|
+
arg = arg.strip()
|
|
181
|
+
if "=" in arg:
|
|
182
|
+
key, val_str = arg.split("=", 1)
|
|
183
|
+
key = key.strip()
|
|
184
|
+
if key in _BOUND_KEYS:
|
|
185
|
+
try:
|
|
186
|
+
bounds[key] = float(_ast.literal_eval(val_str.strip()))
|
|
187
|
+
except (ValueError, SyntaxError):
|
|
188
|
+
pass
|
|
189
|
+
return bounds
|
|
190
|
+
|
|
154
191
|
# ------------------------------------------------------------------
|
|
155
192
|
# Dependency resolution
|
|
156
193
|
# ------------------------------------------------------------------
|
|
@@ -257,9 +294,10 @@ class DistGraph:
|
|
|
257
294
|
def keys(self) -> Any:
|
|
258
295
|
return self._dists.keys()
|
|
259
296
|
|
|
260
|
-
def get_bounds(self, key: str) -> dict[str,
|
|
297
|
+
def get_bounds(self, key: str) -> dict[str, float] | None:
|
|
261
298
|
"""Return the bounds dict for *key*, or ``None`` if no bounds set."""
|
|
262
|
-
|
|
299
|
+
b = self._bounds.get(key)
|
|
300
|
+
return b if b else None
|
|
263
301
|
|
|
264
302
|
@property
|
|
265
303
|
def order(self) -> list[str]:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: distparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Parse function-call strings into frozen scipy.stats distributions
|
|
5
5
|
Author-email: Juyoung Choi <njuyoung35@gmail.com>
|
|
6
6
|
Maintainer-email: Juyoung Choi <njuyoung35@gmail.com>
|
|
@@ -33,19 +33,13 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# distparser
|
|
35
35
|
|
|
36
|
-
**Parse function‑call
|
|
36
|
+
**Parse function‑call strings into frozen `scipy.stats` distributions —**
|
|
37
|
+
**with dependency resolution, arithmetic expressions, and seed management.**
|
|
37
38
|
|
|
38
39
|
[](https://badge.fury.io/py/distparser)
|
|
39
40
|
[](https://www.python.org/downloads/)
|
|
40
41
|
[](https://opensource.org/licenses/MIT)
|
|
41
42
|
|
|
42
|
-
## Why `distparser`?
|
|
43
|
-
|
|
44
|
-
When you have configuration files or user input that specifies a probability
|
|
45
|
-
distribution (e.g. `"norm(loc=0, scale=1)"`), you want to turn that string
|
|
46
|
-
into a ready‑to‑sample object. `distparser` does exactly that, with a tiny
|
|
47
|
-
API and zero surprises.
|
|
48
|
-
|
|
49
43
|
## Installation
|
|
50
44
|
|
|
51
45
|
```bash
|
|
@@ -54,42 +48,47 @@ pip install distparser
|
|
|
54
48
|
|
|
55
49
|
## Quick Start
|
|
56
50
|
|
|
57
|
-
```
|
|
58
|
-
|
|
51
|
+
```python
|
|
52
|
+
import distparser as lib
|
|
59
53
|
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
# Parse a single distribution string
|
|
55
|
+
d = lib.parse_dist("norm(loc=0, scale=1)")
|
|
56
|
+
print(d.rvs(size=3)) # array([...])
|
|
57
|
+
```
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
dist = parse_dist("norm(loc=0, scale=1)")
|
|
66
|
-
samples = dist.rvs(size=5) # array([-0.12, 1.03, ...])
|
|
59
|
+
## DistGraph — dependency-aware evaluation
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```
|
|
61
|
+
```python
|
|
62
|
+
from distparser import DistGraph
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
config = {
|
|
65
|
+
"mean": 5.0,
|
|
66
|
+
"noise": "norm(0, 0.1)",
|
|
67
|
+
"measurement": "mean + noise",
|
|
68
|
+
}
|
|
73
69
|
|
|
74
|
-
|
|
70
|
+
graph = DistGraph(config, seed=42)
|
|
71
|
+
result = graph.resolve_all()
|
|
72
|
+
print(result["measurement"]) # 5.0 + sample from N(0, 0.1)
|
|
73
|
+
```
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
- `norm(loc, scale)`
|
|
78
|
-
- `expon(loc, scale)`
|
|
79
|
-
- … and many more (see [docs](https://distparser.readthedocs.io)).
|
|
75
|
+
## Features
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
- **15 built-in distributions** — `uniform`, `norm`, `expon`, `gamma`, `beta`, `lognorm`, `weibull_min`, `t`, `chi2`, `f`, `pareto`, `cauchy`, `laplace`, `logistic`, `rayleigh`
|
|
78
|
+
- **Distribution aliases** — `normal` → `norm`, `gaussian` → `norm`, `unif` → `uniform`
|
|
79
|
+
- **DistGraph** — automatic dependency resolution via topological sort
|
|
80
|
+
- **Arithmetic expressions** — `"60 + 30 * uniform(0, 1)"` with `sin`, `cos`, `exp`, `sqrt`, `pi` and more
|
|
81
|
+
- **Seed management** — global `seed()`, context manager `seed_context()`, per-instance `DistGraph(seed=...)`
|
|
82
|
+
- **Bounds clipping** — clamp sampled values with `min`/`max` or `lbound`/`rbound`
|
|
83
|
+
- **Extensible registry** — register custom distributions at runtime
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
from distparser import register_distribution
|
|
85
|
-
from scipy.stats import gumbel_r
|
|
85
|
+
## Docs
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
```
|
|
87
|
+
Full documentation at [distparser.readthedocs.io](https://distparser.readthedocs.io).
|
|
89
88
|
|
|
90
89
|
## Development
|
|
91
90
|
|
|
92
|
-
See [AGENTS.md](AGENTS.md) for
|
|
91
|
+
See [AGENTS.md](https://github.com/njuyoung35/distparser/blob/main/AGENTS.md) for setup and guidelines.
|
|
93
92
|
|
|
94
93
|
## License
|
|
95
94
|
|
|
@@ -213,6 +213,17 @@ class TestParseDistAliases:
|
|
|
213
213
|
parse_dist("not_an_alias(0, 1)")
|
|
214
214
|
|
|
215
215
|
|
|
216
|
+
class TestParseDistBounds:
|
|
217
|
+
"""Bound keys are stripped before passing to scipy."""
|
|
218
|
+
|
|
219
|
+
def test_bound_keys_are_stripped(self):
|
|
220
|
+
d = parse_dist("norm(loc=0, scale=1, min=-2, max=2)")
|
|
221
|
+
assert abs(d.kwds["loc"] - 0.0) < 1e-12
|
|
222
|
+
assert abs(d.kwds["scale"] - 1.0) < 1e-12
|
|
223
|
+
assert "min" not in d.kwds
|
|
224
|
+
assert "max" not in d.kwds
|
|
225
|
+
|
|
226
|
+
|
|
216
227
|
# ---------------------------------------------------------------------------
|
|
217
228
|
# parse_dist – error cases
|
|
218
229
|
# ---------------------------------------------------------------------------
|
|
@@ -158,45 +158,45 @@ class TestSeedManagement:
|
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
class TestDistGraphBounds:
|
|
161
|
-
def
|
|
162
|
-
g = DistGraph(
|
|
163
|
-
{
|
|
164
|
-
"wall": {
|
|
165
|
-
"dist": "norm(loc=0.25, scale=0.025)",
|
|
166
|
-
"bounds": {"loc": (0.0, 1.0), "scale": (0.001, 0.1)},
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
)
|
|
161
|
+
def test_bounds_in_expression(self):
|
|
162
|
+
g = DistGraph({"x": "uniform(0, 1, min=0.1, max=0.9)"}, seed=42)
|
|
170
163
|
result = g.resolve_all()
|
|
171
|
-
|
|
164
|
+
# Clipped to [0.1, 0.9]
|
|
165
|
+
assert 0.1 <= result["x"] <= 0.9
|
|
172
166
|
|
|
173
167
|
def test_get_bounds(self):
|
|
174
|
-
g = DistGraph({"x":
|
|
168
|
+
g = DistGraph({"x": "uniform(0, 1, min=0, max=2)"})
|
|
175
169
|
bounds = g.get_bounds("x")
|
|
176
|
-
assert bounds == {"
|
|
170
|
+
assert bounds == {"min": 0.0, "max": 2.0}
|
|
171
|
+
|
|
172
|
+
def test_get_bounds_lbound_rbound(self):
|
|
173
|
+
g = DistGraph({"x": "norm(0, 1, lbound=-1, rbound=1)"})
|
|
174
|
+
bounds = g.get_bounds("x")
|
|
175
|
+
assert bounds == {"lbound": -1.0, "rbound": 1.0}
|
|
177
176
|
|
|
178
177
|
def test_get_bounds_none(self):
|
|
179
178
|
g = DistGraph({"x": "uniform(0, 1)"})
|
|
180
179
|
assert g.get_bounds("x") is None
|
|
181
180
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
)
|
|
181
|
+
def test_bounds_clip_sampling(self):
|
|
182
|
+
# Bounds ARE applied as clipping — value should be clamped
|
|
183
|
+
g = DistGraph({"x": "norm(loc=100, scale=0.01, max=50)"}, seed=42)
|
|
184
|
+
result = g.resolve_all()
|
|
185
|
+
assert result["x"] <= 50.0
|
|
186
|
+
|
|
187
|
+
def test_min_alternative(self):
|
|
188
|
+
g = DistGraph({"x": "uniform(0, 1, min=-10, max=-5)"}, seed=42)
|
|
189
|
+
result = g.resolve_all()
|
|
190
|
+
# Clipped to [-10, -5] — tightly constrained
|
|
191
|
+
assert -10 <= result["x"] <= -5
|
|
192
|
+
|
|
193
|
+
def test_nonliteral_bound_ignored_in_extract(self):
|
|
194
|
+
# min=a is not a literal — _extract_bounds ignores it gracefully
|
|
195
|
+
g = DistGraph({"a": 0.5, "x": "uniform(0, 1, min=a)"})
|
|
196
|
+
assert g.get_bounds("x") is None # not a literal → not extracted
|
|
197
|
+
# But sampler still clips since 'a' is resolved as a variable
|
|
197
198
|
result = g.resolve_all()
|
|
198
|
-
|
|
199
|
-
assert abs(result["x"] - 100.0) < 1.0
|
|
199
|
+
assert result["x"] >= 0.5
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
# ---------------------------------------------------------------------------
|
distparser-0.3.0/README.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# distparser
|
|
2
|
-
|
|
3
|
-
**Parse function‑call syntax strings into frozen `scipy.stats` distributions**
|
|
4
|
-
|
|
5
|
-
[](https://badge.fury.io/py/distparser)
|
|
6
|
-
[](https://www.python.org/downloads/)
|
|
7
|
-
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
|
|
9
|
-
## Why `distparser`?
|
|
10
|
-
|
|
11
|
-
When you have configuration files or user input that specifies a probability
|
|
12
|
-
distribution (e.g. `"norm(loc=0, scale=1)"`), you want to turn that string
|
|
13
|
-
into a ready‑to‑sample object. `distparser` does exactly that, with a tiny
|
|
14
|
-
API and zero surprises.
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
pip install distparser
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
from distparser import parse_dist
|
|
26
|
-
|
|
27
|
-
# Positional arguments (order matters)
|
|
28
|
-
dist = parse_dist("uniform(0, 1)")
|
|
29
|
-
sample = dist.rvs() # e.g. 0.374
|
|
30
|
-
|
|
31
|
-
# Keyword arguments (order ignored)
|
|
32
|
-
dist = parse_dist("norm(loc=0, scale=1)")
|
|
33
|
-
samples = dist.rvs(size=5) # array([-0.12, 1.03, ...])
|
|
34
|
-
|
|
35
|
-
# Mixed (positional + keyword) – works, but not recommended
|
|
36
|
-
dist = parse_dist("expon(scale=2, loc=1)")
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Supported Distributions
|
|
40
|
-
|
|
41
|
-
Built‑in registry covers the most common continuous distributions:
|
|
42
|
-
|
|
43
|
-
- `uniform(loc, scale)`
|
|
44
|
-
- `norm(loc, scale)`
|
|
45
|
-
- `expon(loc, scale)`
|
|
46
|
-
- … and many more (see [docs](https://distparser.readthedocs.io)).
|
|
47
|
-
|
|
48
|
-
You can **add your own** at runtime:
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
from distparser import register_distribution
|
|
52
|
-
from scipy.stats import gumbel_r
|
|
53
|
-
|
|
54
|
-
register_distribution("gumbel", gumbel_r, ["loc", "scale"])
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Development
|
|
58
|
-
|
|
59
|
-
See [AGENTS.md](AGENTS.md) for development setup and guidelines.
|
|
60
|
-
|
|
61
|
-
## License
|
|
62
|
-
|
|
63
|
-
MIT
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|