Skip to content

Commit 89b4cbb

Browse files
Merge pull request #120 from BlockScience/dev
release: gds-psuu v0.2.0
2 parents 8271db8 + ab2e8de commit 89b4cbb

36 files changed

Lines changed: 2667 additions & 58 deletions

docs/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Key guides include embedded [marimo](https://marimo.io) notebooks — run code,
3636
| `gds-games` | `ogs` | Typed DSL for compositional game theory (Open Games) |
3737
| `gds-software` | `gds_software` | Software architecture DSL (DFD, state machine, C4, ERD, etc.) |
3838
| `gds-business` | `gds_business` | Business dynamics DSL (CLD, supply chain, value stream map) |
39+
| `gds-sim` | `gds_sim` | Simulation engine — Model, Simulation, Results |
40+
| `gds-psuu` | `gds_psuu` | Parameter space search under uncertainty for gds-sim |
3941
| `gds-examples` || Tutorial models demonstrating framework features |
4042

4143
## Architecture
@@ -51,6 +53,10 @@ gds-software ← software architecture DSL (depends on gds-framework)
5153
gds-business ← business dynamics DSL (depends on gds-framework)
5254
5355
gds-examples ← tutorials (depends on gds-framework + gds-viz)
56+
57+
gds-sim ← simulation engine (standalone — no gds-framework dep)
58+
59+
gds-psuu ← parameter search under uncertainty (depends on gds-sim)
5460
```
5561

5662
## License

docs/psuu/api/evaluation.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.evaluation
2+
3+
Evaluation bridge between parameter points and gds-sim.
4+
5+
::: gds_psuu.evaluation
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/api/init.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu
2+
3+
Public API -- top-level exports.
4+
5+
::: gds_psuu
6+
options:
7+
show_submodules: false
8+
members: false

docs/psuu/api/kpi.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.kpi
2+
3+
KPI wrapper and legacy helper functions.
4+
5+
::: gds_psuu.kpi
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/api/metric.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.metric
2+
3+
Metric and Aggregation primitives for composable KPI construction.
4+
5+
::: gds_psuu.metric
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/api/optimizers.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# gds_psuu.optimizers
2+
3+
Search strategy implementations.
4+
5+
## Base
6+
7+
::: gds_psuu.optimizers.base
8+
options:
9+
show_source: true
10+
members_order: source
11+
12+
## Grid Search
13+
14+
::: gds_psuu.optimizers.grid
15+
options:
16+
show_source: true
17+
members_order: source
18+
19+
## Random Search
20+
21+
::: gds_psuu.optimizers.random
22+
options:
23+
show_source: true
24+
members_order: source
25+
26+
## Bayesian (optional)
27+
28+
::: gds_psuu.optimizers.bayesian
29+
options:
30+
show_source: true
31+
members_order: source

docs/psuu/api/results.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.results
2+
3+
Sweep results and summary types.
4+
5+
::: gds_psuu.results
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/api/space.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.space
2+
3+
Parameter space definitions for search.
4+
5+
::: gds_psuu.space
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/api/sweep.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# gds_psuu.sweep
2+
3+
Sweep orchestrator -- the main entry point for parameter search.
4+
5+
::: gds_psuu.sweep
6+
options:
7+
show_source: true
8+
members_order: source

docs/psuu/getting-started.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Getting Started
2+
3+
## Installation
4+
5+
```bash
6+
uv add gds-psuu
7+
# or: pip install gds-psuu
8+
```
9+
10+
For Bayesian optimization (optional):
11+
12+
```bash
13+
uv add "gds-psuu[bayesian]"
14+
# or: pip install gds-psuu[bayesian]
15+
```
16+
17+
For development (monorepo):
18+
19+
```bash
20+
git clone https://github.com/BlockScience/gds-core.git
21+
cd gds-core
22+
uv sync --all-packages
23+
```
24+
25+
## Your First Parameter Sweep
26+
27+
Define a `gds-sim` model, then sweep a parameter to find the best value:
28+
29+
```python
30+
from gds_sim import Model, StateUpdateBlock
31+
from gds_psuu import (
32+
KPI,
33+
Continuous,
34+
GridSearchOptimizer,
35+
ParameterSpace,
36+
Sweep,
37+
final_value,
38+
mean_agg,
39+
)
40+
41+
42+
# 1. Define a growth model
43+
def growth_policy(state, params, **kw):
44+
return {"delta": state["population"] * params["growth_rate"]}
45+
46+
47+
def update_pop(state, params, *, signal=None, **kw):
48+
return ("population", state["population"] + signal["delta"])
49+
50+
51+
model = Model(
52+
initial_state={"population": 100.0},
53+
state_update_blocks=[
54+
StateUpdateBlock(
55+
policies={"growth": growth_policy},
56+
variables={"population": update_pop},
57+
)
58+
],
59+
)
60+
61+
# 2. Define what to search
62+
space = ParameterSpace(
63+
params={"growth_rate": Continuous(min_val=0.01, max_val=0.2)}
64+
)
65+
66+
# 3. Define what to measure
67+
kpis = [
68+
KPI(
69+
name="avg_final_pop",
70+
metric=final_value("population"), # per-run: final value
71+
aggregation=mean_agg, # cross-run: mean
72+
),
73+
]
74+
75+
# 4. Run the sweep
76+
sweep = Sweep(
77+
model=model,
78+
space=space,
79+
kpis=kpis,
80+
optimizer=GridSearchOptimizer(n_steps=5),
81+
timesteps=10,
82+
runs=3, # 3 Monte Carlo runs per parameter point
83+
)
84+
results = sweep.run()
85+
86+
# 5. Inspect results
87+
best = results.best("avg_final_pop")
88+
print(f"Best growth_rate: {best.params['growth_rate']:.3f}")
89+
print(f"Best avg final pop: {best.scores['avg_final_pop']:.1f}")
90+
```
91+
92+
## Composable KPIs
93+
94+
The key design is the **Metric + Aggregation = KPI** pattern:
95+
96+
```python
97+
from gds_psuu import (
98+
KPI,
99+
final_value,
100+
trajectory_mean,
101+
max_value,
102+
mean_agg,
103+
std_agg,
104+
percentile_agg,
105+
probability_above,
106+
)
107+
108+
# Mean of final population across runs
109+
avg_final = KPI(name="avg_pop", metric=final_value("population"), aggregation=mean_agg)
110+
111+
# Standard deviation of final population (measures uncertainty)
112+
std_final = KPI(name="std_pop", metric=final_value("population"), aggregation=std_agg)
113+
114+
# 90th percentile of trajectory means
115+
p90_mean = KPI(name="p90_mean", metric=trajectory_mean("population"), aggregation=percentile_agg(90))
116+
117+
# Probability that max population exceeds 500
118+
risk = KPI(name="boom_risk", metric=max_value("population"), aggregation=probability_above(500.0))
119+
```
120+
121+
**Metric** extracts a scalar from each run. **Aggregation** reduces the per-run values to a single score.
122+
123+
If no aggregation is specified, `mean_agg` is used by default:
124+
125+
```python
126+
# These are equivalent:
127+
KPI(name="avg_pop", metric=final_value("population"))
128+
KPI(name="avg_pop", metric=final_value("population"), aggregation=mean_agg)
129+
```
130+
131+
## Per-Run Distributions
132+
133+
Metric-based KPIs track the full distribution across Monte Carlo runs:
134+
135+
```python
136+
results = sweep.run()
137+
138+
for ev in results.evaluations:
139+
dist = ev.distributions["avg_final_pop"]
140+
print(f" params={ev.params}, per_run={dist}")
141+
# e.g. per_run=[265.3, 265.3, 265.3] for deterministic model
142+
```
143+
144+
## Multiple Optimizers
145+
146+
```python
147+
from gds_psuu import GridSearchOptimizer, RandomSearchOptimizer
148+
149+
# Exhaustive grid (good for 1-2 dimensions)
150+
grid = GridSearchOptimizer(n_steps=10) # 10 points per continuous dim
151+
152+
# Random sampling (good for higher dimensions)
153+
rand = RandomSearchOptimizer(n_samples=50, seed=42)
154+
```
155+
156+
For Bayesian optimization (requires `gds-psuu[bayesian]`):
157+
158+
```python
159+
from gds_psuu.optimizers.bayesian import BayesianOptimizer
160+
161+
bayes = BayesianOptimizer(n_calls=30, target_kpi="avg_final_pop", seed=42)
162+
```
163+
164+
## Legacy KPI Support
165+
166+
The older `fn`-based KPI interface still works:
167+
168+
```python
169+
from gds_psuu import KPI, final_state_mean
170+
171+
# Legacy style (backwards compatible)
172+
kpi = KPI(name="pop", fn=lambda r: final_state_mean(r, "population"))
173+
```
174+
175+
Legacy KPIs don't track per-run distributions -- use metric-based KPIs for full Monte Carlo awareness.
176+
177+
## Next Steps
178+
179+
- [Concepts](guide/concepts.md) -- Metric, Aggregation, KPI, and the full conceptual hierarchy
180+
- [Parameter Spaces](guide/spaces.md) -- dimensions, validation, and grid generation
181+
- [Optimizers](guide/optimizers.md) -- grid, random, and Bayesian search strategies
182+
- [API Reference](api/init.md) -- complete auto-generated API docs

0 commit comments

Comments
 (0)