feat: gds-analysis package + audit fixes + examples#146
Merged
rororowyourboat merged 22 commits intomainfrom Mar 28, 2026
Merged
feat: gds-analysis package + audit fixes + examples#146rororowyourboat merged 22 commits intomainfrom
rororowyourboat merged 22 commits intomainfrom
Conversation
New package connecting structural annotations from gds-framework to gds-sim execution, completing the spec-to-simulation pipeline. adapter.py: spec_to_model() maps GDSSpec blocks to gds_sim.Model - BoundaryAction + Policy → policies - Mechanism.updates → state update functions (keyed by state var) - Default initial state from entities - Optional constraint enforcement via AdmissibleInputConstraint constraints.py: guarded_policy() wraps policies with admissibility checks at runtime (warn/raise/zero on violation) metrics.py: trajectory_distances() computes StateMetric distances between successive trajectory states 21 tests covering adapter, constraints, metrics, and end-to-end simulation with constraint enforcement and distance computation.
…s 4-5) reachability.py implements Paper Definitions 4.1 and 4.2: - reachable_set(): compute R(x) by running one timestep per input sample, deduplicating reached states by fingerprint - reachable_graph(): BFS expansion from initial states, building an adjacency dict of state transitions - configuration_space(): Tarjan's SCC algorithm on the reachability graph, returning mutually reachable state sets sorted by size 11 new tests: single/multiple/duplicate inputs, empty inputs, depth-1 and depth-2 graph expansion, SCC cases (self-loop, cycle, DAG, disconnected), and end-to-end thermostat integration.
Add gds-continuous package — continuous-time ODE integration engine mirroring gds-sim's architecture but wrapping scipy.integrate.solve_ivp. Standalone (pydantic-only), scipy is an optional dependency. Restructure root metapackage from hard dependencies to optional extras so users pick adapters: `uv add gds-core[control,continuous]`. 43 tests, 90% coverage. Covers exponential decay (exact solution), harmonic oscillator (energy conservation), parameter sweeps, 6 solver methods, and terminal event detection.
Extends ControlModel with symbolic ODEs (StateEquation, OutputEquation) that compile to plain Python callables via sympy.lambdify. Includes Jacobian linearization at arbitrary operating points. Pipeline: SymbolicControlModel → to_ode_function() → ODEFunction callable that feeds directly into gds-continuous. SymPy is optional. 29 tests, 95% coverage. Covers decay, oscillator, Van der Pol models with exact RHS verification, linearization Jacobians, and end-to-end integration through gds-continuous.
Full pipeline demonstration: structural spec → adapter → simulate → metrics + reachability. Tests population conservation, epidemic progression, trajectory distances, reachable set, reachability graph, configuration space (SCCs), and constraint enforcement. 8 tests using float-valued SIR dynamics (beta=0.3, gamma=0.1, N=1000).
Recreates key numerical results from mzargham/hc-marimo using gds-continuous, proving the ODE engine handles a real differential game (Isaacs 1951). 5 tests verify: Hamiltonian conservation (H* ~ 0), costate norm conservation (||p||^2 invariant), forward/backward capture round-trip, stationary evader straight-line capture, and parameter sweep over evader speed ratios.
Full differential game example demonstrating the gds-symbolic + gds-continuous pipeline against mzargham/hc-marimo reference: - Symbolic derivation of Hamiltonian and optimal controls (SymPy) - Hand-coded numpy RHS as independent cross-check - Backward reachable set (isochrone) computation - Conservation law verification (H*, ||p||^2) - Forward/backward capture round-trip - Usable part boundary conditions 9 tests tracing to hc-marimo verification IDs (T1-T7, ISO).
…operties End-to-end test of the crosswalk mechanism design problem using gds-analysis. Behavioral functions implement discrete Markov transitions from the Zargham & Shorish crosswalk lectures. Tests grounded in NotebookLM analysis of 6 source documents: - Crosswalk safety guarantee: p=k → Stopped, never Accident - Accident reachable via jaywalking with bad luck - Flowing unreachable from Accident in one step - Not crossing preserves Flowing - All three states reachable from Flowing - Configuration space via SCC analysis
Interactive notebook with parameter sliders (v_E, omega_max, ell), backward trajectory computation via gds-continuous, isochrone visualization, and conservation law verification table. 16 cells: symbolic derivation (SymPy), lambdification, ODEModel integration, matplotlib trajectory/isochrone plots.
Crosswalk analysis: reachable set from each traffic state, reachability graph, configuration space (SCCs), trajectory metrics, and verification that Flowing is unreachable from Accident — grounded in Zargham & Shorish crosswalk lectures. SIR analysis: epidemic simulation with population conservation check, Euclidean state distance metrics, and reachable set from initial (S=999, I=1, R=0) under varied infection rates. Both scripts use the full gds-analysis pipeline: structural spec → adapter → simulate → metrics + reachability.
Critical: 1. _step_once strips metadata keys (timestep, substep, run, subset) from returned state dicts. BFS at depth > 1 no longer passes corrupted state to nested calls. 2. Adapter now warns when constraints are registered for non- BoundaryAction blocks (Policy/ControlAction). Important: 3. guarded_policy projects state to depends_on fields before calling constraint — enforces R1 structural skeleton at runtime. 4. ControlAction blocks now handled by adapter (raises ValueError if missing policy function, same as BoundaryAction/Policy). 5. Docstring corrected: "All blocks packed into a single StateUpdateBlock" (was falsely claiming per-wiring groups). 6. Tarjan SCC rewritten as iterative (no recursion limit for large reachability graphs). 7. assert replaced with ValueError in trajectory_distances (safe under python -O). Minor: 8. TYPE_CHECKING guards used for type-only imports. 9. Documentation claims left for separate update.
C-1: Wire output_fn into engine — now called per-timepoint with test C-2: Replace sympify with parse_expr (no eval, restricted local_dict) I-4: Move gds-continuous from hard dep to optional extra in gds-symbolic I-5: Remove runs field from ODESimulation (no purpose for deterministic ODEs) M-2: SymbolicError now inherits from CSError (exception hierarchy) M-3: Remove dead require_numpy() from _compat.py M-4: Replace relative imports with absolute in HC example tests I-2/I-3: Add CLAUDE.md documenting bridge gap and time-varying input limitation Also: add gds-analysis to root modular extras
feat: continuous-time ODE engine, SymPy bridge, and modular package extras
New gds_viz.phase module with vector fields, trajectories, nullclines, and full phase_portrait() combining all three. Supports >2D systems via fixed_states projection. Behind [phase] optional extra (matplotlib + numpy + gds-continuous) — existing Mermaid functionality has no new dependencies. 10 tests: vector field computation, trajectory integration, nullclines, Lorenz 3D projection. 97% package coverage. Closes #126.
Add `ogs.equilibrium` module with Nash equilibrium solving for 2-player normal-form games extracted from PatternIR terminal conditions. - `payoffs: dict[str, float]` field on TerminalCondition for numeric payoffs - `extract_payoff_matrices(ir)` builds numpy payoff matrices from action spaces and terminal conditions - `compute_nash(ir)` delegates to nashpy (support/vertex/Lemke-Howson) - `NashResult` with support() and expected_payoffs() methods nashpy is an optional dep: `uv add gds-games[nash]` 11 tests: Prisoner's Dilemma (unique pure NE), Matching Pennies (unique mixed NE at 0.5/0.5), Battle of Sexes (3 NE), expected payoffs, error cases. Closes #77.
Software audit fixes:
- Remove phantom spec parameter from reachable_set/reachable_graph
(was accepted but never read)
- Document state key convention ("Entity.Variable") in adapter module
docstring
- Warn on multi-update Mechanisms (same SUF registered per variable)
- Add exhaustive/sampled distinction to reachability docstrings
Data science audit fixes:
- Document coverage semantics: discrete systems should use exhaustive
input enumeration, continuous results are approximate
- Add SCC completeness caveat to configuration_space docstring
…e 2) reachable_set() now returns ReachabilityResult dataclass with: - states: list of distinct reached states - n_samples: number of input samples tried - n_distinct: number of distinct states found - is_exhaustive: caller-declared exhaustive enumeration flag New float_tolerance parameter rounds float values before fingerprinting, preventing distinct states from float rounding noise. Passed through to reachable_graph and _state_fingerprint. 3 new tests: metadata fields, exhaustive flag, float tolerance. Total: 52 tests, 94% coverage.
- Crosswalk reachability tests now set exhaustive=True (the 3 input
samples are exhaustive for the discrete {cross, safe_crossing} space)
- Float tolerance test uses assertEqual(n_distinct, 1) instead of <=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
spec_to_model(): GDSSpec → gds_sim.Model adapterguarded_policy(): runtime AdmissibleInputConstraint enforcement with depends_on projectiontrajectory_distances(): StateMetric computation on trajectoriesreachable_set(): R(x) via trajectory sampling with ReachabilityResult metadatareachable_graph(): BFS reachability with float_toleranceconfiguration_space(): iterative Tarjan SCC for X_CTest plan