Skip to content

Commit e64c479

Browse files
Merge pull request #2914 from MasonProtter/another_diag_noise_fix
Detect cases where there's fewer brownians than equations, but noise still 'diagonal'
2 parents ec870e2 + fe1a3ee commit e64c479

File tree

3 files changed

+62
-17
lines changed

3 files changed

+62
-17
lines changed

src/systems/diffeqs/sdesystem.jl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,30 @@ function Base.:(==)(sys1::SDESystem, sys2::SDESystem)
245245
all(s1 == s2 for (s1, s2) in zip(get_systems(sys1), get_systems(sys2)))
246246
end
247247

248-
function __num_isdiag(mat)
249-
for i in axes(mat, 1), j in axes(mat, 2)
250-
i == j || isequal(mat[i, j], 0) || return false
248+
function __num_isdiag_noise(mat)
249+
for i in axes(mat, 1)
250+
nnz = 0
251+
for j in axes(mat, 2)
252+
if !isequal(mat[i, j], 0)
253+
nnz += 1
254+
end
255+
end
256+
if nnz > 1
257+
return (false)
258+
end
259+
end
260+
true
261+
end
262+
function __get_num_diag_noise(mat)
263+
map(axes(mat, 1)) do i
264+
for j in axes(mat, 2)
265+
mij = mat[i, j]
266+
if !isequal(mij, 0)
267+
return mij
268+
end
269+
end
270+
0
251271
end
252-
return true
253272
end
254273

255274
function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys),
@@ -258,9 +277,6 @@ function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys),
258277
if isdde
259278
eqs = delay_to_function(sys, eqs)
260279
end
261-
if eqs isa AbstractMatrix && __num_isdiag(eqs)
262-
eqs = diag(eqs)
263-
end
264280
u = map(x -> time_varying_as_func(value(x), sys), dvs)
265281
p = if has_index_cache(sys) && get_index_cache(sys) !== nothing
266282
reorder_parameters(get_index_cache(sys), ps)

src/systems/systems.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ function __structural_simplify(sys::AbstractSystem, io = nothing; simplify = fal
133133
# we get a Nx1 matrix of noise equations, which is a special case known as scalar noise
134134
noise_eqs = sorted_g_rows[:, 1]
135135
is_scalar_noise = true
136-
elseif isdiag(sorted_g_rows)
137-
# If the noise matrix is diagonal, then the solver just takes a vector column of equations
138-
# and it interprets that as diagonal noise.
139-
noise_eqs = diag(sorted_g_rows)
136+
elseif __num_isdiag_noise(sorted_g_rows)
137+
# If each column of the noise matrix has either 0 or 1 non-zero entry, then this is "diagonal noise".
138+
# In this case, the solver just takes a vector column of equations and it interprets that to
139+
# mean that each noise process is independent
140+
noise_eqs = __get_num_diag_noise(sorted_g_rows)
140141
is_scalar_noise = false
141142
else
142143
noise_eqs = sorted_g_rows

test/sdesystem.jl

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -725,12 +725,12 @@ end
725725

726726
@testset "Non-diagonal noise check" begin
727727
@parameters σ ρ β
728-
@variables x(t) y(t) z(t)
729-
@brownian a b c
730-
eqs = [D(x) ~ σ * (y - x) + 0.1a * x + 0.1b * y,
731-
D(y) ~ x *- z) - y + 0.1b * y,
732-
D(z) ~ x * y - β * z + 0.1c * z]
733-
@mtkbuild de = System(eqs, t)
728+
@variables x(tt) y(tt) z(tt)
729+
@brownian a b c d e f
730+
eqs = [D(x) ~ σ * (y - x) + 0.1a * x + d,
731+
D(y) ~ x *- z) - y + 0.1b * y + e,
732+
D(z) ~ x * y - β * z + 0.1c * z + f]
733+
@mtkbuild de = System(eqs, tt)
734734

735735
u0map = [
736736
x => 1.0,
@@ -746,5 +746,33 @@ end
746746

747747
prob = SDEProblem(de, u0map, (0.0, 100.0), parammap)
748748
# SOSRI only works for diagonal and scalar noise
749+
@test_throws ErrorException solve(prob, SOSRI()).retcode==ReturnCode.Success
750+
# ImplicitEM does work for non-diagonal noise
749751
@test solve(prob, ImplicitEM()).retcode == ReturnCode.Success
752+
@test size(ModelingToolkit.get_noiseeqs(de)) == (3, 6)
753+
end
754+
755+
@testset "Diagonal noise, less brownians than equations" begin
756+
@parameters σ ρ β
757+
@variables x(tt) y(tt) z(tt)
758+
@brownian a b
759+
eqs = [D(x) ~ σ * (y - x) + 0.1a * x, # One brownian
760+
D(y) ~ x *- z) - y + 0.1b * y, # Another brownian
761+
D(z) ~ x * y - β * z] # no brownians -- still diagonal
762+
@mtkbuild de = System(eqs, tt)
763+
764+
u0map = [
765+
x => 1.0,
766+
y => 0.0,
767+
z => 0.0
768+
]
769+
770+
parammap = [
771+
σ => 10.0,
772+
β => 26.0,
773+
ρ => 2.33
774+
]
775+
776+
prob = SDEProblem(de, u0map, (0.0, 100.0), parammap)
777+
@test solve(prob, SOSRI()).retcode == ReturnCode.Success
750778
end

0 commit comments

Comments
 (0)