Skip to content

Commit ff724e9

Browse files
singhharsh1708Your Name
authored andcommitted
get_differential_vars: check Diagonal.diag, not the full matrix (#3922)
all(!iszero, mm) visits every (i, j) pair, including scalar getindex on the underlying diagonal vector. For a GPU-backed diagonal mass matrix (Diagonal{Float64, <:CuVector}), that's disallowed and throws on every DAE init, which is why every case in test/gpu/dae_tests.jl fails regardless of solver or jac_prototype. Diagonal structurally guarantees off-diagonal zeros, so check mm.diag directly. Also wires up show_errors(results) in the GPU DAE test entry point, which was defined but never called, so a real failure prints its actual exception instead of just a status glyph.
1 parent 4e1a434 commit ff724e9

3 files changed

Lines changed: 31 additions & 0 deletions

File tree

lib/OrdinaryDiffEqCore/src/misc_utils.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ function get_differential_vars(f, u)
213213
elseif mm isa SciMLOperators.ScalarOperator
214214
# λ·I: every variable is algebraic when λ is zero.
215215
return iszero(mm.val) ? falses(size(u)) : nothing
216+
elseif mm isa Diagonal
217+
# `Diagonal` structurally guarantees off-diagonal zeros, so check
218+
# only `mm.diag` rather than the generic `all(!iszero, ::AbstractMatrix)`
219+
# / `_isdiag` paths below, both of which visit every (i, j) pair via
220+
# scalar `getindex` -- disallowed when `mm.diag` is GPU-backed
221+
# (e.g. `Diagonal{Float64, <:CuVector}`), unlike a reduction over
222+
# `mm.diag` itself, which dispatches to a proper GPU kernel.
223+
return reshape(mm.diag .!= 0, size(u))
216224
elseif all(!iszero, mm)
217225
return trues(size(mm, 1))
218226
elseif !(mm isa SciMLOperators.AbstractSciMLOperator) && _isdiag(mm)

lib/OrdinaryDiffEqCore/test/algebraic_vars_detection_tests.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,21 @@ end
113113
ff_singular = ODEFunction(f!, mass_matrix = ScalarOperator(0.0))
114114
@test get_differential_vars(ff_singular, u) == falses(3)
115115
end
116+
117+
@testset "get_differential_vars: Diagonal mass matrix" begin
118+
# `get_differential_vars` must only ever check `mm.diag`, not the full matrix
119+
# via `all(!iszero, mm)` -- that visits every (i, j) pair, which is wrong (both
120+
# slow: O(n^2) instead of O(n)) and, for a GPU-backed diagonal
121+
# (`Diagonal{Float64, <:CuVector}`), unsafe: it calls `getindex` on the
122+
# underlying vector one element at a time, which CUDA disallows by default.
123+
# Can't construct a CuVector here (no GPU in CI); this pins the CPU behavior
124+
# so a regression back to the whole-matrix check is caught.
125+
f!(du, u, p, t) = (du .= -u; nothing)
126+
u = zeros(4)
127+
ff_mixed = ODEFunction(f!, mass_matrix = Diagonal([1.0, 1.0, 0.0, 0.0]))
128+
@test get_differential_vars(ff_mixed, u) == Bool[1, 1, 0, 0]
129+
ff_alldiff = ODEFunction(f!, mass_matrix = Diagonal([1.0, 2.0, 3.0, 4.0]))
130+
@test get_differential_vars(ff_alldiff, u) == Bool[1, 1, 1, 1]
131+
ff_allalg = ODEFunction(f!, mass_matrix = Diagonal(zeros(4)))
132+
@test get_differential_vars(ff_allalg, u) == Bool[0, 0, 0, 0]
133+
end

test/gpu/dae_tests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,11 @@ end
458458
@testset "GPU DAE solver compatibility" begin
459459
global results = run_all()
460460
show_results(results)
461+
# Print the actual caught exceptions on failure -- `show_results` only prints
462+
# a per-case status glyph, which is not enough on its own to diagnose what
463+
# broke from a CI log.
464+
any(r -> r.status (:gpu_error, :gpu_mismatch, :gpu_failed), results) &&
465+
show_errors(results)
461466
@testset "$(r.case)" for r in results
462467
# :cpu_skip = the CPU couldn't solve this case, so there is no reference
463468
# to compare against — record as skipped, not pass/fail.

0 commit comments

Comments
 (0)