<jupyter_start><jupyter_text>Example: Multiple species Reaction Kinetics using ODEModelIn this example we shall fit to a complex system of ODEs, based on that published by [Polgar et al](https://doi.org/10.1016/j.eurpolymj.2017.03.020). However, we shall be generating some mock data instead of using the real deal.<jupyter_code>from symfit import (
	variables, parameters, ODEModel, D, Fit
) 
from symfit.core.support import key2str
import numpy as np
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>First we build a model representing the system of equations.<jupyter_code>t, F, MM, FMM, FMMF = variables('t, F, MM, FMM, FMMF')
k1_f, k1_r, k2_f, k2_r = parameters('k1_f, k1_r, k2_f, k2_r')

MM_0 = 10  # Some made up initial amount of [FF]

model_dict = {
    D(F, t): - k1_f * MM * F + k1_r * FMM - k2_f * FMM * F + k2_r * FMMF,
	D(FMM, t): k1_f * MM * F - k1_r * FMM - k2_r * FMM * F + k2_f * FMMF,
    D(FMMF, t): k2_f * FMM * F - k2_r * FMMF,
    D(MM, t): - k1_f * MM * F + k1_r * FMM,
}
model = ODEModel(
    model_dict, 
    initial={t: 0.0, MM: MM_0, FMM: 0.0, FMMF: 0.0, F: 2 * MM_0}
)
print(model)<jupyter_output>Derivative(F, t; k1_f, k1_r, k2_f, k2_r) = -k1_f*F*MM + k1_r*FMM - k2_f*F*FMM + k2_r*FMMF
Derivative(FMM, t; k1_f, k1_r, k2_f, k2_r) = k1_f*F*MM - k1_r*FMM + k2_f*FMMF - k2_r*F*FMM
Derivative(FMMF, t; k1_f, k1_r, k2_f, k2_r) = k2_f*F*FMM - k2_r*FMMF
Derivative(MM, t; k1_f, k1_r, k2_f, k2_r) = -k1_f*F*MM + k1_r*FMM<jupyter_text>Generate mock data.<jupyter_code>tdata = np.linspace(0, 3, 20)
data = model(t=tdata, k1_f=0.1, k1_r=0.2, k2_f=0.3, k2_r=0.3)._asdict()
sigma_data = 0.3
np.random.seed(42)
for var in data:
	data[var] += np.random.normal(0, sigma_data, size=len(tdata))

plt.scatter(tdata, data[MM], label='[MM]')
plt.scatter(tdata, data[FMM], label='[FMM]')
plt.scatter(tdata, data[FMMF], label='[FMMF]')
plt.scatter(tdata, data[F], label='[F]')
plt.xlabel(r'$t$')
plt.ylabel(r'$C$')
plt.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>Perform the fit. Let's pretend that for experimental reasons, we can only measure the concentration for ``MM`` and ``F``, but not for the intermediate `FMM` nor the product `FMMF`. This is no problem, as we can tell ``symfit`` to ignore those components by setting the data for them to `None`.<jupyter_code>k1_f.min, k1_f.max = 0, 1
k1_r.min, k1_r.max = 0, 1
k2_f.min, k2_f.max = 0, 1
k2_r.min, k2_r.max = 0, 1

fit = Fit(model, t=tdata, MM=data[MM], F=data[F],
          FMMF=None, FMM=None,
          sigma_F=sigma_data, sigma_MM=sigma_data)
fit_result = fit.execute()
print(fit_result)
taxis = np.linspace(tdata.min(), tdata.max(), 1000)
model_fit = model(t=taxis, **fit_result.params)._asdict()
for var in data:
	plt.scatter(tdata, data[var], label='[{}]'.format(var.name))
	plt.plot(taxis, model_fit[var], label='[{}]'.format(var.name))
plt.legend()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16328635269852182, 'fraction_numerical': 0.04022000687521485, 'mean_word_length': 4.728346456692913, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 16, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '333555', 'license': 'MIT', 'n_tokens_mistral': 1403, 'n_tokens_neox': 1335, 'n_words': 316, 'path': 'docs/examples/ex_ode_system.ipynb', 'repo_name': 'ford--prefect/symfit', 'size': '2909'}