# Surrogatize ODEProblem

JuliaSim Surrogates can help users generate surrogate models from their ODE code in just a few short steps:

1. Problem Definition
2. Data Generation
3. Pre-processing
4. Surrogatization
5. Visualisation

## Julia Environment

First we prepare the environment by listing the packages we are using within the example. Each package below (except for OrdinaryDiffEq) represents a module of JuliaSimSurrogates.

using OrdinaryDiffEq
using DataGeneration
using PreProcessing
using Surrogatize
using Visualisations
using LoggingUtils

## Model Setup

This example builds upon the well-documented rober model. First, we define the classic system with an out-of-place form (non-mutating). This form will be useful at the end when we want to make inferences on our trained model.

function rober(u, p, t)
y₁, y₂, y₃ = u
k₁, k₂, k₃ = p
[-k₁ * y₁ + k₃ * y₂ * y₃;
k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃;
k₂ * y₂^2]
end
rober (generic function with 1 method)

Then we define another version of the system, this time accounting for control sampling with an in-place form.

function rober(u, x, p, t)
y₁, y₂, y₃ = u
k₁, k₂, k₃ = p
x₁, x₂, x₃ = x
[-k₁ * y₁ + k₃ * y₂ * y₃ + x₁;
k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃ + x₂;
k₂ * y₂^2 + x₃]
end
rober (generic function with 2 methods)

Finally, in order to declare our ODEProblem, we provide initial conditions, time span and paramter values.

tstop = 1e4
p = [0.04, 3e7, 1e4]
u0 = [1.0, 0.0, 0.0]
tspan = (0.0, tstop)
prob = ODEProblem{false}(rober, u0, tspan, p)
ODEProblem with uType Vector{Float64} and tType Float64. In-place: false
timespan: (0.0, 10000.0)
u0: 3-element Vector{Float64}:
1.0
0.0
0.0

## Generating Data

Now that we have a working model we want to generate many samples. A collection of samples can then be used to train a surrogate model. JuliaSim's DataGeneration.jl will help us generate the data.

### Parameter Space

For simplicity, this example will only define a ParameterSpace wherein we define the desired number of samples, bounds of the space, and values in order to produce our final parameter space.

nsamples = 100
param_space = [(0.036, 0.044), (2.7e7, 3.3e7), (0.9e4, 1.1e4)]
p_lb = first.(param_space)
p_ub = last.(param_space)
param_space = ParameterSpace(p_lb, p_ub, nsamples)
ParameterSpace{Matrix{Float64}}
3 dimension space with 100 samples)
Samples : [0.0361875 0.0401875 … 0.0424687 0.0384687; 2.93906e7 3.23906e7 … 2.88984e7 3.18984e7; 10640.6 9640.62 … 10492.2 9492.19]


### Producing Simulations

In order to generate the data we now define a simulator configuration and run the simulations! Define a Simulator configuration which holds information about all the sampling spaces and apply this Simulator to the problem definition.

sim_config = SimulatorConfig(param_space)
exp_data = sim_config(prob; alg = Rosenbrock23(), abstol = 1e-12, reltol = 1e-12);
[ Info: #Processes = 1
[ Info: Simulating samples from worker processes

## Surrogatization

All that's left to create a surrogate from our SimulationResults is to declare the model for surrogatization. Options include CTESN, ELM, and Augmented ELM. This example will employ the ELM (Extreme Learning Machine) model with 3 input dimensions and 50 hidden features.

elm = ELM(3, 250)
surr = surrogatize(exp_data, elm);

We can now make inferences on this surr object!

surr([1.1, 0.1, 0.1], [0.04, 3e7, 1e4], (0.0, 1e4))