Skip to content
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

Add NNKolmogorov and NNParamKolmogorov #83

Merged
merged 22 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e976fa2
feat: add a dispatch on `PIDEProblem` where `f` (non linear function)…
ashutosh-b-b Jan 27, 2024
5169183
feat: add `NNKolmogorov`
ashutosh-b-b Jan 27, 2024
2eaf6c0
test: add tests for NNKolmogorov
ashutosh-b-b Jan 27, 2024
6ff67d1
fix: add `Distributions` to test deps
ashutosh-b-b Jan 27, 2024
ecbe769
fix: remove need for xdims. allow multiple domains for `x`
ashutosh-b-b Jan 28, 2024
4426a95
test: update tests
ashutosh-b-b Jan 28, 2024
7c41b3a
feat: add NNParamKolmogorov alg
ashutosh-b-b Jan 30, 2024
cde9de8
fix: update PIDEProblem dispatch
ashutosh-b-b Jan 30, 2024
192b3f5
test: add tests for NNParamKolmogorov
ashutosh-b-b Jan 30, 2024
beadd2a
fix: remove dispatch on PIDEProblem
ashutosh-b-b Feb 1, 2024
541a11c
test: change Flux.ADAM to Flux.Optimisers.Adam
ashutosh-b-b Feb 1, 2024
ac6bde7
fix: update NNKolmogorov and NNParamKolmogorov to ParabolicPDEProblem
ashutosh-b-b Feb 4, 2024
e1185aa
test: update tests for NNKolmogorov and NNParamKolmogorov
ashutosh-b-b Feb 4, 2024
6999b39
chore: export NNKolmogorov and NNParamKolmogorov
ashutosh-b-b Feb 7, 2024
b8f2f45
test: update tests
ashutosh-b-b Feb 7, 2024
934cc85
test: fix typo
ashutosh-b-b Feb 7, 2024
d7da58e
build: add compat for test dependency `Distributions`
ashutosh-b-b Feb 9, 2024
691304d
docs: add docstrings and docs for NNKolmogorov and NNParamKolmogorov
ashutosh-b-b Feb 12, 2024
e55a1b4
fix: fix bug in indexing solutions in NNParamKolmogorov
ashutosh-b-b Feb 12, 2024
f0bfef0
test: replace `pdf(d,x)` with `map(Base.Fix1(pdf, d), x)`
ashutosh-b-b Feb 12, 2024
8fb87fe
docs: bump HighDimPDE version to 2
ashutosh-b-b Feb 12, 2024
aaa400d
docs: update docs
ashutosh-b-b Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ cuDNN = "02a925ec-e4fe-4b08-9a7e-0d78e3d38ccd"
Aqua = "0.8"
CUDA = "4.4, 5"
DiffEqBase = "6.137"
Distributions = "v0.25.107"
DocStringExtensions = "0.9"
Flux = "0.13.12, 0.14"
Functors = "0.4"
Expand All @@ -44,8 +45,9 @@ julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "Test", "SafeTestsets"]
test = ["Aqua", "Distributions", "Test", "SafeTestsets"]
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ HighDimPDE = "57c578d5-59d4-4db8-a490-a9fc372d19d2"
[compat]
Documenter = "1"
Flux = "0.13, 0.14"
HighDimPDE = "1.2"
HighDimPDE = "2"
6 changes: 5 additions & 1 deletion docs/pages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ pages = [
"Solver Algorithms" => ["MLP.md",
"DeepSplitting.md",
"DeepBSDE.md",
"NNStopping.md"],
"NNStopping.md",
"NNKolmogorov.md",
"NNParamKolmogorov.md"],
"Tutorials" => [
"tutorials/deepsplitting.md",
"tutorials/deepbsde.md",
"tutorials/mlp.md",
"tutorials/nnstopping.md",
"tutorials/nnkolmogorov.md",
"tutorials/nnparamkolmogorov.md",
],
"Feynman Kac formula" => "Feynman_Kac.md",
]
30 changes: 30 additions & 0 deletions docs/src/NNKolmogorov.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# [The `NNKolmogorov` algorithm](@id nn_komogorov)

