Skip to content

Commit 38024ea

Browse files
Merge pull request #566 from ChrisRackauckas-Claude/fix-zero-type-any-error
Fix MethodError: no method matching zero(::Type{Any}) in SINDy solve
2 parents bce42d8 + 21ab97d commit 38024ea

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

lib/DataDrivenSparse/test/sparse_linear_solve.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,40 @@ end
7070
@test vec(rescoeff)[0.25; 0.0; -1.0; 0.11; 0.0; -0.5] atol=5e-2
7171
end
7272
end
73+
74+
# Issue #564: Test that solve doesn't throw MethodError when coefficients are all zero
75+
# This can happen with very small data values or high regularization
76+
@testset "Zero coefficients handling (Issue #564)" begin
77+
rng = StableRNG(1111)
78+
79+
# Test case 1: Very small data values that lead to zero coefficients after regularization
80+
N = 3
81+
= randn(rng, N, 50) * 1e-10
82+
Ŷ = randn(rng, 1, 50) * 1e-10
83+
84+
@variables u[1:N]
85+
b = polynomial_basis(u, 2)
86+
basis = Basis(b, u)
87+
problem = DirectDataDrivenProblem(X̂, Ŷ)
88+
89+
λ = 1e-1
90+
opt = ADMM(λ)
91+
options = DataDrivenCommonOptions()
92+
93+
# This should not throw MethodError: no method matching zero(::Type{Any})
94+
result = @test_nowarn solve(problem, basis, opt, options = options)
95+
@test result isa DataDrivenSolution
96+
@test eltype(result.prob) == Float64
97+
98+
# Test case 2: High regularization that forces all coefficients to zero
99+
X̂2 = randn(rng, N, 50)
100+
Ŷ2 = randn(rng, 1, 50)
101+
problem2 = DirectDataDrivenProblem(X̂2, Ŷ2)
102+
103+
λ_high = 1e10 # Very high regularization
104+
opt_high = ADMM(λ_high)
105+
106+
result2 = @test_nowarn solve(problem2, basis, opt_high, options = options)
107+
@test result2 isa DataDrivenSolution
108+
@test eltype(result2.prob) == Float64
109+
end

src/basis/type.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,13 @@ This extends `getmetadata` in a way that all parameters have a numeric value.
532532
Values are unwrapped from symbolic wrappers to ensure compatibility with ODEProblem.
533533
"""
534534
function get_parameter_values(x::Basis)
535-
map(parameters(x)) do p
535+
ps = parameters(x)
536+
# Return a properly typed empty vector if there are no parameters
537+
# to avoid creating Any[] which causes type instability issues
538+
if isempty(ps)
539+
return Float64[]
540+
end
541+
map(ps) do p
536542
val = if hasmetadata(p, Symbolics.VariableDefaultValue)
537543
Symbolics.getdefaultval(p)
538544
else

src/solution.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,23 @@ function DataDrivenSolution(b::AbstractBasis, p::AbstractDataDrivenProblem,
3535
ps = get_parameter_values(b)
3636
prob = remake_problem(p, p = ps)
3737

38-
rss = sum(abs2, get_implicit_data(prob) .- b(prob))
38+
# Calculate residual sum of squares, handling potential type instability
39+
# when basis has no equations (which can result in Any-typed matrices)
40+
Y = get_implicit_data(prob)
41+
Ŷ = b(prob)
42+
T = eltype(p)
43+
residuals = Y .- Ŷ
44+
# Handle case where residuals may have element type Any
45+
# (e.g., when basis has no equations due to all-zero coefficients)
46+
if eltype(residuals) === Any
47+
if isempty(residuals)
48+
rss = zero(T)
49+
else
50+
rss = sum(abs2, T.(residuals))
51+
end
52+
else
53+
rss = sum(abs2, residuals)
54+
end
3955

4056
return DataDrivenSolution{eltype(p)}(b,
4157
retcode,

0 commit comments

Comments
 (0)