Repository navigation
Reaction catalog and oracle equilibria, spectra, timescales (#65) - #71
Merged
Merged
Conversation
Refs #65. Fills in koopman_graph.datasets.reaction.catalog and .oracles: - catalog: first_order_chain, reversible_dimerization, michaelis_menten (closed, element-balanced), brusselator and lotka_volterra (open), with frozen default parameters exposed as module constants. - oracles: conservation_laws (orthonormal left nullspace of N), stoichiometric_rank, equilibrium (exact for linear systems; damped non-negative Newton within the compatibility class otherwise; reports missing steady states and non-convergence), and oracle_spectrum / oracle_timescales / OracleSpectrum (Jacobian, dynamic eigenvalues with conservation zero modes removed, -1/Re(λ) timescales, exp(λ Δt) flow-map eigenvalues). Pure PyTorch; reuses mass_action_jacobian and linear_rate_equations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Linear equilibrium returns c_p + P0 (c0 - c_p), the t -> inf limit, so non-unique steady states (A -> B, A -> C) split by rate instead of falling back to lstsq's minimum-norm point; a defective zero mode raises. Rounding-level values are snapped to zero instead of going negative. - Newton also requires a small residual to converge and raises when the line search stalls, instead of reporting a tiny clamped step as a root. - OracleSpectrum gains zero_tolerance (1e-10 ||J||_F from oracle_spectrum) so near-zero real parts read as neutral in timescales and is_stable. - One SVD of N gives rank, range basis and conservation laws. - Every catalog constructor rejects non-finite or non-positive rates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 15, 2026
…e' into prop/catalog-oracles
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
tjkessler
added a commit
that referenced
this pull request
Oct 15, 2026
Carries the reviewed #69-#73 fixes up to #74. Conflict in datasets/reaction/oracles.py resolved by taking #71's hardened version (exact projector linear equilibrium, residual-checked Newton with line-search stall detection, OracleSpectrum.zero_tolerance, one SVD for rank / range / conservation laws) and re-applying #74's additions: - _require_stationary flux check on both Newton return paths, so a clamped one-way boundary stall is still refused; - eigenvalue_timescales as the single timescale implementation, now with an optional absolute zero_tolerance (OracleSpectrum.timescales passes its own; 0 compares exactly) and the round-off default otherwise. test_equilibrium_rejects_one_way_boundary_point accepts either guard's message, since both refuse the stalled point. Committed cards are unchanged (build_reaction_cards.py --check passes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Refs #65. Fourth PR for the reaction-network datasets. Stacked on #70, which is stacked on #69. Merge in order #69 → #70 → this one; each PR retargets to
mainwhen the one below it merges (branch auto-delete is on). CI runs only on PRs targetingmain, so it starts once this PR retargets.Changes
koopman_graph.datasets.reaction.catalog: reference systems with frozen defaults. The defaults are module constants, because changing them changes every dataset built from the catalog.first_order_chain(n, rate_constants=None)S0 → … → S(n-1); default rates1, 0.5, 0.25, …keep the eigenvalues distinctreversible_dimerization()2 A ⇌ A2(3.0, 0.5); elementXcounts monomer unitsmichaelis_menten()E + S ⇌ ES → E + P(10, 1, 0.5); elementsEnz,Sub; equilibrium on the boundarybrusselator(a, b)(1, 3): unstable focus with a limit cycle (b > 1 + a²)lotka_volterra(α, β, γ)±i√(αγ)koopman_graph.datasets.reaction.oraclesconservation_lawsreturns an orthonormal basisWof the left nullspace ofN, soW N = 0. This covers element balance plus any extra stoichiometric conservation.stoichiometric_rankgivesrank(N).equilibrium(system, initial, *, guess=None)finds a steady state with the same conserved totals asinitial.[A; W] c = [-b; W c0]. RaisesValueErrorwhen there is no steady state, e.g. a feed into a species with no outflow.c ≥ 0. RaisesRuntimeErrorif it doesn't converge.oracle_spectrum(system, state)returns anOracleSpectrumwith:mass_action_jacobian);S − rank(N)conservation zero modes removed (J = N ∂r/∂chas rank ≤rank(N));timescales = −1/Re(λ): positive for decaying modes,inffor a centre, negative for growing modes;is_stable, anddiscrete_eigenvalues(Δt) = exp(λ Δt)for comparison with a learned discrete Koopman operator.oracle_timescalesis a shortcut for the timescales alone.Tests (all against closed forms)
(2, 1, 0.25)gives eigenvalues{−0.25, −1, −2}plus one removed zero, and timescales{4, 1, 0.5}.(1/4, 3/8)withλ = −(4k_f A* + k_r) = −3.5. A long RK4 run converges to that equilibrium within 1e-10.(E0, 0, 0, S0). Its two dynamic eigenvalues match the free-substrate block, two conservation modes are removed, and RK4 converges to it.(a, b/a)hasλ = 0.5 ± i√3/2, so it's unstable with timescales−2.λ = ±i, infinite timescales, and|exp(λΔt)| = 1. Its RK4 orbit conservesV = x − ln x + y − ln y.(feed/k1, k1A*/k_out). A missing steady state and Newton non-convergence both raise.W N = 0,Whas orthonormal rows, and the element-matrix rows lie in the span ofW.check_element_balance.Verification (macOS arm64, Python 3.13)
ruff checkandruff format --checkonsrc/ tests/pass.pytest -n auto -m "not slow and not distributed and not ray" --cov=koopman_graph --cov-fail-under=90: 3410 passed, 26 skipped, 97.25%.catalog.pyis at 100% andoracles.pyat 99%.sphinx-build -W --keep-goingpasses.🤖 Generated with Claude Code