### Problems Supported:
1. [`ParabolicPDEProblem`](@ref)

```@autodocs
Modules = [HighDimPDE]
Pages = ["NNKolmogorov.jl"]
```

`NNKolmogorov` obtains a
- terminal solution for Forward Kolmogorov Equations of the form:
```math
\partial_t u(t,x) = \mu(t, x) \nabla_x u(t,x) + \frac{1}{2} \sigma^2(t, x) \Delta_x u(t,x)
```
with initial condition given by `g(x)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terminal condition?

- or an initial condition for Backward Kolmogorov Equations of the form:
```math
\partial_t u(t,x) = - \mu(t, x) \nabla_x u(t,x) - \frac{1}{2} \sigma^2(t, x) \Delta_x u(t,x)
```
with terminal condition given by `g(x)`

We can use the Feynman-Kac formula :
```math
S_t^x = \int_{0}^{t}\mu(S_s^x)ds + \int_{0}^{t}\sigma(S_s^x)dB_s
```
And the solution is given by:
```math
f(T, x) = \mathbb{E}[g(S_T^x)]
```
30 changes: 30 additions & 0 deletions docs/src/NNParamKolmogorov.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# [The `NNParamKolmogorov` algorithm](@id nn_komogorov)

### Problems Supported:
1. [`ParabolicPDEProblem`](@ref)

```@autodocs
Modules = [HighDimPDE]
Pages = ["NNParamKolmogorov.jl"]
```

`NNParamKolmogorov` obtains a
- terminal solution for parametric families of Forward Kolmogorov Equations of the form:
```math
\partial_t u(t,x) = \mu(t, x, γ_mu) \nabla_x u(t,x) + \frac{1}{2} \sigma^2(t, x, γ_sigma) \Delta_x u(t,x)
```
with initial condition given by `g(x, γ_phi)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

terminal condition? I'm not sure this really matches??

Copy link
Contributor Author

@ashutosh-b-b ashutosh-b-b Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

From : https://arxiv.org/pdf/1806.00421.pdf
Incase of forward eqns it is the initial condition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes in forward equations it's an initial condition. But for backwards we have a terminal condition. How are the two separate cases being handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't in this case the conversion as simple as v(t,x) = u(T - t, x) for changing from fwd to backward. I found this:
image

I got this from here : https://jberner.info/data/Thesis_Berner.pdf

- or an initial condition for parametric families of Backward Kolmogorov Equations of the form:
```math
\partial_t u(t,x) = - \mu(t, x) \nabla_x u(t,x) - \frac{1}{2} \sigma^2(t, x) \Delta_x u(t,x)
```
with terminal condition given by `g(x, γ_phi)`

We can use the Feynman-Kac formula :
```math
S_t^x = \int_{0}^{t}\mu(S_s^x)ds + \int_{0}^{t}\sigma(S_s^x)dB_s
```
And the solution is given by:
```math
f(T, x) = \mathbb{E}[g(S_T^x, γ_phi)]
```
33 changes: 33 additions & 0 deletions docs/src/tutorials/nnkolmogorov.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# `NNKolmogorov`

## Solving high dimensional Rainbow European Options for a range of initial stock prices:

```julia
d = 10 # dims
T = 1/12
sigma = 0.01 .+ 0.03.*Matrix(Diagonal(ones(d))) # volatility
mu = 0.06 # interest rate
K = 100.0 # strike price
function μ_func(du, u, p, t)
du .= mu*u
end

function σ_func(du, u, p, t)
du .= sigma * u
end

tspan = (0.0, T)
# The range for initial stock price
xspan = [(98.00, 102.00) for i in 1:d]

g(x) = max(maximum(x) -K, 0)

sdealg = EM()
# provide `x0` as nothing to the problem since we are provinding a range for `x0`.
prob = ParabolicPDEProblem(μ_func, σ_func, nothing, tspan, g = g, xspan = xspan)
opt = Flux.Optimisers.Adam(0.01)
alg = NNKolmogorov(m, opt)
m = Chain(Dense(d, 16, elu), Dense(16, 32, elu), Dense(32, 16, elu), Dense(16, 1))
sol = solve(prob, alg, sdealg, verbose = true, dt = 0.01,
dx = 0.0001, trajectories = 1000, abstol = 1e-6, maxiters = 300)
```
61 changes: 61 additions & 0 deletions docs/src/tutorials/nnparamkolmogorov.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# `NNParamKolmogorov`

## Solving Parametric Family of High Dimensional Heat Equation.

In this example we will solve the high dimensional heat equation over a range of initial values, and also over a range of thermal diffusivity.
```julia
d = 10
# models input is `d` for initial values, `d` for thermal diffusivity, and last dimension is for stopping time.
m = Chain(Dense(d + 1 + 1, 32, relu), Dense(32, 16, relu), Dense(16, 8, relu), Dense(8, 1))
ensemblealg = EnsembleThreads()
γ_mu_prototype = nothing
γ_sigma_prototype = zeros(1, 1)
γ_phi_prototype = nothing

sdealg = EM()
tspan = (0.00, 1.00)
trajectories = 100000
function phi(x, y_phi)
sum(x .^ 2)
end
function sigma_(dx, x, γ_sigma, t)
dx .= γ_sigma[:, :, 1]
end
mu_(dx, x, γ_mu, t) = dx .= 0.00

xspan = [(0.00, 3.00) for i in 1:d]

p_domain = (p_sigma = (0.00, 2.00), p_mu = nothing, p_phi = nothing)
p_prototype = (p_sigma = γ_sigma_prototype, p_mu = γ_mu_prototype, p_phi = γ_phi_prototype)
dps = (p_sigma = 0.1, p_mu = nothing, p_phi = nothing)

dt = 0.01
dx = 0.01
opt = Flux.Optimisers.Adam(5e-2)

prob = ParabolicPDEProblem(mu_,
sigma_,
nothing,
tspan;
g = phi,
xspan,
p_domain = p_domain,
p_prototype = p_prototype)

sol = solve(prob, NNParamKolmogorov(m, opt), sdealg, verbose = true, dt = 0.01,
abstol = 1e-10, dx = 0.1, trajectories = trajectories, maxiters = 1000,
use_gpu = false, dps = dps)
```
Similarly we can parametrize the drift function `mu_` and the initial function `g`, and obtain a solution over all parameters and initial values.

# Inferring on the solution from `NNParamKolmogorov`:
```julia
x_test = rand(xspan[1][1]:0.1:xspan[1][2], d)
p_sigma_test = rand(p_domain.p_sigma[1]:dps.p_sigma:p_domain.p_sigma[2], 1, 1)
t_test = rand(tspan[1]:dt:tspan[2], 1, 1)
p_mu_test = nothing
p_phi_test = nothing
```
```julia
sol.ufuns(x_test, t_test, p_sigma_test, p_mu_test, p_phi_test)
```
4 changes: 3 additions & 1 deletion src/HighDimPDE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,10 @@ include("DeepBSDE.jl")
include("DeepBSDE_Han.jl")
include("MLP.jl")
include("NNStopping.jl")
include("NNKolmogorov.jl")
include("NNParamKolmogorov.jl")

export PIDEProblem, ParabolicPDEProblem, PIDESolution, DeepSplitting, DeepBSDE, MLP, NNStopping

export NNKolmogorov, NNParamKolmogorov
export NormalSampling, UniformSampling, NoSampling, solve
end
122 changes: 122 additions & 0 deletions src/NNKolmogorov.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
Algorithm for solving Kolmogorov Equations.

```julia
HighDimPDE.NNKolmogorov(chain, opt)
```
Arguments:
- `chain`: A Chain neural network with a d-dimensional output.
- `opt`: The optimizer to train the neural network. Defaults to `ADAM(0.1)`.
[1]Beck, Christian, et al. "Solving stochastic differential equations and Kolmogorov equations by means of deep learning." arXiv preprint arXiv:1806.00421 (2018).
"""
struct NNKolmogorov{C, O} <: HighDimPDEAlgorithm
chain::C
opt::O
end
NNKolmogorov(chain; opt = Flux.ADAM(0.1)) = NNKolmogorov(chain, opt)

"""
$(TYPEDSIGNATURES)

Returns a `PIDESolution` object.

# Arguments

- `sdealg`: a SDE solver from [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/solvers/sde_solve/).
If not provided, the plain vanilla [DeepBSDE](https://arxiv.org/abs/1707.02568) method will be applied.
If provided, the SDE associated with the PDE problem will be solved relying on
methods from DifferentialEquations.jl, using [Ensemble solves](https://diffeq.sciml.ai/stable/features/ensemble/)
via `sdealg`. Check the available `sdealg` on the
[DifferentialEquations.jl doc](https://diffeq.sciml.ai/stable/solvers/sde_solve/).
- `maxiters`: The number of training epochs. Defaults to `300`
- `trajectories`: The number of trajectories simulated for training. Defaults to `100`
- Extra keyword arguments passed to `solve` will be further passed to the SDE solver.
"""
function DiffEqBase.solve(prob::ParabolicPDEProblem,
pdealg::HighDimPDE.NNKolmogorov,
sdealg;
ensemblealg = EnsembleThreads(),
abstol = 1.0f-6,
verbose = false,
maxiters = 300,
trajectories = 1000,
save_everystep = false,
use_gpu = false,
dt,
dx,
kwargs...)
tspan = prob.tspan
sigma = prob.σ
μ = prob.μ

noise_rate_prototype = get(prob.kwargs, :noise_rate_prototype, nothing)
phi = prob.g

xspan = prob.kwargs.xspan

xspans = isa(xspan, Tuple) ? [xspan] : xspan

d = length(xspans)
ts = tspan[1]:dt:tspan[2]
xs = map(xspans) do xspan
xspan[1]:dx:xspan[2]
end
N = size(ts)
T = tspan[2]

#hidden layer
chain = pdealg.chain
opt = pdealg.opt
ps = Flux.params(chain)
xi = mapreduce(x -> rand(x, 1, trajectories), vcat, xs)
#Finding Solution to the SDE having initial condition xi. Y = Phi(S(X , T))
sdeproblem = SDEProblem(μ,
sigma,
xi[:, 1],
tspan,
noise_rate_prototype = noise_rate_prototype)

function prob_func(prob, i, repeat)
SDEProblem(prob.f,
xi[:, i],
prob.tspan,
noise_rate_prototype = prob.noise_rate_prototype)
end
output_func(sol, i) = (sol.u[end], false)
ensembleprob = EnsembleProblem(sdeproblem,
prob_func = prob_func,
output_func = output_func)
sim = solve(ensembleprob,
sdealg,
ensemblealg,
dt = dt,
trajectories = trajectories,
adaptive = false)

x_sde = Array(sim)

y = reduce(hcat, phi.(eachcol(x_sde)))

y = use_gpu ? y |> gpu : y
xi = use_gpu ? xi |> gpu : xi

#MSE Loss Function
loss(m, x, y) = Flux.mse(m(x), y)

losses = AbstractFloat[]

opt_state = Flux.setup(opt, chain)
for epoch in 1:maxiters
gs = Flux.gradient(chain) do model
loss(model, xi, y)
end
Flux.update!(opt_state, chain, gs[1])
l = loss(chain, xi, y)
@info "Current Epoch: $epoch Current Loss: $l"
push!(losses, l)
end
# Flux.train!(loss, chain, data, opt; cb = callback)
chainout = chain(xi)
xi, chainout
return PIDESolution(xi, ts, losses, chainout, chain, nothing)
end
Loading
Loading