From bb403dd0b0692d091e5cf7c6caeae514651e9dc5 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 17:13:50 +0200 Subject: [PATCH 1/9] detect fewer brownians than equations, but noise still diag --- src/systems/diffeqs/sdesystem.jl | 12 ++++++------ src/systems/systems.jl | 9 +++++---- test/sdesystem.jl | 31 +++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index b1f81240f3..639c5b1355 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -245,11 +245,11 @@ function Base.:(==)(sys1::SDESystem, sys2::SDESystem) all(s1 == s2 for (s1, s2) in zip(get_systems(sys1), get_systems(sys2))) end -function __num_isdiag(mat) - for i in axes(mat, 1), j in axes(mat, 2) - i == j || isequal(mat[i, j], 0) || return false - end - return true +function __num_isdiag_noise(mat) + all(col -> count(!iszero, col) <= 1, eachcol(mat)) +end +function __get_num_diag_noise(mat) + vec(sum(mat; dims = 2)) end function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), @@ -258,7 +258,7 @@ function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), if isdde eqs = delay_to_function(sys, eqs) end - if eqs isa AbstractMatrix && __num_isdiag(eqs) + if eqs isa AbstractMatrix && __num_isdiag_noise(eqs) eqs = diag(eqs) end u = map(x -> time_varying_as_func(value(x), sys), dvs) diff --git a/src/systems/systems.jl b/src/systems/systems.jl index a166ff8d8a..2e99183f7a 100644 --- a/src/systems/systems.jl +++ b/src/systems/systems.jl @@ -133,10 +133,11 @@ function __structural_simplify(sys::AbstractSystem, io = nothing; simplify = fal # we get a Nx1 matrix of noise equations, which is a special case known as scalar noise noise_eqs = sorted_g_rows[:, 1] is_scalar_noise = true - elseif isdiag(sorted_g_rows) - # If the noise matrix is diagonal, then the solver just takes a vector column of equations - # and it interprets that as diagonal noise. - noise_eqs = diag(sorted_g_rows) + elseif __num_isdiag_noise(sorted_g_rows) + # If each column of the noise matrix has either 0 or 1 non-zero entry, then this is "diagonal noise". + # In this case, the solver just takes a vector column of equations and it interprets that to + # mean that each noise process is independant + noise_eqs = __get_num_diag_noise(sorted_g_rows) is_scalar_noise = false else noise_eqs = sorted_g_rows diff --git a/test/sdesystem.jl b/test/sdesystem.jl index d71848bf4b..0e55ed567e 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -725,12 +725,12 @@ end @testset "Non-diagonal noise check" begin @parameters σ ρ β - @variables x(t) y(t) z(t) + @variables x(tt) y(tt) z(tt) @brownian a b c eqs = [D(x) ~ σ * (y - x) + 0.1a * x + 0.1b * y, D(y) ~ x * (ρ - z) - y + 0.1b * y, D(z) ~ x * y - β * z + 0.1c * z] - @mtkbuild de = System(eqs, t) + @mtkbuild de = System(eqs, tt) u0map = [ x => 1.0, @@ -746,5 +746,32 @@ end prob = SDEProblem(de, u0map, (0.0, 100.0), parammap) # SOSRI only works for diagonal and scalar noise + @test_throws ErrorException solve(prob, SOSRI()).retcode==ReturnCode.Success + # ImplictEM does work for non-diagonal noise @test solve(prob, ImplicitEM()).retcode == ReturnCode.Success end + +@testset "Diagonal noise, less brownians than equations" begin + @parameters σ ρ β + @variables x(tt) y(tt) z(tt) + @brownian a b + eqs = [D(x) ~ σ * (y - x) + 0.1a * x, # One brownian + D(y) ~ x * (ρ - z) - y + 0.1b * y, # Another brownian + D(z) ~ x * y - β * z] # no brownians -- still diagonal + @mtkbuild de = System(eqs, tt) + + u0map = [ + x => 1.0, + y => 0.0, + z => 0.0 + ] + + parammap = [ + σ => 10.0, + β => 26.0, + ρ => 2.33 + ] + + prob = SDEProblem(de, u0map, (0.0, 100.0), parammap) + @test solve(prob, SOSRI()).retcode == ReturnCode.Success +end From 0a3f62cc58e9b8dc0203bf81d1b219fb1c01dc41 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 17:20:41 +0200 Subject: [PATCH 2/9] whoops, missed a line --- src/systems/diffeqs/sdesystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 639c5b1355..50b65e5223 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -259,7 +259,7 @@ function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), eqs = delay_to_function(sys, eqs) end if eqs isa AbstractMatrix && __num_isdiag_noise(eqs) - eqs = diag(eqs) + eqs = __get_num_diag_noise(eqs) end u = map(x -> time_varying_as_func(value(x), sys), dvs) p = if has_index_cache(sys) && get_index_cache(sys) !== nothing From 6793f137f4db83f4154a6780a0f791275fcbe8c8 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 17:32:21 +0200 Subject: [PATCH 3/9] spelling --- src/systems/systems.jl | 2 +- test/sdesystem.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systems/systems.jl b/src/systems/systems.jl index 2e99183f7a..569ef05b91 100644 --- a/src/systems/systems.jl +++ b/src/systems/systems.jl @@ -136,7 +136,7 @@ function __structural_simplify(sys::AbstractSystem, io = nothing; simplify = fal elseif __num_isdiag_noise(sorted_g_rows) # If each column of the noise matrix has either 0 or 1 non-zero entry, then this is "diagonal noise". # In this case, the solver just takes a vector column of equations and it interprets that to - # mean that each noise process is independant + # mean that each noise process is independent noise_eqs = __get_num_diag_noise(sorted_g_rows) is_scalar_noise = false else diff --git a/test/sdesystem.jl b/test/sdesystem.jl index 0e55ed567e..439b61f6b9 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -747,7 +747,7 @@ end prob = SDEProblem(de, u0map, (0.0, 100.0), parammap) # SOSRI only works for diagonal and scalar noise @test_throws ErrorException solve(prob, SOSRI()).retcode==ReturnCode.Success - # ImplictEM does work for non-diagonal noise + # ImplicitEM does work for non-diagonal noise @test solve(prob, ImplicitEM()).retcode == ReturnCode.Success end From 7ae1b8d1d616ddbdb512f61c1a0816dbc9576d8a Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 18:03:02 +0200 Subject: [PATCH 4/9] try to fix downstream catalyst failure --- src/systems/diffeqs/sdesystem.jl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 50b65e5223..486cb9f838 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -246,7 +246,18 @@ function Base.:(==)(sys1::SDESystem, sys2::SDESystem) end function __num_isdiag_noise(mat) - all(col -> count(!iszero, col) <= 1, eachcol(mat)) + for j in axes(mat, 2) + nnz = 0 + for i in axes(mat, 1) + if !isequal(mat[i, j], 0) + nnz += 1 + end + end + if nnz > 1 + return false + end + end + true end function __get_num_diag_noise(mat) vec(sum(mat; dims = 2)) From e2de2392b551f8628b7c006bd93bebdaa14d00a1 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 21:21:03 +0200 Subject: [PATCH 5/9] try another fix for downstream catalyst failure --- src/systems/diffeqs/sdesystem.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 486cb9f838..cabb2bb327 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -260,7 +260,14 @@ function __num_isdiag_noise(mat) true end function __get_num_diag_noise(mat) - vec(sum(mat; dims = 2)) + map(axes(mat, 2)) do j + for i ∈ axes(mat, 1) + if !isequal(mat[i, j], 0) + return mat[i, j] + end + end + 0 + end end function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), From 37e96894eb90e29a0ad38ce52e607491e384e323 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 31 Jul 2024 21:24:47 +0200 Subject: [PATCH 6/9] formatting --- src/systems/diffeqs/sdesystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index cabb2bb327..cefa5dc485 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -261,7 +261,7 @@ function __num_isdiag_noise(mat) end function __get_num_diag_noise(mat) map(axes(mat, 2)) do j - for i ∈ axes(mat, 1) + for i in axes(mat, 1) if !isequal(mat[i, j], 0) return mat[i, j] end From cc6abdef19e33335905149d2536f6a16d5ee7a9e Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Thu, 1 Aug 2024 12:52:40 +0200 Subject: [PATCH 7/9] fix iteration order, add test that catches catalyst case --- src/systems/diffeqs/sdesystem.jl | 40 ++++++++++++++++++++++++++------ test/sdesystem.jl | 9 +++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index cefa5dc485..6a47af2868 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -246,30 +246,56 @@ function Base.:(==)(sys1::SDESystem, sys2::SDESystem) end function __num_isdiag_noise(mat) - for j in axes(mat, 2) + for i in axes(mat, 1) nnz = 0 - for i in axes(mat, 1) + for j in axes(mat, 2) if !isequal(mat[i, j], 0) nnz += 1 end end if nnz > 1 - return false + return (false) end end true end function __get_num_diag_noise(mat) - map(axes(mat, 2)) do j - for i in axes(mat, 1) - if !isequal(mat[i, j], 0) - return mat[i, j] + map(axes(mat, 1)) do i + for j in axes(mat, 2) + mij = mat[i, j] + if !isequal(mij, 0) + return mij end end 0 end end +# function __num_isdiag_noise(mat) +# for j in axes(mat, 2) +# nnz = 0 +# for i in axes(mat, 1) +# if !isequal(mat[i, j], 0) +# nnz += 1 +# end +# end +# if nnz > 1 +# return false +# end +# end +# true +# end +# function __get_num_diag_noise(mat) +# map(axes(mat, 2)) do j +# for i in axes(mat, 1) +# if !isequal(mat[i, j], 0) +# return mat[i, j] +# end +# end +# 0 +# end +# end + function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), ps = full_parameters(sys); isdde = false, kwargs...) eqs = get_noiseeqs(sys) diff --git a/test/sdesystem.jl b/test/sdesystem.jl index 439b61f6b9..6e83d40d5f 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -726,10 +726,10 @@ end @testset "Non-diagonal noise check" begin @parameters σ ρ β @variables x(tt) y(tt) z(tt) - @brownian a b c - eqs = [D(x) ~ σ * (y - x) + 0.1a * x + 0.1b * y, - D(y) ~ x * (ρ - z) - y + 0.1b * y, - D(z) ~ x * y - β * z + 0.1c * z] + @brownian a b c d e f + eqs = [D(x) ~ σ * (y - x) + 0.1a * x + d, + D(y) ~ x * (ρ - z) - y + 0.1b * y + e, + D(z) ~ x * y - β * z + 0.1c * z + f] @mtkbuild de = System(eqs, tt) u0map = [ @@ -749,6 +749,7 @@ end @test_throws ErrorException solve(prob, SOSRI()).retcode==ReturnCode.Success # ImplicitEM does work for non-diagonal noise @test solve(prob, ImplicitEM()).retcode == ReturnCode.Success + @test size(ModelingToolkit.get_noiseeqs(de)) == (3, 6) end @testset "Diagonal noise, less brownians than equations" begin From 37613278a6d58e85d6a623aa81d7e390d93cc9c9 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Thu, 1 Aug 2024 13:06:12 +0200 Subject: [PATCH 8/9] remove comments I didn't mean to push --- src/systems/diffeqs/sdesystem.jl | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 6a47af2868..cac295d0f2 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -271,31 +271,6 @@ function __get_num_diag_noise(mat) end end -# function __num_isdiag_noise(mat) -# for j in axes(mat, 2) -# nnz = 0 -# for i in axes(mat, 1) -# if !isequal(mat[i, j], 0) -# nnz += 1 -# end -# end -# if nnz > 1 -# return false -# end -# end -# true -# end -# function __get_num_diag_noise(mat) -# map(axes(mat, 2)) do j -# for i in axes(mat, 1) -# if !isequal(mat[i, j], 0) -# return mat[i, j] -# end -# end -# 0 -# end -# end - function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), ps = full_parameters(sys); isdde = false, kwargs...) eqs = get_noiseeqs(sys) From fe1a3eee000240a225d2399ee490ccdddbfffe2f Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Thu, 1 Aug 2024 14:39:10 +0200 Subject: [PATCH 9/9] remove diag transformation from `generate_diffusion_function` --- src/systems/diffeqs/sdesystem.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index cac295d0f2..439e525670 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -277,9 +277,6 @@ function generate_diffusion_function(sys::SDESystem, dvs = unknowns(sys), if isdde eqs = delay_to_function(sys, eqs) end - if eqs isa AbstractMatrix && __num_isdiag_noise(eqs) - eqs = __get_num_diag_noise(eqs) - end u = map(x -> time_varying_as_func(value(x), sys), dvs) p = if has_index_cache(sys) && get_index_cache(sys) !== nothing reorder_parameters(get_index_cache(sys), ps)