diff --git a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.4 Espaces propres.ipynb b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.4 Espaces propres.ipynb index fb107ce..cf1a042 100644 --- a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.4 Espaces propres.ipynb +++ b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.4 Espaces propres.ipynb @@ -1,127 +1,146 @@ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### Définition 1\n", "Soient $\\phi:V\t\\to V$ une transformation linéaire d'un espace vectoriel $V$ et $\\lambda \\in \\mathbb{R}$ une valeur propre de $\\phi.$ Alors l'espace propre de $\\phi$ associé à $\\lambda$ est le sous-ensemble de $V$ défini par $E_{\\lambda}=\\{v\\in V: \\phi(v)=\\lambda v\\}$.\n", "\n", "De manière similaire, si $\\lambda\\in \\mathbb{R}$ est une valeur propre de la matrice $A\\in M_{n \\times n}(\\mathbb{R}),$ alors l'espace propre de $A$ associé à $\\lambda$ est le sous-ensemble de $M_{n \\times 1}(\\mathbb{R})$ défini par $E_{\\lambda}=\\{X\\in M_{n \\times 1}(\\mathbb{R}) : AX=\\lambda X\\}$.\n", "\n", "### Proposition 2\n", "Le sous-ensemble $E_\\lambda$ est un sous-espace vectoriel." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import plotly\n", "import plotly.graph_objects as go\n", "import sympy as sp\n", "import sys, os \n", "sys.path.append('../Librairie')\n", "import AL_Fct as al\n", "from IPython.utils import io\n", "from IPython.display import display, Latex\n", "from Ch8_lib import *" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "#A_sp = sp.Matrix([[0, -4, -1], [0, -8, 8], [0, -4, -4]])\n", - "#display(A_sp.rref())\n", - "#A_np = np.array([[0, -4, -1], [0, -8, 8], [0, -4, -4]])\n", - "#b = np.zeros(3)\n", - "#display(al.echelonMat('ER', A))\n", - "#A = np.array([[4, -1, 6],[2, 1, 6], [2, -1, 8]])\n", + "### Exercice 1\n", + "**Trouvez les valeurs propres des matrices suivantes puis pour chacune des valeurs propres, trouvez une base de l'espace propre correspondant.**\n", + "\n", "\n", - "#A = np.random.randint(-5,5, (3,3))\n", - "#A = np.eye(3)\n", - "A = sp.Matrix([[3, -4, -1], [0, -5, 8], [0, -1, -4]])\n", - "A_sp = sp.Matrix(A)\n", + "La fonction `eigen_basis` détaille étape par étape comment obtenir une base de l'espace propre associé à une valeur propre donnée d'une matrice. Elle vérifie également que la base de l'espace propre rentrée est correcte.\n", + "Cette fonction prend 3 arguments: \n", + "* La matrice (définie comme `A = sp.Matrix([[1, 0], [0, 2]])` par exemple)\n", + "* La valeur propre pour laquelle on souhaite calculer l'espace propre (définie comme `l = 2` par exemple)\n", + "* Une base de l'espace propre à vérifier. La base doit être exprimée comme une liste de vecteurs de base: \n", + " * Exemples:\n", + " * `base = [[v11, v12, v13], [v21, v22, v23]]` si la base contient deux vecteurs de dimension 3\n", + " * `base = [[v11, v12]]` si la base n'a qu'un seul vecteur de dimension 2\n", "\n", - "eig = A_sp.eigenvects()\n", - "eig_list = np.array(eig[0][2]).astype(np.float64)\n", + "Pour appeler la fonction, on execute simplement:\n", + "`eigen_basis(A, l, base)`\n", "\n", - "for sp_mat in eig_list:\n", - " sp_mat = np.array(sp_mat)\n", - " \n", - "basis, basic_idx, free_idx = eigen_basis_sp(A_sp, float(eig[0][0]), eig_list, disp=True)\n", - "\n" + "Si vous ne trouvez pas de base pour l'espace propre, vous pouvez simplement afficher la solution en exécutant `eigen_basis(A, l)`. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "A = np.array([[1, 1, 1],[1, 1, 1], [1, 1, 2]])\n", + "A1 = sp.Matrix([[-1, 3], [-1, 3]])\n", + "A2 = sp.Matrix([[4, 4], [3, 0]])\n", "\n", - "A_sp = sp.Matrix(A)\n", + "# Simplement pour lambda = 3 (les autres valeurs propores sont complexes)\n", + "A3 = sp.Matrix([[3, -4, -1], [0, -5, 8], [0, -1, -4]])\n", "\n", - "plot3x3_eigspace(A_sp, plot_vector=False)\n" + "# Trouver les valeurs propres en utilisant les racines évidentes du polynôme caractéristique\n", + "A4 = sp.Matrix([[4, -1, 6],[2, 1, 6], [2, -1, 8]])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "A = sp.Matrix([[4, -1, 6],[2, 1, 6], [2, -1, 8]])\n", - "\n" + "# Matrice (A1, A2, A3 ou A4)\n", + "A = A1\n", + "\n", + "# Rentrer une valeur propre que vous avez calculé\n", + "l = 0\n", + "\n", + "# Base que vous avez calculé associé à la valeur propre ci-dessus\n", + "base = [[1, 1], [1, 2]]\n", + "\n", + "# Solution étape par étape et vérification de la base calculée\n", + "eigen_basis(A1, l, base)" ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], + "cell_type": "markdown", + "metadata": {}, "source": [ - "A = sp.Matrix([[1, 1], [-1, 3]])\n", + "### Visualisation des espaces propres\n", + "Les espaces propres des matrices $2\\times2$ et $3\\times3$ peuvent être représenté graphiquement.\n", + "\n", + "La fonction `plot_eigspace` permet de visualiser les espaces propres associer à chaque valeurs propres d'une matrice $2\\times2$ ou $3\\times3$. Pour utiliser cette function, il suffit de donner comme argument une matrice. Les valeurs propres et leur espace propre associé sont calculés par la fonction et ensuite affichés.\n", "\n", - "plot2x2_eigspace(A)" + "Utilisez les matrices de l'**Exercice 1** afin de visualiser les espaces propres dont vous avez calculé les bases." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "A_sp = sp.Matrix([[1,0,0], [0,2,0], [0, 0, 2]])\n", + "plot_eigspace(A_sp)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Observations\n", + "* On voit que l'origine est toujours compris dans n'importe quel espace propre. Un espace propre étant un espace sous-vectoriel, il contient forcément le vecteur nul (l'origine).\n", + "\n", + "* Deux espaces propres associé à deux valeurs propres distinctes de la même matrice n'ont en commun que le vecteur nul (l'origine)." + ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.2" } }, "nbformat": 4, "nbformat_minor": 2 } diff --git "a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.7 Multiplicit\303\251 alg\303\251brique, multiplicit\303\251 g\303\251om\303\251trique.ipynb" "b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.7 Multiplicit\303\251 alg\303\251brique, multiplicit\303\251 g\303\251om\303\251trique.ipynb" new file mode 100644 index 0000000..8ad2b54 --- /dev/null +++ "b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.7 Multiplicit\303\251 alg\303\251brique, multiplicit\303\251 g\303\251om\303\251trique.ipynb" @@ -0,0 +1,71 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Définition 1:\n", + "Soient $\\phi: V \\rightarrow V$ une transformation linéaire d'un $\\mathbb{R}$ -espace vectoriel $V$ de dimension finie $n \\in \\mathbb{N}$ et $\\lambda \\in \\mathbb{R}$ une valeur propre de $\\phi$. Comme toute valeur propre de $\\phi$ est racine de $c_{\\phi}(t),$ on peut factoriser\n", + "$$c_{\\phi}(t)=(t-\\lambda)^{m} p(t)$$\n", + "où $p(\\lambda) \\neq 0$ (i.e. $t-\\lambda$ ne divise pas $p(t)$). L'entier $m$ est appelée la multiplicité algébrique de $\\lambda$. Aussi, la dimension du sous-espace $E_{\\lambda}$ de $V$ est appelée la multiplicité géométrique de $\\lambda$.\n", + "\n", + "### Proposition 2:\n", + "\n", + "Soient $\\phi: V \\rightarrow V$ une transformation linéaire d'un $\\mathbb{R}$-espace vectoriel $V$ de dimension finie $n \\in \\mathbb{N}$ et $\\lambda \\in \\mathbb{R}$ une valeur propre de $\\phi$. Alors la multiplicité géométrique de $\\lambda$ est plus grande ou égale à 1. Aussi, celle-ci est toujours plus petite ou égale à la multiplicité algébrique de $\\lambda$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from Ch8_lib import *\n", + "import sys, os\n", + "sys.path.append('../Librairie')\n", + "import AL_Fct as al\n", + "import numpy as np\n", + "import sympy as sp\n", + "from IPython.utils import io\n", + "from IPython.display import display, Latex\n", + "import plotly\n", + "import plotly.graph_objects as go" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 1: Calcul des multiplicités algébriques et géométriques." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git "a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.8 Crit\303\250re de diagonalisabilit\303\251.ipynb" "b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.8 Crit\303\250re de diagonalisabilit\303\251.ipynb" new file mode 100644 index 0000000..43c2a0e --- /dev/null +++ "b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/8.8 Crit\303\250re de diagonalisabilit\303\251.ipynb" @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Théorème\n", + "Soit $\\phi: V \\rightarrow V$ une transformation linéaire d'un $\\mathbb{R}$-espace vectoriel $V$ de dimension finie $n \\in \\mathbb{N}$. Alors $\\phi$ est diagonalisable si et seulement si il existe $a \\in \\mathbb{R}, \\lambda_{1}, \\ldots, \\lambda_{r} \\in \\mathbb{R}$ distincts et $m_{1}, \\ldots, m_{r} \\in \\mathbb{N}$ tels que\n", + "$$c_{\\phi}(t)=a\\left(t-\\lambda_{1}\\right)^{m_{1}} \\ldots\\left(t-\\lambda_{r}\\right)^{m_{r}}$$\n", + "et $m_{i}=\\operatorname{dim} E_{\\lambda_{i}}$ pour tout $1 \\leq i \\leq n$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.append('../Librairie')\n", + "import AL_Fct as al\n", + "import numpy as np\n", + "import sympy as sp\n", + "from IPython.utils import io\n", + "from IPython.display import display, Latex\n", + "import plotly\n", + "import plotly.graph_objects as go\n", + "from Ch8_lib import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 1\n", + "À l'aide de la représentation graphique des espaces propres associés aux différentes valeurs propres d'une matrice, determinez si cette dernière est diagonalisable. (Aucun calcul requis).\n", + "\n", + "**Si besoin, la méthode a appliquer est donnée plus bas.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "A1 = sp.Matrix([[3, 4], [0, 3]])\n", + "A2 = sp.Matrix([[1, 3], [4,5]])\n", + "A3 = sp.Matrix([[5, 0, 0],[0, 1, 0], [0, 0, 1]])\n", + "A4 = sp.Matrix([[1, 0, -2],[2 ,1, 0], [0, 0, 3]])\n", + "\n", + "# Choisir matrice (A = A1 ou A = A2 ...)\n", + "A = A1\n", + "plot_eigspace(A)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Affiche la solution pour la matrice A = A1 ou A = A2 ...\n", + "A = A1\n", + "# Votre réponse a : La matrice A est-elle diagonlisable ? (True pour oui, False pour non)\n", + "my_answer = True\n", + "\n", + "ch8_8_ex_1(A, my_answer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Méthode exercice 1\n", + "On sait que le polynôme caractéristique d'une matrice $A (n \\times n)$ est d'ordre $n$. \n", + "\n", + "Avec $c_A(t)=a(t-\\lambda_{1})^{m_{1}} \\ldots (t-\\lambda_{r})^{m_{r}}$, avec $a \\in \\mathbb{R}$, $r\\leq n$ et $\\lambda_{1}, \\ldots, \\lambda_{r} \\in \\mathbb{R}$ distincts, on peut en déduire que $\\sum_{i=1}^r m_i = n$\n", + "\n", + "Sachant également que $\\dim E_{\\lambda_i} \\leq m_i$, on a $m_i = \\dim E_{\\lambda_i} $ pour tout $ i = 1, .., r$ si et seulement si $\\sum_{i=1}^r \\dim E_{\\lambda_i} = n$ \n", + "\n", + "Le théorème donné plus haut indique qu'une matrice est diagonalisable si et seulement si $m_i = \\dim E_{\\lambda_i}$ pour tout $ i = 1, .., r$. \n", + "\n", + "On vient de montrer que c'est équivalent à $\\sum_{i=1}^r \\dim E_{\\lambda_i} = n$.\n", + "\n", + "Grâce à la représentation graphique des espaces propres, on connait la dimension de chaque espace propre (droite -> $\\dim E_{\\lambda_i} = 1$, plan -> $\\dim E_{\\lambda_i}=2$). Il suffit donc de vérifier que la somme des dimensions de tous les espaces propres est bien égal à $n$. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/Ch8_lib.py b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/Ch8_lib.py index e1d205b..d995f0c 100644 --- a/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/Ch8_lib.py +++ b/Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation/Ch8_lib.py @@ -1,653 +1,680 @@ import sys, os sys.path.append('../Librairie') import AL_Fct as al import numpy as np import sympy as sp from IPython.utils import io from IPython.display import display, Latex import plotly import plotly.graph_objects as go def vector_plot_3D(v, b): """ Show 3D plot of a vector (v) and of b = A * v @param v: numpy array of shape (3,) @param b: numpy array of shape (3,) @return: """ fig = go.Figure() fig.add_trace(go.Scatter3d(x=[0, v[0]], y=[0, v[1]], z=[0, v[2]], line=dict(color='red', width=4), mode='lines+markers', name='$v$')) fig.add_trace(go.Scatter3d(x=[0, b[0]], y=[0, b[1]], z=[0, b[2]], line=dict(color='royalblue', width=4, dash='dash'), mode='lines+markers', name='$A \ v$')) fig.show() def CheckEigenVector(A, v): """ Check if v is an eigenvector of A, display step by step solution @param A: square sympy Matrix of shape (n,n) @param v: 1D sympy Matrix of shape (n,1) @return: """ # Check Dimensions if A.shape[0] != A.shape[1] or v.shape[0] != A.shape[1]: raise ValueError('Dimension problem, A should be square (n x n) and v (n x 1)') if v == sp.zeros(v.shape[0], 1): display(Latex("$v$ est le vecteur nul, il ne peut pas être un vecteur propre par définition.")) else: # Matrix Multiplication b = A * v # Print some explanation about the method display(Latex("On voit que $ b = A v = " + sp.latex(b) + "$")) display(Latex("On cherche alors un nombre $\lambda \in \mathbb{R}$ tel que $b = \lambda v" \ + "\Leftrightarrow" + sp.latex(b) + " = \lambda" + sp.latex(v) + '$')) # Symbol for lambda l = sp.symbols('\lambda', real=True) # Check if there is a solution lambda of eq: A*v = lambda * v eq = sp.Eq(b, l * v) sol = sp.solve(eq, l) # If there is l st b = l*v if sol: display(Latex("Il existe bien une solution pour $\lambda$. Le vecteur $v$ est donc un vecteur \ propre de la matrice $A$.")) display(Latex("La valeur propre associée est $\lambda = " + sp.latex(sol[l]) + "$.")) # Otherwise else: display(Latex("L'equation $b = \lambda v$ n'a pas de solution.")) display(Latex("Le vecteur $v$ n'est donc pas un vecteur propre de la matrice $A$.")) def ch8_1_exo_2(A, l, vp, v): """ Display step by step @param A: Square sympy matrix @param l: eigenvalue (float or int) @param vp: Boolean, given answer to question is l an eigenvalue of A @param v: proposed eigenvector @return: """ # Check Dimensions if A.shape[0] != A.shape[1] or v.shape[0] != A.shape[1]: raise ValueError('Dimension problem, A should be square (n x n) and v (n x 1)') n = A.shape[0] eig = list(A.eigenvals().keys()) for i, w in enumerate(eig): eig[i] = float(w) eig = np.array(eig) if np.any(abs(l-eig) < 10**-10): if vp: display(Latex("$\lambda = " + str(l) + "$ est bien une valeur propre de la matrice $A$.")) else: display(Latex("Non, $\lambda = " + str(l) + "$ est bien une valeur propre de la matrice $A$.")) if v != sp.zeros(n, 1): # Check the eigen vector v z = sp.simplify(A * v - l * v) if z == sp.zeros(n, 1): display(Latex("$v$ est bien un vecteur propre de $A$ associé à $\lambda = " + str(l) + "$ car on a:")) display(Latex("$$" + sp.latex(A) + sp.latex(v) + "= " + str(l) + "\cdot " + sp.latex(v) + "$$")) else: display(Latex("$v$ n'est pas un vecteur propre de $A$ associé à $\lambda = " + str(l) + "$ car on a:")) display(Latex("$$" + sp.latex(A) + sp.latex(v) + "\\neq \lambda" + sp.latex(v) + "$$")) else: display(Latex("$v$ est le vecteur nul et ne peut pas être par définition un vecteur propre.")) else: if vp: display(Latex("En effet, $\lambda$ n'est pas une valeur propre de $A$.")) else: display(Latex("Non, $\lambda = " + str(l) + "$ n'est pas une valeur propre de $A$.")) def red_matrix(A, i, j): """ Return reduced matrix (without row i and col j)""" row = [0, 1, 2] col = [0, 1, 2] row.remove(i - 1) col.remove(j - 1) return A[row, col] def pl_mi(i, j, first=False): """ Return '+', '-' depending on row and col index""" if (-1) ** (i + j) > 0: if first: return "" else: return "+" else: return "-" def brackets(expr): """Takes a sympy expression, determine if it needs parenthesis and returns a string containing latex of expr with or without the parenthesis.""" expr_latex = sp.latex(expr) if '+' in expr_latex or '-' in expr_latex: return "(" + expr_latex + ")" else: return expr_latex def Determinant_3x3(A, step_by_step=True, row=True, n=1): """ Step by step computation of the determinant of a 3x3 sympy matrix strating with given row/col number @param A: 3 by 3 sympy matrix @param step_by_step: Boolean, True: print step by step derivation of det, False: print only determinant @param row: True to compute determinant from row n, False to compute determinant from col n @param n: row or col number to compute the determinant from (int between 1 and 3) @return: display step by step solution for """ if A.shape != (3, 3): raise ValueError('Dimension of matrix A should be 3x3. The input A must be a sp.Matrix of shape (3,3).') if n < 1 or n > 3 or not isinstance(n, int): raise ValueError('n should be an integer between 1 and 3.') # Construc string for determinant of matrix A detA_s = sp.latex(A).replace('[', '|').replace(']', '|') # To print all the steps if step_by_step: # If we compute the determinant with row n if row: # Matrix with row i and col j removed (red_matrix(A, i, j)) A1 = red_matrix(A, n, 1) A2 = red_matrix(A, n, 2) A3 = red_matrix(A, n, 3) detA1_s = sp.latex(A1).replace('[', '|').replace(']', '|') detA2_s = sp.latex(A2).replace('[', '|').replace(']', '|') detA3_s = sp.latex(A3).replace('[', '|').replace(']', '|') line1 = "$" + detA_s + ' = ' + pl_mi(n, 1, True) + brackets(A[n - 1, 0]) + detA1_s + pl_mi(n, 2) + \ brackets(A[n - 1, 1]) + detA2_s + pl_mi(n, 3) + brackets(A[n - 1, 2]) + detA3_s + '$' line2 = '$' + detA_s + ' = ' + pl_mi(n, 1, True) + brackets(A[n - 1, 0]) + "\cdot (" + sp.latex(sp.det(A1)) \ + ")" + pl_mi(n, 2) + brackets(A[n - 1, 1]) + "\cdot (" + sp.latex(sp.det(A2)) + ")" + \ pl_mi(n, 3) + brackets(A[n - 1, 2]) + "\cdot (" + sp.latex(sp.det(A3)) + ')$' line3 = '$' + detA_s + ' = ' + sp.latex(sp.simplify(sp.det(A))) + '$' # If we compute the determinant with col n else: # Matrix with row i and col j removed (red_matrix(A, i, j)) A1 = red_matrix(A, 1, n) A2 = red_matrix(A, 2, n) A3 = red_matrix(A, 3, n) detA1_s = sp.latex(A1).replace('[', '|').replace(']', '|') detA2_s = sp.latex(A2).replace('[', '|').replace(']', '|') detA3_s = sp.latex(A3).replace('[', '|').replace(']', '|') line1 = "$" + detA_s + ' = ' + pl_mi(n, 1, True) + brackets(A[0, n - 1]) + detA1_s + pl_mi(n, 2) + \ brackets(A[1, n - 1]) + detA2_s + pl_mi(n, 3) + brackets(A[2, n - 1]) + detA3_s + '$' line2 = '$' + detA_s + ' = ' + pl_mi(n, 1, True) + brackets(A[0, n - 1]) + "\cdot (" + sp.latex(sp.det(A1))\ + ")" + pl_mi(n, 2) + brackets(A[1, n - 1]) + "\cdot (" + sp.latex(sp.det(A2)) + ")" + \ pl_mi(n, 3) + brackets(A[2, n - 1]) + "\cdot (" + sp.latex(sp.det(A3)) + ')$' line3 = '$' + detA_s + ' = ' + sp.latex(sp.simplify(sp.det(A))) + '$' # Display step by step computation of determinant display(Latex(line1)) display(Latex(line2)) display(Latex(line3)) # Only print the determinant without any step else: display(Latex("$" + detA_s + "=" + sp.latex(sp.det(A)) + "$")) def valeurs_propres(A): if A.shape[0] != A.shape[1]: raise ValueError("A should be a square matrix") l = sp.symbols('\lambda') n = A.shape[0] poly = sp.det(A - l * sp.eye(n)) poly_exp = sp.expand(poly) poly_factor = sp.factor(poly) det_str = sp.latex(poly_exp) + "=" + sp.latex(poly_factor) display(Latex("On cherche les valeurs propres de la matrice $ A=" + sp.latex(A) + "$.")) display(Latex("Le polynome caractéristique de $A$ est: $$\det(A- \lambda I)= " + det_str + "$$")) eq = sp.Eq(poly, 0) sol = sp.solve(eq, l) if len(sol) > 1: display(Latex("Les racines du polynôme caractéristique sont $" + sp.latex(sol) + "$.")) display(Latex("Ces racines sont les valeurs propres de la matrice $A$.")) else: display(Latex("L'unique racine du polynôme caractéristique est" + str(sol[0]))) def texVector(v): """ Return latex string for vertical vector Input: v, 1D np.array() """ n = v.shape[0] return al.texMatrix(v.reshape(n, 1)) def check_basis(sol, prop): """ Checks if prop basis is equivalent to sol basis @param sol: verified basis, 2D numpy array, first dim: vector indexes, second dim: idx of element in a basis vect @param prop: proposed basis @return: boolean """ prop = np.array(prop, dtype=np.float64) # number of vector in basis n = len(sol) # Check dimension of proposed eigenspace if n != len(prop): display(Latex("Le nomber de vecteur(s) propre(s) donné(s) est incorrecte. " + "La dimension de l'espace propre est égale au nombre de variable(s) libre(s).")) return False else: # Check if the sol vector can be written as linear combination of prop vector # Do least squares to solve overdetermined system and check if sol is exact A = np.transpose(prop) lin_comb_ok = np.zeros(n, dtype=bool) for i in range(n): x, _, _, _ = np.linalg.lstsq(A, sol[i], rcond=None) res = np.sum((A @ x - sol[i]) ** 2) lin_comb_ok[i] = res < 10 ** -13 return np.all(lin_comb_ok) -def eigen_basis_sp(A, l, prop_basis=None, disp=True): +def eigen_basis(A, l, prop_basis=None, disp=True, return_=False): """ Display step by step method for finding a basis of the eigenspace of A associated to eigenvalue l Eventually check if the proposed basis is correct. Display or not @param A: Square sympy Matrix with real coefficients @param l: real eigen value of A (float or int) @param prop_basis: Proposed basis: list of base vector (type list of list of floats) @param disp: boolean if display the solution. If false it displays nothing + @param return_: boolean if return something or nothing @return: basis: a correct basis for the eigen space (2D numpy array) basic_idx: list with indices of basic variables of A - l*I free_idx: list with indices of free variables of A - l*I """ if not A.is_Matrix: raise ValueError("A should be a sympy Matrix.") # Check if A is square n = A.shape[0] if n != A.shape[1]: raise ValueError('A should be a square matrix.') - - # Compute eigenvals in symbolic eig = A.eigenvals() eig = list(eig.keys()) # Deal with complex number (removal) complex_to_rm = [] for idx, el in enumerate(eig): if not el.is_real: complex_to_rm.append(idx) for index in sorted(complex_to_rm, reverse=True): del eig[index] eig = np.array(eig) # evaluate symbolic expression eig_eval = np.array([float(el) for el in eig]) # Check that entered eigenvalue is indeed an eig of A if np.all(abs(l - eig_eval) > 1e-10) and len(eig) > 0: display(Latex("$\lambda$ n'est pas une valeur propre de $A$.")) return None, None, None # Change value of entered eig to symbolic expression (for nice print) l = eig[np.argmin(np.abs(l - eig))] I = sp.eye(n) Mat = A - l * I b = np.zeros(n) if disp: display(Latex("On a $ A = " + sp.latex(A) + "$.")) display(Latex("On cherche une base de l'espace propre associé à $\lambda = " + str(l) + "$.")) # ER matrix e_Mat, basic_idx = Mat.rref() # Idx of basic and free varialbe basic_idx = list(basic_idx) basic_idx.sort() free_idx = [idx for idx in range(n) if idx not in basic_idx] free_idx.sort() n_free = len(free_idx) # String to print free vars free_str = "" for i in range(n): if i in free_idx: free_str += "x_" + str(i + 1) + " \ " # Display echelon matrix if disp: display(Latex("On échelonne la matrice du système $A -\lambda I = 0 \Rightarrow " + al.texMatrix(np.array(Mat), np.reshape(b, (n, 1))) + "$")) display(Latex("On obtient: $" + al.texMatrix(np.array(e_Mat[:, :n]), np.reshape(b, (n, 1))) + "$")) display(Latex("Variable(s) libre(s): $" + free_str + "$")) # Build a list of n_free basis vector: # first dim: which eigenvector (size of n_free) # second dim: which element of the eigenvector (size of n) basis = np.zeros((n_free, n)) for i in range(n_free): basis[i, free_idx[i]] = 1.0 for idx, j in enumerate(free_idx): for i in basic_idx: basis[idx, i] = - float(e_Mat[i, j]) # Show calculated basis basis_str = "" for idx, i in enumerate(free_idx): basis_str += "x_" + str(i + 1) + " \cdot" + texVector(basis[idx]) if idx < n_free - 1: basis_str += " + " if disp: display(Latex("On peut donc exprimer la base de l'espace propre comme: $" + basis_str + "$")) if prop_basis is not None and disp: correct_answer = check_basis(basis, prop_basis) if correct_answer: display(Latex("La base donnée est correcte car on peut retrouver la base calculée ci-dessus" \ " avec une combinaison linéaire de la base donnée. " "Aussi les deux bases ont bien le même nombre de vecteurs.")) else: display(Latex("La base donnée est incorrecte.")) - return basis, basic_idx, free_idx + if return_: + return basis, basic_idx, free_idx def generate_eigen_vector(basis, l, limit): + """ + Function to generate a random eigenvector associated to a eigenvalue given a basis of the eigenspace + The returned eigenvector is such that itself and its multiplication with the matrix will stay in range of limit + in order to have a nice plot + @param basis: basis of eigenspace associated to eigenvalue lambda + @param l: eigenvalue + @param limit: limit of the plot: norm that the engenvector or its multiplication with the matrix will not exceed + @return: eigen vector (numpy array) + """ n = len(basis) basis_mat = np.array(basis).T basis_mat = basis_mat.astype(np.float64) coeff = 2 * np.random.rand(n) - 1 vect = basis_mat @ coeff if abs(l) <= 1: vect = vect / np.linalg.norm(vect) * (limit - 1) else: vect = vect / np.linalg.norm(vect) * (limit - 1) / l return vect -def plot3x3_eigspace(A, xL=-10, xR=10, p=None, plot_vector=True): +def plot3x3_eigspace(A, xL=-10, xR=10, p=None, plot_vector=False): # To have integer numbers if p is None: p = xR - xL + 1 n = A.shape[0] # Check 3 by 3 if n != 3 or n != A.shape[1]: raise ValueError("A should be 3 by 3") w = A.eigenvals() w = list(w.keys()) # Deal with complex number (removal) complex_to_rm = [] for idx, el in enumerate(w): if not el.is_real: complex_to_rm.append(idx) for index in sorted(complex_to_rm, reverse=True): del w[index] display("Des valeurs propres sont complexes, on les ignore.") if len(w)==0: display("Toute les valeurs propres sont complexes.") return gr = 'rgb(102,255,102)' org = 'rgb(255,117,26)' # red = 'rgb(255,0,0)' blue = 'rgb(51, 214, 255)' colors = [blue, gr, org] s = np.linspace(xL, xR, p) t = np.linspace(xL, xR, p) tGrid, sGrid = np.meshgrid(s, t) data = [] A_np = np.array(A).astype(np.float64) for i, l in enumerate(w): l_eval = float(l) - basis, basic_idx, free_idx = eigen_basis_sp(A, l_eval, disp=False) + basis, basic_idx, free_idx = eigen_basis(A, l_eval, disp=False, return_=True) n_free = len(basis) if n_free != len(free_idx): raise ValueError("len(basis) and len(free_idx) should be equal.") gr = 'rgb(102,255,102)' colorscale = [[0.0, colors[i]], [0.1, colors[i]], [0.2, colors[i]], [0.3, colors[i]], [0.4, colors[i]], [0.5, colors[i]], [0.6, colors[i]], [0.7, colors[i]], [0.8, colors[i]], [0.9, colors[i]], [1.0, colors[i]]] X = [None] * 3 if n_free == 2: X[free_idx[0]] = tGrid X[free_idx[1]] = sGrid X[basic_idx[0]] = tGrid * basis[0][basic_idx[0]] + sGrid * basis[1][basic_idx[0]] plot_obj = go.Surface(x=X[0], y=X[1], z=X[2], showscale=False, showlegend=True, colorscale=colorscale, opacity=1, name="$ \lambda= " + sp.latex(l) + "$") elif n_free == 1: plot_obj = go.Scatter3d(x=t * basis[0][0], y=t * basis[0][1], z=t * basis[0][2], line=dict(colorscale=colorscale, width=4), mode='lines', name="$\lambda = " + sp.latex(l) + "$") elif n_free == 3: display(Latex("La dimension de l'espace propre de l'unique valeur propre est 3: tous les vecteurs" \ "$v \in \mathbb{R}^3 $ appartiennent à l'espace propre de la matrice $A$." \ "On ne peut donc pas reprensenter sous la forme d'un plan ou d'une droite.")) return else: print("error") return data.append(plot_obj) if (plot_vector): v1 = generate_eigen_vector(basis, l_eval, xR) v2 = A_np @ v1 data.append(go.Scatter3d(x=[0, v1[0]], y=[0, v1[1]], z=[0, v1[2]], line=dict(width=6), marker=dict(size=4), mode='lines+markers', name='$v_{' + sp.latex(l) + '}$')) data.append(go.Scatter3d(x=[0, v2[0]], y=[0, v2[1]], z=[0, v2[2]], line=dict(width=6, dash='dash'), marker=dict(size=4), mode='lines+markers', name="$A \ v_{" + sp.latex(l) + "}$")) layout = go.Layout( showlegend=True, # not there WHY???? --> LEGEND NOT YET IMPLEMENTED FOR SURFACE OBJECTS!! legend=dict(orientation="h"), autosize=True, width=800, height=800, scene=go.layout.Scene( xaxis=dict( gridcolor='rgb(255, 255, 255)', zerolinecolor='rgb(255, 255, 255)', showbackground=True, backgroundcolor='rgb(230, 230,230)', range=[xL, xR] ), yaxis=dict( gridcolor='rgb(255, 255, 255)', zerolinecolor='rgb(255, 255, 255)', showbackground=True, backgroundcolor='rgb(230, 230,230)', range=[xL, xR] ), zaxis=dict( gridcolor='rgb(255, 255, 255)', zerolinecolor='rgb(255, 255, 255)', showbackground=True, backgroundcolor='rgb(230, 230,230)', range=[xL, xR] ), aspectmode="cube", ) ) fig = go.Figure(data=data, layout=layout) plotly.offline.iplot(fig) return def plot2x2_eigspace(A, xL = -10, xR = 10, p=None): if p is None: p = xR - xL + 1 w = A.eigenvals() w = list(w.keys()) # Deal with complex number (removal) complex_to_rm = [] for idx, el in enumerate(w): if not el.is_real: complex_to_rm.append(idx) for index in sorted(complex_to_rm, reverse=True): del w[index] display("Une valeur propre est complexe, on l'ignore.") if len(w) == 0: display("Toute les valeurs propres sont complexes.") return data = [] for i, l in enumerate(w): l_eval = float(l) - basis, basic_idx, free_idx = eigen_basis_sp(A, l_eval, disp=False) + basis, basic_idx, free_idx = eigen_basis(A, l_eval, disp=False, return_=True) n_free = len(basis) if n_free != len(free_idx): raise ValueError("len(basis) and len(free_idx) should be equal.") if n_free == 2: - display(Latex("Tous les vecteurs du plan appartiennent à l'espace propre de A associé à $\lambda = " - +sp.latex(l) +"$. On ne peut donc pas le représenter.")) + display(Latex("Tous les vecteurs du plan appartiennent à l'espace propre de A associé à $\lambda = " \ + + sp.latex(l) + "$. On ne peut donc pas le représenter.")) return else: t = np.linspace(xL, xR, p) trace = go.Scatter(x=t*basis[0][0], y=t*basis[0][1], marker=dict(size=6), mode='lines+markers', name="$\lambda = " + sp.latex(l) + "$") data.append(trace) layout = go.Layout(showlegend=True, autosize=True) fig = go.Figure(data=data, layout=layout) plotly.offline.iplot(fig) return def plot_eigspace(A, xL=-10, xR=10, p=None): """ Plot the eigenspaces associated to all eigenvalues of A @param A: Sympy matrix of shape (2,2) or (3,3) @param xL: Left limit of plot @param xR: Right limit of plot @param p: Number of points to use """ n = A.shape[0] # Check 3 by 3 or 2 by 2 if (n != 2 and n!=3) or n != A.shape[1]: raise ValueError("A should be 2 by 2 or 3 by 3.") if not A.is_Matrix: raise ValueError("A should be a sympy Matrix.") if n==2: plot2x2_eigspace(A, xL, xR, p) else: plot3x3_eigspace(A, xL, xR, p) -# def create_eig_set(A, show_vector=True, tol=10 ** -13): -# w, _ = np.linalg.eig(A) -# w_imag = np.imag(w) -# if np.all(np.abs(w_imag) < 10 ** -13): -# w = np.real(w) -# else: -# raise ValueError("Complex eigenvalues detected") -# w_set = [w[0]] -# -# for i in range(1, 3): -# # If no very close element already in w_set, then add w[i] in w_set -# if np.all(np.abs(w_set - w[i]) > tol): -# w_set.append(w[i]) -# -# return w_set -# -# -# def create_eig_set_sp(A): -# A_sp = sp.Matrix(A) -# eig_dic = A_sp.eigenvals() -# return np.array(list(eig_dic.keys()), dtype=np.float64) -# + +def latexp(A): + """ + Function to output latex expression of a sympy matrix but with round parenthesis + @param A: sympy matrix + @return: latex string + """ + return sp.latex(A, mat_delim='(', mat_str='matrix') + + +def ch8_8_ex_1(A, prop_answer): + """ + Check if a matrix is diagonalisable. + @param A: sympy square matrix + @param prop_answer: boolean, answer given by the student + @return: + """ + if not A.is_Matrix: + raise ValueError("A should be a sympy Matrix.") + n = A.shape[0] + if n != A.shape[1]: + raise ValueError('A should be a square matrix.') + + eig = A.eigenvects() + dim_geom = 0 + + for x in eig: + dim_geom += len(x[2]) + answer = dim_geom == n + + if answer: + display(Latex("Oui la matrice $A = " + latexp(A) + "$ est diagonalisable.")) + else: + display(Latex("Non la matrice $A = " + latexp(A) + "$ n'est pas diagonalisable.")) + + if answer == prop_answer: + display(Latex("Votre réponse est correcte !")) + else: + display(Latex("Votre réponse est incorrecte.")) +