`maybe_zeros` check does not look inside a division, so tearing divides by a parameter expression that is zero (NaN in initialization)
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Lĩnh vực
- compilers
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
_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.
- Ngôn ngữ chính
- Julia
- Star
- 8
- Fork
- 14
- Merge trung bình
- 2 ngày 14 giờ
- Pull request đã merge (30 ngày)
- 4
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của JuliaComputing/StateSelection.jl
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
JuliaComputing/StateSelection.jl#183 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Inline linear SCC solves share one `LinearCache` per matrix size, so every call refactorizesĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
JuliaComputing/StateSelection.jl#178 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 30/100
JuliaComputing/StateSelection.jl#177 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Inlined linear SCC of three unknowns: the run-time solve dominates the cost of the right-hand sideĐang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
JuliaComputing/StateSelection.jl#173 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 52/100
JuliaComputing/StateSelection.jl#172 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của JuliaComputing/StateSelection.jl
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 67/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
-
backport 1.14 GC
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
Maintainer thường phản hồi trong vòng 1 ngày