🖥️ Stabilité d’un système non linéaire, bifurcation fourche ★

Notebook Capytale de cet exercice : fe00-11678444

On étudie un système régi par l’équation différentielle

d𝑦d𝑡−𝑦(𝑟−𝑦2)=0

1/ Quelles sont les solutions stationnaires de ce système ? On distinguera les cas en fonction du paramètre 𝑟.

Coup de pouce 1
Que vaut d𝑦d𝑡 si 𝑦(𝑡) est stationnaire (c’est-à-dire indépendant du temps) ? Quelle équation sur 𝑦 obtient-on alors ?
Corrigé

𝑦=0 est toujours une solution stationnaire.

Si 𝑟>0, alors 𝑦=±𝑟 est également une solution stationnaire.

On résout maintenant numériquement le système pour observer le comportement des solutions en fonction du paramètre 𝑟 et de la condition initiale 𝑦0.

2/ Mettre le système sous la forme d’un problème d’Euler, c’est-à-dire sous la forme d𝑦d𝑡=𝑓(𝑡,𝑦) où 𝑓 est une fonction à préciser puis compléter le code ci-dessous.

from scipy.integrate import solve_ivp
def f(t,y):
    return ... # à compléter
Coup de pouce 1
Pour mettre sous la forme d’un problème d’Euler, il suffit d’isoler d𝑦d𝑡 d’un côté de l’équation.
Corrigé
d𝑦d𝑡=𝑦(𝑟−𝑦2)

La fonction 𝑓 est donc définie par 𝑓:(𝑡,𝑦)↦𝑦(𝑟−𝑦2). Ici la fonction ne dépend en pratique pas de 𝑡.

Pour se familiariser avec la résolution numérique, on commence par résoudre le système pour 𝑟=1 et 𝑦0=0.1.

3/ Compléter le code ci-dessous pour.

r = ...  # à compléter
y0 = ... # à compléter
sol = solve_ivp(f, [0,100], [y0]) # la fonction solve_ivp résout le système sur l'intervalle
                                  # de temps [0,100] avec la condition initiale y0
t = sol.t    # on récupère les valeurs de temps calculées par solve_ivp
y = sol.y[0] # et les valeurs de y correspondantes
import matplotlib.pyplot as plt
plt.plot(...) # Tracé de y en fonction de t
plt.xlabel("t")
plt.ylabel("y(t)")
plt.title("Résolution du système pour r=1 et y0=0.1")
plt.show()
Coup de pouce 1
La fonction plt.plot() prend deux arguments : la liste des abscisses puis celle des ordonnées à tracer.
Corrigé
r = 1  # à compléter
y0 = 0.1 # à compléter
sol = solve_ivp(f, [0,100], [y0]) # la fonction solve_ivp résout le système sur l'intervalle
                                  # de temps [0,100] avec la condition initiale y0
t = sol.t    # on récupère les valeurs de temps calculées par solve_ivp
y = sol.y[0] # et les valeurs de y correspondantes
import matplotlib.pyplot as plt
plt.plot(t,y) # Tracé de y en fonction de t
plt.xlabel("t")
plt.ylabel("y(t)")
plt.title("Résolution du système pour r=1 et y0=0.1")
plt.show()

4/ En changeant les paramètres 𝑟 et 𝑦0 (on essaiera des valeurs positives et négatives), qu’observe-t-on sur les limites atteintes par 𝑦 ?

Corrigé
Pour 𝑟≤0
quelle que soit la valeur de 𝑦0, la solution converge vers 0.
Pour 𝑟>0
si 𝑦0 est positif, la solution converge vers 𝑟, si 𝑦0 est négatif, la solution converge vers −𝑟. A part avec 𝑦0=0, aucune solution ne converge vers 0, qui semble donc être une solution instable.

On souhaite maintenant explorer de façon plus exhaustive l’influence de 𝑟 sur la limite atteinte par 𝑦. On prendra 1000 valeurs pour 𝑟 réparties entre −1 et 1. Pour chaque valeur de 𝑟, on résout le système pour une conditions initiales 𝑦0 tirée aléatoirement entre −10 et 10.

5/ Compléter le code ci-dessous pour.

L_r = ...     # liste contenant les valeurs de r à tester
L_limite = [] # liste qui contiendra les limites atteintes par y pour chaque valeur de r

for r in L_r:
    y0 = np.random.uniform(-10,10) # tire une valeur aléatoire entre -10 et 10 pour la condition initiale
    sol = solve_ivp(f, [0, 100], [y0])
    limite = ... # dernière valeur de y calculée par solve_ivp (on suppose que la limite est atteinte)
    L_limite.append(limite)
Coup de pouce 1
Pour générer la liste des valeurs de 𝑟, on peut utiliser la fonction np.linspace ou utiliser une comprehension de liste par exemple.
Coup de pouce 2
S’inspirer du code fourni dans la question précédente récupérer les valeurs de 𝑦. Comment récupérer seulement la dernière valeur ?
Corrigé
L_r = np.linspace(-1,1,1000)# liste contenant les valeurs de r à tester
L_limite = []               # liste qui contiendra les limites atteintes par y pour chaque valeur de r

for r in L_r:
    y0 = np.random.uniform(-10,10) # tire une valeur aléatoire entre -10 et 10 pour la condition initiale
    sol = solve_ivp(f, [0, 100], [y0])
    limite = sol.y[0][-1] # dernière valeur de y calculée par solve_ivp (on suppose que la limite est atteinte)
    L_limite.append(limite)

6/ Tracer les limites atteintes par 𝑦 en fonction de 𝑟. On affichera le trace sous forme d’un nuage de points non reliés entre eux. Justifier le nom de “bifurcation fourche” donné à ce type de diagramme.

Coup de pouce 1
Pour obtenir un nuage de points non reliés entre eux, on peut utiliser le style de tracé '.'.
Corrigé
plt.plot(L_r, L_limite, ".")
plt.xlabel("r")
plt.ylabel("limite atteinte par y")
plt.grid()
plt.show()

La forme de la courbe ressemble à une fourche.