From 2de80ce8c687a54c5640d0c4f6d0bc5683623c4f Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Fri, 24 Oct 2025 10:23:27 -0400 Subject: [PATCH 1/3] Fix noise kwarg propagation for SDEProblem(f::SDESystem) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating an SDEProblem from an SDESystem, the noise keyword argument was being ignored. The constructor always calculated noise and noise_rate_prototype from the system, overwriting any user-provided values. This fix checks if the user has already provided noise or noise_rate_prototype in kwargs before calculating defaults from the system. This allows users to specify custom noise processes for their SDEProblems. Fixes #3664 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Project.toml | 1 + src/problems/sdeproblem.jl | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d5c30152e5..f027a2f934 100644 --- a/Project.toml +++ b/Project.toml @@ -58,6 +58,7 @@ SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" diff --git a/src/problems/sdeproblem.jl b/src/problems/sdeproblem.jl index e322775d2b..f4bdd806f5 100644 --- a/src/problems/sdeproblem.jl +++ b/src/problems/sdeproblem.jl @@ -77,7 +77,20 @@ end t = tspan !== nothing ? tspan[1] : tspan, check_length, eval_expression, eval_module, check_compatibility, sparse, expression, kwargs...) - noise, noise_rate_prototype = calculate_noise_and_rate_prototype(sys, u0; sparsenoise) + # Only calculate noise and noise_rate_prototype if not provided by user + if !haskey(kwargs, :noise) && !haskey(kwargs, :noise_rate_prototype) + noise, noise_rate_prototype = calculate_noise_and_rate_prototype(sys, u0; sparsenoise) + elseif !haskey(kwargs, :noise) + noise, _ = calculate_noise_and_rate_prototype(sys, u0; sparsenoise) + noise_rate_prototype = kwargs[:noise_rate_prototype] + elseif !haskey(kwargs, :noise_rate_prototype) + _, noise_rate_prototype = calculate_noise_and_rate_prototype(sys, u0; sparsenoise) + noise = kwargs[:noise] + else + noise = kwargs[:noise] + noise_rate_prototype = kwargs[:noise_rate_prototype] + end + kwargs = process_kwargs(sys; expression, callback, eval_expression, eval_module, op, kwargs...) From 26164050a470c06e463d7acc193717bfef4a2882 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Fri, 24 Oct 2025 10:47:02 -0400 Subject: [PATCH 2/3] Add test for noise kwarg propagation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added test case to verify that user-provided noise keyword argument is properly propagated when creating SDEProblem from SDESystem. The test verifies: - User-provided noise is actually used (noise.curW is modified) - Using the same noise via NoiseWrapper gives deterministic results - Different seeds produce different results (default behavior still works) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- test/sdesystem.jl | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/sdesystem.jl b/test/sdesystem.jl index f3bb3ba9c9..0df0b2ce31 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -1,5 +1,6 @@ using ModelingToolkit, StaticArrays, LinearAlgebra using StochasticDiffEq, OrdinaryDiffEq, SparseArrays +using DiffEqNoiseProcess: NoiseWrapper using Random, Test using Setfield using Statistics @@ -953,3 +954,47 @@ end @test ModelingToolkit.isbrownian(p) @test ModelingToolkit.isbrownian(q) end + +@testset "noise kwarg propagation (issue #3664)" begin + @parameters σ ρ β + @variables x(tt) y(tt) z(tt) + + u0 = [1.0, 0.0, 0.0] + T = (0.0, 5.0) + + eqs = [D(x) ~ σ * (y - x), + D(y) ~ x * (ρ - z) - y, + D(z) ~ x * y - β * z] + noiseeqs = [3.0, + 3.0, + 3.0] + @mtkbuild sde_lorentz = SDESystem(eqs, noiseeqs, tt, [x, y, z], [σ, ρ, β]) + parammap = [σ, ρ, β] .=> [10, 28.0, 8 / 3] + + # Test that user-provided noise is respected + Random.seed!(1) + noise1 = StochasticDiffEq.RealWienerProcess(0.0, 0.0, 0.0; save_everystep = true) + u0_dict = Dict(unknowns(sde_lorentz) .=> u0) + prob1 = SDEProblem(sde_lorentz, merge(u0_dict, Dict(parammap)), T; noise = noise1) + sol1 = solve(prob1, SRIW1()) + + # Verify noise was actually used (curW should be modified) + @test noise1.curW != 0.0 + + # Test that using the same noise via NoiseWrapper gives deterministic results + noise2 = NoiseWrapper(noise1) + prob2 = SDEProblem(sde_lorentz, merge(u0_dict, Dict(parammap)), T; noise = noise2) + sol2 = solve(prob2, SRIW1()) + + # Same noise should give same results + @test sol1.u[end] ≈ sol2.u[end] + + # Test that without providing noise, different results are obtained + Random.seed!(1) + prob3 = SDEProblem(sde_lorentz, merge(u0_dict, Dict(parammap)), T) + Random.seed!(2) + prob4 = SDEProblem(sde_lorentz, merge(u0_dict, Dict(parammap)), T) + sol3 = solve(prob3, SRIW1(), seed = 1) + sol4 = solve(prob4, SRIW1(), seed = 2) + @test !(sol3.u[end] ≈ sol4.u[end]) +end From f3b84be124689970a1f4b5c82022a296ae618022 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Fri, 24 Oct 2025 12:45:26 -0400 Subject: [PATCH 3/3] Update Project.toml --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index f027a2f934..d5c30152e5 100644 --- a/Project.toml +++ b/Project.toml @@ -58,7 +58,6 @@ SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"