# CAMA : ma20 Convergence de Jacobi avec inertie # Cours du 11 / 05 ## Ajouter de l'inertie à Jacobi :::info La méthode de Jacobi mène au système itératif : $$ {\bf x}^{k+1} = M^{-1} \, ( N\; {\bf x}^k + {\bf b}) $$ ::: Cette méthode converge ssi la matrice $b$ a un rayon spectral inférieur à 1 (cf ma12). :::danger On peut agrandir le rayon de convergence en ajoutant de **l'inertie**: $$ {\bf x}^{k+1} = w \, M^{-1} \, (N\; {\bf x}^k + {\bf b}) + (1-w) \, {\bf x}^k $$ * $0 < w \le 1$. ::: * si $w = 1$ : Jacobi classique * si $w = 0$ : on néglige les termes en dehors de la diagonale et $b$ donc ça ne marche pas :::warning On parle d'inertie car on avance "moins vite": la nouvelle valeur de ${\bf x}^{k+1}$ est comprise entre l'ancienne valeur de ${\bf x}^{k+1}$ et ${\bf x}^k$. C'est la **surrelaxation** ::: ### Programmons l'inertie pour Jacobi On commence pour un Jacobi qui diverge. ```python= np.random.seed(799) A = np.random.randint(10, size=(4,4)) b = A.sum(axis=1) # la solution est [1,1,1,1] ``` ``` A: [[5 7 6 0] [1 7 2 5] [5 6 5 1] [0 6 3 7]] b: [18 15 17 16] ``` ```python= M = np.diag(A) # vecteur N = np.diag(M) - A # np.diag d'une matrice donne un vecteur, np.diag d'un vecteur donne une matrice ``` ``` M: [[5 0 0 0] [0 7 0 0] [0 0 5 0] [0 0 0 7]] N: [[ 0 -7 -6 0] [-1 0 -2 -5] [-5 -6 0 -1] [ 0 -6 -3 0]] ``` ```python= x0 = np.random.random(4) x = x0 for i in range(20): x = (N @ x + b) / M ``` ``` ... x_16 = [-4448.651 -1888.411 -4149.91 -1981.882] x_17 = [7627.267 3238.983 7114.521 3399.456] x_18 = [-13068.402 -5548.37 -12190.539 -5823.066] x_19 = [22399.965 9511.401 20894.459 9982.548] ``` :::warning Ajoutons de l'inertie : ::: ```python= x = x0 # on reprend la même valeur initiale pour la comparaison w = 0.5 # on choisit w for i in range(20): x = w * (N @ x + b) / M + (1-w) * x ``` ``` ... x_17 = [1.059 0.977 0.972 1.03 ] x_18 = [1.063 0.977 0.968 1.031] x_19 = [1.067 0.978 0.963 1.032] ``` :::success La solution est [1,1,1,1], l'inertie fonctionne. ::: ### Étudions la convergence On trace une courbe de: * l'erreur absolue (lorsqu'on connait la solution) * de l'erreur relative (entre 2 ${\bf x}^i$ successifs) * du résidu ($||A \, {\bf x}^i - {\bf b}||$). ```python= x = x0 # on reprend la même valeur initiale pour la comparaison w = 0.5 # on choisit w error = [np.square(x - np.ones(4)).sum()] for i in range(20): x = w * (N @ x + b) / M + (1-w) * x error.append(np.square(x - np.ones(4)).sum()) ``` ![](https://i.imgur.com/tbOjvyX.png) A l'échelle logarithmique: ![](https://i.imgur.com/WM0W8Xv.png) :::warning Il faut toujours regarder une erreur en échelle logarithmique. ::: En faisant le calcul sur 200 itérations : ![](https://i.imgur.com/3dUrGA5.png) :::success On s'est rapproché de la solution puis on a divergé. ::: #### Erreur relative Regardons l'écart entre 2 ${\bf x}^k$ successifs. ```python= x = x0 # on reprend la même valeur initiale pour la comparaison w = 0.5 # on choisit w error2 = [] for i in range(200): old_x = x x = w * (N @ x + b) / M + (1-w) * x error2.append(np.square(x - old_x).sum()) ``` ![](https://i.imgur.com/q2oLsLi.png) :::warning Il y a une relation entre l'écart de deux valeurs successives et l'erreur absolue. ::: :::success L'écart entre 2 ${\bf x}$ successifs est une facon de savoir quand arrêter un algorithme itératif. ::: #### Résidu ```python= x = x0 # on reprend la même valeur initiale pour la comparaison w = 0.5 # on choisit w residu = [] for i in range(200): old_x = x x = w * (N @ x + b) / M + (1-w) * x residu.append(np.square(A @ x - b).sum()) ``` ![](https://i.imgur.com/0mk3Iru.png) ## Normaliser * Si la solution est un milliard, avoir une erreur de 0.1 est très bien. * Si la solution est 0.01, avoir une erreur de 0.1 est énorme. :::info On ne peut juger une erreur qu'avec une référence. Si on connait la solution exacte : $$ \frac{||{\bf x}^k - {\bf x}||}{||{\bf x}||} $$ ::: De même, l'erreur entre 2 itérations successives doit être normalisée : $$ \frac{||{\bf x}^{k+1} - {\bf x}^k||}{||{\bf x}^k||} $$ ```python= def mk_A(seed): np.random.seed(seed) return np.random.randint(10, size=(4,4)) ``` ```python= def plot_error(M, N, b, x0, w, n=200): x = x0 error = [np.square(x - np.ones(4)).sum()] error2 = [] for i in range(n): old_x = x x = w * (N @ x + b) / M + (1-w) * x error.append(np.square(x - np.ones(4)).sum()) error2.append(np.square(x - old_x).sum()) ``` ```python= def plot_error_normalized(M, N, b, x0, w, n=200): x = x0 error = [np.square(x - np.ones(4)).sum()] error2 = [] for i in range(n): old_x = x x = w * (N @ x + b) / M + (1-w) * x error.append((np.square(x - np.ones(4)).sum())/4) # normalisé par rapport à la solution error2.append((np.square(x - old_x).sum())/np.square(x).sum()) # normalisé par rapport à x ``` ```python= A = mk_A(799) b = A.sum(axis=1) M = np.diag(A) N = np.diag(M) - A x0 = np.random.random(4) ``` ```python= plot_error(M, N, b, x0, w=0.1, n=1000) ``` ![](https://i.imgur.com/TmAEH6F.png) ```python= plot_error_normalized(M, N, b, x0, w=0.1, n=1000) ``` ![](https://i.imgur.com/2MTkNOH.png) :::success L'erreur relative normalisée se stabilise. :::