Defining the drift and the volatility coefficient
To complete the definition of a diffusion process we need to specify its drift, as well as its volatility coefficient. For the Lorenz
example from the previous section we can do this by writing:
const DD = DiffusionDefinition
using StaticArrays
function DD.b(t, x, P::Lorenz)
@SVector [
P.p1*(x[2]-x[1]),
P.p2*x[1] - x[2] - x[1]*x[3],
x[1]*x[2] - P.p3*x[3]
]
end
DD.σ(t, x, P::Lorenz) = SDiagonal(P.σ, P.σ, P.σ)
The two functions above are out-of-place i.e. they return new vectors (that live on a stack, because of StaticArrays
). We may alternatively define the drift and diffusion coefficients to be in-place as follows:
function DD.b!(buffer, t, x, P::Lorenz)
buffer.b[1] = P.p1*(x[2]-x[1])
buffer.b[2] = P.p2*x[1] - x[2] - x[1]*x[3]
buffer.b[3] = x[1]*x[2] - P.p3*x[3]
end
DD.σ!(buffer, t, x, P::Lorenz) = (buffer.σ.diag .= P.σ)
In this case case the output is saved to a buffer
, which must have appropriate fields b
and σ
with enough pre-allocated space (see also the section on buffers).
- all of the functions
DD.b
,DD.b!
,DD.σ
andDD.σ!
are defined to overload the functionality inside theDiffusionDefinition
module (accessing it viaDD
) and NOT theMain
module. - the arguments for the out-of-place method are
(t, x, P::DIFFUSION_NAME)
, whereas those for in-place are(buffer, t, x, P::DIFFUSION_NAME)
.
Always use StaticArrays
for out-of-place drift and volatility! If functions using DD.b
and DD.σ
are faster with regular arrays, then you shouldn't be using out-of-place methods in the first place, but DD.b!
and DD.σ!
instead. A general rule of thumb is to use DD.b
and DD.σ
for low dimensional diffusions (up to dimension ~10
for elliptic diffusions with dense volatility coefficients or up to dimension ~100
for those with sparse volatility coefficients) and use in-place methods otherwise.
Telling Julia whether to use (DD.b
, DD.σ
) or (DD.b!
, DD.σ!
)
Some functions implemented in this package have two versions: one relying on out-of-place methods, another on in-place methods. For instance:
DiffusionDefinition.solve!
— Methodsolve!(XX, WW, P, y1)
Compute a trajectory, started from y1
and following the diffusion law P
, from the sampled Wiener process WW
. Save the sampled path in XX
. Return prematurely with a false
massage if the numerical scheme has led to the solver violating the state-space restrictions.
and
DiffusionDefinition.solve!
— Methodsolve!(XX, WW, P, y1, buffer)
Same as solve!(XX, WW, P, y1)
, but additionally provides a pre-allocated buffer for performing in-place computations.
Calling one or the other will tell Julia which pair of functions (DD.b
, DD.σ
) or (DD.b!
, DD.σ!
) to use. However, most functions do not explicitly come in two versions, and instead, they rely on some hint from the user to decide on the mode of computation. There are two ways in which Julia can be given such hints:
- If the function accepts optional inputs (such as a starting point) or expects to receive containers that the results are saved into (such as containers for diffusion paths), then the
DataType
of said inputs will be used to decide on the mode of computation (ifDataType
used for state space is immutable, then out-of-place methods are used, otherwise in-place methods are used). These hints will overwrite the second method below. - Sometimes no such inputs can be passed or there is an option of relying on defaults. In that case
Julia
will use default information specified by functions:
DiffusionDefinition.default_type
— Functiondefault_type(::DiffusionProcess{T,DP})
Allows for inference of data type that encodes the state space of a given diffusion.
DiffusionDefinition.default_wiener_type
— Functiondefault_wiener_type(::DiffusionProcess{T,DP,DW})
Allows for inference of data type that encodes the state space of the Brownian motion driving a given diffusion process.
By default, these two are set to StaticArrays
resulting in out-of-place computations by default. To change that, overwrite the two functions for your diffusion type. For instance, for the Lorenz
example, to change the default mode of computation to out-of-place write:
default_type(::Lorenz) = Vector{Float64}
default_wiener_type(::Lorenz) = Vector{Float64}