# CAMA : ma31 x.T A x sur un maillage en Numpy # Cours du 17/05 ## Calculons $x^TAx$ avec Numpy Nous voulons tracer la courbe, avec $x\in\mathbb{R}^2$ : $$ J_A(x) = x^TAx $$ On prendra $x\in\mathbb{R}^n$ plus tard. :::info Pour une valeur de $x$ le calcul est `x.T @ A @ x`, mais on veut faire ce calcul pour un ensemble de $x$ ::: :::danger On construit un **maillage**: un ensemble de point $x$ pour lesquels on calculera $J_A(x)$ ::: On utilise `np.meshgrid` ```python= x = np.linspace(-1,1,3) # retourne des nombres espaces egalement dans un intervalle specifie y = np.linspace(-1,2,4) mesh = np.meshgrid(x,y) # donne les x puis les y du maillage M = np.array(mesh) M = M.transpose([1,2,0]) ``` ``` shape of M: (4, 3, 2) M = array([[[-1., -1.], [ 0., -1.], [ 1., -1.]], [[-1., 0.], [ 0., 0.], [ 1., 0.]], [[-1., 1.], [ 0., 1.], [ 1., 1.]], [[-1., 2.], [ 0., 2.], [ 1., 2.]]]) ``` Pour calculer $x^TAx$, on commence par calculer $x^TA$ pour tous points du maillage. On utilise la matrice identité pour vérifier nos calculs. ### Cas avec $A = 2*Id$ ```python= A = 2 * np.diag([1, 1]) # Construit un array diagonal MA = np.einsum("ijk, ka -> ija", M, A) # Notation d'Einstein ``` ``` MA = array([[[-2., -2.], [ 0., -2.], [ 2., -2.]], [[-2., 0.], [ 0., 0.], [ 2., 0.]], [[-2., 2.], [ 0., 2.], [ 2., 2.]], [[-2., 4.], [ 0., 4.], [ 2., 4.]]]) ``` :::success On retrouve `2*M`. ::: On peut vérifier sur un certain point: ```python= M[0,1] @ A ``` ``` array([ 0., -2.]) ``` :::info Notez qu'on a écrit $xA$ et non $x^TA$. Lorsqu'on a un vecteur, Numpy privilégie le produit matrice vecteur qui donne un vecteur. Ainsi: `m[0,1] @ A == m[0,1].T @ A` ::: Si on veut une différence entre un vecteur vertical et horizontal, il faut utiliser des `arrays` 2D de taille `1*n` ou `n*1` ```python= np.einsum("ijk, ijk -> ij", MA, M) # comme k n'est pas dans le résultat, c'est sur lui qu'on fait la somme ``` ``` array([[ 4., 2., 4.], [ 2., 0., 2.], [ 4., 2., 4.], [10., 8., 10.]]) ``` :::success Comme A est la matrice identité x2, on retrouve pour tout point sa norme au carré ::: ## Optimisons :`np.tensordot` :::danger Un tensor est une matrice en N dimensions. ::: Comparons les temps de calcul. ```python= %timeit np.einsum("ijk, ijk -> ij", np.einsum("ijk, ka -> ija", M, A), M) ``` ``` 135 µs ± 2.56 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) ``` ```python= %%timeit # pour calculer le temps d'execution de toute la cellule %% MA = np.tensordot(M, A, axes=(2,1)) # on somme sur l'axe 2 de M (les points) et l'axe 1 de A (les colonnes) np.einsum("ijk, ijk -> ij", MA, M) ``` ``` 102 µs ± 1.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) ``` :::info On peut avoir un gain de temps 30% et plus ou un peu moins bien que einsum :::