Skip to content

Inline linear SCCs: 296×296 dense runtime solve for an ~18-unknown core; non-rank-tolerant; symbolic shrinkage intractable — solve sparse+rank-tolerant instead #95

Description

@baggepinnen

Summary

For multibody vehicle models, the inline-linear-SCC pass (DefaultReassembleAlgorithm(inline_linear_sccs = true)) emits a runtime dense A \ b over a system that is an order of magnitude larger than the mathematically-required core, and solves it with a non-rank-tolerant dense LU. Measured on a half-car suspension model (22 states after mtkcompile):

  • The big algebraic SCC enters get_linear_scc_linsol with 396 rows.
  • __reduce_linear_system! removes only 101 (the all-constant-coefficient rows) → a 296×296 dense runtime LU, evaluated every RHS call and during initialization reconstruction.
  • Of the 396 rows, 384 are matched (torn) and 378 of those have a constant pivot coefficient — i.e. the irreducible simultaneous core is only ~18–24 unknowns (consistent with the same model compiled with inline_linear_sccs=false, which yields a 48-state DAE = 22 differential + 26 algebraic).
  • The emitted 296×296 pattern is 1.6% dense and, after a Dulmage–Mendelsohn analysis, ~93% block-triangular given the tearing order; only the small core is truly coupled.

The inflation mechanism: rows whose non-pivot coefficients are symbolic (every product-rule-differentiated definitional equation — rotation-matrix entries, contact-frame vectors, etc.) fail the all(isconst, coeffs) gate in __reduce_linear_system! and are kept inside the numeric solve, even though tearing already matched them with constant pivots.

Why the obvious fix doesn't work (tested)

Relaxing the gate to "pivot coefficient constant" (which is sufficient for a division-free elimination, and the alias/substitution machinery in __reduce_linear_system!/get_new_mm handles symbolic alias coefficients as-is) shrinks the system but explodes symbolically:

  1. First failure: iszero(::Num) inside get_new_mm's duplicate-entry pruning goes through fraction_iszero → expand, which is exponential for the deep product-of-sums coefficients → OutOfMemoryError. (Workaroundable with a cheap syntactic zero test — see suggestion below.)
  2. With that bypassed, mtkcompile succeeds (~90 s) but ODEProblem construction stalls indefinitely: the substituted core coefficients are enormous expression trees and codegen blows up.

So full symbolic elimination of the torn rows is intractable; the inflation is an inherent artifact of numeric tearing. Sequential runtime evaluation of the eliminated rows is also impossible (they depend on the core unknowns — circular).

Suggested fixes

  1. Solve the emitted system sparse and rank-tolerant at runtime. The pattern is already available (the ArrayMaker regions); a sparse LU/QR with fill-reducing ordering automatically exploits the ~93% triangular structure, so the 296-system costs about as much as the 18-core. Crucially it should be rank-revealing (or regularized): these acceleration/reaction systems are legitimately rank-deficient whenever constraint reactions are statically indeterminate (vehicle with ≥2 rigid suspension mounts on one body) — the current dense \ throws SingularException (LU) and the DyadCompilerPasses LDIV rewrite produces silent Inf (unpivoted QR). See JuliaComputing/DyadCompilerPasses.jl#40.
  2. Independent robustness improvement: the duplicate-entry pruning in get_new_mm should use a cheap syntactic zero test (term construction already cancels syntactically identical terms; an entry that is mathematically zero but kept is harmless), e.g. an overridable prune_iszero(x) = iszero(x) specialized for Num to a syntactic check — exact iszero(::Num) can OOM via polynomial expansion.
  3. Optionally expose the SCC composition (total/matched/const-pivot counts) as a debug aid — these numbers immediately reveal the inflation.

Context

Found while debugging SingularException(13) during initialization of suspension vehicle models (Multibody-style components). ModelingToolkit v11.26.8 (#master), ModelingToolkitTearing v1.14.1, StateSelection v1.9.3. Related: SciML/ModelingToolkit.jl#4237 (init reconstruction LU of indeterminate reactions), SciML/ModelingToolkit.jl#4607.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions