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

[FEAT]: Add NNStopping #81

Merged
merged 38 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
eea3b1f
feat: Add NNStopping for Optimal Stopping Problems
ashutosh-b-b Jan 23, 2024
cfd5b45
test: add tests for NNStopping
ashutosh-b-b Jan 23, 2024
2c516a3
feat: add `NNStopping` for OptimalStopping Problem
ashutosh-b-b Jan 26, 2024
1fe1d3f
chore: export `NNStopping`
ashutosh-b-b Jan 26, 2024
3faa879
test: add tests for NNStopping
ashutosh-b-b Jan 26, 2024
d346a80
docs: add docstring for `NNStopping`
ashutosh-b-b Jan 26, 2024
f5c4fd5
test: enable tests
ashutosh-b-b Jan 26, 2024
ab340cf
chore: fix formatting
ashutosh-b-b Jan 26, 2024
e0d07fb
Merge branch 'main' into bb/nn_stopping
ashutosh-b-b Jan 26, 2024
e670a5a
docs: add docs for NNStopping
ashutosh-b-b Jan 30, 2024
71ff37e
docs: add tutorial for NNStopping
ashutosh-b-b Jan 30, 2024
de266d2
docs: add tutorial and page to pages.jl
ashutosh-b-b Jan 30, 2024
a935123
feat: add dispatch on `PIDEProblem` for non linear term 0
ashutosh-b-b Jan 30, 2024
71fe64c
fix: update NNStopping to work with PIDEProblem
ashutosh-b-b Jan 30, 2024
d9d9f4c
test: update tests
ashutosh-b-b Jan 30, 2024
425ab15
docs: update docs
ashutosh-b-b Jan 30, 2024
0ada89c
chore: fix formatting in pages.jl
ashutosh-b-b Jan 30, 2024
ab4be5a
docs: fix spelling errors
ashutosh-b-b Jan 30, 2024
31a64f1
test: update tests
ashutosh-b-b Jan 30, 2024
a698b96
test: Increase maxiters in DeepSplitting heat eqn tests
ashutosh-b-b Jan 31, 2024
901470e
feat: update PIDEProblem definition to accomodate all kinds of equations
ashutosh-b-b Feb 1, 2024
130cd20
test: add tests for PIDEProblem
ashutosh-b-b Feb 1, 2024
cd1d23b
chore: update all tests with the new definition of PIDEProblem
ashutosh-b-b Feb 1, 2024
f5ed8e4
docs: update all docs with the new definition of PIDEProblem
ashutosh-b-b Feb 1, 2024
9656d8e
test: add Random.seed! to MLP tests
ashutosh-b-b Feb 1, 2024
32a66cc
test: add Random.seed! to DeepSplitting
ashutosh-b-b Feb 1, 2024
518a2f1
feat: add `ParabolicPDEProblem`
ashutosh-b-b Feb 3, 2024
dc66a82
fix: update `solve` dispatches for ParabolicPDEProblem
ashutosh-b-b Feb 3, 2024
ee934ad
test: update tests
ashutosh-b-b Feb 3, 2024
3fc048a
test: make chain f64 in NNStopping tests
ashutosh-b-b Feb 3, 2024
ce172b4
test: fix bug in runtests
ashutosh-b-b Feb 4, 2024
9ba1455
test: increase error tolerance
ashutosh-b-b Feb 4, 2024
125182d
docs: update docs for MLP and DeepSplitting
ashutosh-b-b Feb 9, 2024
9534761
docs: add eqn for ParabolicPDEProblem
ashutosh-b-b Feb 9, 2024
a7358f7
docs: add section on problems and add notes on defining problems
ashutosh-b-b Feb 9, 2024
53cc92b
Update docs/src/problems.md
ChrisRackauckas Feb 9, 2024
0f1c148
docs: make docstrings have TYPEDSIGNATURES
ashutosh-b-b Feb 9, 2024
66372d3
docs: explicilty add section for supported problems for each solver
ashutosh-b-b Feb 9, 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 docs/pages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ pages = [
"Getting started" => "getting_started.md",
"Solver Algorithms" => ["MLP.md",
"DeepSplitting.md",
"DeepBSDE.md"],
"DeepBSDE.md",
"NNStopping.md"],
"Tutorials" => [
"tutorials/deepsplitting.md",
"tutorials/deepbsde.md",
"tutorials/mlp.md",
"tutorials/nnstopping.md",
],
"Feynman Kac formula" => "Feynman_Kac.md",
]
4 changes: 2 additions & 2 deletions docs/src/DeepSplitting.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ In `HighDimPDE.jl` the right parameter combination $\theta$ is found by iterativ
`DeepSplitting` allows obtaining $u(t,x)$ on a single point $x \in \Omega$ with the keyword $x$.

```julia
prob = PIDEProblem(g, f, μ, σ, x, tspan)
prob = PIDEProblem(μ, σ, x, tspan, g, f,)
Copy link
Member

Choose a reason for hiding this comment

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

this is a breaking change

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. We will need a breaking release. I'd suggest we do that after NNKolmogorov is in

```

### Hypercube
Yet more generally, one wants to solve Eq. (1) on a $d$-dimensional cube $[a,b]^d$. This is offered by `HighDimPDE.jl` with the keyword `x0_sample`.

```julia
prob = PIDEProblem(g, f, μ, σ, x, tspan, x0_sample = x0_sample)
prob = PIDEProblem(μ, σ, x, tspan, g, f; x0_sample = x0_sample)
```
Internally, this is handled by assigning a random variable as the initial point of the particles, i.e.
```math
Expand Down
29 changes: 29 additions & 0 deletions docs/src/NNStopping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# [The `NNStopping` algorithm](@id nn_stopping)

```@autodocs
Modules = [HighDimPDE]
Pages = ["NNStopping.jl"]
```
## The general idea 💡

Similar to DeepSplitting and DeepBSDE, NNStopping evaluates the PDE as a Stochastic Differential Equation. Consider an Obstacle PDE of the form:
```math
max\lbrace\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) , g(t,x) - u(t,x)\rbrace
Copy link
Member

Choose a reason for hiding this comment

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

Is this a PIDE?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically no. Its an obstacle PDE with non linear term f = 0:
image

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I see a difference? It just requires f = 0. That can be checked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, I have a dispatch for f = 0 in the NNKolmogorov PR
:

function PIDEProblem(g,
μ,
σ,
tspan,
xspan;
p = nothing,
x0_sample = NoSampling(),
noise_rate_prototype = nothing,
kwargs...)

We can use that, with a kwarg for g. How does that sound?

```

Such PDEs are commonly used as representations for the dynamics of stock prices that can be exercised before maturity, such as American Options.

Using the Feynman-Kac formula, the underlying SDE will be:

```math
dX_{t}=\mu(X,t)dt + \sigma(X,t)\ dW_{t}^{Q}
```

The payoff of the option would then be:

```math
sup\lbrace\mathbb{E}[g(X_\tau, \tau)]\rbrace
```
Where τ is the stopping (exercising) time. The goal is to retrieve both the optimal exercising strategy (τ) and the payoff.

We approximate each stopping decision with a neural network architecture, inorder to maximise the expected payoff.
6 changes: 3 additions & 3 deletions docs/src/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ g(x) = exp(-sum(x.^2)) # initial condition
μ(x, p, t) = 0.0 # advection coefficients
σ(x, p, t) = 0.1 # diffusion coefficients
f(x, y, v_x, v_y, ∇v_x, ∇v_y, p, t) = max(0.0, v_x) * (1 - max(0.0, v_x)) # nonlocal nonlinear part of the
prob = PIDEProblem(g, f, μ, σ, x0, tspan) # defining the problem
prob = PIDEProblem(μ, σ, x0, tspan, g, f) # defining the problem

## Definition of the algorithm
alg = MLP() # defining the algorithm. We use the Multi Level Picard algorithm
Expand Down Expand Up @@ -62,7 +62,7 @@ g(x) = exp( -sum(x.^2) ) # initial condition
σ(x, p, t) = 0.1 # diffusion coefficients
mc_sample = UniformSampling(fill(-5f-1, d), fill(5f-1, d))
f(x, y, v_x, v_y, ∇v_x, ∇v_y, p, t) = max(0.0, v_x) * (1 - max(0.0, v_y))
prob = PIDEProblem(g, f, μ, σ, x0, tspan) # defining x0_sample is sufficient to implement Neumann boundary conditions
prob = PIDEProblem(μ, σ, x0, tspan, g, f) # defining x0_sample is sufficient to implement Neumann boundary conditions

## Definition of the algorithm
alg = MLP(mc_sample = mc_sample)
Expand All @@ -87,7 +87,7 @@ g(x) = exp.(-sum(x.^2, dims=1)) # initial condition
σ(x, p, t) = 0.1f0 # diffusion coefficients
x0_sample = UniformSampling(fill(-5f-1, d), fill(5f-1, d))
f(x, y, v_x, v_y, ∇v_x, ∇v_y, p, t) = v_x .* (1f0 .- v_y)
prob = PIDEProblem(g, f, μ, σ, x0, tspan,
prob = PIDEProblem(μ, σ, x0, tspan, g, f;
x0_sample = x0_sample)

## Definition of the neural network to use
Expand Down
6 changes: 3 additions & 3 deletions docs/src/tutorials/deepbsde.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ g(X) = log(0.5f0 + 0.5f0 * sum(X.^2))
f(X,u,σᵀ∇u,p,t) = -λ * sum(σᵀ∇u.^2)
μ_f(X,p,t) = zero(X) # Vector d x 1 λ
σ_f(X,p,t) = Diagonal(sqrt(2.0f0) * ones(Float32, d)) # Matrix d x d
prob = PIDEProblem(g, f, μ_f, σ_f, X0, tspan)
prob = PIDEProblem(μ_f, σ_f, X0, tspan, g, f)
hls = 10 + d # hidden layer size
opt = Optimisers.Adam(0.01) # optimizer
# sub-neural network approximating solutions at the desired point
Expand Down Expand Up @@ -75,7 +75,7 @@ g(X) = log(0.5f0 + 0.5f0*sum(X.^2))
f(X,u,σᵀ∇u,p,t) = -λ*sum(σᵀ∇u.^2)
μ_f(X,p,t) = zero(X) #Vector d x 1 λ
σ_f(X,p,t) = Diagonal(sqrt(2.0f0)*ones(Float32,d)) #Matrix d x d
prob = PIDEProblem(g, f, μ_f, σ_f, X0, tspan)
prob = PIDEProblem(μ_f, σ_f, X0, tspan, g, f)
```

#### Define the Solver Algorithm
Expand Down Expand Up @@ -135,7 +135,7 @@ f(X,u,σᵀ∇u,p,t) = r * (u - sum(X.*σᵀ∇u))
g(X) = sum(X.^2)
μ_f(X,p,t) = zero(X) #Vector d x 1
σ_f(X,p,t) = Diagonal(sigma*X) #Matrix d x d
prob = PIDEProblem(g, f, μ_f, σ_f, X0, tspan)
prob = PIDEProblem(μ_f, σ_f, X0, tspan, g, f)
```

As described in the API docs, we now need to define our `NNPDENS` algorithm
Expand Down
4 changes: 1 addition & 3 deletions docs/src/tutorials/deepsplitting.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ g(x) = exp.(- sum(x.^2, dims=1) ) # initial condition
σ(x, p, t) = 0.1f0 # diffusion coefficients
x0_sample = UniformSampling(fill(-5f-1, d), fill(5f-1, d))
f(x, y, v_x, v_y, ∇v_x, ∇v_y, p, t) = v_x .* (1f0 .- v_y)
prob = PIDEProblem(g, f, μ,
σ, x0, tspan,
x0_sample = x0_sample)
prob = prob = PIDEProblem(μ, σ, x0, tspan, g, f; x0_sample = x0_sample)

## Definition of the neural network to use
using Flux # needed to define the neural network
Expand Down
5 changes: 2 additions & 3 deletions docs/src/tutorials/mlp.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ g(x) = exp(- sum(x.^2) ) # initial condition
μ(x, p, t) = 0.0 # advection coefficients
σ(x, p, t) = 0.1 # diffusion coefficients
f(x, y, v_x, v_y, ∇v_x, ∇v_y, p, t) = max(0.0, v_x) * (1 - max(0.0, v_x)) # nonlocal nonlinear part of the
prob = PIDEProblem(g, f, μ, σ, x0, tspan) # defining the problem
prob = PIDEProblem(μ, σ, x0, tspan, g, f) # defining the problem

## Definition of the algorithm
alg = MLP() # defining the algorithm. We use the Multi Level Picard algorithm
Expand All @@ -44,8 +44,7 @@ g(x) = exp( -sum(x.^2) ) # initial condition
σ(x, p, t) = 0.1 # diffusion coefficients
mc_sample = UniformSampling(fill(-5f-1, d), fill(5f-1, d))
f(x, y, v_x, v_y, ∇v_x, ∇v_y, t) = max(0.0, v_x) * (1 - max(0.0, v_y))
prob = PIDEProblem(g, f, μ,
σ, x0, tspan) # defining x0_sample is sufficient to implement Neumann boundary conditions
prob = PIDEProblem(μ, σ, x0, tspan, g, f) # defining x0_sample is sufficient to implement Neumann boundary conditions

## Definition of the algorithm
alg = MLP(mc_sample = mc_sample )
Expand Down
50 changes: 50 additions & 0 deletions docs/src/tutorials/nnstopping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Solving for optimal strategy and expected payoff of a Bermudan Max-Call option

We will calculate optimal strategy for Bermudan Max-Call option with following drift, diffusion and payoff:
```math
μ(x) =(r − δ) x, σ(x) = β diag(x1, ... , xd),\\
g(t, x) = e^{-rt}max\lbrace max\lbrace x1, ... , xd \rbrace − K, 0\rbrace
```
We define the parameters, drift function and the diffusion function for the dynamics of the option.
```julia
d = 3 # Number of assets in the stock
r = 0.05 # interest rate
beta = 0.2 # volatility
T = 3 # maturity
u0 = fill(90.0, d) # initial stock value
delta = 0.1 # delta
f(du, u, p, t) = du .= (r - delta) * u # drift
sigma(du, u, p, t) = du .= beta * u # diffusion
tspan = (0.0, T)
N = 9 # discretization parameter
dt = T / (N)
K = 100.00 # strike price

# payoff function
function g(x, t)
return exp(-r * t) * (max(maximum(x) - K, 0))
end

```
We then define a `PIDEProblem` with no non linear term:
```julia
prob = PIDEProblem(f, sigma, u0, tspan; payoff = g)
```
!!! note
We provide the payoff function with a keyword argument `payoff`

And now we define our models:
```julia
models = [Chain(Dense(d + 1, 32, tanh), BatchNorm(32, tanh), Dense(32, 1, sigmoid))
for i in 1:N]
```
!!! note
The number of models should be equal to the time discritization.

And finally we define our optimizer and algorithm, and call `solve`:
```julia
opt = Flux.Optimisers.Adam(0.01)
alg = NNStopping(models, opt)

