Skip to content

Commit 11c7553

Browse files
Fix sparse noise_rate_prototype: make rand_prototype always dense (#3178)
1 parent 0be95df commit 11c7553

3 files changed

Lines changed: 70 additions & 0 deletions

File tree

lib/StochasticDiffEq/test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ const is_APPVEYOR = Sys.iswindows() && haskey(ENV, "APPVEYOR")
132132
@time @safetestset "Non-diagonal EulerHeun sparse alloc" begin
133133
include("nondiag_noise_eulerheun_test.jl")
134134
end
135+
@time @safetestset "Sparse noise_rate_prototype Tests" begin
136+
include("sparse_noise_tests.jl")
137+
end
135138
@time @safetestset "No Index Tests" begin
136139
include("noindex_tests.jl")
137140
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using StochasticDiffEq, SparseArrays, Test, Random
2+
3+
Random.seed!(42)
4+
5+
# 4-state system driven by 2 independent Wiener processes.
6+
# Only states 1 and 3 are coupled to noise; states 2 and 4 are purely deterministic.
7+
# The noise matrix is 4×2 sparse: g[1,1] = 0.1*u[1], g[3,2] = 0.1*u[3], rest zero.
8+
9+
function f_sparse!(du, u, p, t)
10+
return du .= -u
11+
end
12+
13+
function g_sparse!(du, u, p, t)
14+
du[1, 1] = 0.1 * u[1]
15+
return du[3, 2] = 0.1 * u[3]
16+
end
17+
18+
u0 = ones(4)
19+
tspan = (0.0, 1.0)
20+
21+
noise_prototype = spzeros(4, 2)
22+
noise_prototype[1, 1] = 1.0
23+
noise_prototype[3, 2] = 1.0
24+
25+
prob = SDEProblem(f_sparse!, g_sparse!, u0, tspan, noise_rate_prototype = noise_prototype)
26+
27+
@testset "Sparse noise_rate_prototype — EM IIP" begin
28+
sol = solve(prob, EM(), dt = 0.01)
29+
@test sol.retcode == ReturnCode.Success
30+
@test length(sol) > 1
31+
# States 2 and 4 have zero noise rows → evolve purely as du/dt = -u → u(1) = exp(-1)
32+
@test sol.u[end][2] exp(-1.0) rtol = 0.02
33+
@test sol.u[end][4] exp(-1.0) rtol = 0.02
34+
end
35+
36+
@testset "Sparse noise_rate_prototype — EulerHeun IIP" begin
37+
sol = solve(prob, EulerHeun(), dt = 0.01)
38+
@test sol.retcode == ReturnCode.Success
39+
@test length(sol) > 1
40+
@test sol.u[end][2] exp(-1.0) rtol = 0.02
41+
@test sol.u[end][4] exp(-1.0) rtol = 0.02
42+
end
43+
44+
# OOP variant
45+
function f_sparse_oop(u, p, t)
46+
return -u
47+
end
48+
49+
function g_sparse_oop(u, p, t)
50+
du = spzeros(4, 2)
51+
du[1, 1] = 0.1 * u[1]
52+
du[3, 2] = 0.1 * u[3]
53+
return du
54+
end
55+
56+
prob_oop = SDEProblem(f_sparse_oop, g_sparse_oop, u0, tspan, noise_rate_prototype = noise_prototype)
57+
58+
@testset "Sparse noise_rate_prototype — EM OOP" begin
59+
sol = solve(prob_oop, EM(), dt = 0.01)
60+
@test sol.retcode == ReturnCode.Success
61+
@test sol.u[end][2] exp(-1.0) rtol = 0.02
62+
@test sol.u[end][4] exp(-1.0) rtol = 0.02
63+
end

lib/StochasticDiffEqCore/src/solve.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ function _sde_init(
404404
rand_prototype = adapt(
405405
SciMLBase.parameterless_type(u), zeros(randElType, size(noise_rate_prototype, 2))
406406
)
407+
elseif issparse(noise_rate_prototype)
408+
# Wiener increments must always be a dense vector regardless of whether
409+
# the noise matrix g is sparse; sparse dW has no defined mul! with sparse g.
410+
rand_prototype = zeros(randElType, size(noise_rate_prototype, 2))
407411
else
408412
rand_prototype = false .* noise_rate_prototype[1, :]
409413
end

0 commit comments

Comments
 (0)