Repository navigation
klu: step each colored FD column on the error weights' scale (#1254) - #1295
Merged
Merged
Conversation
KLU's colored FD Jacobian stepped column j by sqrt(eps)*max(|y_j|, 1), a floor of 1 in absolute terms: a species of 1e-9 moved by fifteen times itself, and the matrix KLU factored was the Jacobian somewhere else. The step is now CVODE's own dense difference quotient's, max(sqrt(eps)*|y_j|, min_inc/ewt_j), from the session's error weights and step. lanl#1254's equality is as right on KLU as on dense, and three BioModels that failed on KLU with the FD Jacobian run.
# Conflicts: # include/bngsim/sparse_jacobian.hpp # src/cvode_simulator.cpp
…l#1254's xfail The colored quotient's session pointer was a second field beside main's dq_cvode_mem, which lanl#1002 added for exactly this, and it was set at the two CVodeSetUserData sites rather than where the callback is chosen. It is now dq_cvode_mem, set in setup_linsol_and_jac's colored branch as the dense branch sets it. min_inc is formed by one helper, cvode_dq_min_inc, which the dense quotient also uses; it returns false where CVodeGetCurrentStep fails, where the colored callback used to carry on with h = 0 and step a species at 0 by 0. lanl#1291's strict xfail on equality-dwell / klu_vs_dense is dropped: this is the fix it was waiting for.
…s on its weights too (lanl#1254) Two places the new step reached that the PR left as they were: - Where codegen compiles the right-hand side (256+ species, which KLU's routing reaches), CVODE's fy came from the compiled RHS while the colored columns are differenced on the interpreted one. Over CVODE's step their last bits read as slope: with jacobian="fd" on KLU, fceri_ji's compiled run took 241 steps where the interpreted one took 251 (1.6e-10 of the peak apart), Barua_2013 293 steps and 45 Jacobians against 288 and 39, fceri_fyn 257 against 242. The quotient's base is now the interpreted f(y) there, at one RHS call more per Jacobian, and the compiled runs take the dense route's steps on all four (4e-15 apart on fceri_ji). - The steady-state march's colored quotient kept the absolute floor while its dense quotient takes CVODE's step (lanl#1002). A nanomolar reversible chain marched 3574 steps on KLU without converging, where dense converged in 197; it now converges in dense's 197. On six corpus models the KLU march now takes dense's steps on four (SHP2_base_model, Barua_2007, Barua_2013, before_bunching) and lands where it did. Tests: one for each, and the PR's docstrings re-measured against main, which moved with lanl#1002 (KLU was 145x to 598x dense's error on the equality; it now reads dense's value in all four cases). The "untouched" control is renamed for what it checks, the fixture. KLU_PASSED moves to 28, floor 26.
…1254) The two routes difference on one step rule now, but they still factor and mend differently, and their step counts can part: 202 and 214 at rtol 1e-8 and 1001 samples, on the same Y(10). So the bound against dense is 10x, not 2x; main still fails it by 38x or more. The rtol 1e-10, 1001-sample case is dropped: there dense with the analytic Jacobian is 3.4e-3 off, as far as KLU with the wrong step was, so it cannot tell a wrong matrix from luck. The compiled-RHS test says what it guards, the base under the new step, which main also passes. KLU_PASSED is 27, floor 25.
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.
Fixes #1254.
Root cause
On the KLU route without an analytic Jacobian, CVODE has no difference quotient of its own for a sparse matrix, so bngsim fills the Jacobian by colored finite differences (
colored_fd_jacobian,include/bngsim/sparse_jacobian.hpp). Each column's step was:That's a floor of 1 in absolute terms. A species at 1e-9 was stepped by 1.5e-8, fifteen times itself, so the matrix KLU factored was the Jacobian at a different state. Newton then converged slowly, failed to converge (
CV_CONV_FAILURE), or was pushed to a state where the RHS was not finite (CV_REPTD_RHSFUNC_ERR).The dense route doesn't have this problem, because CVODE's own difference quotient (
cvLsDenseDQJac) takes each step on the error test's scale:Change
sparse_jacobian.hpp:colored_fd_jacobiantakes optional error weights andmin_inc. When given them, it uses CVODE's rule above. Without them it keeps the old step, so the steady-state march's call is unchanged.cvode_simulator.cpp:CvodeUserDatacarries the session'scvode_mem, set where it is registered.tmp1(CVODE's scratch vector for this callback), along with the current step, and computesmin_incexactly ascvLsDenseDQJacdoes.Why it is right
The colored method is exact column by column when the pattern is complete: no two columns in a color share a row. So with the same step rule, it gives the dense difference quotient's entries for the pattern. That is what this PR measures: on #1254's model and on the nanomolar chain below, KLU with the FD Jacobian now matches dense with the FD Jacobian.
Tests
python/tests/test_colored_fd_jacobian_steps.py(KLU-gated; added to thewindows-klu.ymlleg, whichtest_windows_klu_ci_coverage.pyrequires):Mutations (each rebuilt and run against the tests):
min_inctermmin_incalways 1, ignoring the step and normWhat #1254 does and does not close
The KLU part is fixed: KLU with the FD Jacobian is now as right as dense with it.
Not fixed here: the equality itself remains fragile on every route. An equality on a state has no root to stop the solver at, so when its switch flips depends on where the steps fall. Even dense with the analytic Jacobian is 1.1e-3 off at rtol 1e-10 with 1001 samples:
That is a separate problem, an equality rate-law switch with no root registered, and I'll open it as its own issue if wanted.
The corpus: every model this can move
Models #1257's suite flagged (PR #1291), which run on KLU+FD only when forced:
Every model the automatic routing sends to KLU (163: 122 BioModels, 6
.net, 35 SBML suite and event models). Each was run on main and on this PR, against dense:jacobian="auto")jacobian="fd"Some of the larger improvements, against dense+FD: BIOMD0000000152 9.6e-4 → 5.6e-11, metapop_sir_100 3.3e-5 → 1.9e-8, BIOMD0000000574 9.7e-5 → 1.7e-6, SHP2_base_model 3.5e-7 → 1.3e-12.
Of the 9 that drift farther from dense, 7 stay below their own rtol. The other two, measured against a reference at 10⁻⁴ × their tolerance:
BIOMD0000000153 moves from 3.1e-4 to 1.8e-1 from that reference. But every other route is already 1.8e-1 from it at rtol 1e-6: dense+FD, and KLU with the analytic Jacobian. The model isn't converged at its tolerance, and main's KLU+FD was nearer by chance. The fix makes it agree with every other route.
Suite
16,096 passed, 148 skipped, on macOS against main (023ea50), with the 3 failures below.
test_a_relative_interpreter_stays_in_its_venvis my scratch venv's/tmpreading as/private/tmp; it fails the same way on main.test_skip_auditandtest_windows_klu_ci_coverageflagged the first version of the new test file, which was right of them. Both are fixed: the test no longer needs Antimony, and the file is inwindows-klu.yml's run list and paths filter.KLU_PASSEDmoves from 20 to 26, floor 24, slack 5 (below the smallest file, 6), per the floors file's own rule. All 115 tests of those meta-checks and the new file pass.clang-format (pinned 17.0.6), ruff and
ci/changelog.py checkare clean.Interaction with #1291
#1291's shape test marks
equality-dwell / klu_vs_denseas a strict xfail on #1254. Whichever of the two merges second will see it unexpectedly pass, and must drop that xfail. I'll do it in whichever PR is left.Remaining risks
windows-klu.ymlwill run the tests on Windows.Maintainer review: the three strongest objections
CVodeGetErrWeightsandCVodeGetCurrentStep, which give exactly whatcvLsDenseDQJacreads internally. Without a session, the old step stays.Diff (source; the test, the workflow, the floors and the changelog fragment are not shown)