sol = solve(prob, alg, SRIW1(); dt = dt, trajectories = 1000, maxiters = 1000, verbose = true)
```
6 changes: 3 additions & 3 deletions paper/paper.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ vol = prod(x0_sample[2] - x0_sample[1])
f(y, z, v_y, v_z, p, t) = max.(v_y, 0f0) .* (m(y) .- vol * max.(v_z, 0f0) .* m(z)) # nonlocal nonlinear part of the

# defining the problem
prob = PIDEProblem(g, f, μ, σ, tspan,
prob = PIDEProblem(μ, σ, tspan, g, f,
x0_sample = x0_sample
)
# solving
Expand Down Expand Up @@ -162,8 +162,8 @@ g(x) = exp( -sum(x.^2) ) # initial condition
σ(x, p, t) = 0.1 # diffusion coefficients
x0_sample = [-1/2, 1/2]
f(x, y, v_x, v_y, ∇v_x, ∇v_y, t) = max(0.0, v_x) * (1 - max(0.0, v_y))
prob = PIDEProblem(g, f, μ,
σ, x0, tspan,
prob = PIDEProblem(μ,
σ, x0, tspan, g, f,
x0_sample = x0_sample) # defining x0_sample is sufficient to implement Neumann boundary conditions

## Definition of the algorithm
Expand Down
4 changes: 2 additions & 2 deletions src/DeepBSDE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
g(X) = sum(X.^2)
μ_f(X,p,t) = zero(X) #Vector d x 1
σ_f(X,p,t) = Diagonal(sigma*X) #Matrix d x d
prob = PIDEProblem(g, f, μ_f, σ_f, x0, tspan)
prob = PIDEProblem(μ_f, σ_f, x0, tspan, g, f)

hls = 10 + d #hidden layer size
opt = Flux.Optimise.Adam(0.001)
Expand Down Expand Up @@ -75,7 +75,7 @@
[Deep Primal-Dual algorithm for BSDEs](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3071506).
- Extra keyword arguments passed to `solve` will be further passed to the SDE solver.
"""
function DiffEqBase.solve(prob::PIDEProblem,
function DiffEqBase.solve(prob::ParabolicPDEProblem,

Check warning on line 78 in src/DeepBSDE.jl

View check run for this annotation

Codecov / codecov/patch

src/DeepBSDE.jl#L78

Added line #L78 was not covered by tests
pdealg::DeepBSDE,
sdealg;
verbose = false,
Expand Down
2 changes: 1 addition & 1 deletion src/DeepBSDE_Han.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# called whenever sdealg is not specified.
function DiffEqBase.solve(prob::PIDEProblem,
function DiffEqBase.solve(prob::ParabolicPDEProblem,

Check warning on line 2 in src/DeepBSDE_Han.jl

View check run for this annotation

Codecov / codecov/patch

src/DeepBSDE_Han.jl#L2

Added line #L2 was not covered by tests
alg::DeepBSDE;
dt,
abstol = 1.0f-6,
Expand Down
10 changes: 8 additions & 2 deletions src/DeepSplitting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Returns a `PIDESolution` object.
- `use_cuda` : set to `true` to use CUDA.
- `cuda_device` : integer, to set the CUDA device used in the training, if `use_cuda == true`.
"""
function DiffEqBase.solve(prob::PIDEProblem,
function DiffEqBase.solve(prob::Union{PIDEProblem, ParabolicPDEProblem},
alg::DeepSplitting,
dt;
batch_size = 1,
Expand Down Expand Up @@ -98,7 +98,13 @@ function DiffEqBase.solve(prob::PIDEProblem,
K = alg.K
opt = alg.opt
λs = alg.λs
g, f, μ, σ, p = prob.g, prob.f, prob.μ, prob.σ, prob.p
g, μ, σ, p = prob.g, prob.μ, prob.σ, prob.p

f = if isa(prob, ParabolicPDEProblem)
(y, z, v_y, v_z, ∇v_y, ∇v_z, p, t) -> prob.f(y, v_y, ∇v_y, p, t )
else
prob.f
end
T = eltype(x0)

# neural network model
Expand Down
Loading
Loading