Populations en interaction (2)

Le modèle de Rosenzweig MacArthur

Nous considérons le modèle de dynamique de populations attribué à Rosenzweig et MacArthur (voir Rosenzweig and MacArthur (1963), Turchin (2003), Smith (2008)).

\left\{\begin{array}{l} \dot x = \displaystyle rx\left(1-\frac{x}{K}\right) - c \frac{x}{h+x} y\\[.3cm] \dot y = b\displaystyle \frac{x}{h+x} y - m y \end{array}\right. \tag{1}

Dynamiques

Il n’y a pas de difficulté particulière à la simulation par rapport au modèle de Lotka Volterra.

Code
using DifferentialEquations
using DataFrames

# conditions initiales
x0 = 1.0
y0 = 1.95
etat0 = [x0, y0]

# paramètres
r = 1.0
K = 10.0
c = 1.0
h = 2.0
b = 2.0
m = 1.0
par_rma = [r, K, c, h, b, m]

# temps d'integration
tspan = (0.0, 55.0)
tstep = .01

# définition du modèle
function rma(u, par, t)
    r, K, c, h, b, m = par
    x = u[1]
    y = u[2]

    dx = r*x*(1-x/K) - c*x/(h+x)*y
    dy = b*x/(h+x)*y - m*y

    return [dx, dy]
end

# problème d'intégration
prob_rma = ODEProblem(
    rma,
    etat0,
    tspan,
    par_rma;
    saveat = tstep,
)

# intégration
sol_rma = solve(prob_rma, reltol = 1e-6)

# dataframe
sol_rma = DataFrame(sol_rma)
rename!(sol_rma, :timestamp => :time, :value1 => :x, :value2 => :y)

Nous utiliserons ici le package de visualisation graphique Makie.jl1 à la place de Plots.jl. Makie.jl permet un contrôle très approfondi des graphiques. Commençons par tracer les dynamiques contre le temps dans une figure simple.

  • 1 entièrement écrit en Julia, présenté comme “le futur” de la représentation graphique avec Julia. Une bonne introduction à Makie.

  • Nous utiliserons le backend CairoMakie pour la visualisation en 2D.

    using CairoMakie

    Un peu comme Matplotlib en Python, Makie définit un triplet FigureAxisPlot : la figure est le conteneur de (éventuellement) plusieurs systèmes d’axes qui contiennent chacun un ou plusieurs graphiques (ligne, point, etc.).

    # on crée la figure
    fig1 = Figure(;
        backgroundcolor = :transparent,
        size = (600,400),
        fontsize = 18,
    )
    
    # on crée un système d'axes en position [1,1] dans la figure
    ax1 = Axis(
        fig1[1,1];
        xlabel = "temps",
        ylabel = "densités de populations",
        title = "Modèle de Rosenzweig MacArthur",
    )
    
    # on trace la population x contre le temps sur le système d'axe ax1
    lines!(
        ax1,
        sol_rma.time,
        sol_rma.x;
        linewidth = 2,
        linestyle = :solid,
        label = L"x(t)",   # formule Latex dans la chaine de caractère
    )
    
    # on rajoute la population y
    lines!(
        ax1,
        sol_rma.time,
        sol_rma.y;
        linewidth = 2,
        linestyle = :solid,
        label = L"y(t)",
    )
    
    # légende
    axislegend(position = :lt)   # position left top
    
    # on affiche la figure, pas de display() ici
    fig1

    Figure 1: Une première figure avec Makie.jl

    Dynamiques et plan de phase

    Préparation de la figure

    Nous allons maintenant tracer un graphique plus complexe comprenant en colonne de droite les dynamiques des proies et des prédateurs sur deux lignes et en colonne de gauche le plan de phase. Préparons la figure et les systèmes d’axes.

    # figure
    fig2 = Figure(;
        backgroundcolor = :transparent,
        size = (800,500),
        fontsize = 20,
    )
    
    # 3 systèmes d'axes
    # position 1e ligne 1e colonne
    ax21 = Axis(fig2[1,1]; title = "Dynamiques")
    
    # position 2e ligne 1e colonne
    ax22 = Axis(fig2[2,1]; xlabel = "temps")
    
    ax23 = Axis(
        fig2[:,2];       # position toutes les lignes, 2e colonne
        xlabel = "proies",
        ylabel = "prédateurs",
        title = "Plan de phase",
    )
    
    # on agrandi un peu la deuxième colonne de la figure
    colsize!(fig2.layout, 2, Auto(1.5))
    
    # ajout d'un titre
    supertitle = Label(
        fig2[0, :],      # position ligne "0" toutes les colonnes
        "Modèle de Rosenzweig MacArthur";
        fontsize = 26,
    )
    
    # ajout d'un label d'axes commun à la première colonne
    sideinfo = Label(
        fig2[1:2, 0],    # position toutes les lignes, 1e colonne
        "densités de populations";
        rotation = π/2,  # \pi + TAB, pi/2 fonctionne aussi ici
    )
    
    # on affiche la figure
    fig2

    Dynamiques contre le temps

    On rajoute les dynamiques :

    # la courbe de dynamique de x sur ax21
    lines!(
        ax21,
        sol_rma.time,
        sol_rma.x;
        color = Cycled(1),  # pick color 1 in the colorcycle
        linewidth = 2,
        linestyle = :solid,
        label = L"x",
    )
    
    # légende pour ce système d'axe
    axislegend(ax21, position = :lt, labelsize = 14)
    
    # la courbe de dynamique de y sur ax22
    lines!(
        ax22,
        sol_rma.time,
        sol_rma.y;
        color = Cycled(2),
        linewidth = 2,
        linestyle = :solid,
        label = L"y",
    )
    
    # légende pour ce système d'axe
    axislegend(ax22, position = :lt, labelsize = 14)
    
    # on enlève les labels de l'axe des x de ax21 (redondants)
    hidexdecorations!(ax21, ticks = false)
    
    # affiche la figure
    fig2

    Plan de phase

    Passons maintenant au plan de phase dans le dernier système d’axes. Commençons par les isoclines nulles de \dot x et \dot y :

    # calcul des isoclines nulles
    # vecteurs pour le plot
    xplot = LinRange(0.0, K+.1, 30)
    yplot = xplot
    
    # isoclines nulles de xdot
    null_x_x = ones(length(yplot)).*0        # x = 0 isocline nulle de xdot
    null_x_y = [r/c*(h+x)*(1-x/K) for x in xplot]  # y = f(x) isocline nulle de xdot
    
    # isoclines nulles de ydot
    null_y_y = ones(length(xplot)).*0     # y = 0 isocline nulle de ydot
    null_y_x = [m*h/(b-m) for x in yplot] # x = mh/(b-m) isocline nulle de ydot
    
    # tracé des isoclines nulle de x
    lines!(
        ax23,
        null_x_x,
        yplot;
        color = Cycled(2),
        linewidth = 2,
        linestyle = :solid,
    )
    
    lines!(
        ax23,
        xplot,
        null_x_y;
        color = Cycled(2),
        linewidth = 2,
        linestyle = :solid,
        label = L"nullcline de $x$",
    )
    
    # tracé des isoclines nulle de y
    lines!(
        ax23,
        xplot,
        null_y_y;
        color = Cycled(3),
        linewidth = 2,
        linestyle = :solid,
    )
    
    lines!(
        ax23,
        null_y_x,
        yplot;
        color = Cycled(3),
        linewidth = 2,
        linestyle = :solid,
        label = L"nullcline de $y$",
    )

    Puis nous ajoutons les équilibres:

    # tracé des équilibres
    # équilibre d'extinction
    scatter!(            # scatter pour des points
        ax23,
        0,
        0;
        color = Cycled(4),
        label = L"équilibres$$",
    )
    
    # prey only
    scatter!(ax23, K, 0, color = Cycled(4))
    
    # équilibre de coexistence
    eq_coex = [m*h/(b-m), r/c*(h+m*h/(b-m))*(1-m*h/(b-m)/K)]
    
    scatter!(ax23, eq_coex[1], eq_coex[2]; color = Cycled(4))

    Pour tracer le champs de vecteurs, nous créons deux vecteurs de coordonnées x et y, et calculons par compréhension de liste des matrices de taille correspondante indiquant les composantes x et y des vecteurs vitesse. Les vecteurs de coordonnées et les matrices de composante des vitesses sont ensuite passées comme argument à la fonction arrows.

    # champs de vecteur
    scale = 10           # il faut mettre à l'échelle sinon on voit rien
    xrange = range(1, 10, length=11)
    yrange = range(1. ,10, length=11)
    
    # composantes des vecteurs vitesses par compréhension de liste
    derx = [rma([x y], par_rma, 0)[1]/scale for x in xrange, y in yrange]
    dery = [rma([x y], par_rma, 0)[2]/scale for x in xrange, y in yrange]
    
    # champs de vecteurs
    arrows!(
        ax23,
        xrange,          # coordonnée x du début d'une flèche
        yrange,          # coordonnée y du début d'une flèche
        derx,            # x fin de la flèche (relativement au debut)
        dery;            # y fin de la flèche (relativement au debut)
        color = :lightgray,
        arrowsize = 10,
    )
    Caution

    Dans les compréhensions de listes à plusieurs variables/itérateurs, la syntaxe a son importance:

    • [1 for x in xrange, y in yrange] crée un array de taille length(xrange) par length(yrange)

    • [1 for x in xrange for y in yrange] crée un vecteur de taille length(xrange) + length(yrange)

    et enfin la trajectoire :

    # trajectoire dans le plan de phase
    lines!(
        ax23,
        sol_rma.x,
        sol_rma.y;
        color = Cycled(1),
        linewidth = 2,
        linestyle = :solid,
        label = L"trajectoire $$",
    )
    
    # ajuste l'espacement des colonnes et lignes
    colgap!(fig2.layout, 20)
    rowgap!(fig2.layout, 20)
    
    # affiche la figure
    fig2

    Figure 2: Une figure plus complexe avec Makie.jl

    On peut sauvegarder la figure dans différents formats (e.g. png, pdf)

    save("rma_fig.png", fig2)
    save("rma_fig.pdf", fig2)

    Diagramme de bifurcations

    Pour finir, nous allons tracer le diagramme de bifurcation du modèle de Rosenzweig MacArthur: asymptotiques des prédateurs y^* en fonction de K, en identifiant les bifurcations transcritique et de Hopf vues en cours, et en estimant et représentant les extremas du cycle limite apparaissant pour K grand.

    Pour rappel, il y a 3 situations asymptotiques distinctes pour le modèle de Rosenzweig MacArthur :

    • si : 0<K<\displaystyle\frac{mh}{b-m} : les prédateurs s’éteignent et les proies convergent vers K, l’équilibre d’extinction des deux populations est instable.

    • si : \displaystyle\frac{mh}{b-m} <K< h+\frac{2mh}{b-m} : proies et prédateurs co-existent à un équilibre globalement asymptotiquement stable, l’équilibre d’extinction des prédateurs est instable, l’équilibre d’extinction des deux populations est instable.

    • si : h+\displaystyle\frac{2mh}{b-m}<K: proies et prédateurs co-existent le long d’un cycle limite globalement asymptotiquement stable, l’équilibre d’extinction des prédateurs est instable, l’équilibre d’extinction des deux populations est instable.

    Dans un premier temps nous allons calculer et représenter les différents équilibres et leur stabilité dans le plan (K, y), puis nous calculerons et rajouterons une représentation du cycle limite.

    Equilibres

    Nous faisons une boucle sur les valeurs de K et calculons les équilibres.

    K_step = 0.1
    
    # before transcritical
    K_plot1 = 0:K_step:m*h/(b-m)
    y_eq01 = ones(length(K_plot1)).*0
    
    # between transcritical and Hopf
    K_plot2 = m*h/(b-m):K_step:h+2*m*h/(b-m)
    y_eq02 = ones(length(K_plot2)).*0
    y_co2 = [r/c*(h+m*h/(b-m))*(1-m*h/(b-m)/K_p) for K_p in K_plot2]
    
    # above Hopf
    K_plot3 = h+2*m*h/(b-m)-K_step/5:(K_step/10):8
    y_eq03 = ones(length(K_plot3)).*0
    y_co3 = [r/c*(h+m*h/(b-m))*(1-m*h/(b-m)/K_p) for K_p in K_plot3]

    Et nous commençons le tracé de la figure :

    Code
    # création d'une figure
    fig3 = Figure(;
        backgroundcolor = :transparent,
        size = (600,400),
        fontsize = 18,
    )
    
    # on crée un système d'axes en position [1,1] dans la figure
    ax31 = Axis(
        fig3[1,1];
        xlabel = L"capacité de charge $K$",
        ylabel = L"densité de population $y^*$",
        title = "Diagramme de bifurcations pour le\n modèle de Rosenzweig MacArthur",
    )
    
    # on trace la population x su ax31
    # left of transcritical
    lines!(
        ax31,
        K_plot1,
        y_eq01;
        color = Cycled(1),
        linewidth = 2,
        label = L"branche stable$$", # $$ to keep the latex font
    )
    
    # between transcritical and Hopf
    lines!(
        ax31,
        K_plot2,
        y_eq02;
        color = Cycled(2),
        linewidth = 2,
        label = L"branche instable$$",
    )
    
    lines!(
        ax31,
        K_plot2,
        y_co2;
        color = Cycled(1),
        linewidth = 2,
    )
    
    # right of Hopf
    lines!(
        ax31,
        K_plot3,
        y_eq03;
        color = Cycled(2),
        linewidth = 2,
    )
    
    lines!(
        ax31,
        K_plot3,
        y_co3;
        color = Cycled(2),
        linewidth = 2,
    )
    
    fig3

    Cycle limite

    Pour estimer le cycle limite pour chaque la valeur de K nous allons simuler le modèle pendant un transitoire assez long, puis repartir de cette valeur de l’état, simuler un cycle et récupérer les extremas pour les tracer. Nous utilisons une méthode basée sur remake pour modifier le problème d’intégration2.

  • 2 voir aussi l’annexe

  • # "long" transient integration time
    t_trans = (0.0, 8000.0)
    
    # for storage
    y_cmin = zero(K_plot3)
    y_cmax = zero(K_plot3)
    # define generic simulation problem
    rma_pbe = ODEProblem(rma, etat0, t_trans, par_rma)
    
    # estimate limit cycle through loop on K
    @time for (i, Kc) in enumerate(K_plot3)     # loop on (index, K) values of K_plot3
        par_rmac = [r, Kc, c, h, b, m]    # set parameters
    
        # transient initial value problem; remake problem with par = par_rmac
        rma_trans_pbe =  remake(rma_pbe; p = par_rmac)
        # with such arguments `solve` yields only final value of simulation
        post_trans2 = solve(
            rma_trans_pbe;
            save_everystep = false,
            save_start = false,
            abstol=1e-6,
            reltol=1e-6,
        )
    
        # limit cycle initial value problem; simulation
        rma_cycle_pbe =  remake(
            rma_pbe;
            p = par_rmac,
            u0 = post_trans2[:,1], # initial condition from transient simulation
            tspan = tspan,
            saveat = tstep,
        )
        # simulation
        sol_cycle = solve(rma_cycle_pbe; abstol=1e-6, reltol=1e-6)
    
        # get the extrema of y, store at index i
        y_cmin[i] = minimum(sol_cycle[2,:])
        y_cmax[i] = maximum(sol_cycle[2,:])
    end
     10.583470 seconds (325.91 M allocations: 24.391 GiB, 13.43% gc time, 10.65% compilation time)

    Diagramme de bifurcations final

    Finalement, on inclut les branches calculées dans le diagramme de bifurcations.

    Code
    lines!(
        ax31,
        K_plot3,
        y_cmin;
        color = Cycled(3),
        linewidth = 2,
        label = L"cycle limite$$",
    )
    
    lines!(
        ax31,
        K_plot3,
        y_cmax;
        color = Cycled(3),
        linewidth = 2,
    )
    
    axislegend(ax31, position = :lt, labelsize = 14)
    
    fig3

    Figure 3: Diagramme de bifurcations du modèle de Rosenzweig MacArthur.


    That’s all folks!

    References

    Rosenzweig, M. L., and R. H. MacArthur. 1963. “Graphical Representation and Stability Conditions of Predator-Prey Interactions.” American Naturalist 97: 209–23.
    Smith, H. L. 2008. “The Rosenzweig MacArthur Predator Prey Model.”
    Turchin, P. 2003. Complex Population Dynamics. Princeton University Press.

    Reuse