`maybe_zeros` check does not look inside a division, so tearing divides by a parameter expression that is zero (NaN in initialization)
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Ambito
- compilers
Direzione di ricerca
Start in lib/ModelingToolkitTearing/src/stateselection_interface.jl, at MaybeZeroQueryFn and maybe_zeros_descend, and at how query_maybe_zeros is called from _check_allow_symbolic_parameter. Run the reproducer with and without maybe_zeros to confirm that a BSImpl.Div root returns false. Done means a quotient whose numerator may be zero is flagged, the trim returns uc = [0, 0, 0] with Success when maybe_zeros is declared, and a regression test covers the Div case.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
_check_allow_symbolic_parameter relies on query_maybe_zeros to refuse dividing by a coefficient that may be zero. Neither maybe_zeros_descend nor MaybeZeroQueryFn handles BSImpl.Div, so a coefficient of the form num / den is always treated as safe, even when every term of num is a product of maybe_zeros parameters. Tearing then solves for a variable through that coefficient, and the generated code divides by zero at run time. The result is NaN with no warning.
I hit this when trimming a rigid body by initialization. Angular acceleration is written D(w) ~ inv(J) * M, with inv(J) spelled out as adjugate / det so that the ODE stays explicit (the mass-matrix form runs into SciML/ModelingToolkit.jl#5279). The moments M are parameters set to missing, and the trim condition is D(w) ~ 0. When the products of inertia Ixy, Ixz and Iyz are zero, the initialization system solves D(w[1]) = 0 for uc[3] and divides by (Ixy*Iyz + Ixz*Iyy), which is 0. Declaring maybe_zeros = [Ixy, Ixz, Iyz] does not help, because the coefficient is (Ixy*Iyz + Ixz*Iyy) / det(J).
Reproducer
using ModelingToolkit, OrdinaryDiffEqTsit5, SciMLBase, LinearAlgebra
using SimpleNonlinearSolve: SimpleNewtonRaphson
using ModelingToolkit: t_nounits as t, D_nounits as D
# D(w) ~ inv(J) * uc, with inv(J) written out as adjugate / det (so the ODE stays explicit).
# Trim: find the parameters uc such that D(w) = 0 (answer: uc = 0).
function model(; mz = false)
@parameters Ixx Iyy Izz Ixy Ixz Iyz uc[1:3] = zeros(3)
@variables w(t)[1:3] = zeros(3)
J = [Ixx -Ixy -Ixz; -Ixy Iyy -Iyz; -Ixz -Iyz Izz]
Jinv = [J[2,2]*J[3,3]-J[2,3]*J[3,2] J[1,3]*J[3,2]-J[1,2]*J[3,3] J[1,2]*J[2,3]-J[1,3]*J[2,2];
J[2,3]*J[3,1]-J[2,1]*J[3,3] J[1,1]*J[3,3]-J[1,3]*J[3,1] J[1,3]*J[2,1]-J[1,1]*J[2,3];
J[2,1]*J[3,2]-J[2,2]*J[3,1] J[1,2]*J[3,1]-J[1,1]*J[3,2] J[1,1]*J[2,2]-J[1,2]*J[2,1]] ./ det(J)
kw = mz ? (; maybe_zeros = [Ixy, Ixz, Iyz]) : (;)
System([D(w[i]) ~ sum(Jinv[i, j] * uc[j] for j in 1:3) for i in 1:3], t; name = :m, kw...)
end
for mz in (false, true)
s = mtkcompile(model(; mz))
vals = [s.Ixx => 3.4, s.Iyy => 8.2, s.Izz => 11.4, s.Ixy => 0.0, s.Ixz => 0.0, s.Iyz => 0.0]
prob = ODEProblem(s, [vals; s.uc => missing], (0.0, 1.0); guesses = [s.uc => ones(3)],
initialization_eqs = collect(D(s.w) .~ 0))
isys = prob.f.initialization_data.initializeprob.f.sys
torn = filter(eq -> any(isequal(eq.lhs), collect(s.uc)), observed(isys))
sol = solve(prob, Tsit5(); initializealg = SciMLBase.OverrideInit(nlsolve = SimpleNewtonRaphson()))
println("maybe_zeros = ", mz ? "[Ixy, Ixz, Iyz]" : "none")
println(" torn in the initialization system: ", join(string.(torn), "; "))
println(" retcode = ", sol.retcode, ", uc = ", sol.ps[s.uc])
end
Output
maybe_zeros = none
torn in the initialization system: uc[3] ~ (<det>*(-(((Ixy*Izz + Ixz*Iyz)*uc[2]) / <det>) - (((Iyy*Izz - (Iyz^2))*uc[1]) / <det>))) / (Ixy*Iyz + Ixz*Iyy)
retcode = DtNaN, uc = [NaN, NaN, NaN]
maybe_zeros = [Ixy, Ixz, Iyz]
torn in the initialization system: uc[3] ~ (<det>*(...)) / (Ixy*Iyz + Ixz*Iyy)
retcode = DtNaN, uc = [NaN, NaN, NaN]
(<det> abbreviates Ixx*(Iyy*Izz - (Iyz^2)) + Ixy*(-Ixy*Izz - Ixz*Iyz) - (Ixy*Iyz + Ixz*Iyy)*Ixz.)
With the same model, all three products non-zero (Ixy = 0.15, Ixz = 0.2, Iyz = -0.1) works, and so does Ixz alone. Which zero pattern fails depends on the pivot that tearing picks: in a larger model (the full 6-DOF body with forces) Ixz alone was the failing case.
Cause
find_eq_solvables! gets the coefficient a = (Ixy*Iyz + Ixz*Iyy) / <det> for uc[3] in D(w[1]) ~ 0. It is not constant, so _check_allow_symbolic_parameter asks query_maybe_zeros. That runs SU.query(MaybeZeroQueryFn(...), a; recurse = maybe_zeros_descend). The root node is a BSImpl.Div: the functor falls through to _ => return false, and maybe_zeros_descend also returns false for Div, so the numerator is never inspected. The ADD rule for the numerator would have returned true, because both of its terms are products containing a maybe_zeros parameter.
Suggested fix
Treat a quotient as possibly zero wherever its numerator may be zero, in MaybeZeroQueryFn (lib/ModelingToolkitTearing/src/stateselection_interface.jl), in the same way #79 added max, min and ifelse:
BSImpl.Div(; num) => return query_maybe_zeros(qf, num)
I checked this locally with an equivalent override of query_maybe_zeros (if operation(ex) === /, query the numerator) on ModelingToolkitTearing 1.20.7. With maybe_zeros = [Ixy, Ixz, Iyz] the initialization system then pivots on (Ixx*Iyy - Ixy^2) and the trim gives uc = [0, 0, 0], retcode Success. Without maybe_zeros it still fails, as expected.
Related
- A model compiled with non-zero defaults for Ixy, Ixz and Iyz, then run with them set to 0, fails in the same way even with the fix, unless
maybe_zerosis declared. Values given only toODEProblemare not used bydiscover_maybe_zeros; the initialization system is built atODEProblemtime and inheritsmaybe_zeros(sys)from the compiled system. I'm not sure whether that is intended. The general case (a pivot that is zero at run time with nothing to flag it) is SciML/ModelingToolkit.jl#5212. mtkcompile(sys; allow_parameter = false)does not change the initialization system's choice either. That looks expected, since the initialization system is compiled separately, but there seems to be no way to pass it through.- SciML/ModelingToolkit.jl#5279: same physical case (an inertia tensor with zero products), different code path (
inline_linear_sccs).
Versions: Julia 1.12.5, ModelingToolkit 11.45.4, ModelingToolkitBase 1.77.4, ModelingToolkitTearing 1.20.7, StateSelection 1.11.1, SymbolicUtils 4.51.0. maybe_zeros_descend and MaybeZeroQueryFn on StateSelection.jl main (checked 2026-10-16) are the same as in 1.20.7.
- Lingua principale
- Julia
- Stelle
- 8
- Fork
- 14
- Merge medio
- 8g 8h
- PR unite (30g)
- 1
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di JuliaComputing/StateSelection.jl
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
-
Inlined linear SCC of three unknowns: the run-time solve dominates the cost of the right-hand sideAperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
JuliaComputing/StateSelection.jl#173 · 3 commenti ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 52/100
JuliaComputing/StateSelection.jl#172 · 1 commento ·
-
State-machine query operators in ModelingToolkitTearing throw UndefVarError for wrapForse già presa @baggepinnen l’ha presa 16 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
Tutte le issue di JuliaComputing/StateSelection.jl
Issue simili
-
🕷️ bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
CliMA/ClimaSeaIce.jl#203 ·
I maintainer di solito rispondono entro 3 giorni
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
JuliaPhysics/BeamletOptics.jl#221 ·
I maintainer di solito rispondono entro 1 giorno
-
release preparation spack
Difficoltà 2/5 1-3 ore Idoneità per principianti 67/100
I maintainer di solito rispondono entro 1 giorno
-
30-minutes algorithm any-language codewars dart easy elixir hackerrank hacktoberfest haskell help wanted interview-prep julia leetcode lua open-source r scala students zig
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
Huerte/algo-solutions-python#163 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
QuantumSavory/QuantumClifford.jl#853 ·
I maintainer di solito rispondono entro 1 giorno