Travail pratique de programmation système avancée

Les ensembles de Julia

Objectif

Ensembles de Julia

Les ensembles de Julia (du mathématicien français Gaston Julia, 1893-1978) sont des objets fractals du plan complexe (plus sur les complexes à la fin du document). Dans ce contexte, le mot fractal signifie que ces objets ont une dimension fractionnaire et présentent des invariances d’échelle. Pour définir les ensembles de Julia, on considère la famille de fonctions complexes Pc:,zz2+c,\begin{align} P_c:&\mathbb{C}\rightarrow\mathbb{C},\\ &z\rightarrow z^2+c, \end{align}cc\in\mathbb{C}.

On dénote KcK_c, l’ensemble des points zz\in\mathbb{C}, tels que la suites des itérés Pcn(z)P^{n}_c(z) est bornée: Kc={z|sup|Pcn(z)|<},(2.1) K_c=\{z\in\mathbb{C}\ |\ \sup\left|P_c^n(z)\right| < \infty \}, \qquad{(2.1)}Pcn(z)P^n_c(z) est défini par Pcn(z)=Pc(Pc(...Pc(z))).(2.2) P^n_c(z)=P_c(P_c(...P_c(z))). \qquad{(2.2)} L’ensemble de Julia JcJ_c est le bord de KcK_c. En d’autres termes, s’il existe MM\in\mathbb{R} tel que |Pcn(z)|M\left|P^n_c(z)\right|\leq M. Dans ce TP, nous considérons M=2M=2.

Les ensembles de Julia ont quelques propriétés importantes

  1. JcJ_c est inclu dans le cercle de centre 00 et de rayon |c|+1|c|+1.
  2. JcJ_c est symétrique par rapport à 0.
  3. JcJ_{\bar c} est le symétrique de JcJ_c par rapport à l’axe des réels. En particulier, si cc est réel, JcJ_c est symétrique par rapport à l’axe des réels.

Comme JcJ_c est inclus dans le disque de rayon |c|+1|c|+1 centré en 0, on peut affirmer que zJcz\notin J_c si et seulement si la suite des itérés Pcn(z)P^n_c(z) sort de ce disque à une itération donnée.

Calcul d’un ensemble de Julia

Pour calculer l’ensemble de Julia, JcJ_c, associé à une valeur, cc\in\mathbb{C}, on peut se restreindre au domaine carré [2,2]×[2i,2i][-2,2]\times[-2i,2i]\in\mathbb{C}. Ce domaine est discrétisé en une grille dont la finesse du maillage déterminera la précision du calcul. La grille peut être stockée sous forme d’un tableau d’entiers. L’élément du tableau correspondant à un zz donné, contient l’itération, kk, à partir de laquelle Pck(z)P^k_c(z) sort du disque de rayon 2. C’est cette valeur, kk, que nous souhaitons calculer et afficher dans ce travail.

Exemple d’un ensemble de Julia pour c=-0.8+0.156i. Source wikipedia
Figure 3.1: Exemple d’un ensemble de Julia pour c=0.8+0.156ic=-0.8+0.156i. Source wikipedia

Travail à réaliser

Le calcul de l’ensemble KcK_c associé à cc\in\mathbb{C}, se fait sur le carré du plan complexe, dont le coin inférieur gauche est 22i-2-2i et supérieur droit +2+2i+2+2i, carré qu’on discrétise en une grille. Pour chacune des valeurs complexes de la grille, appelons les z0z_0, on détermine si la suite des itérés zn+1=Pc(zn)=zn2+c,(4.1) z_{n+1}=P_c(z_n)=z_n^2 + c, \qquad{(4.1)} est bornée ou non. Une bonne heuristique est obtenue en effectuant un certain nombre d’itérations, nb_iter, pour voir si la suite sort du disque de rayon 2. Ceci correspond à la condition |zn|2.(4.2) |z_n|\leq 2. \qquad{(4.2)} Évidemment, nb_iter représente une borne maximale, car on peut interrompre l’itération dès qu’on a quitté le disque de rayon 2. En d’autre termes, on enregistre le nn, tel que zn>2z_n>2. Si n>nb_itern>\mathrm{nb\_iter} on interrompt la boucle et on enregistre ou n=nb_itern=\mbox{nb\_iter}.

Pour visualiser les résultats vous pouvez utiliser la crate image par exemple. Vous pouvez aller sur la page pour trouver des exemples de valeurs de cc et les résultats attendus.

Parallélisme

Une fois le calcul de l’ensemble de Julia étant réalisé de façon séquentuielle, il vous reste la partie importante de ce travail pratique: paralléliser le code en utilisant des threads système. Vous pouvez essayer différentes stratégies de parallélisation. Par exemple, vous pouvez spawn un thread par pixel ou un thread par ligne ou colonne. Une autre façon de paralléliser votre travail est via la crate rayon que vous pouvez également tester. N’oubliez pas d’utiliser les Arc et les Mutex pour les accès concurrents à la matrice de pixels que vous allez créer.

Pour chaque exécution mesurez le temps de calcul de l’ensemble de Julia.

Pour avoir un gain visible pensez à faire des images assez grandes (1000 x 1000 p.ex.).

Nombres complexes

Comme vous l’aurez compris pour réaliser ce travail il faut réaliser une librairie de manipulation de nombres complexes. Chaque nombre complexe est constitué une paire de nombre réels: la partie réelle et la partie imaginaire du nombre. Le type complexe sera donc une structure

struct Complex {
    re: f64,
    im: f64,
}

Ensuite il faut implémenter les règles d’addition, de multiplication entre deux nombres complexes, ainsi que le module d’un nombre complexe.

Soit zz\in\mathbb{C} un nombre complexe. On note ce nombre z=a+ib,(4.3) z=a+i\cdot b, \qquad{(4.3)}aa est la partie réelle de zz, bb la partie imaginaire de zz, et ii le nombre imaginaire qui a la super propriété i2=1i^2=-1. Avec cette notation, on peut calculer presque comme avec des nombres réels. Ainsi l’addition de deux nombres complexes, z1=a1+ib1z_1=a_1+i\cdot b_1, z2=a2+ib2z_2=a_2+i\cdot b_2 s’écrit z1+z2=(a1+a2)+i(b1+b2).(4.4) z_1+z_2=(a_1+a_2)+i\cdot(b_1+b_2). \qquad{(4.4)} Le produit est un peu plus complexe mais s’écrit z1z2=(a1a2b1b2)+i(a1b2+a2b1).(4.5) z_1\cdot z_2=(a_1\cdot a_2 - b_1\cdot b_2)+i\cdot(a_1b_2+a_2b_1). \qquad{(4.5)} Finalement le module de z=a+ibz=a+i\cdot b s’écrit |z|=a2+b2.(4.6) |z|=\sqrt{a^2+b^2}. \qquad{(4.6)} Comme vous pouvez le voir dans la définition de la structure des nombres complexes, à aucun moment nous n’avons besoin de représenter ii dans notre structure de données. ii est nécessaire pour les humains pour qu’ils puissent calculer plus facilement avec les nombres complexes.

Avec ce qui précède vous pouvez implémenter les trait Add et Mul pour le type Complex, ainsi que la fonction asssociée module() et avez en votre possession tout ce qu’il faut pour écrire le code qui vous permettra de faire des figures plus incroyables les unes que les autres.