La méthode de Jacobi mène au système itératif :
Cette méthode converge ssi la matrice
On peut agrandir le rayon de convergence en ajoutant de l'inertie:
On parle d'inertie car on avance "moins vite": la nouvelle valeur de
On commence pour un Jacobi qui diverge.
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]
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]]
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]
Ajoutons de l'inertie :
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]
La solution est [1,1,1,1], l'inertie fonctionne.
On trace une courbe de:
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())
Il faut toujours regarder une erreur en échelle logarithmique.
En faisant le calcul sur 200 itérations :
On s'est rapproché de la solution puis on a divergé.
Regardons l'écart entre 2
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())
Il y a une relation entre l'écart de deux valeurs successives et l'erreur absolue.
L'écart entre 2
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())
On ne peut juger une erreur qu'avec une référence. Si on connait la solution exacte :
De même, l'erreur entre 2 itérations successives doit être normalisée :
def mk_A(seed):
np.random.seed(seed)
return np.random.randint(10, size=(4,4))
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())
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
A = mk_A(799)
b = A.sum(axis=1)
M = np.diag(A)
N = np.diag(M) - A
x0 = np.random.random(4)
plot_error(M, N, b, x0, w=0.1, n=1000)
plot_error_normalized(M, N, b, x0, w=0.1, n=1000)
L'erreur relative normalisée se stabilise.