Adding constraints

Adding constraints#

We again consider the same problem.

\[\frac{dy}{dt} = A\sin(\omega t + \delta)\]
[1]:
### Dataset
import numpy as np

data = {'Time': np.array([ 0.        ,  0.20408163,  0.40816327,  0.6122449 ,  0.81632653,
        1.02040816,  1.2244898 ,  1.42857143,  1.63265306,  1.83673469,
        2.04081633,  2.24489796,  2.44897959,  2.65306122,  2.85714286,
        3.06122449,  3.26530612,  3.46938776,  3.67346939,  3.87755102,
        4.08163265,  4.28571429,  4.48979592,  4.69387755,  4.89795918,
        5.10204082,  5.30612245,  5.51020408,  5.71428571,  5.91836735,
        6.12244898,  6.32653061,  6.53061224,  6.73469388,  6.93877551,
        7.14285714,  7.34693878,  7.55102041,  7.75510204,  7.95918367,
        8.16326531,  8.36734694,  8.57142857,  8.7755102 ,  8.97959184,
        9.18367347,  9.3877551 ,  9.59183673,  9.79591837, 10.        ]), 'y': np.array([0.91963291, 0.99361406, 1.27816619, 1.83477302, 2.0436977 ,
       2.4108738 , 2.84511313, 2.98244074, 3.01343352, 2.81703372,
       2.66941941, 2.31326251, 1.89208529, 1.40799057, 1.2468084 ,
       1.08452949, 1.03682804, 1.22276976, 1.58095538, 1.73274129,
       2.41068701, 2.6162268 , 3.08196062, 3.0313903 , 2.77556916,
       2.75105715, 2.46021415, 1.85709704, 1.60617448, 1.22300332,
       1.03747336, 1.20756063, 1.10867505, 1.42444299, 1.68301796,
       2.09068759, 2.61731029, 2.42544506, 2.97204288, 2.97302454,
       2.98836389, 2.63614576, 2.06298576, 1.73999305, 1.4090971 ,
       1.12819288, 0.92043848, 1.20091581, 1.29993016, 1.38187416])}

General structure#

[2]:
def sine(y, t, A, omega, delta):
    dy_dt = A * omega * np.sin(omega * t + delta)
    return dy_dt
[3]:
def simulate_model(params):
    y0 = params['y0']
    A = params['A']
    omega = params['omega']
    delta = params['delta']
    sol = odeint(sine, y0, time, args=(A, omega, delta))
    return sol.flatten()


Next, we define an error function, this error function, depends on the data and the model predicted outcomes. The optimizer minimizes this error function

[4]:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import os
import sys
import scipy.io
from concurrent.futures import ProcessPoolExecutor

# Get path
# Get path to MCMCwithODEs_primer (3 levels up)
project_root = os.path.abspath(os.path.join(os.getcwd(), '..','..','..'))
sys.path.insert(0, project_root)

import sys
sys.path.append('./..')  # or absolute path if needed

from invode import ODEOptimizer, lhs_sample, MSE

Adding constraint#

For an example, let’s say we want the value at t = 10, is fixed to 1.5. We can use a constraints.

[21]:
hyperparam = 0.2
def mse_constrainted(output):
    return np.mean((output - data['y'])**2) + hyperparam*np.abs(output[-1]-1.5)
[22]:
param_bounds = {
    'y0': 0.91963291, # fixed
    'A': (0.2, 1.5),
    'delta': (-0.5, 0.5),
    'omega': (0.5, 4)
}

time = data['Time']

optimizer_constrained = ODEOptimizer(
    ode_func=simulate_model,
    error_func=mse_constrainted,
    param_bounds=param_bounds,
    seed=42,
    num_top_candidates=2,
    n_samples=300,
    num_iter=10,
    verbose=False,
    verbose_plot=True
)

optimizer_constrained.fit()
Fitting Progress: 100%|█████████████████████████████████████████████████| 10/10 [00:01<00:00,  9.97it/s]
Refining params: {'A': 1.1915368305751737, 'delta': -0.02444698040884663, 'omega': 1.985154886052043}
Refining params: {'A': 0.9695259615925091, 'delta': 0.29615770908917377, 'omega': 1.9648598743384087}
../_images/tutorials_adding_constraints_11_2.png
[22]:
({'A': 1.058478997699118,
  'delta': 0.035691140341976554,
  'omega': 1.9916707918835548,
  'y0': 0.91963291},
 0.013854377444252103)
[26]:
best_params = optimizer_constrained.best_params
best_fit = simulate_model(best_params)



plt.plot(time,data['y'],'o')
plt.plot(time, best_fit, label='Fit with last point fixed to 1.5')
plt.xlabel("Time")
plt.ylabel("y")
plt.legend()

print(f"The last point is {best_fit[-1]}")
The last point is 1.499999984435069
../_images/tutorials_adding_constraints_12_1.png
[ ]: