Skip to content

steady_state: solve a model whose only read of the time is a function that reports (#961) - #1412

Open
Montana wants to merge 2 commits into
lanl:mainfrom
Montana:fix/961-steady-state-time-only-reported
Open

Montana wants to merge 2 commits into
lanl:mainfrom
Montana:fix/961-steady-state-time-only-reported

Conversation

@Montana

@Montana Montana commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Part of #961: lifts the item "A function that reads the time and that nothing in the dynamics reads" (minutes := time/60, BIOMD0000000498, 739, 951, and a time-indexed table nothing reads).

What was refused

#710 refuses steady_state() for any model where some function reads the time (NetworkModel.functions_use_time, one flag for the whole function block). A function that only reports, like minutes := time/60, doesn't touch f(y), so the state those models settle to was refused for no reason. On main:

BIOMD0000000739: SimulationError: steady_state() is not supported for this model: it reads the time ...

The same happens for seen() = 2*time() beside A -> B at k (the existing test test_a_reported_function_that_reads_the_time_is_refused).

The change

Core:

  • RateDeps (the dependency walk behind reaction_rate_reads_time) gains a clock flag. It is set by time and by a time-indexed table, and not by a rateOf, which reads the running derivatives (0 at a steady state).
  • NetworkModel::rate_laws_read_clock(): does any reaction's rate read the clock.
  • NetworkModel::functions_reading_clock(): which functions' values move with the clock, through other functions and derived parameters (via expression_support).
  • find_steady_state refuses on rate_laws_read_clock() in place of functions_use_time(). In its result, each clock-reading function's value and output-sensitivity row are NaN: what was read is its value at t = 0, and it has no steady value.
  • Both predicates are bound, and added to the stub.

Python (_raise_if_no_steady_state_to_solve_for and the new _ss_reads_the_time): still refuses when the time reaches what is reported as a state:

  • a rate law;
  • a rule an assignment-rule species is reported through (S := B*(1 - exp(-time)), via _ar_report_map);
  • a compartment size (C := 1 + time/(1+time), via compartment_size_params).

Otherwise the state is solved for.

Reporting: SteadyStateResult records the clock-reading functions, and output_sensitivities("expression:seen") refuses by that reason ("it reads the time, so it has no steady value ... issue #961") instead of "its value is nan".

Unchanged: the early stop of run(steady_state=True) (the next #961 item), which still uses functions_use_time.

Tests

In test_steady_state_of_a_model_that_reads_time.py, #710's file:

Mutation tests (the core rebuilt for each C++ mutant): all 10 are killed.

mutant killed by
core gate back to functions_use_time the lifted tests
no NaN for the value 7 tests
no NaN for the sensitivity row the sensitivity test
rateOf counted as the clock the rateOf control
rates never read the clock the 7 "a rate reads the time" tests
clock functions not followed through other functions the nested test
a time table not the clock both table tests
no assignment-rule species check the rule-on-time tests
no compartment check the compartment test
no named reason the sensitivity test

Corpus

178 of 1,323 BioModels read the time and have no event.

  • 171 have a rate that reads it: refused, as before.
  • 3 read it through an assignment-rule species or a compartment: refused, as before.
  • 4 are lifted:
model reads the time in result here check
BIOMD0000000739 time_minutes converged, residual 4e-30 matches run() to t = 1e9 to 2.8e-7 at rtol 1e-12 (0.1 at 1e-8, 4e-4 at 1e-10)
BIOMD0000000498 parameter_1 (a pulse nothing reads) converged=False the run still drifts 31% between t = 1e6 and 1e7
BIOMD0000000951 minutes converged=False the run still drifts between 1e6 and 1e7
BIOMD0000000305 V_ave converged=False, naming 4 write-only accumulators and suggesting mask= with that mask the state matches run() at t = 1e6 to 1.5e-15, though converged stays False (residual 1.96)

Every model that ran before takes the same path: the NaN mask applies only where functions_use_time is set, and all such models were refused.

Checks (macOS arm64)

  • python/tests: 16,943 passed, 0 failed.
  • parity_checks/tests: 1,015 passed. The 2 failures (test_bngsim_provenance and the reference-archive replay) also fail on main here: their subprocess can't import bngsim without my local import shim.

pre-commit run --all-files, sphinx -W, and ci/changelog.py check/required are clean (changelog.d/961.changed.md).

Not verified

… that reports (lanl#961)

lanl#710 refused steady_state() wherever any function read the time, which
refused minutes := time/60 in BIOMD0000000739 and 951 though nothing in
the dynamics reads it. The refusal now asks what the time reaches: a
rate law (rate_laws_read_clock, which does not count a rateOf), or a
rule a species or compartment size is reported through. Where it
reaches neither, the state is solved for and each function that reads
the clock (functions_reading_clock) is reported as NaN, with a NaN
sensitivity row output_sensitivities refuses by that reason.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant