Physique non linéaire
Biblio
- Nonlinear dynamics and chaos: with applications to physics, biology, chemistry, and engineering, Strogatz
- cours “Transition vers le chaos” de Vincent Croquette https://cel.archives-ouvertes.fr/cel-00092949
- Hetch optique, pour le doublement de fréquence
- Garing, Oem dans les diélectriques, effet Kerr
Des idées
Eviter de parler d’équation logistique, c’est trop de maths pour peu de physique apparemment. On peut parler d’AO, d’oscillateur à relaxation, ou de régime saturé. Sinon en optique il est possible de faire le doublement de fréquence, l’effet Kerr.
Ne pas oublier les portaits de phase si le titre s’y prête, c’est très utile. Pourquoi pas une méthode RK4 avec scipy sur le pendule simple aux grands angles.
Si jamais la leçon est expressement sur les bifurcations, voici un long code sur l’éolution de populations :
#!/usr/bin/env python
# coding: utf-8
# Imports
# In[6]:
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
import numpy as np
# Etude du comportement général
# In[33]:
r=2
x0=0.1
n=50 #nombre d'itérations
liste_p=[p for p in range(n+1)]
liste_xp=[x0]
# Calcul des termes de la suite logistique : $x_{p+1}=r(1-x_p)x_p$
# In[22]:
for p in liste_p[1:]:
liste_xp.append(r*liste_xp[-1]*(1-liste_xp[-1]))
# Affichage des résultats
# In[28]:
plt.plot(liste_p,liste_xp)
plt.xlabel('Génération p')
plt.ylabel('Population xp')
plt.title('Comportement de la suite logistique avec r = '+str(r))
plt.grid()
# Définition d'une fonction calculant les termes de la suite
# In[71]:
def evolution(r,p_max,x0):
"""
fonction calculant les termes de la suite logistique
paramètres : coefficient r, indice du dernier terme à calculer, valeur initiale
retour : liste des indices, liste des termes de la suite correspondants
"""
liste_p=[p for p in range(p_max+1)]
liste_xp=[x0]
for p in liste_p[1:]:
liste_xp.append(r*liste_xp[-1]*(1-liste_xp[-1]))
return liste_p,liste_xp
# Affichage des courbes obtenues avec différentes valeurs de $r$
# In[101]:
p_max=200 #nombre d'itérations
x0=0.1
len_liste_r=4
liste_r=np.linspace(1,4,len_liste_r)
nb_lignes_fig=len_liste_r//2+1 #nombre de lignes de figures (pour subplot)
# In[95]:
plt.figure(figsize=(20,20))
for i in range(len_liste_r):
liste_p,liste_xp=evolution(liste_r[i],p_max,x0)
plt.subplot(nb_lignes_fig,2,i+1)
plt.plot(liste_p,liste_xp,label='r = '+str(liste_r[i]))
plt.xlabel('Génération p')
plt.ylabel('Population xp')
plt.grid()
plt.legend()
# On observe bien une convergence pour des valeurs de $r$ inférieures à 3 et un comportement chaotique pour les grandes valeurs de $r$.
# Affichage "en escalier" de l'évolution de la suite pour différentes valeurs de $r$
# In[91]:
def visualisation(r,p_max,x0):
"""
fonction affichant sur le même graphique la fonction f:x->r(1-x)x et les termes de la suite logistique
paramètres : coefficient r, indice du dernier terme à calculer, valeur initiale
"""
nb_pts_fonct=100 #nombre de points utilisés pour tracer la fonction f
f=lambda r,x: r*x*(1-x)
liste_x=np.linspace(0,1,nb_pts_fonct)
liste_fx=f(r,liste_x)
liste_p,liste_xp=evolution(r,p_max,x0)
liste_xp_1=[]
liste_xp_2=[0]
for i in range(p_max):
liste_xp_1+=2*[liste_xp[i]]
liste_xp_2+=2*[liste_xp[i+1]]
liste_xp_2.pop()
plt.plot([0,1],[0,1],'g')
plt.plot(liste_x,liste_fx,'b')
plt.plot(liste_xp_1,liste_xp_2,'r')
plt.grid()
plt.title('r = '+str(r))
# In[102]:
plt.figure(figsize=(20,20))
for i in range(len_liste_r):
plt.subplot(nb_lignes_fig,2,i+1)
visualisation(liste_r[i],p_max,x0)
# Affichage d'un diagramme de bifurcation
# In[124]:
nb_val_r=200
liste_r=np.linspace(2,4,nb_val_r)
x0=0.1
p_max=2000
plt.figure(figsize=(20,20))
for r in liste_r:
liste_p,liste_xp=evolution(r,p_max,x0)
if r<3:
plt.plot(100*[r],liste_xp[-100:],'b+',markersize=2)
else:
plt.plot(1000*[r],liste_xp[-1000:],'b+',markersize=2)
plt.grid()
plt.xlabel('Coefficient r')
plt.ylabel('Valeurs limites des populations')
plt.title('Diagramme de bifurcation')
# Le premier "doublement de période" correspond à la valeur de r pour laquelle la suite extraite d'indices pairs converge vers une valeur différente de la suite extraite d'indices impairs. Les doublements de périodes suivant correspondent au doublement du nombre de suites extraites convergeant vers des valeurs différentes (de la forme $x_{kp}$,$x_{kp+1}$,...,$x_{kp+k-1}$ avec k pair).
# Le "chaos déterministe" correspond à un comportement chaotique mais dont le comportement peut être prévu parfaitement (ici par le calcul des termes de la suite logistique.
# Le comportement chaotique apparaît ici pour r valant environ 3,57.
# In[123]:
nb_val_r=200
liste_r=np.linspace(3.738,3.745,nb_val_r)
x0=0.1
p_max=2000
plt.figure(figsize=(20,20))
for r in liste_r:
liste_p,liste_xp=evolution(r,p_max,x0)
if r<3:
plt.plot(100*[r],liste_xp[-100:],'b+',markersize=2)
else:
plt.plot(1000*[r],liste_xp[-1000:],'b+',markersize=2)
plt.grid()
plt.xlabel('Coefficient r')
plt.ylabel('Valeurs limites des populations')
plt.title('Diagramme de bifurcation')
# Pour des valeurs de r comprises entre 3.74 et 3.743 environ, on remarque que le système a à nouveau un comportement non chaotique, avec des limites de suites extraites qui se distinguent nettement.
# Sensibilité aux conditions initiales
# In[172]:
liste_x0=[0.01,0.00999999]
r=3.9 #une valeur de r pour laquelle on a un chaos déterministe
p_max=200
# In[159]:
for x0 in liste_x0:
liste_p,liste_xp=evolution(r,p_max,x0)
plt.plot(liste_p,liste_xp,label='x0 = '+str(x0))
plt.legend()
plt.grid()
plt.xlabel('Génération p')
plt.ylabel('Population xp')
# À partir de la 30ème génération environ, on observe une différence significative entre le comportement des systèmes ayant des conditions initiales légèrement différentes.
# In[161]:
liste_p,liste_xp_1=evolution(r,p_max,liste_x0[0])
liste_p,liste_xp_2=evolution(r,p_max,liste_x0[1])
liste_diff=np.array(liste_xp_1)-np.array(liste_xp_2)
liste_diff=np.abs(liste_diff)
A=10e-9
l=0.6
exp=lambda p:A*np.exp(l*p)
liste_exp=exp(np.array(liste_p))
plt.semilogy(liste_p,liste_exp)
plt.semilogy(liste_p,liste_diff)
plt.grid()
plt.ylim(10e-9,1)
plt.xlabel('Génération p')
plt.ylabel('Ecart entre les deux évolutions de populations')
# On observe que l'écart croit exponentiellement lors des premières générations, il se stabilise ensuite à des valeurs généralement comprises entre 0,01 et 1.
# On peut approximer l'écart entre les deux générations lors des premières génération par une fonction du type $Ae^{\lambda p}$ avec un coefficient de Lyapounov $\lambda$ valant environ 0,6.
# In[173]:
r=3.2
liste_p,liste_xp_1=evolution(r,p_max,liste_x0[0])
liste_p,liste_xp_2=evolution(r,p_max,liste_x0[1])
liste_diff=np.array(liste_xp_1)-np.array(liste_xp_2)
liste_diff=np.abs(liste_diff)
plt.semilogy(liste_p,liste_diff)
plt.grid()
plt.xlabel('Génération p')
plt.ylabel('Ecart entre les deux évolutions de populations')
# Pour $r=3.2$, on constate que l'écart, après avoir augmenté légèrement, diminue jusqu'à être plus petit que la précision machine. Cette valeur correspond donc à un comportement stable où les limites des suites extraites sont identiques, quel que soit la valeur initiale.
# In[174]:
r=3.83
liste_p,liste_xp_1=evolution(r,p_max,liste_x0[0])
liste_p,liste_xp_2=evolution(r,p_max,liste_x0[1])
liste_diff=np.array(liste_xp_1)-np.array(liste_xp_2)
liste_diff=np.abs(liste_diff)
plt.semilogy(liste_p,liste_diff)
plt.grid()
plt.xlabel('Génération p')
plt.ylabel('Ecart entre les deux évolutions de populations')
# Pour $r=3.83$, on observe le même comportement. Cela signifie que, pour cette valeur de $r$, il existe également des limites fixées ne dépendant pas des valeurs initiales. Pourtant, des valeurs de $r$ proches présentent un comportement chaotique. Il s'agit don d'un "ilôt de stabilité".
# Dimensionnalité de l'espace des populations
# In[183]:
p_max=1000
x0=0.1
liste_r=np.linspace(2,4,10)
plt.figure(figsize=(12,12))
for r in liste_r:
liste_p,liste_xp=evolution(r,p_max,x0)
liste_n_box=[]
liste_m=[]
liste_q=[]
for q in range(1,16):
m=2**q
liste_xp_m=np.ceil(m*np.array(liste_xp))
n_box=len(set(liste_xp_m))
liste_m.append(m)
liste_n_box.append(n_box)
liste_q.append(q)
liste_d=np.log(np.array(liste_n_box))/np.log(np.array(liste_m))
plt.plot(liste_q,liste_d,label='r = '+str(r))
plt.legend()
plt.grid()
plt.xlabel('q')
plt.ylabel('Dimensionnalité d')
# On constate que la dimensionnalité est plus faible pour les valeursde $r$ pour lesquelles il y a des limites bien définies (dans les "ilôts de stabilité" par exemple), ce qui est cohérent avec le fait que la suite converge assez rapidement vers ces valeurs limites.