Journey 04 — Python Session and LHS
Drive the same study from Python with Session.from_model, grid sweep(), and method="lhs" for Latin hypercube — the embedding pattern for notebooks and digital-twin pipelines.
Prerequisites: Journey 03 — at least one YAML study completed. Scenario: Same Brusselator physics | grid then Latin hypercube
Key APIs
solvjump.Session.from_model()session.sweep(method="grid")session.sweep(method="lhs")solvjump run examples/brusselator_study_lhs.yaml
Concepts to internalize
- CLI vs Session — YAML
solvjump runand Python Session share the same engine; pick based on orchestration. - LHS — space-filling alternative to full grid when dimensionality grows.
- from_model — loads
make_rhsand optionalterminal_metricsfrom a module path. - Programmatic receipts — sweep returns structured result you can log or post to a portal.
Important code
The guided tour script wraps this pattern. Copy the production shape into your pipeline:
from solvjump import Session
session = Session.from_model("examples/brusselator_model.py")
result = session.sweep(
study_yaml="examples/brusselator_study_lhs.yaml",
method="lhs",
)Act-by-act walkthrough
Act 1 — Why a Python Session
YAML is the production path. Session.from_model is the same routing/calibration with knobs in code: param_schema, y0, t_span, stiffness_nom, solvtune_objective, mode, seeds, catalog_slug.
| Field | Value | Notes |
|---|---|---|
model | <product-root>\python\examples\brusselator_model.py:make_rhs | |
parameters | a in [0.8, 1.2], b in [2.5, 3.5] | |
method_first | grid n=4 |
Act 2 — Calibrate once
One Session, two sweeps — grid then LHS — sharing the operator if gates passed.
| Field | Value | Notes |
|---|---|---|
model | <product-root>\python\examples\brusselator_model.py:make_rhs | |
dim | 2 | |
stiffness_nom | S1 | |
horizon | [0.0, 10.0] | |
problem_set_operators | 256 | kepler |
- Jump recommendation: confidence=exact, cache_ref=<operator_id>, mode=auto
| Field | Value | Notes |
|---|---|---|
seeds | 8 | |
reason | no exact cache match for this model | Collecting trajectory training pairs: jump lag=0.25s, SolvTune arm=solvsrk, objective=balanced, target chain=4 |
-
SRK collection done - 24 train pairs from 3 trajectories (0 failed, need >=8 train)
-
Fitted nominal+guard kernels - train median SCD=2.41, holdout median SCD=2.20 (n=8/8)
-
Rollout validation - SCD by chain depth [1:2.20, 4:1.36], max_valid_chain=4 (single_step_only)
-
Accuracy gate passed (operator <operator_id>)
Operator setup
- SolvJump fitted a fast approximation and checked it against full SolvSRK — it passed.
| Field | Value | Notes |
|---|---|---|
accuracy_check | acceptable quality | |
operator_id | <operator_id> | |
fit_time | 1.3 s (one-time before the sweep) |
Act 3 — Tensor grid
sweep(method='grid', n=4) builds a small tensor product.
-
Sweep finished - example params {'a': 1.2, 'b': 2.5}, final state ~ [2.5436, 1.016]
-
SRK timing sample: 3 point(s) integrated for comparison
-
Horizon aligned: 4 jumps x 0.25 s = 1.0 s (jump_lag matches dt)
Sweep results
| Field | Value | Notes |
|---|---|---|
completed | 4 parameter sets | |
skipped | 0 | Low: params={'a': 0.8, 'b': 2.5} |
-
Mid: params={'a': 0.8, 'b': 3.5}
-
High: params={'a': 1.2, 'b': 3.5}
How long did this take?
| Field | Value | Notes |
|---|---|---|
search_method | fast jump sweep | |
grid_size | 4 | |
operator_fit | 1.3 s | |
fast_sweep | 1 ms | |
this_run_total | 1.3 s | |
vs_full_SolvSRK_on_every_study_point | ~141 ms for all 4 points in this study (extrapolated from 3 spot checks — not a larger search) | |
speedup_vs_full_grid | ~99.5× (jump sweep vs brute estimate) | All results CSV: <session-dir>\sweep_results.csv |
Act 4 — Latin hypercube
method='lhs' spreads the same n points through the box. Production YAML: brusselator_study_lhs.yaml (sweep.method: lhs).
Study definition
| Field | Value | Notes |
|---|---|---|
model | <product-root>\python\examples\brusselator_model.py:make_rhs | |
dynamics_params | a in [0.8, 1.2], b in [2.5, 3.5] | |
y0 | [1.0, 3.0] | |
t_span | [0.0, 10.0] | |
mode | auto | |
calibration_seeds | 8 |
Operator routing, calibration & sweep (3 steps)
| Field | Value | Notes |
|---|---|---|
model | <product-root>\python\examples\brusselator_model.py:make_rhs | |
dim | 2 | |
stiffness_nom | S1 | |
horizon | [0.0, 10.0] | |
problem_set_operators | 256 | kepler |
- Jump recommendation: confidence=exact, cache_ref=<operator_id>, mode=auto
| Field | Value | Notes |
|---|---|---|
seeds | 8 | |
reason | no exact cache match for this model | Collecting trajectory training pairs: jump lag=0.25s, SolvTune arm=solvsrk, objective=balanced, target chain=4 |
-
SRK collection done - 24 train pairs from 3 trajectories (0 failed, need >=8 train)
-
Fitted nominal+guard kernels - train median SCD=2.41, holdout median SCD=2.20 (n=8/8)
-
Rollout validation - SCD by chain depth [1:2.20, 4:1.36], max_valid_chain=4 (single_step_only)
-
Accuracy gate passed (operator <operator_id>)
| Field | Value | Notes |
|---|---|---|
method | lhs | |
grid_points | 8 | |
steps | 4 | |
dt | 0.25 | |
compare_brute_srk | True | |
finalists | 0 | Sweep finished - example params {'a': np.float64(1.077181249573271), 'b': np.float64(3.208828051836704)}, final state ~ [0.9899, 3.1069] |
-
SRK timing sample: 2 point(s) integrated for comparison
-
Horizon aligned: 4 jumps x 0.25 s = 1.0 s (jump_lag matches dt)
| Field | Value | Notes |
|---|---|---|
srk_sample_n | 8 | |
full_grid_truth | True | |
min_spearman | 0.85 | Full-grid SRK truth: 8/8 rankable in 0.1 s |
SRK ground truth (authoritative top-N)
| Field | Value | Notes |
|---|---|---|
grid_points | 8 | |
srk_ranked | 8 | |
export | finalists_ground_truth_top8.csv | |
srk_best_jump_rank | 4 | SRK top-10 in jump top-500: 8/8 (100% recall) |
-
SRK top-50 in jump top-500: 8/8 (100% recall)
-
SRK top-100 in jump top-500: 8/8 (100% recall)
Rank verification (jump vs full SRK)
| Field | Value | Notes |
|---|---|---|
verified_rows | 8/8 jump-ranked rows | |
spearman_rho | 0.833 (gate >= 0.85) | |
srk_verify_time | 0.1 s | |
best_srk_jump_rank | #4 (regret 3) | |
verdict | FAIL — do not cite ranking to clients | top-10 overlap: skipped (sample size 8 < top_n 10) |
-
top-50 overlap: skipped (sample size 8 < top_n 50)
-
top-100 overlap: skipped (sample size 8 < top_n 100)
-
steady_ok agreement (jump vs SRK): 100%
-
Report: <session-dir>\rank_verification_report.json
-
SRK shortlist (verified ranking for HPC handoff): <session-dir>\srk_shortlist.csv
-
SRK ground truth (authoritative): <session-dir>\finalists_ground_truth_top8.csv
-
Authoritative deliverable: finalists_ground_truth_top8.csv (SRK ground truth — use this for HPC handoff)
Act 5 — Takeaway
Pick grid when you want corners; pick LHS when you want space-filling coverage at the same budget. Next: reuse a calibrated session for live what-if.
Why run SolvJump here?
-
Build the business case: measured jump sweep vs extrapolated brute SRK on the same grid.
-
Take timing receipts to a design review instead of arguing from intuition.
-
About 128× faster than full SolvSRK on every grid point (jump sweep vs brute estimate).
Guided tour output
Verified console capture from 04_python_session.py:
Same Brusselator physics | grid then Latin hypercube
-- Act 1 - Why a Python Session --
-> YAML is the production path. Session.from_model is the same routing/calibration with knobs in code: param_schema, y0, t_span, stiffness_nom, solvtune_objective, mode, seeds, catalog_slug.
model: <product-root>\python\examples\brusselator_model.py:make_rhs
parameters: a in [0.8, 1.2], b in [2.5, 3.5]
method_first: grid n=4
-- Act 2 - Calibrate once --
-> One Session, two sweeps — grid then LHS — sharing the operator if gates passed.
model: <product-root>\python\examples\brusselator_model.py:make_rhs
dim: 2
stiffness_nom: S1
horizon: [0.0, 10.0]
problem_set_operators: 256
-> kepler | dim=2 | stiffness=S0 | dynamics=conservative
log: Jump recommendation: confidence=exact, cache_ref=<operator_id>, mode=auto
seeds: 8
reason: no exact cache match for this model
-> Collecting trajectory training pairs: jump lag=0.25s, SolvTune arm=solvsrk, objective=balanced, target chain=4
log: SRK collection done - 24 train pairs from 3 trajectories (0 failed, need >=8 train)
log: Fitted nominal+guard kernels - train median SCD=2.41, holdout median SCD=2.20 (n=8/8)
log: Rollout validation - SCD by chain depth [1:2.20, 4:1.36], max_valid_chain=4 (single_step_only)
log: Accuracy gate passed (operator <operator_id>)
_section: Operator setup
log: SolvJump fitted a fast approximation and checked it against full SolvSRK — it passed.
accuracy_check: acceptable quality
operator_id: <operator_id>
fit_time: 1.3 s (one-time before the sweep)
-- Act 3 - Tensor grid --
-> sweep(method='grid', n=4) builds a small tensor product.
log: Sweep finished - example params {'a': 1.2, 'b': 2.5}, final state ~ [2.5436, 1.016]
log: SRK timing sample: 3 point(s) integrated for comparison
log: Horizon aligned: 4 jumps x 0.25 s = 1.0 s (jump_lag matches dt)
_section: Sweep results
completed: 4 parameter sets
skipped: 0
-> Low: params={'a': 0.8, 'b': 2.5}
log: Mid: params={'a': 0.8, 'b': 3.5}
log: High: params={'a': 1.2, 'b': 3.5}
_section: How long did this take?
search_method: fast jump sweep
grid_size: 4
operator_fit: 1.3 s
fast_sweep: 1 ms
this_run_total: 1.3 s
vs_full_SolvSRK_on_every_study_point: ~141 ms for all 4 points in this study (extrapolated from 3 spot checks — not a larger search)
speedup_vs_full_grid: ~99.5× (jump sweep vs brute estimate)
-> All results CSV: <session-dir>\sweep_results.csv
-- Act 4 - Latin hypercube --
-> method='lhs' spreads the same n points through the box. Production YAML: brusselator_study_lhs.yaml (sweep.method: lhs).
_section: Study definition
model: <product-root>\python\examples\brusselator_model.py:make_rhs
dynamics_params: a in [0.8, 1.2], b in [2.5, 3.5]
y0: [1.0, 3.0]
t_span: [0.0, 10.0]
mode: auto
calibration_seeds: 8
_section: Operator routing, calibration & sweep (3 steps)
model: <product-root>\python\examples\brusselator_model.py:make_rhs
dim: 2
stiffness_nom: S1
horizon: [0.0, 10.0]
problem_set_operators: 256
-> kepler | dim=2 | stiffness=S0 | dynamics=conservative
log: Jump recommendation: confidence=exact, cache_ref=<operator_id>, mode=auto
seeds: 8
reason: no exact cache match for this model
-> Collecting trajectory training pairs: jump lag=0.25s, SolvTune arm=solvsrk, objective=balanced, target chain=4
log: SRK collection done - 24 train pairs from 3 trajectories (0 failed, need >=8 train)
log: Fitted nominal+guard kernels - train median SCD=2.41, holdout median SCD=2.20 (n=8/8)
log: Rollout validation - SCD by chain depth [1:2.20, 4:1.36], max_valid_chain=4 (single_step_only)
log: Accuracy gate passed (operator <operator_id>)
method: lhs
grid_points: 8
steps: 4
dt: 0.25
compare_brute_srk: True
finalists: 0
-> Sweep finished - example params {'a': np.float64(1.077181249573271), 'b': np.float64(3.208828051836704)}, final state ~ [0.9899, 3.1069]
log: SRK timing sample: 2 point(s) integrated for comparison
log: Horizon aligned: 4 jumps x 0.25 s = 1.0 s (jump_lag matches dt)
srk_sample_n: 8
full_grid_truth: True
min_spearman: 0.85
-> Full-grid SRK truth: 8/8 rankable in 0.1 s
_section: SRK ground truth (authoritative top-N)
grid_points: 8
srk_ranked: 8
export: finalists_ground_truth_top8.csv
srk_best_jump_rank: 4
-> SRK top-10 in jump top-500: 8/8 (100% recall)
log: SRK top-50 in jump top-500: 8/8 (100% recall)
log: SRK top-100 in jump top-500: 8/8 (100% recall)
_section: Rank verification (jump vs full SRK)
verified_rows: 8/8 jump-ranked rows
spearman_rho: 0.833 (gate >= 0.85)
srk_verify_time: 0.1 s
best_srk_jump_rank: #4 (regret 3)
verdict: FAIL — do not cite ranking to clients
-> top-10 overlap: skipped (sample size 8 < top_n 10)
log: top-50 overlap: skipped (sample size 8 < top_n 50)
log: top-100 overlap: skipped (sample size 8 < top_n 100)
log: steady_ok agreement (jump vs SRK): 100%
log: Report: <session-dir>\rank_verification_report.json
log: SRK shortlist (verified ranking for HPC handoff): <session-dir>\srk_shortlist.csv
log: SRK ground truth (authoritative): <session-dir>\finalists_ground_truth_top8.csv
log: Authoritative deliverable: finalists_ground_truth_top8.csv (SRK ground truth — use this for HPC handoff)
-- Act 5 - Takeaway --
-> Pick grid when you want corners; pick LHS when you want space-filling coverage at the same budget. Next: reuse a calibrated session for live what-if.
_section: Why run SolvJump here?
log: Build the business case: measured jump sweep vs extrapolated brute SRK on the same grid.
log: Take timing receipts to a design review instead of arguing from intuition.
log: About 128× faster than full SolvSRK on every grid point (jump sweep vs brute estimate).
-- Run complete --
session_id: <session_id>
results_folder: <session-dir>
points_completed: 8 ok, 0 skipped (of 8)
fast_sweep: 0.8 ms
timing_sample: 24.4 ms (2 full-SolvSRK spot checks)
vs_full_SolvSRK_on_every_study_point: ~98 ms for all 8 points in this study (from 2 spot checks — not a larger search)
speedup: ~128.3× vs full SolvSRK on every grid point
receipt_calibration: <session-dir>\calibration_receipt.json
receipt_sweep: <session-dir>\sweep_receipt.json
receipt_rank_verification: <session-dir>\rank_verification_report.json
receipt_srk_shortlist: <session-dir>\srk_shortlist.csv
receipt_finalists: <session-dir>\finalists_ground_truth_top8.csv
-- Sweep results --
completed: 8 parameter sets
skipped: 0
-- How long did this take? --
search_method: fast jump sweep
grid_size: 8
operator_fit: 828 ms
fast_sweep: 1 ms
this_run_total: 828 ms
vs_full_SolvSRK_on_every_study_point: ~98 ms for all 8 points in this study (extrapolated from 2 spot checks — not a larger search)
speedup_vs_full_grid: ~128.3× (jump sweep vs brute estimate)
-- Act 5 - Takeaway --
-- Why run SolvJump here? --
-- Run complete --
session_id: <session_id>
results_folder: <session-dir>
points_completed: 8 ok, 0 skipped (of 8)
fast_sweep: 0.8 ms
timing_sample: 24.4 ms (2 full-SolvSRK spot checks)
vs_full_SolvSRK_on_every_study_point: ~98 ms for all 8 points in this study (from 2 spot checks — not a larger search)
speedup: ~128.3× vs full SolvSRK on every grid point
receipt_calibration: <session-dir>\calibration_receipt.json
receipt_sweep: <session-dir>\sweep_receipt.json
receipt_rank_verification: <session-dir>\rank_verification_report.json
receipt_srk_shortlist: <session-dir>\srk_shortlist.csv
receipt_finalists: <session-dir>\finalists_ground_truth_top8.csvRun it
cd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/04_python_session.py
# Production one-liner — same API call you ship:
solvjump run examples/brusselator_study_lhs.yamlWhat comes next
Journey 05 lists sessions and runs live what-if propagate() on a calibrated operator.
Navigation
← Journey 03 — First YAML study · Journey 05 — Sessions and live what-if →