Add analyze_initialization_jacobian — diagnose rank-deficient initialization#4666
Merged
AayushSabharwal merged 3 commits intoJun 30, 2026
Merged
Conversation
Underdetermined or rank-deficient initialization systems are a common source of
intermittent ("flaky") initialization failures: the solve lands on different
solutions depending on the solver and on the nondeterministic ordering of the
assembled init unknowns/equations, and some orderings hit a singular realization
and throw while others succeed.
`report_initialization_nullspace(prob)` makes the offending degrees of freedom
explicit. It evaluates the Jacobian of the initialization residual at the initial
guess (via ForwardDiff), computes its SVD, and reports the unknowns that span the
null space — the directions along which the initialization is free to move.
Pinning those unknowns (or adding constraints) makes the init well-posed and
deterministic.
`prob` may carry initialization data (an `ODEProblem`/`DAEProblem` built from a
`System`) or be an initialization `NonlinearProblem`/`NonlinearLeastSquaresProblem`
directly. The per-variable weight reported is the diagonal of the null-space
projector (squared row norm over an orthonormal null-space basis), which is
invariant to the arbitrary basis chosen within the null space. A nullity of 0
means the init Jacobian has full column rank at the guess and is locally
well-posed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSDc4tFYygs26KhqZBACLS
…on_jacobian`
Extend the initialization diagnostic to report both sides of the rank deficiency of
the initialization residual Jacobian:
- underdetermined unknowns — its (right) null space, the directions the
initialization is free to move along; and
- redundant equations — its left null space, combinations of initialization
equations whose Jacobian rows are linearly dependent at `u0` and therefore do not
locally constrain any further degree of freedom.
The latter explains the common, confusing situation where an initialization has more
equations than unknowns yet is still underdetermined.
Renamed from `report_initialization_nullspace` to `analyze_initialization_jacobian`
now that it analyzes the full rank structure (rank, nullity, redundancy) rather than
only the null space. The return value gains `rank`, `redundancy`, and
`redundant_equations` (a vector of `equation => weight` pairs), and the per-unknown
result is renamed `underdetermined_unknowns`. Redundant equations are mapped back to
the symbolic initialization equations when available.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSDc4tFYygs26KhqZBACLS
report_initialization_nullspace — diagnose rank-deficient initializationanalyze_initialization_jacobian — diagnose rank-deficient initialization
…acobian` The overdetermined/underdetermined initialization warnings now suggest calling `analyze_initialization_jacobian(prob)` on the constructed problem to see exactly which unknowns are underdetermined and which equations are redundant, rather than just reporting the equation/unknown counts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSDc4tFYygs26KhqZBACLS
AayushSabharwal
approved these changes
Jun 30, 2026
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.
Motivation
Rank-deficient initialization systems are a recurring source of intermittent ("flaky") initialization failures. When the init Jacobian doesn't have full rank, the solve can land on different solutions — or fail — depending on the solver and on the (nondeterministic) ordering of the assembled init unknowns/equations: some orderings hit a singular realization and throw
SingularException, others succeed, so the same model fails maybe 1 run in 10.MTK already warns structurally (
overdetermined/underdetermined … N equations for M unknowns, plussingular_check's problematic-variable list), but it's often hard to tell which unknowns form the free direction, and — when an init has more equations than unknowns yet is still underdetermined — which equations are redundant. I hit this in a multibody model and ended up computing the init-Jacobian SVD by hand; it seems generally useful.What this adds
analyze_initialization_jacobian(prob; rtol, atol, threshold, verbose)evaluates the Jacobian of the initialization residual at the initial guessu0(viaForwardDiff), computes its SVD, and reports both sides of the rank deficiency:u0, so they don't locally constrain any further DOF). This explains the confusing case of "more equations than unknowns, yet still underdetermined."probmay carry initialization data (anODEProblem/DAEProblembuilt from aSystem) or be an initializationNonlinearProblem/NonlinearLeastSquaresProblemdirectly.Per-item weights are the diagonal of the respective null-space projector (squared row norm over an orthonormal basis), so they're invariant to the arbitrary basis chosen within the null space. Returns
(; jacobian, singular_values, rank, nullity, redundancy, underdetermined_unknowns, redundant_equations)and prints a report by default.Example
which immediately points at both the unpinned degrees of freedom to constrain and the equations that aren't pulling their weight.
Notes
src/initialization.jl, exported, with a docstring and tests intest/initialization_jacobian_analysis.jl(registered under theInitializationgroup): an underdetermined DAE (nonzero nullity), an overdetermined system with a redundant init equation (nonzero redundancy, equation named), a determined algebraic init (full rank), and a fully-IC-determined system (no init problem — handled gracefully).🤖 Generated with Claude Code