Home CAMA : ma13 - Système matriciel -- Exercices
Post
Cancel

CAMA : ma13 - Système matriciel -- Exercices

Lien de la note Hackmd

Cours du 04/05

Programmation vectorielle

Méthode du pivot de Gauss partiel

L’ennoncé est dans le cours

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def solve_gauss_partial(A, b):   # on prend le max dans la colonne i parmi les lignes en dessous (plus facile)
    for i in range(len(A)-1):
        pivot = np.argmax(np.abs(A[i:, i]))  # il n'y a que 3 lignes à ajouter pour échanger les lignes
        A[[i, pivot]] = A[[pivot, i]]
        b[[i, pivot]] = b[[pivot, i]]
        E = np.diag(np.array([1.,] * len(A), dtype=A.dtype))
        coefs = - A[i+1:,i] / A[i,i]
        E[i+1:,i] = coefs
        A[i:, i:] = E[i:,i:] @ A[i:,i:]
        b[i+1:] += coefs * b[i]   # multiplication terme à terme
    # A est maintenant triangulaire surpérieur
    res = np.zeros(len(b), dtype=b.dtype)
    res[-1] = b[-1] / A[-1,-1]
    for i in range(len(A)-1)[::-1]:
        res[i] = (b[i] - A[i,i+1:] @ res[i+1:]) / A[i,i]
    return res
1
2
3
4
5
6
7
e = 1E-6
A = np.array([[e, 1], [1, 2]], dtype='float32')
b = np.array([1., 3.], dtype='float32')
print(f"A\n {A} \nb\n {b}\n")
x = solve_gauss_partial(A, b)
print('solution : ',x)
print('vérification\n', A@x)
1
2
3
4
5
6
7
8
9
A
 [[0.000001 1.      ]
 [1.       2.      ]] 
b
 [1. 3.]

solution :  [1.0000019  0.99999905]
vérification
 [3.       0.999997]

Factorisation de Choleski

Il s’agit de décomposer A en $A = B\, B^T$ avec B une matrice triangulaire inférieure. Cela n’est possible que si la matrice A est symétrique et définie positive (c’est d’ailleurs un facon de vérifier qu’une matrice est définie positive).

Écrire l’algorithme de Choleski qui prend A et retourne B (pour deviner l’algorithme, essayez de trouver les coefficients de B à partir des coefficients de A sur une matrice A 4x4).

Solution

\(A = B\, B^T = \begin{bmatrix} b_{11} & 0 & \dots & 0\\ b_{21} & b_{22} & \dots & 0\\ & \vdots&\\ b_{n1} & b_{n2} & \dots& b_{n,n} \end{bmatrix} \begin{bmatrix} b_{11} & b_{21} & \dots & b_{n1}\\ 0 & b_{22} & \dots & b_{n2}\\ & \vdots&\\ b_{n1} & b_{n2} & \dots& b_{n,n} \end{bmatrix}= \begin{bmatrix} b_{11}^2 & b_{11}b_{21} & \dots & b_{11}b_{n1}\\ x & \sum_{i=1}^2b_{2i}^2 & \dots & \sum_{i=1}^2b_{2i}b_{ni}\\ & & \vdots&\\ x & x & \dots& \sum_{i=1}^2b_{n,i}^2 \end{bmatrix}\) avec $x$ la même valeur que de l’autre coté de la diagonale

On voit que $b_{11} = \sqrt{a_{11}}$ et maintenant qu’on a $b_{11}$ on peut trouver toute la première ligne de $B^T$ : $b_{j1}=a_{1j}/b_{11}$.

Une fois qu’on connait la première ligne de $B^T$ , on s’attaque à la deuxième en commencant par trouver $b_{22}$ puis ensuite tous les autres éléments de la ligne comme on a fait pour la première ligne.

On a donc dans le cas général :

  • $b_{ii} = \sqrt{a_{ii} - \sum_{k=1}^{i-1}b_{ik}^2}$
  • $b_{ji} = a_{ij} - \sum_{k=1}^{i-1}b_{ik}b_{jk}/b_{ii} = a_{ij} - \sum_{k=1}^{i-1}b_{ik}b_{kj}^T/b_{ii} \space\forall j\gt i$
1
2
3
4
5
6
def Choleski(A):
    B = np.zeros(A.shape)
    for i in range(len(A)):
        B[i,i] = np.sqrt(A[i,i] - np.sum(np.square(B[i, :i])))         # garanti ok car A est def positive
        B[i+1:, i] = (A[i, i+1:] - B[i, :i] @ B.T[:i, i+1:]) / B[i,i]  # les ∑ sous forme de prod. scalaire
    return B

