I am trying to take derivatives of Montecarlo expectations against parameters using DifferentialEquations.jl. I am doing it using geometric brownian motion, and I want to bump the sigma and reevaluate (later I will try AD). My problem is that I can do it for a single path using the same seed, but I can’t do it with EnsembleProblem. If I fix the seed there, I get the same path many times, which doesn’t make much sense…
Is there a way to basically export one seed per trajectory from the ensembleProblem and reuse them in a subsequent ensembleProblem?
Below my snippet:
using DifferentialEquations
using DiffEqNoiseProcess
using Plots
# Parameters
μ = 0.05
σ = 0.2
t0 = 0.0
T = 1.0
W0 = 100.0
tspan = (t0, T)
N = 250
dt = (T - t0) / N
# Step 1: Simulate GBM with a fixed seed
gbm = GeometricBrownianMotionProcess(μ, σ, t0, W0)
gbm_noise = NoiseProblem(gbm, tspan, seed=1)
sol = DifferentialEquations.solve(gbm_noise, dt=dt)
# Step 2: Reuse the same seed with bumped σ
μ_bumped = μ
σ_bumped = σ + 0.05
gbm_bumped = GeometricBrownianMotionProcess(μ_bumped, σ_bumped, t0, W0)
gbm_bumped_noise = NoiseProblem(gbm_bumped, tspan, seed=1)
sol_bumped = DifferentialEquations.solve(gbm_bumped_noise, dt=dt)
# Step 3: Plot both
plot(sol.t, sol.u, label="μ = $μ", linewidth=2)
plot!(sol_bumped.t, sol_bumped.u, label="μ = $μ_bumped", linestyle=:dash, linewidth=2)
xlabel!("Time")
ylabel!("GBM Value")
title!("GBM with Drift Bump and Reused Seed")
I guess the obvious solution is to bypass EnsembleProblem and loop through many NoiseProblems, just wondering if there’s a more user-friendly way.
I’m not familiar with this package, but let me just provide an alternative solution as I like to implement such things from scratch sometimes. If you stick purely with GBM, you could circumvent the problem by using the analytical solution to the SDE and making appropriate adjustments based on the change in parameters. If I were to simulate things from scratch: simulate a bunch of standard-normal random variables which you keep fixed, then transform these to GBM paths depending on your choice of parameters.
If you set it up right, you can even make it work with automatic differentiation to compute derivatives
In the package the analytical solution (for the final process value) is actually used. The point is how much low level I need to go to achieve this. If I can just export the seeds from the EnsembleProblem and reimport them in another EnsembleProblem that would save me some code.
Also because eventually I want to be able to do so for any SDE where the result is differentiable against the parameters, hence I need the method to be rather abstract.
1 Like
Make your prob_func
for the ensemble set the seed in the problem.
That works perfectly, thanks a lot @ChrisRackauckas!
This doubles allocation and time according to @btime, but I guess it’s a price to pay, given that NoiseProblem is immutable.
using DifferentialEquations
μ = 0.02
σ = 0.04
t0 = 0.0
W0 = 0.0
tspan = (0.0,1.0)
trajectories = 100
proc = GeometricBrownianMotionProcess(μ, σ, t0, W0, nothing)
noise = NoiseProblem(proc, tspan)
seeds = rand(UInt64,trajectories)
prob_func = (prob, i, repeat) -> remake(prob; seed=seeds[i])
problem = EnsembleProblem(noise; prob_func=prob_func)
sol = solve(problem, EM(); trajectories=trajectories, dt=1.0)
It’s because it needs to make new RNG objects in order to seed them
, I’m pretty sure that’s a fundamental cost. You could maybe make a global RNG that you seed!
instead, but then making that work with multithreading would be an absolute pain.
Yeah that makes total sense. Thanks.