#
# this cell does not appear in the rendering, but is executed
#
# for reproducibility purposes, we load the local julia environment/project, containing
# [13f3f980] CairoMakie v0.11.6
# [a93c6f00] DataFrames v1.6.1
# [864edb3b] DataStructures v0.18.16
# [0c46a032] DifferentialEquations v7.12.0
# [e9467ef8] GLMakie v0.9.6
# [91a5bcdd] Plots v1.40.0
# [f27b6e38] Polynomials v4.0.6
# [90137ffa] StaticArrays v1.9.1
# [0c5d862f] Symbolics v5.16.1
#
# to share the environment, copy Project.toml and Manifest.toml files in some directory
# `activate` the local environment
# if necessary `instantiate` to get the correct package versions
#
# to check if some PackageName.jl is used from the local environment
## Pkg.status("PackageName")
using Pkg
Pkg.activate(".")
Respecter la positivité d’un modèle
Modèle positif
En dynamique de populations, les variables des modèles représentent des densités. Un modèle sera bien posé s’il assure la non-négativité de toutes les composantes des trajectoires issues de conditions initiales non-négatives (système positif).
Il est assez facile de vérifier mathématiquement si un modèle est bien positif, en évaluant les dérivées de l’état sur les frontières de l’orthant positif \mathbb{R}^n_+. Cependant, même si le modèle est bien posé, des trajectoires peuvent se rapprocher très fortement des frontières de \mathbb{R}^n_+ et parfois entraîner une instabilité numérique et l’apparition de trajectoires simulées qui ne respectent pas la positivité. Certains schémas numériques permettent de respecter la contrainte de positivité par construction pour certaines classes de modèles (e.g. Blanes, Iserles, and Macnamara (2022)).
Avec DifferentialEquations.jl
, il est possible d’ajouter une contrainte d’appartenance à un domaine (comme e.g. \mathbb{R}^n_+) que le résultat de l’intégration par le schéma numérique doit vérifier à chaque itération; si la contrainte n’est pas respectée, alors le pas d’intégration est diminué. Cette méthode est valable pour tous les schémas numériques disponibles.
Modèle de Bauch (2005)
Le Modèle de Bauch (2005) analyse les interactions entre dynamique des épidémies et comportement individuel de vaccination, sur la base d’un modèle de type SIR. Le modèle s’écrit: \left\{\begin{array}{l} \dot S = \mu (1-x) - \beta S I - \mu S,\\ \dot I = \beta S I - \gamma I - \mu I,\\ \dot x = k x (1-x)(\omega I - 1), \end{array}\right. avec S et I les densités d’invidus susceptibles et infectés; les paramètres \mu,~\beta,~\gamma sont associés respectivement aux processus de naissance/mortalité, transmission et guérison.
La variable x représente la proportion d’individus vaccinés à la naissance. Cette proportion suit une dynamique d’imitation, qui tend à augmenter la couverture vaccinale des nouveaux nés lorsque la prévalence de la maladie est plus forte, avec k la vitesse d’ajustement et \omega un paramètre de sensibilité à la dangerosité de la maladie.
Il est facile de vérifier la positivité de ce modèle (\dot z_i\geq 0,~\forall i), néanmoins les simulations présentent une instabilité numérique, susceptible de générer des trajectoires négatives.
Simulations
Simulation sans contrainte particulière sur l’état
Si l’on ne prend pas de précautions, les simulations peuvent générer des trajectoires négatives1.
1 cf. à ce propos la légende de la figure 3 dans Bauch (2005), (2.10) et (2.12) devant y être respectivement comprises comme (2.11) et (2.10).
Nous reprenons ici pour partie les paramètres de Bauch (2005), dans un objectif illustratif.
# paramètres
= 1/(365*50)
μ = 0.1
γ = 0.001
k = 6000
ω = 10*(μ + γ)
β = [μ, γ, k, ω, β]
param
# horizon temporel
= 365*200
tmax = (0.0, tmax)
tspan = 0.1 tstep
Définissons les équations du modèle:
# modèle
function imit_vacc(etat, param, t)
= param
μ, γ, k, ω, β
= etat[1]
S = etat[2]
I = etat[3]
x
= μ*(1-x) - β*S*I - μ*S
dS = β*S*I - γ*I - μ*I
dI = k*x*(1-x)*(ω*I-1)
dx
return [dS, dI, dx]
end
Et intégrons depuis une condition initiale, en utilisant la méthode usuelle de DifferentialEquations.jl
(avec le shema numérique Rodas4P()
).
using DifferentialEquations
# conditions initiales
= 0.05
S0 = 0.0001
I0 = 0.95
x0 = [S0, I0, x0]
etat0
# probleme aux conditions initiales
= ODEProblem(
tosim
imit_vacc,
etat0,
tspan,
param,= 1,
saveat
)
# intégration
= 1e-10
tol = solve(tosim, Rodas4P(), reltol = tol, abstol = tol) sol
Une représentation graphique montre que la positivité des variables n’est pas respectée par l’intégration numérique.
Code
# représentation graphique
using CairoMakie
# figure setup
= Figure()
fig1
= Axis(
ax1 1, 1],
fig1[= "temps",
xlabel = "densités de populations",
ylabel
)
= Axis(
ax2 1, 2],
fig1[= "temps",
xlabel = "taux de vaccination",
ylabel
)
# plots
lines!(
ax1,
sol.t,1,:];
sol[= 2,
linewidth = L"$S$",
label )
!(
lines
ax1,
sol.t,2,:];
sol[= 2,
linewidth = L"$I$",
label )
(ax1, position = :rc)
axislegend
lines!(
ax2,
sol.t,3,:];
sol[= 2,
linewidth = L"$x$",
label )
(ax2, position = :rc)
axislegend
# titre général en ligne 0
Label(fig1[0, :], "Simulation sans contrainte de positivité", fontsize = 20)
fig1
En fait, on constate que les simulations ne respectent pas le domaine de définition des variables; ici le nombre d’individus infectés I diverge vers -\infty tandis que le nombre d’individus susceptibles S diverge vers +\infty, alors que ces deux variables sont mathématiquement contraintes au domaine [0,1].
Simulation avec contrainte de positivité
Il est possible d’imposer aux solvers de DifferentialEquations.jl
de respecter l’appartenance des variables à un domaine spécifique à chaque pas de temps, via un kwarg isoutofdomain
passé à solve()
. Cet argument est assigné à une fonction qui renvoie true
ou false
selon que l’état est ou pas dans le domaine souhaité. Si la contrainte n’est pas respectée, le pas de temps d’intégration du solver est réduit.
On commence par définir une fonction qui renvoie false
quand l’état a toutes ses composantes positives, et true
sinon. Cette fonction est réalisée sur la base de la fonction any()
et d’une fonction anonyme qui teste la positivité:
function isnotpositive(u, par, t)
return any(z -> z < 0, u)
end
Et on passe cet argument pour l’intégration numérique:
= solve(
sol_constr
tosim,Rodas4P(),
= tol,
reltol = tol,
abstol = isnotpositive, # contrainte sur l'état
isoutofdomain )
Le résultat de l’intégration (même schéma numérique, mêmes tolérances, même horizon temporel) permet d’obtenir le cycle limite attendu dans ce cas (Bauch (2005)):
Code
# représentation graphique
= Figure()
fig2
= Axis(
ax1 1, 1],
fig2[= "temps",
xlabel = "densités de populations",
ylabel
)
= Axis(
ax2 1, 2],
fig2[= "temps",
xlabel = "taux de vaccination",
ylabel
)
lines!(
ax1,
sol_constr.t,1,:];
sol_constr[= 2,
linewidth = L"$S$",
label )
!(
lines
ax1,
sol_constr.t,2,:];
sol_constr[= 2,
linewidth = L"$I$",
label )
(ax1, position = :rc)
axislegend
lines!(
ax2,
sol_constr.t,3,:];
sol_constr[= 2,
linewidth = L"$x$",
label )
(ax2, position = :rc)
axislegend
# titre général en ligne 0
Label(fig2[0, :], "Simulation avec contrainte de positivité", fontsize = 20)
fig2