L’objectif est d’utiliser la détection de cycles pour optimiser Lenia.
En exécutant le Jeu de la Vie à la main, on remarque qu’il est souvent possible de réduire les calculs en identifiant des structures stables, comme les oscillateurs. Il est donc raisonnable de se demander si une optimisation similaire peut être appliquée à Lenia.
- Implémentation du Jeu de la Vie
- Version naïve
- Version avec détection de cycle
- Optimisation de Lenia
- Version naïve
- Version avec FFT
- Version parallélisée sur GPU
- Version avec détection de cycle
- Complétude de Lenia (bonus)
- Écrire un algorithme génétique pour trouver un glider gun
- Trouver un glider gun
On appelle simulation ce qui est défini par :
- Un champ scalaire,
$A : L \mapsto [0, 1]$ , où$L$ est un espace euclidien muni de la distance$d$ (cette implémentation utilise un espace euclidien en deux dimensions, mais Lenia peut être implémentée dans n’importe quelle dimension) - Une fonction de croissance,
$G : [0, 1] \mapsto [-1, 1]$ - Un noyau de convolution,
$K : \mathbb{R} \mapsto [0, 1]$ , tel que
$$ \int_{0}^{2\pi}\int_{0}^{+\infty} K(r) ,dr , d\theta = 1 $$
où
- Un pas de temps
$\Delta t$
La formule pour calculer l’état de
où
Dans le cas du Jeu de la Vie de Conway :
-
$ K(r) = \begin{cases} \frac{1}{9}, & \text{si } r = 1 \text{ ou } \sqrt{2} \ 0, & \text{sinon} \end{cases} $
-
$ G(s) = \begin{cases} 1, & \text{si } s = 2 \ 0, & \text{si } s = 3 \ -1, & \text{sinon} \end{cases} $
-
$\Delta t = 1$ -
On prendra un champ scalaire
$A^0$ initialement aléatoire.
On atteint difficilement les 30 générations par seconde.
Après recherche, il apparaît que parmi toutes les structures oscillantes qui émergent naturellement avec une probabilité supérieure à une sur un milliard, la majorité oscille en seulement deux générations.
On choisit alors de les détecter pour ne pas les recalculer.
Cette optimisation permet un gain significatif en termes de performances, augmentant la vitesse de génération de 30 à 300 générations par seconde.
Lenia est une version continue du Jeu de la Vie.
Pour passer à un modèle continu, on utilise des fonctions gaussiennes :
-
$ \text{bell}(x) = \exp\left(-\frac{(x - m)^2}{2s^2}\right) $
-
$ G(v) = 2 \cdot \text{bell}(v, 0.15, 0.015) - 1 $
-
$ K_R(r) = \text{bell}\left(\frac{r}{R}, 0.5, 0.15\right) \cdot \frac{1}{S_R} $
avec $ S_R = \int_0^{2\pi} \int_0^{+\infty} \text{bell}\left(\frac{r}{R}, 0.5, 0.15\right) , dr , d\theta $
$\Delta t \rightarrow 0$
Absurdemment lente, même pour des simulations très petites.
Le gain est considérable : on peut alors augmenter la résolution de la simulation par un facteur 16 tout en conservant la même fréquence de rafraîchissement.
la Transformée de Fourier Discrète (DFT) peut être vue comme une simple évaluation de polynômes.
Si l’on considère les données d’entrée comme les coefficients d’un polynôme :
alors calculer la DFT revient à évaluer ce polynôme en
les racines
Pour un calcul efficace on utilise la méthode diviser pour régner.
En effet, on s’aperçoit que pour obtenir les valeurs en
où :
$P_\text{pair}(X) = a_0 + a_2 X + a_4 X^2 + \cdots$ $P_\text{impair}(X) = a_1 + a_3 X + a_5 X^2 + \cdots$
Ainsi, au lieu d’évaluer
Ces points sont reliés par des symétries : les racines de l’unité viennent par paires conjuguées (
On obtient donc une récurrence :
Écrivons la récurrence sur plusieurs niveaux :
Après
Choisissons
Alors
La somme géométrique vaut :
Donc :
Pour les grandes valeurs de
Ainsi la solution de :
est bien :
On obtient ainsi une complexité optimale en
Pour effectuer les convolutions, on utilise l'algorithme FFT 2D qui est définie comme une FFT selon un axe puis une FFT selon l'autre axe, d’où une complexité en
En effet, soit
On voit donc qu'il s'agit exactement de la convolution des coefficients
Or, par les polynômes d'interpolation de Lagrange, on sait qu'un polynôme de degré
Ainsi, pour multiplier
- On évalue
$A$ en$n$ points$(x_0, x_1, \dots, x_{n-1})$ → valeurs$A(x_i)$ - On évalue
$B$ aux mêmes points → valeurs$B(x_i)$ - On multiplie point par point :
$$ C(x_i) = A(x_i) \cdot B(x_i) $$ - On retrouve ensuite les coefficients de
$C$ via l'interpolation de Lagrange.
💡 L'idée clé : la FFT permet de transformer la convolution des coefficients en une multiplication point par point très rapide.
Si l'on résout directement le système d'interpolation, la complexité est trop élevée (
Pour y remédier, on choisit astucieusement les points d'évaluation : les racines de l'unité, ce qui permet d'utiliser la FFT.
En effet la FFT n’est rien d’autre qu’une méthode efficace pour obtenir les
c’est-à-dire l’évaluation simultanée du polynôme
Pour faire une convolution, on a deux FFT 2D à effectuer et une multiplication point par point en
On obtient donc la convolution en
👉 Contre
Pour qu’une cellule passe à un état non nul (vivant), elle doit avoir au moins une voisine active dans son voisinage. Or, en pratique, de nombreuses cellules sont recalculées inutilement.
Une solution possible consiste à ne calculer que les cellules susceptibles d’être modifiées à l’état suivant.
On atteint ainsi l’état de l’art actuel, mais les performances pour une simulation en
(À compléter)