-
Notifications
You must be signed in to change notification settings - Fork 30
add coupledSDEs type #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
add coupledSDEs type #212
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
30d0970
add coupledSDEs type
oameye a11ba2c
add CoupledSDEs docs
oameye 62c5e7e
apply suggested format changes
oameye 89225a4
add idfunc
oameye f169386
Fix docs?
oameye 43ad5e9
change coupled sde example to use trajectory
oameye 8de87b8
specify which CoupledODEs docstring
oameye 023ff0c
change to SciML default tolerances
oameye f6a400e
classify noise
oameye 5c5e6dc
correct spelling of invertible
oameye ce7a03e
make noisetype field a namedtuple
oameye 573f18f
Merge branch 'main' into CoupledSDEs
Datseris fba7327
make coupledSDEs an extention
oameye e01bce6
Merge branch 'CoupledSDEs' of https://github.com/oameye/DynamicalSyst…
oameye c875b5e
export pretty print
oameye b3ccb26
export CoupledODEs
oameye b08fd7c
add CoupledODEs <-> CoupledSDEs tests
oameye dfc3053
add extention to modules in makedocs
oameye daca4a7
define dynamics rule sde specific
oameye 73af990
port docs into the docstring
Datseris 92e3369
delete ported documentation
Datseris 548130d
fix small typos in docstring
oameye 1a0c884
add CoupledSDEs examples to docs
oameye 413a25a
remove noise strength
oameye 381820f
update docs with no noise strength
oameye cb956e8
add covariance kwarg and change g to kwarg
oameye 17cf5cb
update docs
oameye ba148d9
diffusion <-> covariance matrix
oameye eb8544b
add noise_strength kwarg
oameye e28b4dd
estimate invertiability with a tolerance
oameye 39057fb
move to SOSRA with non-adaptive timesteps
oameye 400ac12
Merge branch 'main' into CoupledSDEs
oameye 41c900b
Reyk's comments
oameye e5bc61a
implemented review comments - part 1
reykboerner f907929
revised CoupledSDEs docstring
reykboerner 75996be
remove broken docs link
reykboerner 7189029
implemented review comments - part 2
reykboerner 109e7b4
proofread and revised CoupledSDEs docs
reykboerner 5df534a
fixed bug that made tests fail
reykboerner 78f25ee
Update CHANGELOG.md
Datseris 9368e7d
Merge branch 'main' into CoupledSDEs
Datseris de8258e
Update Project.toml
Datseris File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ deps/build.log | |
Manifest.toml | ||
*style.jl | ||
*.scss | ||
*.css | ||
*.css | ||
.vscode |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
using SciMLBase: SDEProblem, AbstractSDEIntegrator, __init, SDEFunction | ||
using StochasticDiffEq: SOSRA | ||
export CoupledSDEs | ||
|
||
########################################################################################### | ||
# DiffEq options | ||
########################################################################################### | ||
const DEFAULT_SDE_SOLVER = SOSRA() # default recommendation for additive noise | ||
const DEFAULT_STOCH_DIFFEQ = (alg=DEFAULT_SDE_SOLVER, DEFAULT_DIFFEQ_KWARGS...) | ||
|
||
# Function from user `@xlxs4`, see | ||
# https://github.com/JuliaDynamics/jl/pull/153 | ||
function _decompose_into_sde_solver_and_remaining(diffeq) | ||
if haskey(diffeq, :alg) | ||
return (diffeq[:alg], _delete(diffeq, :alg)) | ||
else | ||
return (DEFAULT_SDE_SOLVER, diffeq) | ||
end | ||
end | ||
|
||
########################################################################################### | ||
# Type | ||
########################################################################################### | ||
|
||
# Define CoupledSDEs | ||
""" | ||
CoupledSDEs <: ContinuousTimeDynamicalSystem | ||
CoupledSDEs(f, g, u0 [, p, σ]; kwargs...) | ||
|
||
A stochastic continuous time dynamical system defined by a set of | ||
coupled ordinary differential equations as follows: | ||
```math | ||
d\\vec{u} = \\vec{f}(\\vec{u}, p, t) dt + \\vec{g}(\\vec{u}, p, t) dW_t | ||
``` | ||
|
||
Optionally provide the overall noise strength `σ`, the parameter container `p` and initial time as keyword `t0`. If `σ` is provided, the diffusion function `g` is multiplied by `σ`. | ||
oameye marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
For construction instructions regarding `f, u0` see the [DynamicalSystems.jl tutorial](https://juliadynamics.github.io/DynamicalSystems.jl/latest/tutorial/#CoupledODEs). | ||
oameye marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The stochastic part of the differential equation is defined by the function `g` and the keyword arguments `noise_rate_prototype` and `noise`. `noise` indicates the noise process applied during generation and defaults to Gaussian white noise. For details on defining various noise processes, refer to the noise process documentation page. `noise_rate_prototype` indicates the prototype type instance for the noise rates, i.e., the output of `g`. It can be any type which overloads `A_mul_B!` with itself being the middle argument. Commonly, this is a matrix or sparse matrix. If this is not given, it defaults to `nothing`, which means the problem should be interpreted as having diagonal noise. | ||
oameye marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## DifferentialEquations.jl interfacing | ||
|
||
The ODEs are evolved via the solvers of DifferentialEquations.jl. | ||
When initializing a `CoupledODEs`, you can specify the solver that will integrate | ||
`f` in time, along with any other integration options, using the `diffeq` keyword. | ||
For example you could use `diffeq = (abstol = 1e-9, reltol = 1e-9)`. | ||
If you want to specify a solver, do so by using the keyword `alg`, e.g.: | ||
`diffeq = (alg = Tsit5(), reltol = 1e-6)`. This requires you to have been first | ||
`using OrdinaryDiffEq` to access the solvers. The default `diffeq` is: | ||
|
||
```julia | ||
$(DEFAULT_STOCH_DIFFEQ) | ||
``` | ||
|
||
`diffeq` keywords can also include `callback` for [event handling | ||
](https://docs.sciml.ai/DiffEqDocs/stable/features/callback_functions/). | ||
|
||
Dev note: `CoupledSDEs` is a light wrapper of `StochasticDiffEq.SDEIntegrator` from DifferentialEquations.jl. | ||
The integrator is available as the field `integ`, and the `SDEProblem` is `integ.sol.prob`. | ||
The convenience syntax `SDEProblem(ds::CoupledSDEs, tspan = (t0, Inf))` is available | ||
to extract the problem. | ||
""" | ||
struct CoupledSDEs{IIP,D,I,P,S} <: ContinuousTimeDynamicalSystem | ||
# D parametrised by length of u0 | ||
# S indicated if the noise strength has been added to the diffusion function | ||
integ::I | ||
# things we can't recover from `integ` | ||
p0::P | ||
noise_strength | ||
diffeq # isn't parameterized because it is only used for display | ||
end | ||
|
||
function CoupledSDEs( | ||
f, | ||
g, | ||
u0, | ||
p=SciMLBase.NullParameters(), | ||
noise_strength=1.0; | ||
t0=0.0, | ||
diffeq=DEFAULT_STOCH_DIFFEQ, | ||
noise_rate_prototype=nothing, | ||
noise=nothing, | ||
seed=UInt64(0), | ||
) | ||
IIP = isinplace(f, 4) # from SciMLBase | ||
@assert IIP == isinplace(g, 4) "f and g must both be in-place or out-of-place" | ||
|
||
s = correct_state(Val{IIP}(), u0) | ||
T = eltype(s) | ||
prob = SDEProblem{IIP}( | ||
f, | ||
g, | ||
s, | ||
(T(t0), T(Inf)), | ||
p; | ||
noise_rate_prototype=noise_rate_prototype, | ||
noise=noise, | ||
seed=seed, | ||
) | ||
return CoupledSDEs(prob, diffeq, noise_strength) | ||
end | ||
|
||
function CoupledSDEs( | ||
prob::SDEProblem, diffeq=DEFAULT_STOCH_DIFFEQ, noise_strength=1.0; special_kwargs... | ||
) | ||
if haskey(special_kwargs, :diffeq) | ||
throw(ArgumentError("`diffeq` is given as positional argument when an ODEProblem is provided.")) | ||
end | ||
IIP = isinplace(prob) # from SciMLBase | ||
D = length(prob.u0) | ||
P = typeof(prob.p) | ||
if prob.tspan === (nothing, nothing) | ||
# If the problem was made via MTK, it is possible to not have a default timespan. | ||
U = eltype(prob.u0) | ||
prob = SciMLBase.remake(prob; tspan=(U(0), U(Inf))) | ||
end | ||
sde_function = SDEFunction(prob.f, add_noise_strength(noise_strength, prob.g, IIP)) | ||
prob = SciMLBase.remake(prob; f=sde_function) | ||
|
||
solver, remaining = _decompose_into_sde_solver_and_remaining(diffeq) | ||
integ = __init( | ||
prob, | ||
solver; | ||
remaining..., | ||
# Integrators are used exclusively iteratively. There is no reason to save anything. | ||
save_start=false, | ||
save_end=false, | ||
save_everystep=false, | ||
# DynamicalSystems.jl operates on integrators and `step!` exclusively, | ||
# so there is no reason to limit the maximum time evolution | ||
maxiters=Inf, | ||
) | ||
return CoupledSDEs{IIP,D,typeof(integ),P,true}( | ||
integ, deepcopy(prob.p), noise_strength, diffeq | ||
) | ||
end | ||
|
||
""" | ||
CoupledSDEs(ds::CoupledODEs, g, p [, σ]; kwargs...) | ||
|
||
Converts a [`CoupledODEs`](https://juliadynamics.github.io/DynamicalSystems.jl/stable/tutorial/#CoupledODEs) | ||
system into a [`CoupledSDEs`](@ref). | ||
""" | ||
function CoupledSDEs( | ||
ds::CoupledODEs, | ||
g, | ||
p, # the parameter is likely changed as the diffusion function g is added. | ||
noise_strength=1.0; | ||
diffeq=DEFAULT_STOCH_DIFFEQ, | ||
noise_rate_prototype=nothing, | ||
noise=nothing, | ||
seed=UInt64(0), | ||
) | ||
return CoupledSDEs( | ||
dynamic_rule(ds), | ||
g, | ||
current_state(ds), | ||
p, | ||
noise_strength; | ||
diffeq=diffeq, | ||
noise_rate_prototype=noise_rate_prototype, | ||
noise=noise, | ||
seed=seed, | ||
) | ||
end | ||
|
||
""" | ||
CoupledODEs(ds::CoupledSDEs; kwargs...) | ||
|
||
Converts a [`CoupledSDEs`](@ref) into [`CoupledODEs`](@ref). | ||
""" | ||
function CoupledODEs( | ||
sys::CoupledSDEs; diffeq=DEFAULT_STOCH_DIFFEQ, t0=0.0 | ||
) | ||
return CoupledODEs( | ||
sys.integ.f, SVector{length(sys.integ.u)}(sys.integ.u), sys.p0; diffeq=diffeq, t0=t0 | ||
) | ||
end | ||
|
||
# Pretty print | ||
function additional_details(ds::CoupledSDEs) | ||
solver, remaining = _decompose_into_sde_solver_and_remaining(ds.diffeq) | ||
return [ | ||
"Noise strength" => ds.noise_strength, | ||
"SDE solver" => string(nameof(typeof(solver))), | ||
"SDE kwargs" => remaining, | ||
] | ||
end | ||
|
||
########################################################################################### | ||
# API - obtaining information from the system | ||
########################################################################################### | ||
|
||
SciMLBase.isinplace(::CoupledSDEs{IIP}) where {IIP} = IIP | ||
StateSpaceSets.dimension(::CoupledSDEs{IIP,D}) where {IIP,D} = D | ||
current_state(ds::CoupledSDEs) = current_state(ds.integ) | ||
isdeterministic(ds::CoupledSDEs) = false | ||
|
||
function dynamic_rule(sys::CoupledSDEs) | ||
f = sys.integ.f | ||
while hasfield(typeof(f), :f) | ||
f = f.f | ||
end | ||
return f | ||
end | ||
|
||
# so that `ds` is printed | ||
set_state!(ds::CoupledSDEs, u::AbstractArray) = (set_state!(ds.integ, u); ds) | ||
SciMLBase.step!(ds::CoupledSDEs, args...) = (step!(ds.integ, args...); ds) | ||
|
||
function successful_step(integ::AbstractSDEIntegrator) | ||
rcode = integ.sol.retcode | ||
return rcode == SciMLBase.ReturnCode.Default || rcode == SciMLBase.ReturnCode.Success | ||
end | ||
|
||
# This is here to ensure that `u_modified!` is called | ||
function set_parameter!(ds::CoupledSDEs, args...) | ||
_set_parameter!(ds, args...) | ||
u_modified!(ds.integ, true) | ||
return nothing | ||
end | ||
|
||
referrenced_sciml_prob(ds::CoupledSDEs) = ds.integ.sol.prob | ||
|
||
########################################################################################### | ||
# Utilities | ||
########################################################################################### | ||
|
||
function σg(σ, g) | ||
return (u, p, t) -> σ .* g(u, p, t) | ||
end | ||
|
||
function σg!(σ, g!) | ||
function (du, u, p, t) | ||
g!(du, u, p, t) | ||
du .*= σ | ||
return nothing | ||
end | ||
end | ||
|
||
function add_noise_strength(σ, g, IIP) | ||
newg = IIP ? σg!(σ, g) : σg(σ, g) | ||
return newg | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using DynamicalSystemsBase, Test | ||
using StochasticDiffEq: SDEProblem, SRA, SRI, SOSRA | ||
|
||
include("test_system_function.jl") | ||
|
||
# Creation of lorenz | ||
@inbounds function lorenz_rule(u, p, t) | ||
σ = p[1]; ρ = p[2]; β = p[3] | ||
du1 = σ*(u[2]-u[1]) | ||
du2 = u[1]*(ρ-u[3]) - u[2] | ||
du3 = u[1]*u[2] - β*u[3] | ||
return SVector{3}(du1, du2, du3) | ||
end | ||
@inbounds function lorenz_rule_iip(du, u, p, t) | ||
σ = p[1]; ρ = p[2]; β = p[3] | ||
du[1] = σ*(u[2]-u[1]) | ||
du[2] = u[1]*(ρ-u[3]) - u[2] | ||
du[3] = u[1]*u[2] - β*u[3] | ||
return nothing | ||
end | ||
|
||
σ = 0.1 | ||
function diagonal_noise!(σ) | ||
function (du, u, p, t) | ||
du .= σ .* ones(length(u)) | ||
return nothing | ||
end | ||
end | ||
diagonal_noise(σ) = (u, p, t) -> SVector{3}(σ, σ, σ) | ||
|
||
u0 = [0, 10.0, 0] | ||
p0 = [10, 28, 8/3] | ||
|
||
# diagonal additive noise | ||
lorenz_oop = CoupledSDEs(lorenz_rule, diagonal_noise(σ), u0, p0) | ||
lorenz_iip = CoupledSDEs(SDEProblem(lorenz_rule_iip, diagonal_noise!(σ), copy(u0), (0.0, Inf), p0)) | ||
lorenz_SRA = CoupledSDEs(lorenz_rule, diagonal_noise(σ), u0, p0; | ||
diffeq = (alg = SRA(), abstol = 1e-6, reltol = 1e-6) | ||
) | ||
|
||
for (ds, iip) in zip((lorenz_oop, lorenz_iip, lorenz_SRA), (false, true, false)) | ||
|
||
name = (ds === lorenz_SRA) ? "lorSRA" : "lorenz" | ||
@testset "$(name) IIP=$(iip)" begin | ||
@test dynamic_rule(ds) == (iip ? lorenz_rule_iip : lorenz_rule) | ||
test_dynamical_system(ds, u0, p0; idt = false, iip) | ||
end | ||
end | ||
|
||
@testset "correct SDE propagation" begin | ||
lorenz_oop = CoupledSDEs(lorenz_rule, diagonal_noise(σ), u0, p0) | ||
@test lorenz_oop.integ.alg isa SOSRA | ||
|
||
lorenz_SRA = CoupledSDEs(lorenz_rule, diagonal_noise(σ), u0, p0; | ||
diffeq = (alg = SRA(), abstol = 1e-6, reltol = 1e-6, verbose=false) | ||
) | ||
@test lorenz_SRA.integ.alg isa SRA | ||
@test lorenz_SRA.integ.opts.verbose == false | ||
|
||
# # also test SDEproblem creation | ||
prob = lorenz_SRA.integ.sol.prob | ||
|
||
ds = CoupledSDEs(prob, (alg=SRI(), abstol=0.0, reltol=1e-6, verbose=false)) | ||
|
||
@test ds.integ.alg isa SRI | ||
@test ds.integ.opts.verbose == false | ||
|
||
@test_throws ArgumentError CoupledSDEs(prob; diffeq = (alg=SRI(), )) | ||
|
||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.