Matrice symetrique

Rappel : pas de boucles for imbriquées mais des opérations vectorielles et matricielles (sur des sous-matrices).

Créer une matrice symétrique définie positive est vérifier que votre programme marche bien.

Solution
1
2
3
4
5
6
7
A = np.random.randint(10, size=(4,4))
A = A + A.T                             # symmétrique
A = A + np.diag(A.sum(axis=0))          # diagonale dominante
print('A:\n', A)
B = Choleski(A)
print('B\n', B)
print('vérification\n', B @ B.T)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
A:
 [[55  8 18  5]
 [ 8 33  7 10]
 [18  7 54  9]
 [ 5 10  9 28]]
B
 [[7.4161984871 0.           0.           0.          ]
 [1.0787197799 5.6423721639 0.           0.          ]
 [2.4271195049 0.7765914857 6.8924593995 0.          ]
 [0.6741998625 1.6434093681 0.8831939788 4.9055711788]]
vérification
 [[55.  8. 18.  5.]
 [ 8. 33.  7. 10.]
 [18.  7. 54.  9.]
 [ 5. 10.  9. 28.]]

Améliorer Jacobi

Lorsqu’on écrit une itération de la méthode de Jacobi avec l’ensemble des coefficients, on constate que si on calcule la nouvelle valeur de x élément par élement alors lorsqu’on veut mettre à jour x[1], on connait déjà x[0]. Idem lorsqu’on met à jour x[2] on connait déjà x[0] et x[1], etc.

L’idée de la version amméliorée de Jacobi est d’utiliser la nouvelle valeur de x[0] et non pas l’ancienne comme c’est le cas dans l’algorithme du cours. Ainsi en utilisant des valeurs mise à jour on peut espérer converger plus vite.

Dans ce chaque itération demande un calcul ligne par ligne et donc une boucle for dans la boucle for sur les itérations.

Test d’arrêt

On ajoutera un argument error à la fonction qui indique la précision désirée du résultat. Par défaut sa valeur est de 1E-6 et pour offrir une bonne garantie on arrête l’algorithme lorsque $||x_{t+1} - x_t|| < \texttt{error}\, / \, 10$.

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def Jacobi(A, b, error=1E-6, verbose=False):
    L = np.tril(A) 
    U = -np.triu(A, k=1) 
    if verbose:
        print(f"L:\n {L}\nU\n {U}\n")
    previous_x = np.zeros(len(b))
    x = np.random.random(len(b))
    err = (error / 10) ** 2
    while np.sum(np.square(x - previous_x))  > err:
        previous_x = x.copy()
        if verbose:
            print(f"x = {x}")
        # on résoud  L x = U x + b  avec L matrice triangulaire inférieure
        y = U @ x + b
        x[0] = y[0] / L[0,0]
        for i in range(1,len(L)):
            x[i] = (y[i] - L[i,:i] @ x[:i]) / L[i,i]
    return x
1
2
3
4
5
6
A = np.random.randint(10, size=(4,4))
A = A + np.diag(A.sum(axis=0))
b = A.sum(axis=1)                     # ainsi la solution est [1,1,1,1]
print('A:\n', A, "\nb:\n", b, "\n")

Jacobi(A,b, verbose=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
A:
 [[24  2  1  7]
 [ 5 19  4  6]
 [ 9  2 20  9]
 [ 2  9  9 32]] 
b:
 [34 34 40 52] 

L:
 [[24  0  0  0]
 [ 5 19  0  0]
 [ 9  2 20  0]
 [ 2  9  9 32]]
U
 [[ 0 -2 -1 -7]
 [ 0  0 -4 -6]
 [ 0  0  0 -9]
 [ 0  0  0  0]]

x = [0.8870874823 0.8448958895 0.2146829205 0.8281640711]
x = [1.0957657001 1.194392389  1.0147923641 0.9351814319]
x = [1.0020897014 1.0168049182 1.0265474982 0.9876765266]
x = [1.0010877908 0.9980164155 1.0052544156 0.9990120918]
x = [1.0002345046 0.9991440665 1.000424625  1.000106649 ]
x = [1.0000225291 0.9998709979 0.9999547701 1.0000475947]
x = [0.999998753  0.9999948204 0.9999796615 1.0000072549]
x = [0.9999991631 1.000002211  0.9999968908 1.0000003049]
x = [0.9999998564 1.0000005961 0.9999998678 0.9999998785]
x = [0.9999999913 1.0000000685 1.0000000518 0.9999999667]
1
array([1.0000000018, 0.9999999991, 1.0000000142, 0.9999999961])
This post is licensed under CC BY 4.0 by the author.