diff --git "a/Chapitre 10 - Matrices orthogonales, matrices sym\303\251triques/Ch10_lib.py" "b/Chapitre 10 - Matrices orthogonales, matrices sym\303\251triques/Ch10_lib.py"
index 5e29319..55f3b1f 100644
--- "a/Chapitre 10 - Matrices orthogonales, matrices sym\303\251triques/Ch10_lib.py"
+++ "b/Chapitre 10 - Matrices orthogonales, matrices sym\303\251triques/Ch10_lib.py"
@@ -1,1199 +1,1199 @@
import sys, os
import numpy as np
import sympy as sp
from IPython.display import display, Latex, Markdown
import plotly
import plotly.graph_objects as go
sys.path.append('../Chapitre 8 - Valeurs propres, vecteurs propres, diagonalisation')
from Ch8_lib import *
sys.path.append('../Librairie')
import AL_Fct as al
import ipywidgets as widgets
from ipywidgets import interact_manual, Layout
from sympy import sqrt
import random
def points_on_circle(n, center=np.array([0, 0])):
theta = 2 * np.pi / n
s_pts = np.zeros((n, 2))
e_pts = np.zeros((n, 2))
for i in range(n):
s_pts[i] = [np.cos(i * theta) + center[0], np.sin(i * theta) + center[1]]
e_pts[i] = [np.cos((i + 1) * theta) + center[0], np.sin((i + 1) * theta) + center[1]]
return np.array(s_pts), np.array(e_pts)
def plot_geom_2D(s_pts, e_pts, A):
n = len(s_pts)
if A.shape[0]!=2 or A.shape[1]!=2:
raise ValueError("A should be a 2 by 2 numpy array")
if n != len(e_pts):
raise ValueError("start_points and end_points must have same length.")
layout = go.Layout(yaxis=dict(scaleanchor="x", scaleratio=1))
display(Latex("On montre la transformation provoquée par la multiplication par la matrice $A = "
+ al.texMatrix(A) + "$ sur une figure géométrique."))
if np.allclose(A @ A.transpose(), np.eye(2)):
display(Latex("La matrice $A = " + al.texMatrix(A) + "$ est une matrice orthogonale." ))
else:
display(Latex("La matrice $A = " + al.texMatrix(A) + "$ n'est pas une matrice orthogonale." ))
fig = go.Figure(layout=layout)
color = ['black', 'red', 'blue', 'green', 'yellow', 'brown', 'grey', 'cyan', 'orange', 'violet']
n_col = len(color)
if n > n_col:
color = ['blue']
n_col = 1
for i in range(n):
a = np.array(s_pts[i])
b = np.array(e_pts[i])
a2 = A @ a
b2 = A @ b
if i == 0:
show_legend = True
else:
show_legend = False
fig.add_trace(go.Scatter(x=[a[0], b[0]], y=[a[1], b[1]],
line=dict(color=color[i % n_col], width=2),
mode='lines+markers', showlegend=show_legend, name='Original Figure'))
fig.add_trace(go.Scatter(x=[a2[0], b2[0]], y=[a2[1], b2[1]],
line=dict(color=color[i % n_col], width=2, dash='dot'),
mode='lines+markers', showlegend=show_legend, name='Modified Figure'))
fig.show()
def is_orthogonal(A):
A = sp.Matrix(A)
n = A.shape[0]
if n != A.shape[1]:
raise ValueError("A should be a square matrix")
display(Latex("On cherche à savoir si la matrice $A = " + latexp(A) + "$ est orthogonale en utilisant la " +
"définition 1."))
if A.is_lower or A.is_upper:
for i in range(n):
if A[i, i] != 1 and A[i, i] != -1:
display(Latex("Les valeurs propres de $A$ sont ses éléments diagonaux. Une des valeurs propres de "
+ "$A$ est différente de 1 ou -1. Il existe donc un vecteur propres $v$ de $A$ tel "
+ " que $A v = \lambda v$ avec $\lambda \\neq \pm 1$. Donc dans ce cas on a $\|A v\| "
+ "\\neq \|v\| $."
))
return
v = sp.zeros(n, 1)
for i in range(n):
symb_name = "x_" + str(i + 1)
v[i] = sp.symbols(symb_name, real=True)
b = A * v
b_norm = b.norm() ** 2
v_norm = v.norm() ** 2
display(Latex("Il faut vérifier si $\|A v\| = \|v\|$ pour tout $v \in \mathbb{R}^" + str(n) + "$."
+ " On utilise le carré des normes pour s'affranchir des racines carrées."))
display(Latex("On calcule d'abord la norme au carré de $v$ et on obtient : $\|v\|^2 = " + sp.latex(v_norm) +
"$."))
display(Latex("On calcule ensuite le produit $Av = " + latexp(b) + "$"))
if sp.simplify(b_norm) != b_norm:
display(Latex("On calcule la norme au carré de $Av$ et on obtient : $\|Av\|^2 = " + sp.latex(b_norm) +
"=" + sp.latex(sp.simplify(b_norm)) + "$"))
else:
display(Latex("On calcule la norme au carré de $Av$ et on obtient : $\|Av\|^2 = " + sp.latex(b_norm)
+ "$"))
if sp.simplify(b_norm - v_norm) == 0:
display(Latex("Les normes sont toujours égales. La matrice est donc orthogonale."))
else:
display(Latex("Les normes sont différente. La matrice n'est donc pas orthogonale."))
def interactive_is_orthogonal(A):
A = sp.Matrix(A)
n = A.shape[0]
if n != A.shape[1]:
raise ValueError("A should be a square matrix")
display(Latex("La matrice suivante est-elle orthogonale ? $A = " + latexp(A) + "$"))
answer = widgets.RadioButtons(options=["Oui", "Non"], description="Réponse : ", disabled=False)
sol = A.transpose()*A - sp.eye(n) == sp.zeros(n)
display(answer)
def f():
if answer.value == "Oui":
answer_bool = True
else:
answer_bool = False
if answer_bool == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect"))
interact_manual(f)
display(Latex("Si vous n'arrivez pas à trouver la solution, vous pouvez afficher la solution détaillée en"
" cliquant ici"))
def f_sol():
is_orthogonal(A)
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def ortho_diag(A):
A = sp.Matrix(A)
n = A.shape[0]
if A - A.transpose() != sp.zeros(n):
display(Latex("La matrice $A = " + latexp(A) + "$ n'est pas orthogonalement diagonalisable car elle n'est pas "
"symétrique."))
return
eig = A.eigenvals()
eig_list = list(eig.keys())
eig_list.sort()
display(Latex("On cherche à diagonaliser orthogonalement la matrice $ A = " + latexp(A) + "$."))
default_value_P = str(np.ones(A.shape, dtype=np.int16).tolist())
default_value_P = default_value_P.replace("],", "],\n")
default_value_D = str(np.ones(A.shape[0], dtype=np.int16).tolist())
answer_P = widgets.Text(value=default_value_P, description='P: ', disabled=False)
answer_D = widgets.Text(value=default_value_D, description='D: ', disabled=False)
display(Latex("Pour les réponses aux questions suivantes, ne faites pas d'approximations numériques, "
"toutes les valeurs doivent être exactes. Pour les racines carrées "
"(utiles pour normaliser les vecteurs), utilisez 'sqrt('votre valeur')', ex: sqrt(2)."))
display(Latex("Donnez les éléments diagonaux de la matrice $D$"))
display(answer_D)
display(Latex("Donnez la matrice P permettant de diagonaliser orthogonalement la matrice $A$ de sorte que "
"$ A = PDP^T = PDP^{-1} $"))
display(answer_P)
def f():
P_user = eval(answer_P.value)
P_user = sp.Matrix(P_user)
D_diag= eval(answer_D.value)
D_diag_order = set(D_diag.copy())
D_diag_order = list(D_diag_order)
D_diag_order.sort()
D_user = sp.zeros(n)
for i in range(n):
D_user[i, i] = D_diag[i]
D_user = sp.Matrix(D_user)
if np.allclose(np.array(D_diag_order, dtype=np.float64), np.array(eig_list, dtype=np.float64)):
if P_user*P_user.transpose()-sp.eye(n) == sp.zeros(n):
if P_user * D_user * P_user.transpose() - A == sp.zeros(n):
display(Latex("Correct !"))
else:
display(Latex("Incorrect. On a $PDP^{T}\\neq A$ avec $P = " + latexp(P_user) + "$ et $D =" +
latexp(D_user) + "$."))
else:
display(Latex("La matrice $P = " + latexp(P_user) + "$ n'est pas orthogonale, i.e. $P P^{T} \\neq I$."))
else:
display(Latex("Incorrect. "
"Les éléments diagonaux de $D$ ne correspondent pas aux valeurs propres de la matrice $A$."))
return
interact_manual(f)
def correction_ortho_diag():
display(Latex("Pour diagonaliser orthogonalement une matrice, on a applique la méthode donnée dans le cours et rappelée plus haut"))
lamda = sp.symbols('lamda')
poly_char_exp = sp.expand(A.charpoly(lamda).as_expr())
poly_char_fac = sp.factor(A.charpoly(lamda).as_expr())
poly_char_exp_str = sp.latex(poly_char_exp)
poly_char_fac_str = sp.latex(poly_char_fac)
display(Latex("Le polynôme caractéristique de $A$ s'exprime comme: $c_A (t) = " + poly_char_exp_str + "$."))
display(Latex("On trouve les racines du polynôme caractéristique et on factorise. On obtient $c_A (t) = "
+ poly_char_fac_str + "$."))
eig_list_ = list(eig.keys())
mult_list_ = list(eig.values())
display(Latex("On obtient donc les valeurs propres $\lambda$ et leur multiplicité respective $m$: "
" $ \lambda = " + sp.latex(eig_list_) + " \hspace{10mm} m = " + sp.latex(mult_list_) + "$."))
display(Markdown("**On trouve une base orthonormée pour tous les espaces propres.**"))
final_basis = []
P = sp.Matrix([])
k = 0
for l in eig_list_:
basis_orthonorm = []
basis, basic_idx, free_idx = eigen_basis(A, l, prop_basis=None, disp=True, return_=True, dispA=False)
for v in basis:
if np.all(v == np.round(v)):
v = v.astype(np.int16)
basis_orthonorm.append(sp.Matrix(v))
basis_orthonorm = sp.GramSchmidt(basis_orthonorm, True)
for v in basis_orthonorm:
final_basis.append(v)
P = P.col_insert(k, v)
k+=1
display(Latex("On applique le procédé de Gram-Schmidt pour obtenir des vecteurs de norme 1 et orthogonaux. "
"On obtient $" + sp.latex(basis_orthonorm) + "$"))
display(Latex("Finalement, on construit la matrice $P$ en utilisant chaque vecteur de base "
"trouvé précedemment comme colonne de cette matrice. Cette dernière est bien orthogonale car "
"les vecteurs de base sont orthogonaux entre eux et tous de norme unitaire. "
"On construit la matrice $D$ en plaçant les valeurs propres de $A$ sur la diagonale. "
"Le placement des valeurs propres sur la diagonale de $D$ "
"doit correspondre avec celui des vecteurs propres dans $P$."))
D = sp.zeros(n)
i = 0
for idx in range(len(eig_list_)):
for _ in range(mult_list_[idx]):
D[i, i] = eig_list_[idx]
i += 1
display(Latex("On obtient $P = " + latexp(P) + "\hspace{5mm} \mbox{et} \hspace{5mm} D = " + latexp(D) + "$"))
display(Latex("On vérifie le résultat par un calcul :"))
display(Latex("$P D P^T = "+ latexp(P) + latexp(D) + latexp(P.transpose()) + " = " + latexp(P*D*P.transpose()) +
"=A$"))
display(Latex("Si vous n'arrivez pas à résoudre l'exercice, vous pouvez afficher la solution étape "
"par étape en cliquant ici."))
im = interact_manual(correction_ortho_diag)
im.widget.children[0].description = 'Solution'
def nl():
return "$$ $$"
def sol_is_ortho(A):
A = sp.Matrix(A)
n = A.shape[0]
if n != A.shape[1]:
display(Latex("La matrice $A = " + latexp(A) + "$ n'est pas carrée !"))
return
display(Latex("Pour vérifier si la matrice $A$ est orthogonale, on effectue dans cette correction "
"le produit de $A$ et de sa transposée $A^T$."
" On vérifie ensuite si l'on obtient la matrice identité."))
if sp.simplify(A*A.transpose()-sp.eye(n)) == sp.zeros(n):
display(Latex("On a que : $A A^T = " + latexp(A) + latexp(A.transpose()) + "= " + latexp(sp.eye(n)) + "= I_n $"))
display(Latex("La matrice $A$ est donc orthogonale."))
else:
display(Latex("On a que : $A A^T = " + latexp(A) + latexp(A.transpose()) + "\\neq I_n $"))
display(Latex("Par consequent, la matrice $A$ n'est pas orthogonale."))
return
def is_ortho_10_2(A):
A = sp.Matrix(A)
n = A.shape[0]
display(Latex("La matrice $A =" + latexp(A) + "$ est-elle orthogonale ?"))
answer = widgets.RadioButtons(options=["Oui", "Non"], description="Réponse : ", disabled=False)
display(answer)
sol = sp.simplify(A * A.transpose() - sp.eye(n)) == sp.zeros(n)
if n != A.shape[1]:
sol = False
def f():
if answer.value == "Oui":
answer_bool = True
else:
answer_bool = False
if answer_bool == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect"))
if sol:
display(Latex("La matrice $A$ est orthogonale."))
else:
display(Latex("La matrice $A$ n'est pas orthogonale."))
interact_manual(f)
def f_sol():
sol_is_ortho(A)
display(Latex("Pour afficher la solution, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def vf_10_2_2():
display(Latex("Soit $A \in M_{n \\times n}(\mathbb{R})$ une matrice orthogonale. On construit la matrice"
" $B \in M_{n \\times n}(\mathbb{R})$ en réarrangeant les colonnes de $A$ dans un ordre quelquonque. "
"La matrice $B$ est aussi orthogonale."))
answer = widgets.RadioButtons(options=["Vrai", "Faux"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value=="Vrai":
display(Latex("Correct ! En effet, si l'ordre des colonnes est modifié, l'ensemble de ces dernières reste "
"toujours une base orthonormée de l'espace $V$. "
"La proposition (5) est donc vérifiée pour la matrice $B$."))
else:
display(Latex("Incorrect, changez votre réponse !"))
interact_manual(f)
def vf_10_2_3():
display(Latex("Soit $A \in M_{n \\times n}(\mathbb{R})$ une matrice orthogonale. On construit la matrice"
" $B \in M_{n \\times n}(\mathbb{R})$ en réarrangeant les lignes de $A$ dans un ordre quelquonque. "
"La matrice $B$ est aussi orthogonale."))
answer = widgets.RadioButtons(options=["Vrai", "Faux"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value=="Vrai":
display(Latex("Correct ! En effet, si l'ordre des lignes est modifié, l'ensemble de ces dernières reste "
"toujours une base orthonormée de l'espace $V$. "
"La proposition (4) est donc vérifiée pour la matrice $B$."))
else:
display(Latex("Incorrect, changez votre réponse !"))
interact_manual(f)
def vf_10_2_1():
display(Latex("Soit $A \in M_{n \\times n}(\mathbb{R})$ une matrice orthogonale. $A^T$ est aussi orthogonale."))
answer = widgets.RadioButtons(options=["Vrai", "Faux"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == "Vrai":
display(Latex("Correct ! En effet, comme $A$ est orthogonale, on sait que ses colonnes forment "
"une base orthonormée de l'espace $V$. Comme les lignes de $A^T$ sont les colonnes de $A$, "
"alors la proposition (4) est vérifiée pour $A^T$, i.e. les lignes de $A^T$ forment une base "
"orthonormée de $V$. $A^T$ est donc une matrice orthogonale."))
else:
display(Latex("Incorrect, changez votre réponse !"))
interact_manual(f)
def vf_10_2_4():
display(Latex("Soit $A \in M_{n \\times n}(\mathbb{R})$ une matrice ayant une valeur propre $\lambda = 2$. "
"Il est possible que $A$ soit orthogonale."))
answer = widgets.RadioButtons(options=["Vrai", "Faux"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == "Faux":
display(Latex("Correct ! Etant donné que $A$ a pour valeur propre $\lambda=2$, cela signifique qu'il existe "
"un vecteur $v$ tel que $A v = 2 v$. Donc la matrice $A$ ne conserve pas toujours les longeurs lors "
"d'une multiplication avec un vecteur. La proposition (1) n'est pas vérifiée."
" $A$ ne peut donc pas être orthogonale."))
else:
display(Latex("Incorrect, changez votre réponse !"))
interact_manual(f)
def vf_10_4_1():
display(Markdown("**Dire si l'affirmation suivante est vraie ou fausse.**"))
display(Latex("La matrice $A$ est orthogonalement diagonalisable."))
n_pos = [3, 4, 5]
n = random.choice(n_pos)
sym = bool(random.getrandbits(1))
A = sp.randMatrix(n, symmetric=sym, percent=70, min=-20, max=20)
if sp.simplify(A-A.transpose())==sp.zeros(n):
sol = True
else:
sol = False
display(Latex("$A = " + latexp(A) + "$"))
answer = widgets.RadioButtons(options=["Vrai", "Faux"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == "Vrai":
answer_value = True
else:
answer_value = False
if answer_value == sol:
display(Latex("Correct ! "))
if sol:
display(Latex("La matrice $A$ est orthogonalement diagonalisable car elle est symétrique."))
else:
display(Latex("La matrice $A$ n'est pas orthogonalement diagonalisable car elle n'est pas symétrique."))
else:
display(Latex("Incorrect, changez votre réponse."))
interact_manual(f)
def quest_10_4_2():
A = sp.Matrix([[2, -4], [1, -1]])
charpol = A.charpoly().as_expr() * -1
display(Latex("Soit $A$ une matrice carrée ayant pour polynome caractéristique $c_A(\lambda) = " +
sp.latex(charpol) + "$"))
display(Latex("La matrice $A$ est-elle symétrique ?"))
answer = widgets.RadioButtons(options=["Oui", "Pas nécessairement", "Non"], description="Réponse : ", disabled=False)
display(answer)
sol = "Non"
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
display(Latex("Le polynome caractéristique de $A$ admet une ou des racine(s) non réelle(s). "
"Donc la matrice $A$ ne peut pas être symétrique."))
display(Latex("Pour afficher la solution détaillée, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def quest_10_4_3():
# Non symmetric values
A = sp.Matrix([[0, -1, -4], [0, 0, -2], [0, -2, 0]])
charpol = A.charpoly().as_expr()*-1
charpol = charpol.factor()
display(Latex("Soit $A$ une matrice carrée ayant pour polynome caractéristique $c_A(\lambda) = " + sp.latex(charpol) + "$"))
display(Latex("La matrice $A$ est-elle symétrique ?"))
answer = widgets.RadioButtons(options=["Oui", "Pas nécessairement", "Non"], description="Réponse : ", disabled=False)
display(answer)
sol = "Pas nécessairement"
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
A2 = sp.Matrix([[0, 0, 0], [0, 2, 0], [0, 0, -2]])
display(Latex("On sait que le polynome caractéristique d'une matrice symétrique admet uniquement des racines réelles. "
"Or la réciproque est fausse, i.e., les polynomes caractéristiques à racines réelles peuvent être issus de matrices non symétriques. " + nl() +
"On voit que le polynome caractéristique $c_A(\lambda)$ a uniquement des racines réelles mais cela ne suffit pas pour conclure que $A$ est symétrique. "))
display(Latex("La bonne réponse est donc: Pas nécessairement"))
display(Latex("Dans ce cas précis, les matrices $A_1 =" + latexp(A) + "$ et $A_2 = " + latexp(A2) + "$ ont "
"toutes les deux $c_A(\lambda)$ pour polynome caractéristique. On voit que une des matrices est symétrique et l'autre pas."))
display(Latex("Pour afficher la solution détaillée, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def exo_2_1_10_4():
v1 = sp.Matrix([3, 4])
v2 = sp.Matrix([-1, 1/2])
display(Latex("La matrice $A \in M_{2 \\times 2}$ ( $\mathbb{R}$ ) a pour valeur propres $3$ et $-2$. "
"$" + latexp(v1) + "\mbox{ et } " + latexp(v2) + "$ sont des vecteurs propres de $A$ respectivement "
"associés aux valeurs propres $3$ et $-2$. "))
display(Latex("La matrice $A$ est-elle symétrique ?"))
answer = widgets.RadioButtons(options=["Oui", "Non", "Manque d'informations"], description="Réponse : ", disabled=False)
display(answer)
sol = "Non"
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
display(Latex("Les vecteurs propres $" + latexp(v1) + "$ et $" + latexp(v2) +
"$ sont associés à des valeurs propres différentes mais ne sont pas orthogonaux. "
"La matrice $A$ ne peut donc pas être symétrique."))
display(Latex("Pour afficher la solution détaillée, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def exo_2_2_10_4():
v1 = sp.Matrix([1, 0, 2])
v2 = sp.Matrix([-2, 0, -4])
v3 = sp.Matrix([2, 5, -1])
display(Latex("La matrice $A \in M_{3 \\times 3}$ ( $\mathbb{R}$ ) a pour valeur propres $4$ et $2$. "
"$" + latexp(v1) + ", " + latexp(v2) + "\mbox{ et } " + latexp(v3) + "$ sont des vecteurs propres de $A$ respectivement "
"associés aux valeurs propres $4$, $4$ et $-2$. "))
display(Latex("La matrice $A$ est-elle symétrique ?"))
opt = ["Oui", "Non", "Manque d'informations"]
answer = widgets.RadioButtons(options=opt, description="Réponse : ", disabled=False)
display(answer)
sol = opt[2]
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
display(Latex("Les vecteurs propres associés à des valeurs propres différentes sont bien orthogonaux. "
"Mais on ne connait pas la dimension de l'espace propre associé à $\lambda = 4$ car les deux "
"vecteurs donnés dans l'énoncé, $ " + latexp(v1) + " \mbox{ et } " + latexp(v2) + "$, "
"sont colinéaires." + nl() +
"Pour connaitre la dimension de cette espace, il faudrait une base de ce dernier. "
"Cela permettrait de vérifier si la dimension de l'espace est de 2 et si tous les vecteurs de "
"cet espace sont orthogonaux aux vecteurs composant l'espace propre associé à $\lambda = 2$. "
"Si ces deux conditions étaient vérifiées, la matrice $A$ serait symétrique."))
display(Latex("Il manque donc d'informations pour répondre."))
display(Latex("Pour afficher la solution détaillée, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def exo_2_3_10_4():
v1 = sp.Matrix([1, 0, 1])
v2 = sp.Matrix([-2, 0, 1])
v3 = sp.Matrix([0, 3, 0])
display(Latex("La matrice $A \in M_{3 \\times 3}$ ( $\mathbb{R}$ ) a pour valeur propres $-1$, $2$ et $4$. "
"$" + latexp(v1) + ", " + latexp(v2) + "\mbox{ et } " + latexp(v3) + "$ sont des vecteurs propres de $A$ respectivement "
"associés aux valeurs propres $-1$, $2$ et $4$. "))
display(Latex("La matrice $A$ est-elle symétrique ?"))
opt = ["Oui", "Non", "Manque d'informations"]
answer = widgets.RadioButtons(options=opt, description="Réponse : ", disabled=False)
display(answer)
sol = opt[1]
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
display(Latex("Les vecteurs propres $" + latexp(v1) + "$ et $" + latexp(v2) +
"$ sont associés à des valeurs propres différentes mais ne sont pas orthogonaux. "
"La matrice $A$ ne peut donc pas être symétrique."))
display(Latex("Pour afficher la solution détaillée, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def exo_2_4_10_4():
v1 = sp.Matrix([1, 0, 1])
v2 = sp.Matrix([-2, 0, 1])
v3 = sp.Matrix([0, 3, 0])
display(Latex("La matrice $A \in M_{3 \\times 3}$ ( $\mathbb{R}$ ) a pour valeur propres $-1$, et $4$. "
"$" + latexp(v1) + ", " + latexp(v2) + "\mbox{ et } " + latexp(v3) + "$ sont des vecteurs propres de $A$ respectivement "
"associés aux valeurs propres $-1$, $-1$ et $4$. "))
display(Latex("La matrice $A$ est-elle symétrique ?"))
opt = ["Oui", "Non", "Manque d'informations"]
answer = widgets.RadioButtons(options=opt, description="Réponse : ", disabled=False)
display(answer)
sol = opt[0]
def f():
if answer.value == sol:
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
base_1 = sp.GramSchmidt([v1, v2], orthonormal=True)
base_4 = sp.GramSchmidt([v3], orthonormal=True)
display(Latex("Les vecteurs propres $" + latexp(v1) + "$ et $" + latexp(v2) + "$ associés à $\lambda = -1$ sont linéairement indépendant "
"et orthogonaux au vecteur propre $" + latexp(v3) + "$ associé à $\lambda = 4$. "
"On peut utiliser ces deux ensembles de vecteurs propres pour former des bases des espaces propres "
"associés à $\lambda = -1$ et $\lambda = 4$, respectivement. "
"On peut othogonaliser et normaliser ces bases avec le schema de Gram-Schmidt."))
display(Latex("On obtient les bases des espaces propres associées respectivement à $\lambda = -1$ et $\lambda = 4$"))
display(Latex("$ " + latexp(base_1[0]) + ", " + latexp(base_1[1]) + "\hspace{15mm} " + latexp(base_4[0]) + "$"))
display(Latex("On voit que l'espace $V = \mathbb{R}^3$ possède une base othornormée composée de vecteurs propres de $A$. "
"La transformation associée à la matrice $A$ est donc orthogonalement diagonalisable et $A$ est donc symétrique."))
display(Latex("Pour afficher la solution détaillée, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def ex_3_1_10_4():
l_1 = 2
l_2 = -1
v_1 = sp.Matrix([1, -1])
display(Latex("Trouver une matrice $A \in M_{2 \\times 2} (\mathbb{R})$ symétrique ayant pour valeurs propres 2 "
"et -1 et $" + latexp(v_1) + "$ comme vecteur propres associé à $\lambda = 2$."))
default_value_A = str(np.ones((2, 2), dtype=np.int16).tolist())
answer = widgets.Text(value=default_value_A, description='A: ', disabled=False, display='flex')
display(answer)
def f():
A = eval(answer.value)
A = sp.Matrix(A)
if A.shape[0] != 2 or A.shape[1] != 2:
display(Latex("Incorrect, la matrice doit être carrée et 2x2."))
return
if sp.simplify(A - A.transpose()) != sp.zeros(2):
display(Latex("Incorrect, la matrice n'est pas symétrique !"))
return
eig = A.eigenvals()
eig_list = list(eig.keys())
if l_1 not in eig_list or l_2 not in eig_list:
if l_1 not in eig_list:
display(Latex("Incorrect, 2 n'est pas une valeur propre de la matrice rentrée."))
if l_2 not in eig_list:
display(Latex("Incorrect, -1 n'est pas une valeur propre de la matrice rentrée."))
return
A_v = A*v_1
if sp.simplify(A_v - l_1 * v_1) != sp.zeros(2, 1):
display(Latex("Incorrect, le vecteur $" + latexp(v1) + "$ n'est pas un vecteur propre de $A$ associé à "
"la valeur propre $\lambda = 2$."))
return
display(Latex("Correct !"))
interact_manual(f)
def f_sol():
display(Latex("On utilise la formule $A=PDP^T$ pour constuire la matrice symetrique $A$. $P$ est une matrice "
"orthogonale composée de vecteurs propres de $A$ et $D$ est une matrice diagonale avec les valeurs"
" propres de $A$ sur la diagonale. "))
D = sp.Matrix([[2, 0],[0, -1]])
display(Latex("On construit simplement la matrice $D =" + latexp(D) + "$"))
v_2 = sp.Matrix([1, 1])
display(Latex("On ne dispose pas d'informations sur les vecteurs propres associés à $\lambda = -1$. "
"Mais pour obtenir une matrice symétrique on sait que $P$ est orthogonale, "
"donc il faut trouver un vecteur propre orthogonal à $" + latexp(v_1) + "$. Simplement, on prend $" + latexp(v_2) + "$."))
display(Latex("On normalise les deux vecteurs propres orthogonaux et on forme les colonnes de $P$. "
"Chaque vecteur est placé dans la même colonne que sa valeur propre correspondante dans $D$."))
P = sp.Matrix([[1, 1], [-1, 1]])/sp.sqrt(2)
display(Latex("On obtient $P = " + latexp(P) + "$."))
A = P*D*P.transpose()
display(Latex("$A = P D P^T = " + latexp(P) + latexp(D) + latexp(P.transpose()) + "=" + latexp(A) +"$"))
display(Latex("Pour afficher la solution détaillée, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def ch10_7_ex_1_1():
x1, x2, x3, x4 = sp.symbols("x_1 x_2 x_3 x_4")
Q = x1**2 - 5*x1*x2 + x1*x3 - x1*x4 + 4*x2**2 + 2*x2*x3 - 4*x2*x4 + x3**2 + x3*x4 - x4**2
sol = "Oui"
display(Latex("Le polynôme $Q = " + sp.latex(Q) + "$ est-il une forme quadratique ?"))
answer = widgets.RadioButtons(options=["Oui", "Non"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == sol:
display(Latex("Correct ! "))
display(Latex("Le polynôme $Q = "+ sp.latex(Q) + "$ est une forme quadratique car "
"il contient uniquement des termes d'ordre 2."))
else:
display(Latex("Incorrect, changez votre réponse."))
interact_manual(f)
def ch10_7_ex_1_2():
x1, x2, x3 = sp.symbols("x_1 x_2 x_3")
Q = (x1 + x2)**2 + (x3 - 1)**2
sol = "Non"
display(Latex("Le polynôme $Q = " + sp.latex(Q) + "$ est-il une forme quadratique ?"))
answer = widgets.RadioButtons(options=["Oui", "Non"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == sol:
display(Latex("Correct ! "))
display(Latex("Le polynôme $Q = " + sp.latex(Q) + "$ n'est pas une forme quadratique car "
"il ne contient pas uniquement des termes d'ordre 2."))
display(Latex("On peut developper $Q$ pour expliciter l'ordre de chaque terme."))
display(Latex("$$ Q = " + sp.latex(Q.expand()) + "$$"))
else:
display(Latex("Incorrect, changez votre réponse."))
interact_manual(f)
def ch10_7_ex_1_3():
x1, x2, x3 = sp.symbols("x_1 x_2 x_3")
Q = 3 * x1 ** 2 + x1 * x2 + x2 ** 2 + 2 * x2 * x3 - x1 * x3 + x3 ** 3
sol = "Non"
display(Latex("Le polynôme $Q = " + sp.latex(Q) + "$ est-il une forme quadratique ?"))
answer = widgets.RadioButtons(options=["Oui", "Non"], description="Réponse : ", disabled=False)
display(answer)
def f():
if answer.value == sol:
display(Latex("Correct ! "))
display(Latex("Le polynôme $Q = " + sp.latex(Q) + "$ n'est pas une forme quadratique car "
"il ne contient pas uniquement des termes d'ordre 2."))
else:
display(Latex("Incorrect, changez votre réponse."))
interact_manual(f)
def ch10_7_ex_2(n):
x1, x2, x3, x4, x5 = sp.symbols("x_1 x_2 x_3 x_4 x_5")
x_list = [sp.Matrix([x1, x2]), sp.Matrix([x1, x2, x3]), sp.Matrix([x1, x2, x3, x4])]
A1 = sp.Matrix([[-2, 3], [3, 4]])
A2 = sp.Matrix([[1, 0, 3], [0, -2, -1/4], [3, -1/4, 4]])
A3 = sp.Matrix([[-1, -1, 3, 0],
[-1, -2, 0, 4],
[3, 0, 0, -1],
[0, 4, -1, 2]])
A_list = [A1, A2, A3]
A = A_list[n-1]
x = x_list[n-1]
Q = x.dot(A * x).expand()
display(Latex("Rentrez la matrice symétrique $A$ telle que $\mathbf{x}^T A \mathbf{x} = Q(\mathbf{x})$ "
"avec $\mathbf{x} = " + latexp(x) + "$"))
display(Latex("où $Q(\mathbf{x}) = " + sp.latex(Q) + "$"))
default_value_A = str(np.ones(A.shape, dtype=np.int16).tolist())
answer = widgets.Text(value=default_value_A, description='A = ', disabled=False,
layout=widgets.Layout(width='600px'))
display(answer)
def f():
A_user = eval(answer.value)
A_user = sp.Matrix(A_user)
display(Latex("Vous avez rentré la matrice $A_{user} = " + latexp(A_user) + "$"))
if A_user.shape != A.shape:
display(Latex("Incorrect, la matrice $A$ n'a pas les bonnes dimensions."))
return
if sp.simplify(A_user - A_user.transpose()) != sp.zeros(A.shape[0]):
display(Latex("Incorrect, la matrice rentrée n'est pas symétrique. "))
return
Q_user = x.dot(A_user * x).expand()
if sp.simplify(Q_user - Q) == 0:
display(Latex("Correct !"))
else:
display(Latex("Incorrect, avec votre matrice on obtient $ Q_{user}(\mathbf{x}) = "
+ sp.latex(Q_user) + "\\neq Q(\mathbf{x}) $"))
interact_manual(f)
def f_sol():
display(Latex("La matrice $A = " + latexp(A) + "$ est telle que $Q(\mathbf{x}) = \mathbf{x}^T A \mathbf{x} $"))
display(Latex("Pour afficher la solution, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def plot_quad(A, n_points=50j, lims=2):
A_sp = sp.Matrix(A)
eig = A_sp.eigenvects()
# Create list with principal axis
p_axis = []
for x in eig:
v_list = sp.GramSchmidt(x[2], orthonormal=True)
for v in v_list:
p_axis.append(np.array(v).astype(np.float64).flatten())
A = np.array(A)
if A.shape == (3, 3):
X, Y, Z = np.mgrid[-lims:lims:n_points, -lims:lims:n_points, -lims:lims:n_points]
# ellipsoid
values = A[0, 0] * X * X + A[1, 1] * Y * Y + A[2, 2] * Z * Z + (A[0, 1]+A[1, 0])* X * Y + (A[0, 2] + A[2, 0]) * X * Z + (A[1, 2] + A[1, 2]) * Y * Z
layout = go.Layout(scene=go.layout.Scene(aspectmode="cube"))
fig = go.Figure(data=go.Isosurface(
x=X.flatten(),
y=Y.flatten(),
z=Z.flatten(),
value=values.flatten(),
isomin=1,
isomax=1,
showscale=False,
opacity=0.5,
caps=dict(x_show=False, y_show=False),
showlegend=True,
name='Quadrique'
),
layout=layout)
fig.add_trace(go.Scatter3d(x=[0, p_axis[0][0]],
y=[0, p_axis[0][1]],
z=[0, p_axis[0][2]],
line=dict(width=2, color='black'),
mode='lines+markers',
marker=dict(size=3),
showlegend=True,
name='Axes principaux',
))
fig.add_trace(go.Scatter3d(x=[0, p_axis[1][0]],
y=[0, p_axis[1][1]],
z=[0, p_axis[1][2]],
line=dict(width=2, color='black'),
mode='lines+markers',
marker=dict(size=3),
showlegend=False,
name='Principal axis 2',
))
fig.add_trace(go.Scatter3d(x=[0, p_axis[2][0]],
y=[0, p_axis[2][1]],
z=[0, p_axis[2][2]],
mode='lines+markers',
line=dict(width=2, color='black'),
marker=dict(size=3),
showlegend=False,
name='Principal axis 3',
))
elif A.shape == (2, 2):
X, Y = np.mgrid[-lims:lims:n_points, -lims:lims:n_points]
Z = A[0, 0] * X * X + A[1, 1] * Y * Y + (A[1, 0] + A[0, 1]) * X * Y
fig = go.Figure(data=go.Contour(
x=X[:, 0],
y=Y[0, :],
z=Z,
contours=dict(start=1, end=1),
contours_coloring='lines',
line_width=2,
opacity=0.5,
showscale=False, showlegend=True,
name='Quadrique'),
)
fig.update_layout(xaxis=dict(range=[-lims, lims], constrain="domain",), yaxis = dict(scaleanchor= "x", scaleratio = 1,))
fig.add_trace(go.Scatter(x=[0, p_axis[0][0]],
- y=[0, p_axis[0][1]],
- mode='lines+markers',
- line=dict(width=2, color='black'),
- marker=dict(size=3),
- showlegend=True,
- name='Axes principaux',
- ))
+ y=[0, p_axis[0][1]],
+ mode='lines+markers',
+ line=dict(width=2, color='black'),
+ marker=dict(size=3),
+ showlegend=True,
+ name='Axes principaux',
+ ))
fig.add_trace(go.Scatter(x=[0, p_axis[1][0]],
y=[0, p_axis[1][1]],
mode='lines+markers',
line=dict(width=2, color='black'),
marker=dict(size=3),
showlegend=False,
name = 'Principal axis 2',
))
else:
return
fig.show()
def ex1_10_8(n):
A_list = [sp.Matrix([[5, 2], [2, 5]]), sp.Matrix([[3, 2, 0], [2, 0, 0], [0, 0, 2]])]
x1, x2, x3 = sp.symbols("x_1 x_2 x_3")
x_list = [sp.Matrix([x1, x2]), sp.Matrix([x1, x2, x3])]
A = A_list[n-1]
x = x_list[n-1]
Q = x.dot(A * x).expand()
display(Latex("$Q(x) = " + sp.latex(Q) + "$"))
display(Latex("Donnez les coefficients $d_1, ..., d_n$ et la matrice $S$ de changement de variable tel "
"que $x = Sy$. "))
default_value_d = str(np.ones(A.shape[0], dtype=np.int16).tolist())
default_value_S = str(np.ones(A.shape, dtype=np.int16).tolist())
answer_d = widgets.Text(value=default_value_d, description='d = ', disabled=False,
layout=widgets.Layout(width='600px'))
answer_S = widgets.Text(value=default_value_S, description='S = ', disabled=False,
layout=widgets.Layout(width='600px'))
display(answer_d)
display(answer_S)
def f():
d_user = eval(answer_d.value)
d_user = np.array(d_user, dtype=np.int16)
D_user = np.diag(d_user)
D_user = sp.Matrix(D_user)
S_user = eval(answer_S.value)
S_user = sp.Matrix(S_user)
display(Latex("Vous avez rentré la matrice suivante: $S_{user} = " + latexp(S_user) + "$"))
A_user = S_user * D_user * S_user.transpose()
if sp.simplify(A-A_user) == sp.zeros(A.shape[0]):
display(Latex("Correct !"))
else:
display(Latex("Incorrect."))
interact_manual(f)
def f_sol():
display(Latex("On commence par trouver la matrice symétrique $A$ telle que $Q(x) = x^T A x$ avec "
"$x = " + latexp(x) + "$."))
display(Latex("Par identification, on trouve $A = " + latexp(A) + "$."))
display(Latex("Ensuite, l'exercice consiste simplement à diagonaliser orthogonalement la matrice symétrique "
"$A$."))
display(Latex("En effet, si on a $D = S^T A S$, avec $D$ diagonale, en effectuant le changement de variable "
"$x = Sy$, on obtient : " + nl() + " $Q(x) = x^T A x = (Sy)^T A (Sy) = y^T S^T A S y = y^T D y = "
"d_1 y_1^2 + d_2 y_2^2 + ... + d_n y_n^2 $ "))
lamda = sp.symbols('lamda')
poly_char_exp = sp.expand(A.charpoly(lamda).as_expr())
poly_char_fac = sp.factor(A.charpoly(lamda).as_expr())
poly_char_exp_str = sp.latex(poly_char_exp)
poly_char_fac_str = sp.latex(poly_char_fac)
display(Latex("Le polynôme caractéristique de $A$ s'exprime comme: $c_A (t) = " + poly_char_exp_str + "$."))
display(Latex("On trouve les racines du polynôme caractéristique et on factorise. On obtient $c_A (t) = "
+ poly_char_fac_str + "$."))
eig = A.eigenvals()
eig_list_ = list(eig.keys())
mult_list_ = list(eig.values())
display(Latex("On obtient donc les valeurs propres $\lambda$ et leur multiplicité respective $m$: "
" $ \lambda = " + sp.latex(eig_list_) + " \hspace{10mm} m = " + sp.latex(mult_list_) + "$."))
display(Markdown("**On trouve une base orthonormée pour tous les espaces propres.**"))
final_basis = []
P = sp.Matrix([])
k = 0
for l in eig_list_:
basis_orthonorm = []
basis, basic_idx, free_idx = eigen_basis(A, l, prop_basis=None, disp=True, return_=True, dispA=False)
for v in basis:
# if all ints
if np.all(v == np.round(v)):
v = v.astype(np.int16)
# if there is some float
else:
for idx, el in enumerate(v):
v = sp.Matrix(v)
q = sp.Rational(el)
# If rational are not too high and weird
if abs(q.numerator()) < 100 and abs(q.denominator()) < 100:
v[idx] = q
basis_orthonorm.append(sp.Matrix(v))
basis_orthonorm = sp.GramSchmidt(basis_orthonorm, True)
for v in basis_orthonorm:
final_basis.append(v)
P = P.col_insert(k, v)
k += 1
display(
Latex("On applique le procédé de Gram-Schmidt pour obtenir des vecteurs de norme 1 et orthogonaux. "
"On obtient $" + sp.latex(basis_orthonorm) + "$"))
display(Latex("Finalement, on construit la matrice $S$ en utilisant chaque vecteur de base "
"trouvé précedemment comme colonne de cette matrice. Cette dernière est bien orthogonale car "
"les vecteurs de base sont orthogonaux entre eux et tous de norme unitaire. "
"On construit la matrice $D$ en plaçant les valeurs propres de $A$ sur la diagonale. "
"Le placement des valeurs propres sur la diagonale de $D$ "
"doit correspondre avec celui des vecteurs propres dans $S$."))
D = sp.zeros(A.shape[0])
i = 0
for idx in range(len(eig_list_)):
for _ in range(mult_list_[idx]):
D[i, i] = eig_list_[idx]
i += 1
display(
Latex("On obtient $S = " + latexp(P) + "\hspace{5mm} \mbox{et} \hspace{5mm} D = " + latexp(D) + "$"))
display(Latex("On vérifie le résultat par un calcul :"))
display(Latex(
"$S D S^T = " + latexp(P) + latexp(D) + latexp(P.transpose()) + " = " + latexp(P * D * P.transpose()) +
"=A$"))
display(Latex("Pour afficher la solution, cliquez ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def f_plot():
A_np = np.array(A, dtype=np.float64)
plot_quad(A_np)
if A.shape[0]==2 or A.shape[0]==3:
display(Latex("Pour visualiser la quadrique associée et les axes principaux, cliquez ici"))
im = interact_manual(f_plot)
im.widget.children[0].description = 'Plot'
\ No newline at end of file
diff --git "a/Chapitre 2 - Algebre matricielle/2.10 D\303\251composition LU (applications aux syst\303\250mes lin\303\251aires).ipynb" "b/Chapitre 2 - Algebre matricielle/2.10 D\303\251composition LU (applications aux syst\303\250mes lin\303\251aires).ipynb"
index 9fd0dd8..accab3a 100644
--- "a/Chapitre 2 - Algebre matricielle/2.10 D\303\251composition LU (applications aux syst\303\250mes lin\303\251aires).ipynb"
+++ "b/Chapitre 2 - Algebre matricielle/2.10 D\303\251composition LU (applications aux syst\303\250mes lin\303\251aires).ipynb"
@@ -1,372 +1,311 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "###### Concept(s)-clé(s) et théorie\n",
+ "# Concept(s)-clé(s) et théorie\n",
"\n",
"## APPLICATION DE LA DÉCOMPOSITION AUX SYSTÈMES LINÉAIRES \n",
"Soit un système d'équations linéaires aux inconnues $x_1, \\dots, x_n$, représenté sous forme matricielle $A\\vec{x}=\\vec{b}$. Supposons que $A=LU$ où $L$ est triangulaire inférieure et $U$ est un forme échelonnée. Alors on résout le système de la manière suivante.\n",
"\n",
"1. Poser $Y = (y_1, y_2, \\dots, y_n)^T$;\n",
"2. Résoudre le système $LY=b$ avec la méthode de substitution en avant;\n",
"3. Résoudre le sytème $Ux=y$ avec la méthode de substitution en arrière;"
]
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
+ "outputs": [],
"source": [
"import Librairie.AL_Fct as al\n",
"import Corrections.corrections as corrections\n",
"from ipywidgets import interact_manual\n",
"import numpy as np\n",
"import time\n",
"from scipy.linalg import solve_triangular\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 1\n",
"\n",
"Considerez le système linéaire $Ax=b$, avec $A$ et $b$ donné par:\n",
"\n",
"\\begin{equation}\n",
"A =\n",
"\\begin{pmatrix}\n",
"1 & -1 & 0 \\\\\n",
"2 & 0 & 1 \\\\\n",
"1 & 1 & 1 \n",
"\\end{pmatrix}\n",
"\\qquad b = \n",
"\\begin{pmatrix}\n",
"2 \\\\\n",
"1 \\\\\n",
"-1 \n",
"\\end{pmatrix}\n",
"\\end{equation}\n",
"\n",
"**Sans calculer aucune décomposition LU ni résoudre explicitement le système**, lesquelles des affirmations suivantes sont clairement correctes? [Exécutez la cellule suivante]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex1Chapitre2_10()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 2 \n",
"\n",
"Considerez le système linéaire $Ax=b$ avec $A \\in \\mathcal{M}_{4 \\times 4}(\\mathbb{R})$ et $b \\in \\mathcal{M}_{4 \\times 1}(\\mathbb{R})$ donnés par:\n",
"\n",
"\\begin{equation}\n",
"A = \n",
"\\begin{pmatrix}\n",
"1 & 0 & -1 & -2 \\\\\n",
"0 & -2 & -2 & 1 \\\\\n",
"1 & 2 & 2 & 1 \\\\\n",
"0 & 1 & 1 & -1\n",
"\\end{pmatrix}\n",
"\\qquad b = \n",
"\\begin{pmatrix}\n",
"1 \\\\\n",
"-2 \\\\\n",
"1 \\\\\n",
"0\n",
"\\end{pmatrix}.\n",
"\\end{equation}\n",
"\n",
"En utilisation la décomposition LU de A, résolvez, si possible, le système linéaire."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#Entrez les coefficients de A et b\n",
"A=[[1,0,-1,-2], [0,-2,-2,1], [1,2,2,1], [0,1,1,-1]]\n",
"b = [[1], [-2], [1], [0]]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Vous allez échelonner la matrice A')\n",
"al.printA(A)\n",
"[i,j,r,alpha]= al.manualEch(A)\n",
"LList = [np.eye(4)]\n",
"UList=[np.array(A).astype(float)]\n",
"print('\\033[1mExécutez la ligne suivante pour effectuer l\\'opération choisie \\033[0m')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m=al.LU_interactive(i,j,r,alpha, LList, UList)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Entrez ci-dessous les coefficients de la variable temporaire y et de la solution x du système"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"y = [[1], [-2], [-2], [-1]] # variable temporaire\n",
"x = [[-5], [12], [-10], [2]] # solution du système\n",
"\n",
"corrections.Ex2Chapitre2_10(LList[-1], UList[-1], b, x, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 3\n",
"\n",
"Considerez le système linéaire $Ax=b$ avec $A \\in \\mathcal{M}_{3 \\times 4}(\\mathbb{R})$ et $b \\in \\mathcal{M}_{3 \\times 1}(\\mathbb{R})$ donné par:\n",
"\n",
"\\begin{equation}\n",
"A = \n",
"\\begin{pmatrix}\n",
"1 & 2 & 0 & -1 \\\\\n",
"-2 & -2 & -1 & 0 \\\\\n",
"0 & 2 & -2 & 1\n",
"\\end{pmatrix}\n",
"\\qquad b = \n",
"\\begin{pmatrix}\n",
"1 \\\\\n",
"-1 \\\\\n",
"2 \n",
"\\end{pmatrix}\n",
"\\end{equation}\n",
"\n",
"Profitant de la décomposition LU, résolvez, si possible, le système linéaire et marquez ceux des énoncés suivants qui sont corrects"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#Entrez les coefficients de A et b\n",
"A = [[1,2,0,-1], [-2,-2,-1,0], [0,2,-2,1]]\n",
"b = [[1], [-1], [2]]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Vous allez échelonner la matrice A')\n",
"al.printA(A)\n",
"[i,j,r,alpha]= al.manualEch(A)\n",
"LList = [np.eye(3)]\n",
"UList=[np.array(A).astype(float)]\n",
"print('\\033[1mExécutez la ligne suivante pour effectuer l\\'opération choisie \\033[0m')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m=al.LU_interactive(i,j,r,alpha, LList, UList)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex3Chapitre2_10()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exemple 1\n",
"\n",
"Il peut être difficile de comprendre pourquoi la décomposition LU est si importante. En effet il semble que ce ne soit rien de plus qu'une manière différente de mettre en œuvre la méthode d'élimination de Gauss, où au lieu d'impliquer le vecteur b directement dans la procédure de réduction, on va construire une matrice (L) qui contient toutes les opérations élémentaires effectuées sur les lignes de la matrice du système.\n",
"\n",
"En fin de compte, ce changement apparemment simple est la clé qui rend la décomposition LU (avec toutes ses variantes) très utile dans la pratique réelle; en effet, dans de nombreuses utilisations, il est nécessaire de résoudre plusieurs systèmes linéaires (qui peuvent êtres avec un très grand nombre de variables!). Tous ces systèmes ont la même matrice (L ou U), mais différents vecteurs du côté droit. \n",
"Dans de telles situations, il est très utile d'utiliser la décomposition LU! D'abord, la décomposition LU de la matrice est calculée avant la résolution de tous les systèmes linéaires et on ne la calcule qu'une seule fois. En suite, chaque système est rapidement résolu via des schémas de substitution avant / arrière (sur les matrices L et U qui possèdent beaucoup de coefficients nuls). \n",
"Si la décomposition LU n'est pas utilisée, alors à chaque étape un système linéaire complet devrait être résolu, conduisant à une augmentation significative en termes de nombre d'opérations et de temps de calcul.\n",
"\n",
"Afin de le montrer, nous présentons ci-dessous comment le nombre d'opérations et le temps d'exécution se comparent si plusieurs grands systèmes linéaires (partageant tous la même matrice) sont résolus en s'appuyant ou non sur la décomposition LU.\n",
"\n",
"**Exécutez la cellule suivante et évaluez les différences de performances ... cela peut prendre quelques secondes**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------------------------Décomposition LU--------------------------------\n",
- "Nombre d'opérations élémentaires (I, II, III): 499500\n",
- "Coût de la décomposition LU (nombre total d'additions, soustractions, multiplications et divisions): 667166500\n",
- "Temps d'exécution: 2.385104 s\n",
- "\n",
- "---------------------Résolution du système linéaire-------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"N = 1000 # dimension of the linear systems\n",
"Nt = 5000 # number of linear systems to be solved\n",
"A = np.random.rand(N, N);\n",
- "start = time.time()\n",
+ "\n",
"print(\"----------------------------Décomposition LU--------------------------------\")\n",
+ "start = time.time()\n",
"L, U = al.LU_no_pivoting(A)\n",
"time_lu = time.time() - start\n",
"n_op_lu = 2/3*(N**3 - N)\n",
"n_op_no_lu = 0\n",
"print(\"Temps d'exécution: % f s\" %(time_lu))\n",
"\n",
"print(\"\\n---------------------Résolution du système linéaire-------------------------\")\n",
"# solve without using LU \n",
"start = time.time()\n",
"for cnt in range(Nt):\n",
" b = np.random.rand(N,1)\n",
" x = np.linalg.solve(A, b)\n",
" n_op_no_lu += N**3 # --> N^3 operations per cycle, according to Numpy/LAPACK documentation on benchmark cases\n",
"time_no_lu = time.time() - start\n",
"print(\"Sans décomposition LU: coût computationnelle: % e, temps d'exécution: % f s\" %(n_op_no_lu, time_no_lu))\n",
"\n",
"# solve using LU\n",
"start = time.time()\n",
"for cnt in range(Nt):\n",
" b = np.random.rand(N,1)\n",
" y = solve_triangular(L, b)\n",
" n_op_lu += 2*N**2 - N # computational cost of forward substitution\n",
" x = solve_triangular(U, y)\n",
" n_op_lu += 2*N**2 - N # computational cost of backward substitution\n",
"time_lu += time.time() - start\n",
"\n",
"print(\"Avec décomposition LU: coût computationnelle: % e, temps d'exécution: % f s\" %(n_op_lu, time_lu))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Vous pouvez comparer les temps d'exécution et le nombre d'opérations pour différentes tailles de matrice (c'est-à-dire changer le paramètre N) et pour un nombre différent de systèmes lineaires (c'est-à-dire changer le paramètre N_t)**"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Passez au notebook 2.11: Décomposition en blocs](2.11%20Décomposition%20en%20blocs.ipynb)"
]
}
],
"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.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 4 - Bases et dimension/4.1-2. D\303\251pendance et ind\303\251pendance lin\303\251aire.ipynb" "b/Chapitre 4 - Bases et dimension/4.1-2. D\303\251pendance et ind\303\251pendance lin\303\251aire.ipynb"
index 52b7b45..b4242c0 100644
--- "a/Chapitre 4 - Bases et dimension/4.1-2. D\303\251pendance et ind\303\251pendance lin\303\251aire.ipynb"
+++ "b/Chapitre 4 - Bases et dimension/4.1-2. D\303\251pendance et ind\303\251pendance lin\303\251aire.ipynb"
@@ -1,207 +1,214 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"jupyter": {
"outputs_hidden": false
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"data": {
"text/html": [
" \n",
" "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concepts-clés et théorie\n",
"\n",
"### DÉFINITION 1\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel et $S\\subset V$ une collection de vecteurs dans $V.$ On dit que $S$ est linéairement dépendante (ou liée) s'il existe des vecteurs distincts $v_1,\\ldots,v_r\\in S$ et des scalaires $\\lambda_1,\\ldots,\\lambda_r\\in \\mathbb{R}$ non tous nuls tels que $\\lambda_1v_1+\\cdots+\\lambda_rv_r=0.$ (Autrement dit, s'il existe une combinaison linéaire (non triviale) de vecteurs de $S$ qui se réduit au vecteur nul.) S'il n'existe pas de tels vecteurs dans $S,$ alors on dit que $S$ est linéairement indépendante (ou libre).\n",
"\n",
"### PROPOSITION 1\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel et $v_1,\\ldots,v_r\\in V$ des vecteurs de $V.$ Alors ces derniers sont linéairement dépendants si et seulement s'il existe $1\\leq i\\leq r$ tels que $v_i\\in \\mbox{Vect}(\\{v_1,\\ldots,v_{i-1},v_{i+1},\\ldots,v_r\\}),$ c'est-à-dire si et seulement si l'on peut exprimer un des vecteurs de la liste comme une combinaison linéaire des autres.\n",
"\n",
"### PROPOSITION 2\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel et $S\\subset V$ une famille libre de vecteurs dans $V.$ Alors tout sous-ensemble $T\\subset S$ est aussi libre.\n",
"\n",
"### PROPOSITION 3\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel et $S\\subset V$ une famille liée de vecteurs dans $V.$ Alors toute collection de vecteurs $T$ contenant $S$ est également liée."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 1\n",
"\n",
"Soient:\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 0 \\\\ 2 \\end{pmatrix} \\ \\ v_2 = \\begin{pmatrix} 0 \\\\ 1 \\\\ 0 \\end{pmatrix} \\ \\ v_3 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix}$$\n",
"\n",
"Trouver une combinaison linéaire telle que:\n",
"\n",
"$$\\lambda_1 v_1 + \\lambda_2 v_2 + \\lambda_3 v_3 = \\begin{pmatrix} 3 \\\\ 5 \\\\ 4 \\end{pmatrix}$$\n",
"\n",
"Entrez les coefficients $\\lambda_i$ dans le vecteur ci-dessous puis exécutez les deux cellules pour vérifier votre réponse."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"solution = [1, 0, 2] # Réponse à compléter\n",
"\n",
"ch4_1_2ex1(solution)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 2\n",
"\n",
"D'après les mêmes données que l'exercice 1, la collection des vecteurs $v_1$, $v_2$, $v_3$ et $\\begin{pmatrix} 3 \\\\ 0 \\\\ 4 \\end{pmatrix}$ est-elle liée ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_1_2ex2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 3\n",
"\n",
"Les collections de vecteurs suivantes sont-elles liées ?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### (a)\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix} \\ \\ v_2 = \\begin{pmatrix} 1 \\\\ 2 \\\\ 1 \\end{pmatrix} \\ \\ v_3 = \\begin{pmatrix} 0 \\\\ 1 \\\\ 0 \\end{pmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_1_2ex3a()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### (b)\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 0 \\\\ 4 \\end{pmatrix} \\ \\ v_2 = \\begin{pmatrix} 6 \\\\ 12 \\\\ 7 \\end{pmatrix} \\ \\ v_3 = \\begin{pmatrix} 0 \\\\ 9 \\\\ 7 \\end{pmatrix} \\ \\ v_4 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_1_2ex3b()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### (c)\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 0 \\\\ -1 \\end{pmatrix} \\ \\ v_2 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 0 \\end{pmatrix} \\ \\ v_3 = \\begin{pmatrix} 0 \\\\ 1 \\\\ -1 \\end{pmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_1_2ex3c()"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.3. Bases et dimension](./4.3.%20Bases%20et%20dimension.ipynb)"
+ ]
}
],
"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"
},
"pycharm": {
"stem_cell": {
"cell_type": "raw",
"metadata": {
"collapsed": false
},
"source": []
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 4 - Bases et dimension/4.11. Coordonn\303\251es par rapport \303\240 une base.ipynb" "b/Chapitre 4 - Bases et dimension/4.11. Coordonn\303\251es par rapport \303\240 une base.ipynb"
index df012d9..335d8a1 100644
--- "a/Chapitre 4 - Bases et dimension/4.11. Coordonn\303\251es par rapport \303\240 une base.ipynb"
+++ "b/Chapitre 4 - Bases et dimension/4.11. Coordonn\303\251es par rapport \303\240 une base.ipynb"
@@ -1,292 +1,305 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concept(s)-clé(s)\n",
"\n",
- "### Définition 1 :\n",
- "Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie $n$, $\\mathscr{B} =\\{v_1,\\ldots,v_n\\}$ une base ordoné de $V$ et $v \\in V$. Comme $\\mathscr{B}$ est une base de $V$, il existe des uniques scalaires $\\alpha_1,\\ldots,\\alpha_n \\in \\mathbb{R}$ tels que $v = \\alpha_1v_1 + \\ldots + \\alpha_nv_n$. On appelle $\\alpha_1,\\ldots,\\alpha_n$ les coordonnées de $v$ par rapport à la base $\\mathscr{B}$ et on écrit\n",
+ "### DÉFINITION 1 :\n",
+ "Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie $n, \\mathscr{B}=\\left(v_{1}, \\ldots, v_{n}\\right)$ une base ordonnée de $V$ et $v \\in V$. Comme $\\mathscr{B}$ est une base de $V,$ il existe des uniques scalaires $\\alpha_{1}, \\ldots, \\alpha_{n} \\in \\mathbb{R}$ tels que $v=\\alpha_{1} v_{1}+\\cdots+\\alpha_{n} v_{n} .$ On appelle $\\alpha_{1}, \\ldots, \\alpha_{n}$ les coordonnées de $v$ par rapport $\\dot{a}$ la base $\\mathscr{B}$ et on écrit\n",
+ "$$\n",
+ "[v]_{\\mathscr{B}}=\\left(\\begin{array}{c}\n",
+ "\\alpha_{1} \\\\\n",
+ "\\alpha_{2} \\\\\n",
+ "\\vdots \\\\\n",
+ "\\alpha_{n}\n",
+ "\\end{array}\\right)\n",
+ "$$\n",
"\n",
- "\n",
- "$$[v]_{\\mathscr{B}} = \\begin{pmatrix} \\alpha_1 \\\\ \\alpha_2 \\\\ \\vdots \\\\ \\alpha_n \\end{pmatrix}.$$\n",
- "\n",
- "### Proposition 2 :\n",
- "Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie $n$ et $\\mathscr{B} = (v_1,\\ldots,v_n)$ une base ordonnée de $V$. Alors les deux affirmations suivantes sont vérifiées.\n",
- "\n",
- "1. Pour tous $v_1,v_2 \\in V$, on a $[v_1 + v_2 ]_{\\mathscr{B}} = [v_1]_{\\mathscr{B}}+[v_2]_{\\mathscr{B}}.$\n",
- "\n",
- "2. Pour tout $v \\in V$ et tout $\\lambda \\in \\mathbb{R}$, on a $[\\lambda v]_{\\mathscr{B}} = \\lambda[v]_{\\mathscr{B}}$"
+ "### PROPOSITION 2 :\n",
+ "Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie $n$ et $\\mathscr{B}=\\left(v_{1}, \\ldots, v_{n}\\right)$ une base ordonnée de $V$. Alors les deux affirmations suivantes sont vérifiées.\n",
+ "1. Pour tous $v_{1}, v_{2} \\in V,$ on a $\\left[v_{1}+v_{2}\\right]_{\\mathscr{B}}=\\left[v_{1}\\right]_{\\mathscr{B}}+\\left[v_{2}\\right]_{\\mathscr{B}}$\n",
+ "2. Pour tout $v \\in V$ et tout $\\lambda \\in \\mathbb{R},$ on a $[\\lambda v]_{\\mathscr{B}}=\\lambda[v]_{\\mathscr{B}}$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 1:\n",
"\n",
- "Soit $\\mathbb{R}^2$ l'espace du plan. La base la plus évidnte de cet espace vectoriel est la base orthonormée $\\mathscr{B} = ((1,0),(0,1))$. Cette base est représentée dans le graphique suivant."
+ "Soit $\\mathbb{R}^2$ l'espace du plan. La base la plus évidente de cet espace vectoriel est la base canonique $\\mathscr{B} = \\left\\{\\left(\\begin{array}{l}1 \\\\ 0 \\end{array}\\right), \\left(\\begin{array}{l}0 \\\\ 1 \\end{array}\\right)\\right\\}$. Cette base est représentée dans le graphique suivant."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "#ici un graph pas distordu (graph 1)"
+ "ch4_11_plot(show_can_base=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Quelle base de $\\mathbb{R}^2$ est représentée ci-dessous?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "#ici un graph avec grille distordue (graph 2)"
+ "ch4_11ex1_plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les vecteurs qui forment la base réprésentée ci-dessus\n",
"# Par exemple : [[1, 2], [3, 4]]\n",
- "base = [[-2,1],[4,1]]\n",
+ "base = [[0, 0],[0, 0]]\n",
"\n",
"ch4_11ex1(base)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 2\n",
"\n",
- "a) Soit le vecteur $\\textbf{v}$ représenté dans le graph suivant, $\\mathscr{B}_1$ la base orthonormée de $\\mathbb{R}^2$ et $\\mathscr{B}_2 = ((1,2),(3,2))$. Quelles sont les coordonnées de de $\\textbf{v}$ dans $\\mathscr{B}_1$? "
+ "a) Soit le vecteur $\\textbf{v}$ représenté dans le graph suivant, $\\mathscr{B}_1$ la base canonique de $\\mathbb{R}^2$ et $\\mathscr{B}_2 = \\left\\{\\left(\\begin{array}{l}1 \\\\ 2 \\end{array}\\right),\\left(\\begin{array}{l}3 \\\\ 2 \\end{array}\\right)\\right\\}$. Quelles sont les coordonnées de de $\\textbf{v}$ dans $\\mathscr{B}_1$? "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "#ici graph avec v desssus (graph 3)"
+ "#ici graph avec v desssus (graph 3)\n",
+ "ch4_11ex2a_plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur v dans la base B1\n",
"# Par exemple : [1,2]\n",
- "vB1 = [5,6]\n",
+ "vB1 = [0, 0]\n",
"\n",
"ch4_11ex2aB1(vB1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Et dans $\\mathscr{B}_2$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur v dans la base B2\n",
"# Par exemple : [1,2]\n",
- "vB2 = [2,1]\n",
+ "vB2 = [0, 0]\n",
"\n",
"ch4_11ex2aB2(vB2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"b) Avec les mêmes bases, quelles sont les coordonnées du vecteur $\\textbf{u}$ dans $\\mathscr{B}_1$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "#ici graph avec u desssus (graph 4)"
+ "#ici graph avec u desssus (graph 4)\n",
+ "ch4_11ex2b_plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur u dans la base B1\n",
"# Par exemple : [1,2]\n",
- "uB1 = [8,4]\n",
+ "uB1 = [0, 0]\n",
"\n",
"ch4_11ex2bB1(uB1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Et dans $\\mathscr{B}_2$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur u dans la base B2\n",
"# Par exemple : [1,2]\n",
- "uB2 = [-1,3]\n",
+ "uB2 = [0, 0]\n",
"\n",
"ch4_11ex2bB2(uB2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"c) Toujours avec les mêmes bases, quelles sont les coordonnées du vecteur $\\textbf{w}$ dans $\\mathscr{B}_1$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "#ici graph avec w desssus (graph 5)"
+ "#ici graph avec w desssus (graph 5)\n",
+ "ch4_11ex2c_plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur w dans la base B1\n",
"# Par exemple : [1,2]\n",
- "wB1 = [5,4]\n",
+ "wB1 = [0, 0]\n",
"\n",
"ch4_11ex2cB1(wB1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Et dans $\\mathscr{B}_2$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur w dans la base B2\n",
"# Par exemple : [1,2]\n",
- "wB2 = [.5,1.5]\n",
+ "wB2 = [0, 0]\n",
"\n",
"ch4_11ex2cB2(wB2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 3\n",
"\n",
- "Considérons maintenant le vecteur $\\textbf{s}$ tel que $\\textbf{s} = \\textbf{v}+\\textbf{u}$. A l'aide de la proposition 2 et de l'exerciceprécédent, déduire les coordonnées de $\\textbf{s}$ dans $\\mathscr{B}_1$."
+ "Considérons maintenant le vecteur $\\textbf{s}$ tel que $\\textbf{s} = \\textbf{v}+\\textbf{u}$. A l'aide de la proposition 2 et de l'exercice précédent, déduire les coordonnées de $\\textbf{s}$ dans $\\mathscr{B}_1$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur s dans la base B1\n",
"# Par exemple : [1,2]\n",
- "sB1 = [13,10]\n",
+ "sB1 = [0, 0]\n",
"\n",
"ch4_11ex3B1(sB1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Et dans $\\mathscr{B}_2$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les coordonnées du vecteur s dans la base B2\n",
"# Par exemple : [1,2]\n",
- "sB2 = [1,4]\n",
+ "sB2 = [0, 0]\n",
"\n",
"ch4_11ex3B2(sB2)"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.12. Trouver une base à partir d'un système générateur](./4.12.%20Trouver%20une%20base%20%C3%A0%20partir%20d'un%20syst%C3%A8me%20g%C3%A9n%C3%A9rateur.ipynb)"
+ ]
}
],
"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.6.8"
+ "version": "3.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 4 - Bases et dimension/4.12. Trouver une base \303\240 partir d'un syst\303\250me g\303\251n\303\251rateur.ipynb" "b/Chapitre 4 - Bases et dimension/4.12. Trouver une base \303\240 partir d'un syst\303\250me g\303\251n\303\251rateur.ipynb"
index a078e1f..8e406ee 100644
--- "a/Chapitre 4 - Bases et dimension/4.12. Trouver une base \303\240 partir d'un syst\303\250me g\303\251n\303\251rateur.ipynb"
+++ "b/Chapitre 4 - Bases et dimension/4.12. Trouver une base \303\240 partir d'un syst\303\250me g\303\251n\303\251rateur.ipynb"
@@ -1,135 +1,135 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concept(s)-clé(s)\n",
"\n",
"\n",
"\n",
"### MÉTHODE POUR TROUVER UNE BASE À PARTIR D'UN SYSTÈME DE GÉNÉRATEURS :\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie $n,$ $\\mathscr{B}=(v_1,\\ldots,v_n)$ une base de $V,$ $S\\subset V$ une partie finie et $W=\\mbox{Vect}(S).$ Pour trouver une base de $W$ et la compléter en une base de $V,$ on procède comme suit.\n",
"\n",
"\n",
"\n",
"1. Pour chaque $v\\in S,$ on écrit $[v]_{\\mathscr{B}} = \\begin{pmatrix} \\alpha_1 \\ \\alpha_2 \\ \\cdots \\ \\alpha_n \\end{pmatrix}^T.$\n",
"\n",
"2. On définit la matrice $A$ dont les lignes sont les vecteurs $[v]_{\\mathscr{B}}^T$ ($v\\in S$).\n",
"\n",
"3. On échelonne la matrice $A$ : les lignes non-nulles ainsi obtenues forment une base de l'espace ligne de cette matrice. De plus, les vecteurs de $W$ correspondants forment une base de $W.$\n",
"\n",
"4. On remplace les lignes nulles de la matrice échelonnée par des lignes non-nulles de manière à ce que celle-ci contienne $n$ pivots. Les vecteurs de $V$ associés aux lignes de cette nouvelle matrice forment une base de $V.$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 1\n",
"\n",
"Soit $S = (v_1,v_2,v_3,v_4,v_5) \\in \\mathbb{R}^6$ et $W=Vect(S)$. Selon la méthode vue en cours, quelles sont les dimensions de la matrice $A$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_12ex1()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 2\n",
"\n",
"Sachant que $v_2 = 2v_3+3v_5$, combien faut-il rajouter, au minimum, de lignes non-nulles à la matrice $A$, dans sa forme échelonnée, pour que les vecteurs associés aux lignes de celle-ci forment une base de $\\mathbb{R}^6$?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_12ex2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercice 3\n",
"\n",
"Voici la matrice $A$ échelonnée, nommée $B$:\n",
"\n",
"$$B = \\begin{pmatrix} 1 & 4 & 0 & 2 & 0 & 1 \\\\ 0 & 0 & 0 & 0 & 0 & 0 \\\\ 0 & 1 & 1 & 2 & 1 & 0 \\\\ 0 & 0 & 1 & 3 & 3 & 2 \\\\ 0 & 0 & 0 & 0 & 0 & 0\\end{pmatrix}$$\n",
"\n",
"a) Combien de vecteurs doivent être ajoutés aux lignes non-nulles de $B$ pour que celles-co soient une base de $\\mathbb{R}^6$ ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_12ex3a()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"b) Donner une collection de vecteurs qui complètent les lignes non-nulles de $B$ en une base de $\\mathbb{R}^6$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les vecteurs qui complètent les lignes non-nulles de B en une base de R^6\n",
"# Par exemple : [[1, 2], [3, 4]]\n",
- "vecteurs_manquants = [[0,0,0,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]]\n",
+ "vecteurs_manquants = [[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]]\n",
"\n",
"ch4_12ex3b(vecteurs_manquants)"
]
}
],
"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.6.8"
+ "version": "3.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git a/Chapitre 4 - Bases et dimension/4.3. Bases et dimension.ipynb b/Chapitre 4 - Bases et dimension/4.3. Bases et dimension.ipynb
index 1c3d445..ee91912 100644
--- a/Chapitre 4 - Bases et dimension/4.3. Bases et dimension.ipynb
+++ b/Chapitre 4 - Bases et dimension/4.3. Bases et dimension.ipynb
@@ -1,183 +1,190 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"# Concepts-clés et théorie\n",
"\n",
"### DÉFINITION 1 :\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel et $\\mathscr{B}\\subset V$ un ensemble de vecteurs de $V.$ On dit que $\\mathscr{B}$ est une *base* $V$ si les deux conditions suivantes sont vérifiées.\n",
"\n",
"1. Tout $v\\in V$ est une combinaison linaire de vecteurs de $\\mathscr{B},$ i.e. $\\mbox{Vect}\\mathscr{B}=V$ \n",
"\n",
"2. Le sous-ensemble $\\mathscr{B}$ est linéairement indépendant.\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 1\n",
"\n",
"Cochez votre réponse puis cliquez sur \"Vérifier\"."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. L'ensemble $\\begin{Bmatrix}\\begin{pmatrix} 1 \\\\ 0 \\\\ 1 \\end{pmatrix}, \\begin{pmatrix} 0 \\\\ 2 \\\\ 0 \\end{pmatrix}, \\begin{pmatrix} 0 \\\\ 0 \\\\ 3 \\end{pmatrix}, \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix}\\end{Bmatrix}$ est une base de $\\mathbb{R}^3$ :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex1a()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. L'ensemble $\\begin{Bmatrix}\\begin{pmatrix} 1 \\\\ 0 \\\\ 1 \\end{pmatrix}, \\begin{pmatrix} 0 \\\\ 1 \\\\ 0 \\end{pmatrix}\\end{Bmatrix}$ est une base de $\\mathbb{R}^3$ :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex1b()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. L'ensemble $\\begin{Bmatrix}\\begin{pmatrix} 1 \\\\ -2 \\\\ 4 \\end{pmatrix}, \\begin{pmatrix} 7 \\\\ -1 \\\\ 2 \\end{pmatrix}, \\begin{pmatrix} 2 \\\\ 5 \\\\ -3 \\end{pmatrix}\\end{Bmatrix}$ est une base de $\\mathbb{R}^3$ :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex1c()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### DÉFINITION 2 :\n",
"\n",
"On dit d'un $\\mathbb{R}$-espace vectoriel $V$ qu'il est *de dimension finie* s'il possède une base finie. Sinon, on dit que $V$ est *de dimension infinie*.\n",
"\n",
"\n",
"### THÉORÈME 3 :\n",
"Soit $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie. Alors toutes les bases de $V$ sont finies et possèdent le mème nombre d'éléments."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 2\n",
"\n",
"Sélectionnez un ensemble de vecteurs parmi la liste suivante tel que cet ensemble forme une base de $\\mathbb{R}^3$ :\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix},\\ v_2 = \\begin{pmatrix} 0 \\\\ 1 \\\\ 2 \\end{pmatrix},\\ v_3 = \\begin{pmatrix} 2 \\\\ 1 \\\\ 4 \\end{pmatrix},\\ v_4 = \\begin{pmatrix} 2 \\\\ 1 \\\\ 0 \\end{pmatrix},\\ v_5 = \\begin{pmatrix} 1 \\\\ 0 \\\\ -1 \\end{pmatrix}$$\n",
"\n",
"(maintenez CTRL pour sélectionner plusieurs cellules à la fois)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 3\n",
"\n",
"Soit $\\mathbb{P}^n(\\mathbb{R})$ l'ensemble des polynômes de degré $n$ à coefficients réels.\n",
"\n",
"- L'ensemble $\\{x, x^2, x^3, ..., x^{n}\\}$ est-il une base de $\\mathbb{P}^n(\\mathbb{R})$ ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex3()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 4\n",
"\n",
"Quelle est la dimension de l'espace engendré par l'ensemble de vecteurs suivant :\n",
"\n",
"$$\\begin{Bmatrix}\\begin{pmatrix} 1 & 1 \\\\ 0 & 0 \\end{pmatrix},\\ \\begin{pmatrix} 1 & 0 \\\\ 1 & 0 \\end{pmatrix},\\ \\begin{pmatrix} 0 & 0 \\\\ 1 & 1 \\end{pmatrix},\\ \\begin{pmatrix} 0 & 1 \\\\ 0 & 1 \\end{pmatrix},\\ \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix}\\end{Bmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_3ex4()"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.4-5. Dimension et base dans un espace de dimension connue](./4.4-5.%20Dimension%20et%20base%20dans%20un%20espace%20de%20dimension%20connue.ipynb)"
+ ]
}
],
"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": 4
}
diff --git a/Chapitre 4 - Bases et dimension/4.4-5. Dimension et base dans un espace de dimension connue.ipynb b/Chapitre 4 - Bases et dimension/4.4-5. Dimension et base dans un espace de dimension connue.ipynb
index ed0c9e0..2fbf53a 100644
--- a/Chapitre 4 - Bases et dimension/4.4-5. Dimension et base dans un espace de dimension connue.ipynb
+++ b/Chapitre 4 - Bases et dimension/4.4-5. Dimension et base dans un espace de dimension connue.ipynb
@@ -1,100 +1,107 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concepts-clés et théorie\n",
"\n",
"\n",
"\n",
"### DÉFINITION 1 :\n",
"\n",
"Soit $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie. Le nombre d'éléments dans une base s'appelle la *dimension* de $V$ et on le désigne par $\\mbox{dim} V.$\n",
"\n",
"### PROPOSITION 2 :\n",
"\n",
"Soit $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie. Alors les deux affirmations suivantes sont vérifiées.\n",
"\n",
"\n",
"\n",
"1. Si $\\{v_1,\\ldots,v_r\\}$ est un ensemble générateur de $V,$ alors il existe une base $\\mathscr{B}$ de $V$ telle que $\\mathscr{B}\\subset \\{v_1,\\ldots,v_r\\}.$ On parle d'*extraction de base*.\n",
"\n",
"2. Si $\\{v_1,\\ldots,v_r\\}$ est une partie libre de $V,$ alors il existe une base $\\mathscr{B}$ de $V$ telle que $\\{v_1,\\ldots,v_r\\}\\subset \\mathscr{B}.$ On parle de *complétion en une base*."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 1\n",
"\n",
"Extraire une base $\\mathscr{B}$ de $\\mathbb{R}^3$ à partir des vecteurs suivants :\n",
"\n",
"$$v_1 = \\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \\end{pmatrix},\\ v_2 = \\begin{pmatrix} 2 \\\\ 0 \\\\ 3 \\end{pmatrix},\\ v_3 = \\begin{pmatrix} 4 \\\\ 0 \\\\ 0 \\end{pmatrix},\\ v_4 = \\begin{pmatrix} 1 \\\\ 0 \\\\ 0 \\end{pmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_4_5ex1()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 2\n",
"\n",
"Ajouter un vecteur à la liste suivante afin de former une base $\\mathscr{B}$ de $M_{2 \\times 2}(\\mathbb{R})$ :\n",
"\n",
"$$\\begin{Bmatrix}\\begin{pmatrix} 1 & 4 \\\\ 3 & 0 \\end{pmatrix}, \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix}, \\begin{pmatrix} 0 & 1 \\\\ 1 & 0 \\end{pmatrix}\\end{Bmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Complétez les valeurs puis exécutez la cellule\n",
"v = [[0, 0], [0, 0]]\n",
"\n",
"ch4_4_5ex2(v)"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.6. Systèmes homogènes et base de l'espace des solutions](./4.6.%20Syst%C3%A8mes%20homog%C3%A8nes%20et%20base%20de%20l'espace%20des%20solutions.ipynb)"
+ ]
}
],
"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": 4
}
diff --git "a/Chapitre 4 - Bases et dimension/4.6. Syst\303\250mes homog\303\250nes et base de l'espace des solutions.ipynb" "b/Chapitre 4 - Bases et dimension/4.6. Syst\303\250mes homog\303\250nes et base de l'espace des solutions.ipynb"
index 0830422..b9834e6 100644
--- "a/Chapitre 4 - Bases et dimension/4.6. Syst\303\250mes homog\303\250nes et base de l'espace des solutions.ipynb"
+++ "b/Chapitre 4 - Bases et dimension/4.6. Syst\303\250mes homog\303\250nes et base de l'espace des solutions.ipynb"
@@ -1,125 +1,133 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concepts-clés et théorie\n",
"\n",
"\n",
"\n",
"### RAPPEL 1 :\n",
"\n",
"Soient $A\\in M_{m \\times n}(\\mathbb{R})$ et $X = \\begin{pmatrix} x_1 \\; x_2 \\;\\cdots \\; x_n\\end{pmatrix}^T,$ où $x_1,\\ldots,x_n$ sont des inconnues réelles. Alors l'ensemble des solutions du système linéaire $AX=0$ est un sous-espace vectoriel de $\\mathbb{R}^n.$\n",
"\n",
"### PROPOSITION 1 :\n",
"\n",
"Soient $A$ et $X$ comme ci-dessus. Alors la dimension de l'espace des solutions du système $AX=0$ est égale au nombre de variable(s) libre(s) dans une forme échelonnée de $A.$\n",
"\n",
"\n",
"\n",
"### PROPOSITION 2 :\n",
"\n",
"Soient $A$ et $X$ comme ci-dessus. Pour trouver une base de l'espace des solutions du système $AX=b,$ on pose successivement une des variables libre égale à $1$ et toutes les autres égales à $0.$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 1\n",
"\n",
"_Dimension maximale de l'espace des solutions_\n",
"\n",
"Soit une matrice $A$ à $4$ lignes et $6$ colonnes non nulles. Quelle est la dimension maximale de l'espace des solutions du système $AX = 0$ ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_6ex1()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 2\n",
"\n",
"Soit la matrice $A$ avec les coefficients suivants :\n",
"\n",
"$$A = \\begin{pmatrix} 1 & 3 & 5 & 1 & 1 \\\\ -3 & -8 & -13 & 1 & 0 \\\\ 0 & -1 & -1 & -1 & -1 \\\\ 2 & 6 & 12 & 8 & 6 \\end{pmatrix}$$\n",
"\n",
"A l'aide de sa forme échelonnée (donnée ci-dessous), indiquer la dimension de l'espace des solutions du système homogène $AX = 0$.\n",
"\n",
"$$A' = \\begin{pmatrix} 1 & 3 & 5 & 1 & 1 \\\\ 0 & 1 & 2 & 4 & 3 \\\\ 0 & 0 & 1 & 3 & 2 \\\\ 0 & 0 & 0 & 0 & 0 \\end{pmatrix}$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_6ex2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 3\n",
"\n",
"_Base de l'espace des solutions_\n",
"\n",
"A l'aide de de l'énoncé et de la réponse de l'exercice 2 et de la proposition 2, donnez une base de l'espace des solutions du système homogène $AX = 0$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Entrez les vecteurs qui formeront une base de la solution\n",
"# Par exemple : [[1, 2], [3, 4]]\n",
"base = []\n",
"\n",
"ch4_6ex3(base)"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.7-8. Dimension d'un sous-espace et d'une somme de sous-espaces\n",
+ "](./4.7-8.%20Dimension%20d'un%20sous-espace%20et%20d'une%20somme%20de%20sous-espaces.ipynb)"
+ ]
}
],
"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": 4
}
diff --git a/Chapitre 4 - Bases et dimension/4.7-8. Dimension d'un sous-espace et d'une somme de sous-espaces.ipynb b/Chapitre 4 - Bases et dimension/4.7-8. Dimension d'un sous-espace et d'une somme de sous-espaces.ipynb
index 0203365..6612950 100644
--- a/Chapitre 4 - Bases et dimension/4.7-8. Dimension d'un sous-espace et d'une somme de sous-espaces.ipynb
+++ b/Chapitre 4 - Bases et dimension/4.7-8. Dimension d'un sous-espace et d'une somme de sous-espaces.ipynb
@@ -1,226 +1,234 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from Ch4Functions import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Concepts-clés et théorie\n",
"\n",
"\n",
"\n",
"### THÉORÈME 1 :\n",
"\n",
"Soient $V$ un $\\mathbb{R}$-espace vectoriel de dimension finie et $W$ un sous-espace vectoriel de $V.$ Alors les affirmations suivantes sont vérifiées.\n",
"\n",
"\n",
"\n",
"1. Le sous-espace vectoriel $W$ est de dimension finie.\n",
"\n",
"2. La dimension de $W$ satisfait $\\dim W\\leq \\dim V$.\n",
"\n",
"3. Si $\\dim W=\\dim V,$ alors $W=V$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 1\n",
"\n",
"Soit un système de $3$ équations à $5$ inconnues.\n",
"\n",
"Quelles sont les dimensions possibles pour l'espace des solutions ? Cochez toutes les réponses valides (maintenez CTRL pour en sélectionner plusieurs)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex1()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 2\n",
"\n",
"Soit $S_1$ un système de $2$ équations à $3$ inconnues et dont l'espace de solution forme un plan.\n",
"\n",
"On définit $S_2$ comme étant le système $S_1$ privé d'une équation.\n",
"\n",
"Quelle est la dimension de l'espace des solutions de $S_2$ ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 3\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Remarquez la propriété suivante :\n",
"\n",
"- $\\dim(A + B) + \\dim(A \\cap B) = \\dim(A) + \\dim(B)$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3_venn()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Donner dans chaque cas la dimension de la somme des deux espaces vectoriels ; appuyez sur le bouton \"Visualiser\" pour vous aider :"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(a) $\\dim (W + V)$ avec $W$ un plan et $V$ une droite coplanaire à ce plan ; les deux passant par l'origine."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3a()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(b) $\\dim (W + V)$ avec $W$ un plan et $V$ une droite non coplanaire à ce plan ; les deux passant par l'origine."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3b()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(c) $\\dim (V + V)$ avec $V$ une droite passant par l'origine."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3c()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(d) $\\dim (V_1 + V_2)$ avec $V_1$ et $V_2$ deux droites distinctes passant par l'origine."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3d()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(e) $\\dim (W_1 + W_2)$ avec $W_1$ et $W_2$ deux plans distincts passant par l'origine."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3e()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(f) $\\dim (U + X)$ avec $U$ l'origine et $X$ l'espace $\\mathbb{R}^3$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ch4_7_8ex3f()"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Passez au notebook du chapitre 4.9. Rang-ligne et rang-colonne d'une matrice\n",
+ "](./4.9.%20Rang-ligne%20et%20rang-colonne%20d'une%20matrice.ipynb)"
+ ]
}
],
"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": 4
}
diff --git a/Chapitre 4 - Bases et dimension/Ch4Functions.py b/Chapitre 4 - Bases et dimension/Ch4Functions.py
index d6f65e2..789a587 100644
--- a/Chapitre 4 - Bases et dimension/Ch4Functions.py
+++ b/Chapitre 4 - Bases et dimension/Ch4Functions.py
@@ -1,1252 +1,1404 @@
import sys
sys.path.insert(0,'..')
import Librairie.AL_Fct as al
sys.path.pop(0)
import numpy as np
from IPython.display import display, Markdown, Latex
import plotly.graph_objs as go
import plotly
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, interact_manual, Layout, VBox, HBox
import plotly.express as px
import sympy as sp
import matplotlib.pyplot as plt
from itertools import permutations
from IPython.display import display, Latex, Markdown
def ch4_1_2ex1(solution):
v = np.array([[1, 0, 2], [0, 1, 0], [1, 1, 1]]).transpose()
e = np.array([3, 5, 4])
s = np.array(solution)
r = v @ s
if np.allclose(e, r):
display(Markdown("**Correction:** C'est correct!"))
else:
display(Markdown("**Correction:** C'est incorrect car: $\lambda_1 v_1 + \lambda_2 v_2 + \lambda_3 v_3 = \\begin{pmatrix} %s \\\\ %s \\\\ %s \end{pmatrix} \\neq \\begin{pmatrix} %s \\\\ %s \\\\ %s \end{pmatrix}$" % (r[0], r[1], r[2], e[0], e[1], e[2])))
w = s * v
cumsum = np.cumsum(np.insert(w, 0, 0, axis=1), axis=1).transpose()
colors = px.colors.qualitative.Plotly
global color_index
color_index = 0
data = []
def addVector(start, v):
global color_index
color = colors[color_index]
color_index = (color_index + 1) % len(colors)
end = start + v
trace = go.Scatter3d(
x=[start[0], end[0], None],
y=[start[1], end[1], None],
z=[start[2], end[2], None],
mode='lines',
name=str(v),
line=dict(color=color, width=4)
)
norm = np.sqrt(np.sum(v * v))
n = v if norm == 0 else v / norm
n = 1.5 * n
c_end = end - 0.37 * n
cone = go.Cone(x=[c_end[0]], y=[c_end[1]], z=[c_end[2]], u=[n[0]], v=[n[1]], w=[n[2]], name=str(v), colorscale=[[0, color], [1, color]], hoverinfo="none", showscale=False)
data.append(trace)
data.append(cone)
addVector(np.zeros(3), e)
for i in range(len(cumsum) - 1):
start = cumsum[i]
v = cumsum[i + 1] - start
addVector(start, v)
fig = go.Figure(data=data)
fig.show()
def ch4_1_2ex2():
radio = widgets.RadioButtons(
options=['Oui, les vecteurs sont dépendants', 'Non, les vecteurs sont indépendants'],
layout={'width': 'max-content'},
value=None,
description='Réponse:',
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
display(radio)
display(button)
display(out)
def verification_2(e):
if radio.value is not None:
out.clear_output()
with out:
if radio.value.startswith('Non'):
display(Markdown("C'est incorrect, il existe $\lambda_1$, $\lambda_2$ et $\lambda_3$ tels que $\lambda_1 v_1 + \lambda_2 v_2 + \lambda_3 v_3 - \\begin{pmatrix} 3 \\\\ 0 \\\\ 4 \end{pmatrix} = 0$."))
else:
display(Markdown("C'est correct!"))
button.on_click(verification_2)
def ch4_1_2ex3(answer):
radio = widgets.RadioButtons(
options=['Oui, les vecteurs sont dépendants', 'Non, les vecteurs sont indépendants'],
layout={'width': 'max-content'},
value=None,
description='Réponse:',
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
display(radio)
display(button)
display(out)
def verification_3(e):
if radio.value is not None:
out.clear_output()
with out:
if radio.value.startswith('Oui') == answer:
display(Markdown("C'est correct!"))
else:
display(Markdown("C'est incorrect!"))
button.on_click(verification_3)
def ch4_1_2ex3a():
ch4_1_2ex3(True)
def ch4_1_2ex3b():
ch4_1_2ex3(True)
def ch4_1_2ex3c():
ch4_1_2ex3(False)
def ch4_3ex1(answer, reason, callback, options=['Oui, les vecteurs forment une base', 'Non, les vecteurs ne forment pas une base']):
radio = widgets.RadioButtons(
options=options,
layout={'width': 'max-content'},
value=None,
description='Réponse:',
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
display(radio)
display(button)
display(out)
def verification(e):
if radio.value is not None:
out.clear_output()
with out:
if options.index(radio.value) == answer:
display(Markdown("C'est correct! %s" % reason))
else:
display(Markdown("C'est incorrect! %s" % reason))
callback()
button.on_click(verification)
def plot_vectors(vectors, selected, solution):
v = np.array(vectors).transpose()
e = np.array(selected)
s = np.array(solution)
r = v @ s
w = s * v
cumsum = np.cumsum(np.insert(w, 0, 0, axis=1), axis=1).transpose()
colors = px.colors.qualitative.Plotly
global color_index
color_index = 0
data = []
def addVector(start, v):
global color_index
color = colors[color_index]
color_index = (color_index + 1) % len(colors)
end = start + v
trace = go.Scatter3d(
x=[start[0], end[0], None],
y=[start[1], end[1], None],
z=[start[2], end[2], None],
mode='lines',
name=str(v),
line=dict(color=color, width=4)
)
norm = np.sqrt(np.sum(v * v))
n = v if norm == 0 else v / norm
n = 0.5 * n
c_end = end - 0.37 * n
cone = go.Cone(x=[c_end[0]], y=[c_end[1]], z=[c_end[2]], u=[n[0]], v=[n[1]], w=[n[2]], name=str(v), colorscale=[[0, color], [1, color]], hoverinfo="none", showscale=False)
data.append(trace)
data.append(cone)
addVector(np.zeros(3), e)
for i in range(len(cumsum) - 1):
start = cumsum[i]
v = cumsum[i + 1] - start
addVector(start, v)
fig = go.Figure(data=data)
fig.show()
def ch4_3ex1a():
ch4_3ex1(1, """
En effet, les quatres vecteurs ne sont pas linéairement indépendants (Déf. 1-2) car il est possible d'exprimer l'un avec une combinaison linéaire des autres. Par exemple :
$$\\begin{pmatrix} 1 \\\\ 1 \\\\ 1 \end{pmatrix} = \\begin{pmatrix} 1 \\\\ 0 \\\\ 1 \end{pmatrix} + \\frac{1}{2} \\begin{pmatrix} 0 \\\\ 2 \\\\ 0 \end{pmatrix} + 0 \\begin{pmatrix} 0 \\\\ 0 \\\\ 3 \end{pmatrix}$$
Comme tous les vecteurs sont issus de $\mathbb{R}^3$ on peut facilement représenter la combinaison dans l'espace :
""", lambda: plot_vectors([[1, 0, 1], [0, 2, 0], [0, 0, 3]], [1, 1, 1], [1, 0.5, 0]))
def ch4_3ex1b():
ch4_3ex1(1, """
On ne peut pas générer $\mathbb{R}^3$ à partir de cet ensemble. En effet, on ne peut par exemple pas obtenir le vecteur $\\begin{pmatrix} 1 \\\\ 0 \\\\ 0 \end{pmatrix} \\in \mathbb{R}^3$ avec une combinaison linéaire de $\\begin{pmatrix} 1 \\\\ 0 \\\\ 1 \end{pmatrix}$ et $\\begin{pmatrix} 0 \\\\ 1 \\\\ 0 \end{pmatrix}$.
Graphiquement les deux vecteurs ne peuvent engendrer qu'un plan:
""", lambda: plot_vectors([[1, 0, 0]], [0, 1, 0], [1]))
def ch4_3ex1c():
ch4_3ex1(0, """
Ces trois vecteurs sont linéairement indépendants, ils engendrent donc $\mathbb{R}^3$ et forment une base de cet espace.
""", lambda: None)
def ch4_3ex2():
vecs = np.array([[1, 1, 1], [0, 1, 2], [2, 1, 4], [2, 1, 0], [1, 0, -1]])
select = widgets.SelectMultiple(
options=['v1', 'v2', 'v3', 'v4', 'v5'],
description='Sélection :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
answer = [int(v[1:])-1 for v in select.value]
out.clear_output()
with out:
if len(answer) == 0: # Empty
pass
elif len(answer) < 3:
display(Markdown("C'est incorrect! La solution entrée ne permet pas d'engendrer $\mathbb{R}^3$."))
elif len(answer) > 3:
display(Markdown("C'est incorrect! La solution entrée contient des vecteurs qui sont dépendants."))
else:
mat = np.array([vecs[answer[0]], vecs[answer[1]], vecs[answer[2]]]).transpose()
det = np.linalg.det(mat)
if det == 0:
display(Markdown("C'est incorrect! La solution entrée contient des vecteurs qui sont dépendants."))
else: # Correct
display(Markdown("C'est correct! Il s'agit d'_une_ base de $\mathbb{R}^3$."))
button.on_click(callback)
display(select)
display(button)
display(out)
def ch4_3ex3():
ch4_3ex1(1, """
Cet ensemble n'est pas une base de $\mathbb{P}^n(\mathbb{R})$ car il ne permet pas (par exemple) de générer les polynômes constants.
$\mathbb{P}^n(\mathbb{R})$ est néanmoins engendré par $\{1, x, x^2, x^3, ..., x^{n-1}\}$.
""", lambda: None)
def ch4_3ex4():
ch4_3ex1(2, """
L'espace engendré par cet ensemble est $M_{2 \\times 2}(\mathbb{R})$ et donc sa dimension est $4$.
Attention cet ensemble n'est pas une base de $M_{2 \\times 2}(\mathbb{R})$ car ses éléments sont linéairement dépendants !
""", lambda: None, ['2', '3', '4', '5'])
def ch4_4_5ex1():
vs = np.array([[1, 1, 1], [2, 0, 3], [4, 0, 0], [1, 0, 0]])
select = widgets.SelectMultiple(
options=['v1', 'v2', 'v3', 'v4'],
description='Sélection :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
answer = [int(v[1:])-1 for v in select.value]
out.clear_output()
with out:
if len(answer) == 0: # Empty
pass
elif len(answer) < 3:
display(Markdown("C'est incorrect! La solution entrée ne permet pas d'engendrer $\\mathbb{R}^3$, et n'est donc pas une base."))
elif len(answer) > 3:
display(Markdown("C'est incorrect! La solution entrée engendre $\\mathbb{R}^3$ mais n'est pas une base."))
else:
mat = np.array([vs[answer[0]], vs[answer[1]], vs[answer[2]]]).transpose()
if np.linalg.matrix_rank(mat) != len(mat):
display(Markdown("C'est incorrect! La solution entrée ne permet pas d'engendrer $\\mathbb{R}^3$, et n'est donc pas une base."))
else: # Correct
display(Markdown("C'est correct! Il s'agit d'_une_ base de $\\mathbb{R}^3$."))
button.on_click(callback)
display(select)
display(button)
display(out)
def ch4_4_5ex2(v):
v = np.array(v)
vs = np.array([[1, 4, 3, 0], [1, 0, 0, 1], [0, 1, 1, 0], v.flatten()])
is_base = np.linalg.matrix_rank(vs) == len(vs)
out = widgets.Output()
display(out)
with out:
if is_base:
display(Markdown("C'est correct!"))
else:
display(Markdown("C'est incorrect, ce vecteur ne permet pas de former une base."))
def ch4_6ex1():
text = widgets.IntText(
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
feedback = ""
is_correct = False
if r == 6:
feedback = "Comme la matrice n'est pas nulle, au moins une variable n'est pas libre."
elif r >= 7:
feedback = "La dimension de l'espace des solutions ne peut excéder la dimension de l'espace des variables."
elif r == 5:
is_correct = True
feedback = "Le nombre maximal de variables libres dans ce système est $5$. Par la proposition 1 on en déduit la dimension maximale de l'espace des solutions du système homogène $AX = 0$."
elif r >= 2 and r <= 4:
feedback = "Ce n'est pas le nombre maximal de variables libres dans ce système."
elif r <= 1:
feedback = "Le nombre maximal de variables libres dans ce système ne peut être inférieur à $2$ ($\\text{nb. colonnes} - \\text{nb. lignes}$)."
correct_text = "C'est correct! "
incorrect_text = "C'est incorrect. "
with out:
display(Markdown((correct_text if is_correct else incorrect_text) + feedback))
button.on_click(callback)
display(text)
display(button)
display(out)
def ch4_6ex2():
text = widgets.IntText(
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
if r == 2:
display(Markdown("C'est correct! Le nombre de variables libres est $2$, par la proposition 1 on trouve la dimension de l'espace des solutions."))
else:
display(Markdown("C'est incorrect."))
button.on_click(callback)
display(text)
display(button)
display(out)
def ch4_7_8ex3_venn():
ax = plt.gca()
r = 5
rd = r / 2
rc = rd * 2
f = 25
a = plt.Circle((-rd, 0), radius=r, color="#fc032c90")
b = plt.Circle((rd, 0), radius=r, color="#0377fc90")
ax.add_patch(a)
ax.add_patch(b)
ax.annotate("A", xy=(-rc, 0), fontsize=f, ha='center', va='center')
ax.annotate("A ∩ B", xy=(0, 0), fontsize=f, ha='center', va='center')
ax.annotate("B", xy=(rc, 0), fontsize=f, ha='center', va='center')
plt.axis('scaled')
plt.axis('off')
plt.show()
def ch4_6ex3(base):
base = np.array(base)
out = widgets.Output()
display(out)
with out:
out.clear_output()
feedback = ""
is_correct = False
s = base.shape
if len(base) == 0:
feedback = "L'ensemble ne peut pas être vide."
elif len(s) != 2 or s[1] != 5:
feedback = "Le format des vecteurs n'est pas bon."
elif s[0] < 2:
feedback = "L'ensemble entré ne contient pas assez de vecteurs pour engendrer toutes les solutions du système."
elif s[0] > 2:
feedback = "L'ensemble entré n'est pas une base."
else:
expected = np.array(sp.Matrix([[6, 1, -2, 0, 1], [8, 2, -3, 1, 0]]).rref()[0])
actual = np.array(sp.Matrix(base).rref()[0])
if not np.array_equal(actual, expected):
feedback = "L'ensemble entré n'engendre pas l'espace solution du système."
else:
is_correct = True
correct_text = "C'est correct! "
incorrect_text = "C'est incorrect. "
display(Markdown((correct_text if is_correct else incorrect_text) + feedback))
def ch4_7_8ex1():
select = widgets.SelectMultiple(
options=['0', '1', '2', '3', '4', '5'],
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
display(select)
display(button)
display(out)
def verification(e):
if len(select.value) > 0:
out.clear_output()
with out:
other = " La dimension de l'espace des solutions dépend du nombre de variables libres : dans ce système celle-ci peut être au plus $5-3=2$."
if select.value != ('0', '1', '2'):
display(Markdown("C'est incorrect!" + other))
else:
display(Markdown("C'est correct." + other))
button.on_click(verification)
def ch4_7_8ex2():
text = widgets.IntText(description='Réponse :')
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
other = " En effet, comme la solution de $S_1$ est un plan les deux équations sont dépendantes. La solution de $S_2$ est donc aussi un plan, donc sa dimension est $2$."
if r == 2:
display(Markdown("C'est correct!" + other))
else:
display(Markdown("C'est incorrect." + other))
button.on_click(callback)
display(text)
display(button)
display(out)
def ch4_7_8ex3_cube(name, color):
return go.Mesh3d(
name=name,
x=[-1, -1, 1, 1, -1, -1, 1, 1],
y=[-1, 1, 1, -1, -1, 1, 1, -1],
z=[-1, -1, -1, -1, 1, 1, 1, 1],
i=[7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],
j=[3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],
k=[0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],
color=color,
opacity=0.25
)
def ch4_7_8ex3_plane(name, color, variant):
ep = 1.25
p1 = ep * np.array([-1, 1, 1, -1])
p2 = ep * np.array([-1, -1, 1, 1])
pz = np.array([0, 0, 0, 0] if variant == 0 else [0, 0.01, 0.02, 0.03]) # Bug workaround
ps = list(permutations([p1, p2, pz]))[variant]
return go.Mesh3d(
name=name,
x=ps[0],
y=ps[1],
z=ps[2],
color=color,
opacity=0.25
)
def ch4_7_8ex3_line(name, color, variant):
el = 1.5
l1 = [-el, el, None]
lz = [0, 0, None]
ps = list(permutations([l1, lz, lz]))[variant]
return go.Scatter3d(
name=name,
x=ps[0],
y=ps[1],
z=ps[2],
opacity=0.25,
line=dict(color=color, width=4)
)
def ch4_7_8ex3(answer, explanation, plot):
text = widgets.IntText(description='Réponse :')
button = widgets.Button(description='Vérifier')
show = widgets.Button(description='Visualiser')
box = widgets.HBox(children=[button, show])
out = widgets.Output()
out2 = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
other = " " + explanation
if r == answer:
display(Markdown("C'est correct!" + other))
else:
display(Markdown("C'est incorrect." + other))
def callback2(e):
with out2:
plot()
button.on_click(callback)
show.on_click(callback2)
display(text)
display(box)
display(out)
display(out2)
def ch4_7_8ex3a():
ch4_7_8ex3(2, "$V$ est un sous-ensemble de $W$, donc $\dim (W + V) = \dim W = 2$.", lambda: go.Figure(data=[ch4_7_8ex3_plane('A', 'red', 0), ch4_7_8ex3_line('B', 'blue', 0)]).show())
def ch4_7_8ex3b():
ch4_7_8ex3(3, "$W$ et $V$ sont deux espaces indépendants, donc $\dim (W + V) = \dim W + \dim V = 2 + 1 = 3$.", lambda: go.Figure(data=[ch4_7_8ex3_plane('A', 'red', 0), ch4_7_8ex3_line('B', 'blue', 3)]).show())
def ch4_7_8ex3c():
ch4_7_8ex3(1, "La somme d'un même espace n'a pas d'effet : $\dim (V + V) = \dim V = 1$.", lambda: go.Figure(data=[ch4_7_8ex3_line('A', 'red', 0), ch4_7_8ex3_line('B', 'blue', 0)]).show())
def ch4_7_8ex3d():
ch4_7_8ex3(2, "$V_1$ et $V_2$ sont deux espaces indépendants, donc $\dim (V_1 + V_2) = \dim V_1 + \dim V_2 = 1 + 1 = 2$.", lambda: go.Figure(data=[ch4_7_8ex3_line('A', 'red', 0), ch4_7_8ex3_line('B', 'blue', 2)]).show())
def ch4_7_8ex3e():
ch4_7_8ex3(3, "$\dim (W_1 + W_2) = 2 + 2 - 1 = 3$.", lambda: go.Figure(data=[ch4_7_8ex3_plane('A', 'red', 0), ch4_7_8ex3_plane('B', 'blue', 1)]).show())
def ch4_7_8ex3f():
ch4_7_8ex3(3, "$\dim (U + X) = 0 + 3 - 0 = 3$.", lambda: go.Figure(data=[ch4_7_8ex3_cube('B', 'blue')]).show())
def ch4_9ex1():
r = ['Le rang ligne de 𝐴 est plus petit ou égal à 2 car c\'est un sous-espace vectoriel de ℝ2.',
'Le rang ligne de 𝐴 est plus petit ou égal à 3 car c\'est un sous-espace vectoriel de ℝ3.',
'Le rang ligne de 𝐴 est plus petit ou égal à 3 car engendré par 3 vecteurs.',
'Le rang ligne de 𝐴 est plus petit ou égal à 2 car engendré par 2 vecteurs.',
'Le rang colonne de A est plus petit ou égal à 2.']
buttons = []
for i in range(5):
b = widgets.ToggleButton(
value=False,
description=r[i],
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Description',
layout=Layout(width='auto', height='auto')
)
buttons.append(b)
button = widgets.Button(description='Vérifier',
layout=Layout(width='auto', height='auto'),
button_style='info'
)
def callback(e):
out.clear_output()
with out:
if (buttons[0].value or buttons[2].value):
print('Mauvaise réponse. \nAttention à ne pas confondre les espaces des lignes et colonnes')
elif (not buttons[1].value) or (not buttons[3].value) or (not buttons[4].value):
print('Il manque au moins une réponse.')
elif (buttons[1].value and buttons[3].value and buttons[4].value):
print('Correct !')
buttons.append(button)
buttons[5].on_click(callback)
box = VBox(children = buttons)
out = widgets.Output()
display(box)
display(out)
def ch4_9ex2_1():
text = widgets.IntText(
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
if r == 2:
display(Markdown("Correct!"))
elif r > 2:
display(Markdown("Incorrect, le rang ligne est plus petit."))
else:
display(Markdown("Incorrect, le rang ligne est plus grand."))
button.on_click(callback)
display(text)
display(button)
display(out)
def ch4_9ex2_2():
options = ['E1', 'E2', 'E3', 'E4']
buttons = []
for i in range(4):
b = widgets.ToggleButton(
value=False,
description=options[i],
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Description'
)
buttons.append(b)
button = widgets.Button(description='Vérifier',
# layout=Layout(width='auto', height='auto'),
button_style='info'
)
out = widgets.Output()
def callback(e):
out.clear_output()
with out:
if buttons[1].value or buttons[2].value or buttons[3].value:
display(Markdown("Faux"))
elif buttons[0].value:
display(Markdown("Correct !"))
buttons.append(button)
buttons[4].on_click(callback)
box = VBox(children=buttons)
display(box)
display(out)
def ch4_9ex2_3():
text = widgets.IntText(description='Réponse :', disabled=False)
button = widgets.Button(description='Vérifier')
button2 = widgets.Button(description='Solution', disabled=True)
box = widgets.HBox(children=[button, button2])
out = widgets.Output()
def callback(e):
out.clear_output()
button2.disabled = False
with out:
if (text.value == 2):
print('Correct !')
else:
print('Faux, essayez encore ou regardez la solution.')
def solution(e):
out.clear_output()
with out:
A = np.array([[1, 2, 3], [0, 1, 2]])
A_t = A.transpose()
display(Markdown(
'Pour trouver le rang colonne de $A$, on utilise la remarque 1 et trouve le rang ligne de la transposée de $A$.'))
display(Markdown(
'Par les propositions 1 et 2, on échelonne la matrice transposée et on trouve le nombre de lignes contenant des pivots'))
M = al.echelonMat('E', A_t)
display(Markdown('Ainsi le rang colonne de $A$ est 2.'))
button.on_click(callback)
button2.on_click(solution)
display(text)
display(box)
display(out)
def ch4_10ex1_1(A, b):
A_sol = [[1, 4, 3, 4],[2, 6, 5, 8],[1, 0, 1, 4]]
b_sol = [[1], [1], [1]]
if A == [] or b == []:
print("Attention, vous avez laissé au moins une des deux entrée vide")
elif not (len(A) == len(b)):
print("Les tailles de la matrice et du vecteur ne correspondent pas")
else:
b = np.reshape(np.array(b), (len(b), 1)).tolist()
if A == A_sol:
if b == b_sol:
print("Correct !")
else:
print("Le vecteur b est faux, votre reponse correspond au système suivant:")
al.printSyst(A, b)
elif b == b_sol:
print("La Matrice A est fausse, votre reponse correspond au système suivant:")
al.printSyst(A, b)
else:
print("Faux, votre réponse correspond au système suivant:")
al.printSyst(A, b)
def ch4_10ex1_2_1_ech():
global m
display(Latex('Échelonnez la matrice transposée de A'))
A_sol = np.array([[1, 4, 3, 4], [2, 6, 5, 8], [1, 0, 1, 4]])
A_sol_t = A_sol.transpose()
al.printA(A_sol_t)
[i, j, r, alpha] = al.manualEch(A_sol_t)
MatriceList = [np.array(A_sol_t)]
m = A_sol_t
button = widgets.Button(description='Appliquer')
out = widgets.Output()
def applique(e):
global m
out.clear_output()
with out:
m = al.echelonnage(i, j, r, alpha, A_sol_t, m, MatriceList)
button.on_click(applique)
display(button)
display(out)
def f_sol():
al.echelonMat('E', A_sol.transpose())
display(Latex("Pour afficher la solution, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def ch4_10ex1_2_1():
text_rang = widgets.IntText()
box = widgets.VBox([widgets.Label('Rang colonne de A:'), text_rang])
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
with out:
if(text_rang.value == 2):
display(Latex('Correct !'))
else:
display(Latex('Faux, essayez encore ou regardez la solution.'))
button.on_click(callback)
display(box)
display(button)
display(out)
def f_sol():
display(Latex("Le rang colonne de $A$ correspond au nombre de pivot(s) de la forme échelonnée de $A^T$."))
display(Latex("Pour afficher la solution, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
def ch4_10ex1_2_2(base):
base_sol = [[1, 2, 1], [0, 1, 2]]
correct = check_basis(base_sol, base)
def f_sol():
display(Latex("Une base de l'espace ligne de $A$ est donnée par les lignes de la forme echelonnée contenant "
"un pivots. Pour trouver une base de l'espace colonne $A$, on utilise l'espace ligne de $A^T$."))
display(Latex("Une base de l'espace colone de $A$ est donc:"))
print(base_sol)
if base_sol == []:
display(Latex("L'entrée est vide"))
display(Latex("Pour afficher la solution, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
else:
if correct:
display(Latex("Correct !"))
if base_sol != base:
display(Latex("Pour la suite de l'exercice, on utilise la base equivalente suivante:"))
print(base_sol)
else:
display(Latex("Faux"))
display(Latex("Pour afficher la solution, cliquez-ici."))
im = interact_manual(f_sol)
im.widget.children[0].description = 'Solution'
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
"""
sol = np.array(sol, dtype=np.float64)
prop = np.array(prop, dtype=np.float64)
# number of vector in basis
n = len(sol)
# Check dimension
if n != len(prop):
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 ch4_10ex1_3_ech():
display(Latex("Echelonnez la matrice suivante."))
global m2
A = [[1, 2, 1], [0, 1, 2], [1, 1, 1]]
al.printA(A)
[i, j, r, alpha] = al.manualEch(A)
MatriceList = [np.array(A)]
m2 = A
button = widgets.Button(description='Appliquer')
out = widgets.Output()
def callback(e):
global m2
out.clear_output()
with out:
m2 = al.echelonnage(i, j, r, alpha, A, m2, MatriceList)
button.on_click(callback)
display(button)
display(out)
def ch4_10ex1_3():
display(Latex("Le système admet-il une solution ?"))
radio = widgets.RadioButtons(
options=['Oui', 'Non'],
description='Réponse:',
disabled=False
)
button = widgets.Button(description='Vérifier')
button2 = widgets.Button(description='Solution', disabled=True)
box = widgets.HBox(children=[button, button2])
out = widgets.Output()
def callback(e):
out.clear_output()
button2.disabled = False
with out:
if (radio.value == "Oui"):
print('Mauvaise réponse.')
else:
print('Correct !')
def solution(e):
out.clear_output()
with out:
A = np.array([[1, 2, 1], [0, 1, 2], [0, 0, 1]])
display(Markdown('Après échelonnage, la matrice devient'))
al.printA(A)
display(
Markdown("Ainsi le rang colonne de la matrice augmentée est 3, et le système n'admet pas de solution."))
button2.on_click(solution)
button.on_click(callback)
display(radio)
display(box)
display(out)
def ch4_10ex1_3_2_ech():
display(Latex("Echelonnez la matrice suivante."))
global m3
A = [[1, 4, 3, 4], [2, 6, 5, 8], [1, 0, 1, 4]]
b = [[1], [1], [1]]
al.printA(A, b)
[i, j, r, alpha] = al.manualEch(A, b)
MatriceList = [np.array(A)]
RHSList = [np.array(b)]
m3 = np.concatenate((A, b), axis=1)
button = widgets.Button(description='Appliquer')
out = widgets.Output()
def callback(e):
global m3
out.clear_output()
with out:
m3 = al.echelonnage(i, j, r, alpha, A, m3, MatriceList, RHSList)
button.on_click(callback)
display(button)
display(out)
def ch4_10ex2_1_ech():
global m21
A = [[1, 2, 1], [0, 1, 2], [2, 5, 4]]
al.printA(A)
[i, j, r, alpha] = al.manualEch(A)
MatriceList = [np.array(A)]
m21 = A
button = widgets.Button(description='Appliquer')
out = widgets.Output()
def callback(e):
global m21
out.clear_output()
with out:
m21 = al.echelonnage(i, j, r, alpha, A, m21, MatriceList)
button.on_click(callback)
display(button)
display(out)
def ch4_10ex2_2():
radio = widgets.RadioButtons(
options=['Oui', 'Non'],
description='Réponse:',
disabled=False
)
button = widgets.Button(description='Vérifier')
button2 = widgets.Button(description='Solution', disabled=True)
box = HBox(children=[button, button2])
out = widgets.Output()
def callback(e):
out.clear_output()
button2.disabled = False
with out:
if (radio.value == "Oui"):
print('Correct !')
else:
print('Mauvaise réponse.')
def solution(e):
out.clear_output()
with out:
A = np.array([[1, 2, 1], [0, 1, 2], [0, 0, 0]])
display(Markdown('Après échelonnage, la matrice devient'))
al.printA(A)
display(Markdown(
"Ainsi le rang colonne de la matrice augmentée est 2, et d'après le lemme, le système admet une solution."))
button2.on_click(solution)
button.on_click(callback)
display(radio)
display(box)
display(out)
+def ch4_11_plot(base=None, show_grid_lines=False, show_can_base=False, vector_list=[], vector_name_list=[]):
+
+ if base is not None:
+ x1 = np.array(base[0])
+ x2 = np.array(base[1])
+ n1 = 30
+ n2 = 20
+
+ fig = go.Figure()
+ fig.update_layout(xaxis=dict(range=[-8.5, 8.5], constrain="domain", ),
+ yaxis=dict(range=[-8.5, 8.5], scaleanchor="x", scaleratio=1), showlegend=True)
+ fig.update_xaxes(tick0=0, dtick=2)
+ fig.update_yaxes(tick0=0, dtick=2)
+
+ if show_grid_lines:
+ grid_x1_x = np.array([[-n1 * x1[0] + i * x2[0] for i in range(-n2, n2)],
+ [n1 * x1[0] + i * x2[0] for i in range(-n2, n2)]])
+ grid_x1_y = np.array([[-n1 * x1[1] + i * x2[1] for i in range(-n2, n2)],
+ [n1 * x1[1] + i * x2[1] for i in range(-n2, n2)]])
+
+ grid_x2_x = np.array([[-n1 * x2[0] + i * x1[0] for i in range(-n2, n2)],
+ [n1 * x2[0] + i * x1[0] for i in range(-n2, n2)]])
+ grid_x2_y = np.array([[-n1 * x2[1] + i * x1[1] for i in range(-n2, n2)],
+ [n1 * x2[1] + i * x1[1] for i in range(-n2, n2)]])
+
+ grid_lines = go.scatter.Line(color='grey', width=0.5)
+
+ for i in range(2 * n2):
+ fig.add_trace(go.Scatter(x=grid_x1_x[:, i], y=grid_x1_y[:, i], mode='lines', showlegend=False,
+ line=grid_lines))
+
+ fig.add_trace(go.Scatter(x=grid_x2_x[:, i], y=grid_x2_y[:, i], mode='lines', showlegend=False,
+ line=grid_lines))
+
+ if show_can_base:
+ fig.add_annotation(x=1, # arrows' head
+ y=0, # arrows' head
+ ax=0, # arrows' tail
+ ay=0, # arrows' tail
+ xref='x',
+ yref='y',
+ axref='x',
+ ayref='y',
+ text='', # if you want only the arrow
+ showarrow=True,
+ arrowhead=3,
+ arrowsize=2,
+ arrowwidth=1,
+ arrowcolor='black',
+ )
+
+ fig.add_annotation(x=1.3, y=0, text="$x_1$", showarrow=False)
+
+ fig.add_annotation(x=0, # arrows' head
+ y=1, # arrows' head
+ ax=0, # arrows' tail
+ ay=0, # arrows' tail
+ xref='x',
+ yref='y',
+ axref='x',
+ ayref='y',
+ text='', # if you want only the arrow
+ showarrow=True,
+ arrowhead=3,
+ arrowsize=2,
+ arrowwidth=1,
+ arrowcolor='black'
+ )
+ fig.add_annotation(x=0, y=1.3, text="$y_1$", showarrow=False)
+
+ if base is not None:
+
+ fig.add_annotation(x=base[0][0], # arrows' head
+ y=base[0][1], # arrows' head
+ ax=0, # arrows' tail
+ ay=0, # arrows' tail
+ xref='x',
+ yref='y',
+ axref='x',
+ ayref='y',
+ text='', # if you want only the arrow
+ showarrow=True,
+ arrowhead=3,
+ arrowsize=2,
+ arrowwidth=1,
+ arrowcolor='blue'
+ )
+ fig.add_annotation(x=base[0][0]+0.2, y=base[0][1]+0.2, text="$x_2$", showarrow=False)
+
+ fig.add_annotation(x=base[1][0], # arrows' head
+ y=base[1][1], # arrows' head
+ ax=0, # arrows' tail
+ ay=0, # arrows' tail
+ xref='x',
+ yref='y',
+ axref='x',
+ ayref='y',
+ text='', # if you want only the arrow
+ showarrow=True,
+ arrowhead=3,
+ arrowsize=2,
+ arrowwidth=1,
+ arrowcolor='blue'
+ )
+ fig.add_annotation(x=base[1][0]+0.2, y=base[1][1]+0.2, text="$y_2$", showarrow=False)
+
+ color_list = ['red', 'orange', 'grey', 'brown']
+ for idx, v in enumerate(vector_list):
+ fig.add_trace(go.Scatter(x=[0, v[0]], y=[0, v[1]], mode='lines + markers', showlegend=True,
+ name=vector_name_list[idx],
+ line=dict(color=color_list[idx]),
+ marker=dict(color=color_list[idx]),
+ ))
+
+ fig.show()
+
+ return
+
+
+def ch4_11ex1_plot():
+ ch4_11_plot(base=[[1, 2], [3, 2]], show_can_base=True, show_grid_lines=True, vector_list=[], vector_name_list=[])
+ return
+
+
def ch4_11ex1(base):
base_solution = [[1, 2], [3, 2]]
is_correct = all(item in base_solution for item in base)
if is_correct:
is_correct = all(item in base for item in base_solution)
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
+def ch4_11ex2a_plot():
+ ch4_11_plot(base=[[1, 2], [3, 2]], show_can_base=True, show_grid_lines=True, vector_list=[[5, 6]],
+ vector_name_list=['v'])
+ return
+
def ch4_11ex2aB1(vB1):
v = [5, 6]
if vB1 == v:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
def ch4_11ex2aB2(vB2):
v = [2, 1]
if vB2 == v:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
+def ch4_11ex2b_plot():
+ ch4_11_plot(base=[[1, 2], [3, 2]], show_can_base=True, show_grid_lines=True, vector_list=[[8, 4]],
+ vector_name_list=['u'])
+ return
+
+
def ch4_11ex2bB1(uB1):
u = [8, 4]
if uB1 == u:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
def ch4_11ex2bB2(uB2):
u = [-1, 3]
if uB2 == u:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
+def ch4_11ex2c_plot():
+ ch4_11_plot(base=[[1, 2], [3, 2]], show_can_base=True, show_grid_lines=True, vector_list=[[5, 4]],
+ vector_name_list=['w'])
+ return
+
+
def ch4_11ex2cB1(wB1):
w = [5, 4]
if wB1 == w:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
-
+
+
def ch4_11ex2cB2(wB2):
- w = [0.5,1.5]
+ w = [0.5, 1.5]
if wB2==w:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
-
+
+
def ch4_11ex3B1(sB1):
s = [13,10]
if sB1==s:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
+
def ch4_11ex3B2(sB2):
s = [1,4]
if sB2==s:
is_correct = 1
else:
is_correct = 0
correct_text = "C'est correct!"
incorrect_text = "C'est incorrect."
display(correct_text if is_correct else incorrect_text)
-
+
+
def ch4_12ex1():
+ style = {'description_width': 'initial'}
nbr_ligne = widgets.IntText(
description='Nombre de lignes : \n',
- disabled=False
+ disabled=False,
+ style=style
)
nbr_colonne = widgets.IntText(
- description='Nombre de colonne : \n',
- disabled=False
+ description='Nombre de colonnes : \n',
+ disabled=False,
+ style=style
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r_l = nbr_ligne.value
r_c = nbr_colonne.value
with out:
if r_l == 5 and r_c == 6:
display(Markdown("C'est correct!"))
else:
display(Markdown("C'est incorrect."))
button.on_click(callback)
display(nbr_ligne)
display(nbr_colonne)
display(button)
display(out)
+
def ch4_12ex2():
text = widgets.IntText(
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
if r == 2:
display(Markdown("C'est correct!"))
else:
display(Markdown("C'est incorrect."))
button.on_click(callback)
display(text)
display(button)
display(out)
+
def ch4_12ex3a():
text = widgets.IntText(
description='Réponse :',
disabled=False
)
button = widgets.Button(description='Vérifier')
out = widgets.Output()
def callback(e):
out.clear_output()
r = text.value
with out:
if r == 3:
display(Markdown("C'est correct!"))
else:
display(Markdown("C'est incorrect."))
button.on_click(callback)
display(text)
display(button)
display(out)
-
+
+
def ch4_12ex3b(base):
- base = base + [[1,4,0,2,0,1],[0,1,1,2,1,0],[0,0,1,3,3,2]]
+ base = base + [[1, 4, 0, 2, 0, 1], [0, 1, 1, 2, 1, 0], [0, 0, 1, 3, 3, 2]]
base = np.array(base)
out = widgets.Output()
display(out)
with out:
out.clear_output()
feedback = ""
is_correct = False
-
+
s = base.shape
-
if len(base) == 0:
feedback = "Les vecteurs nuls ne peuvent pas ."
elif len(s) != 2 or s[1] != 6:
feedback = "Le format des vecteurs n'est pas bon."
elif s[0] < 6:
feedback = "L'ensemble entré ne contient pas assez de vecteurs pour engendrer toutes les solutions du système."
elif s[0] > 6:
feedback = "L'ensemble entré contient trop de vecteurs pour être une famille libre."
else:
- expected = np.array(sp.Matrix([[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,1,0,0,0],[0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0],[0, 0, 0, 0, 0, 1]]).rref()[0])
+ expected = np.array(sp.Matrix(
+ [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1]]).rref()[0])
actual = np.array(sp.Matrix(base).rref()[0])
-
+
if not np.array_equal(actual, expected):
feedback = "L'ensemble entré n'engendre pas l'espace solution du système."
else:
is_correct = True
-
+
correct_text = "C'est correct! "
incorrect_text = "C'est incorrect. "
-
+
display(Markdown((correct_text if is_correct else incorrect_text) + feedback))
diff --git a/Chapitre 9 - Produits scalaires et espaces euclidens/9.10-9.11 La meilleure approximation quadratique.ipynb b/Chapitre 9 - Produits scalaires et espaces euclidens/9.10-9.11 La meilleure approximation quadratique.ipynb
index 006c6bc..ebe2504 100644
--- a/Chapitre 9 - Produits scalaires et espaces euclidens/9.10-9.11 La meilleure approximation quadratique.ipynb
+++ b/Chapitre 9 - Produits scalaires et espaces euclidens/9.10-9.11 La meilleure approximation quadratique.ipynb
@@ -1,296 +1,301 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Concept(s)-clé(s) et théorie**\n",
"\n",
"## Proposition 1\n",
"Soient $V$ un espace euclidien et $W \\subset V$ un sous-espace vectoriel de $V$. Alors pour tout $x \\in V$ et tout $y \\in W$, on a\n",
"\n",
"\\begin{equation}\n",
"||x - proj_W x|| \\leq ||x-y||\n",
"\\end{equation}\n",
"\n",
"## Définition 1\n",
"Soient $V$ un espace euclidien, $W \\subset V$ un sous-espace vectoriel de $V$ et $x \\in V$; considérez aussi le produit scalaire usuel. Alors le vecteur $proj_Wx$ est appelé la **meilleure approximation quadratique** (ou la **meilleure approximation au sens des moindres carrées**) **de $\\boldsymbol{x}$ par un vecteur dans $\\boldsymbol{W}$**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exercises et Examples"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import Librairie.AL_Fct as al\n",
"import Corrections.corrections as corrections\n",
"import numpy as np\n",
"import sympy as sp"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercice 1\n",
"\n",
"Soit $V = \\mathbb{R}^n$. Considérez les paires suivantes, faites par un ensemble de vecteurs $\\mathcal{S}$ générant un sous-espace vectoriel $W$ de $V$ et par un élément $v$ de $V$. Calculez la meilleure approximation au sens des moindres carrés de $v$ par un vecteur dans $W$.\n",
"\n",
"1. $V = \\mathbb{R}^2 \\qquad \\mathcal{S} = \\left\\{ \\begin{pmatrix}1 \\\\ -2\\end{pmatrix} \\right\\} \\qquad \\qquad \\quad \\ v = \\begin{pmatrix} -2 \\\\ 1 \\end{pmatrix}$\n",
"2. $V = \\mathbb{R}^3 \\qquad \\mathcal{S} = \\left\\{ \\begin{pmatrix}0 \\\\ 1 \\\\ 0\\end{pmatrix}, \\begin{pmatrix} 1 \\\\ -1 \\\\ 0 \\end{pmatrix} \\right\\} \\qquad \\qquad v = \\begin{pmatrix} -3 \\\\ 2 \\\\ 1 \\end{pmatrix}$\n",
"3. $V = \\mathbb{R}^4 \\qquad \\mathcal{S} = \\left\\{ \\begin{pmatrix}1 \\\\ 2 \\\\ -1 \\\\-2 \\end{pmatrix}, \\begin{pmatrix}0 \\\\ 1 \\\\ 0 \\\\-1 \\end{pmatrix} \\right\\} \\qquad \\quad \\ \\ \\ v = \\begin{pmatrix} 0 \\\\ -1 \\\\ 1 \\\\ -1\\end{pmatrix}$"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Aide\n",
+ "\n",
+ "Pour calculer la meiileure approximation quadratique de $v$ par un vecteur dans $W$,, il peut être utile de dériver une base ortogonale (ou orthonormée) pour ce dernier. Vous pouvez utiliser la cellule suivante pour exécuter l'algorithme interactif de Gram-Schmidt.\n",
+ "\n",
+ "#### Instructions\n",
+ "\n",
+ "Pour utiliser la méthode interactive de Gram-Schmidt, procédez comme suit:\n",
+ "\n",
+ "1. Insérez le numéro du cas souhaité dans la cellule suivante. Exécutez le cellules appelées \"SÉLECTION DU CAS\" et \"INITIALISATION DES VARIABLES\"\n",
+ "2. Exécutez la cellule appelée \"SÉLECTION DES PARAMÈTRES\" pour sélectionner le type d'opération et les coefficients nécessaires\n",
+ "3. Exécutez la cellule appelée \"EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\" pour exécuter l'étape de l'algorithme de Gram-Schmidt avec les paramètres précédemment sélectionnés\n",
+ "4. Répétez les étapes 2 et 3 jusqu'à ce que l'algorithme soit terminée\n",
+ "\n",
+ "En outre:\n",
+ "\n",
+ "1. Vous pouvez annuler une opération en sélectionnant le bouton \"Revert\".\n",
+ "\n",
+ "2. Si les coefficients insérés sont incorrects, vous pouvez essayer avec de nouvelles valeurs sans effectuer une opération \"Revert\".\n",
+ "\n",
+ "3. Les coefficients qui ne sont pas liés à l'opération sélectionnée peuvent être définis sur n'importe quelle valeur, car ils ne sont pas utilisés dans le code."
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "case_number = 1"
+ "# SÉLECTION DU CAS\n",
+ "case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
+ "# INITIALISATION DES VARIABLES\n",
"if case_number == 1:\n",
" S = [[1,-2]]\n",
" v = [-2,1]\n",
" dim=1\n",
"elif case_number == 2:\n",
" S = [[0,1,0], [1,-1,0]]\n",
" v = [-3,2,1]\n",
" dim=2\n",
"elif case_number == 3:\n",
" S = [[1,2,-1,-2], [0,1,0,-1]]\n",
" v = [0,-1,1,-1]\n",
" dim=2\n",
"else:\n",
" print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
" f\"Numéros de cas disponibles: [1,2,3]\")\n",
"\n",
"step = 0\n",
"VectorsList = [S]"
]
},
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Aide\n",
- "\n",
- "Pour calculer la meiileure approximation quadratique de $v$ par un vecteur dans $W$,, il peut être utile de dériver une base ortogonale (ou orthonormée) pour ce dernier. Vous pouvez utiliser la cellule suivante pour exécuter l'algorithme interactif de Gram-Schmidt.\n",
- "\n",
- "#### Instructions\n",
- "\n",
- "Pour utiliser la méthode interactive de Gram-Schmidt, procédez comme suit:\n",
- "\n",
- "1. Insérez le numéro de dossier souhaité dans la cellule suivante\n",
- "2. Exécutez la cellule appelée \"SÉLECTION DES PARAMÈTRES\" pour sélectionner le type d'opération et les coefficients nécessaires\n",
- "3. Exécutez la cellule appelée \"EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\" pour exécuter l'étape de l'algorithme de Gram-Schmidt avec les paramètres précédemment sélectionnés\n",
- "\n",
- "En outre:\n",
- "\n",
- "1. Vous pouvez annuler une opération en sélectionnant le bouton \"Revert\".\n",
- "\n",
- "2. Si les coefficients insérés sont incorrects, vous pouvez essayer avec de nouvelles valeurs sans effectuer une opération \"Revert\".\n",
- "\n",
- "3. Les coefficients qui ne sont pas liés à l'opération sélectionnée peuvent être définis sur n'importe quelle valeur, car ils ne sont pas utilisés dans le code."
- ]
- },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# SÉLECTION DES PARAMÈTRES\n",
"norm_coeff, proj_coeffs, operation, step_number = al.manual_GS(dim=dim)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\n",
"S = al.interactive_gram_schmidt(norm_coeff, proj_coeffs,\n",
" operation, step_number, \n",
" S.copy(), VectorsList)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# INSÉREZ ICI LE VALEUR DE LA MEILLEURE APPROXIMATION DE v AU SENS DES MOINDRES CARRÉES DANS W\n",
"best_appr = [0, 0] "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex1Chapitre9_10_11(best_appr, \n",
" case_nb=case_number)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercice 2\n",
"\n",
"Soit $V = \\mathcal{C}\\left(I, \\mathbb{R}\\right)$, ou $I$ est un interval dans $\\mathbb{R}$. Considérez les paires suivantes, faites par un ensemble de fonctions $\\mathcal{S}$ générant un sous-espace vectoriel $W$ de $V$ et par un élément $v$ de $V$. Calculez la meilleure approximation au ses des moindres carrés de $v$ par un vecteur dans $W$.\n",
"\n",
"1. $\\quad \\mathcal{S} = \\left\\{ 1, x \\right\\} = \\mathbb{P}^1(\\mathbb{R}) \\qquad \\qquad \\quad v = |x| \\qquad \\qquad \\ I = [-1,1]$\n",
"2. $\\quad \\mathcal{S} = \\left\\{ 1, x, x^2 \\right\\} = \\mathbb{P}^2(\\mathbb{R}) \\qquad \\quad \\ \\ v = |x| \\qquad \\qquad \\ I = [-1,1]$\n",
"3. $\\quad \\mathcal{S} = \\left\\{ 1, x, x^2 \\right\\} = \\mathbb{P}^2(\\mathbb{R}) \\qquad \\quad \\ \\ v = sin(x) \\qquad \\quad I = [-\\pi,\\pi]$\n",
"4. $\\quad \\mathcal{S} = \\left\\{ 1, x, x^2, x^3 \\right\\} = \\mathbb{P}^3(\\mathbb{R}) \\qquad \\ v = e^x \\qquad \\qquad \\ I=[0,1]$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "case_number=1"
+ "# SÉLECTION DU CAS\n",
+ "case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
+ "# INITIALISATION DES VARIABLES\n",
"x = sp.Symbol('x')\n",
"if case_number == 1:\n",
" S = [1+0*x, x]\n",
" v = sp.Abs(x)\n",
" int_limits = [-1,1]\n",
" dim=2\n",
"elif case_number == 2:\n",
" S = [1+0*x, x, x**2]\n",
" v = sp.Abs(x)\n",
" int_limits = [-1,1]\n",
" dim=3\n",
"elif case_number == 3:\n",
" S = [1+0*x, x, x**2]\n",
" v = sp.sin(x)\n",
" int_limits = [-np.pi,np.pi]\n",
" dim=3\n",
"elif case_number == 4:\n",
" S = [1, x, x**2, x**3]\n",
" v = sp.exp(x)\n",
" int_limits = [0,1]\n",
" dim=4\n",
"else:\n",
" print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
" f\"Numéros de cas disponibles: [1,2,3,4]\")\n",
"\n",
"step = 0\n",
"VectorsList = [S]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Aide\n",
"\n",
"Pour calculer la meiileure approximation quadratique de $v$ par un vecteur dans $W$, il peut\n",
"aider à dériver une base orthogonale (ou orthonormée) pour ce dernier. Vous pouvez utiliser la cellule suivante pour exécuter l'algorithme interactif de Gram-Schmidt pour fonctions."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# SÉLECTION DES PARAMÈTRES\n",
"norm_coeff, proj_coeffs, operation, step_number = al.manual_GS(dim=dim)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\n",
"S = al.interactive_gram_schmidt_func(norm_coeff, proj_coeffs,\n",
" operation, step_number, \n",
" S.copy(), VectorsList,\n",
" int_limits=int_limits,\n",
" weight_function=None)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# INSÉREZ ICI LE VALEUR DE LA MEILLEURE APPROXIMATION DE v AU SENS DES MOINDRES CARRÉES DANS W\n",
"best_appr = 1 + 0*x"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex2Chapitre9_10_11(best_appr, \n",
" int_limits=int_limits, \n",
" case_nb=case_number)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Passez au notebook du chapitre 9.12: Solution au sens du moindres carrées](./9.12%20Solution%20au%20sens%20du%20moindres%20carrées.ipynb)"
]
}
],
"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.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.12 Solution au sens du moindres carr\303\251es.ipynb" "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.12 Solution au sens du moindres carr\303\251es.ipynb"
index 6d1fe02..d8ca310 100644
--- "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.12 Solution au sens du moindres carr\303\251es.ipynb"
+++ "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.12 Solution au sens du moindres carr\303\251es.ipynb"
@@ -1,407 +1,412 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Concept(s)-clé(s) et théorie**\n",
"\n",
"## Définition 1\n",
"Soient $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$, $b \\in \\mathcal{M}_{m \\times 1}(\\mathbb{R})$ et $X = \\left(x_1, \\dots, x_n\\right)^T$. Aussi, désignons par $\\phi: \\mathbb{R}^n \\rightarrow \\mathbb{R}^m$ l'application linéaire associée à $A$. Une **solution du système $\\boldsymbol{AX=b}$ au sens du moindres carrées** est une solution du systeme\n",
"\n",
"\\begin{equation}\n",
"AX = proj_{Im(\\phi)}b\n",
"\\end{equation}\n",
"\n",
"## Théorème 1\n",
"Soient $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$, $b \\in \\mathcal{M}_{m \\times 1}(\\mathbb{R})$ et $X = \\left(x_1, \\dots, x_n\\right)^T$. Alors une solution du système $AX=b$ au sens du moindres carrées est une solution du système $A^TAX = A^Tb$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exercises et Examples"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import Librairie.AL_Fct as al\n",
"import Corrections.corrections as corrections\n",
"import numpy as np\n",
"import sympy as sp"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercice 1\n",
"\n",
"Considérez les systèmes linéaires suivants, avec forme $Ax=b$. Calculez leur solution au sens des moindres carrés en projetant $b$ sur l'espace image de $A$ et en résolvant $Ax = proj_{Im(A)}b$.\n",
"\n",
"1. $\\quad A = \\begin{pmatrix}1 & 0 \\\\ 1 & 0\\end{pmatrix} \\qquad \\qquad b = \\begin{pmatrix}1 \\\\ 3\\end{pmatrix}$\n",
"2. $\\quad A = \\begin{pmatrix}1 & 1 \\\\ 1 & -1 \\\\ 2 & 0\\end{pmatrix} \\qquad \\qquad b = \\begin{pmatrix}1 \\\\ 2 \\\\ -2\\end{pmatrix}$\n",
"3. $\\quad A = \\begin{pmatrix}1 & 0 & 0\\\\ 0 & 1 & 1 \\\\ 1 & 0 & 0\\end{pmatrix} \\qquad \\quad \\ \\ b = \\begin{pmatrix}-1 \\\\ 2 \\\\ 1\\end{pmatrix}$\n",
"4. $\\quad A = \\begin{pmatrix}1 & 0 & 1\\\\ -1 & 1 & -1 \\\\ 0 & 1 & 1 \\\\ 1 & 1 & 0 \\\\ -1 & 0 & 1\\end{pmatrix} \\qquad \\ \\ b = \\begin{pmatrix}0 \\\\ 2 \\\\ 0 \\\\ 1 \\\\ 4\\end{pmatrix}$"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Aide\n",
+ "\n",
+ "Pour calculer la projection orthogonal de $b$ sur $Im(A)$, il peut être utile de dériver une base ortogonale (ou orthonormée) pour ce dernier. Vous pouvez utiliser la cellule suivante pour exécuter l'algorithme interactif de Gram-Schmidt.\n",
+ "\n",
+ "#### Instructions\n",
+ "\n",
+ "Pour utiliser la méthode interactive de Gram-Schmidt, procédez comme suit:\n",
+ "\n",
+ "1. Insérez le numéro du cas souhaité dans la cellule suivante. Exécutez le cellules appelées \"SÉLECTION DU CAS\" et \"INITIALISATION DES VARIABLES\"\n",
+ "2. Exécutez la cellule appelée \"SÉLECTION DES PARAMÈTRES\" pour sélectionner le type d'opération et les coefficients nécessaires\n",
+ "3. Exécutez la cellule appelée \"EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\" pour exécuter l'étape de l'algorithme de Gram-Schmidt avec les paramètres précédemment sélectionnés\n",
+ "4. Répétez les étapes 2 et 3 jusqu'à ce que l'algorithme soit terminée\n",
+ "\n",
+ "En outre:\n",
+ "\n",
+ "1. Vous pouvez annuler une opération en sélectionnant le bouton \"Revert\".\n",
+ "\n",
+ "2. Si les coefficients insérés sont incorrects, vous pouvez essayer avec de nouvelles valeurs sans effectuer une opération \"Revert\".\n",
+ "\n",
+ "3. Les coefficients qui ne sont pas liés à l'opération sélectionnée peuvent être définis sur n'importe quelle valeur, car ils ne sont pas utilisés dans le code."
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "case_number = 3 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
+ "# SÉLECTION DU CAS\n",
+ "case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
+ "# INITIALISATION DES VARIABLES\n",
"if case_number == 1:\n",
" A_cols = [[1,1], [0,0]]\n",
" A = np.array(A_cols).T\n",
" b = [1,3]\n",
" dim=2\n",
"elif case_number == 2:\n",
" A_cols = [[1,1,2], [1,-1,0]]\n",
" A = np.array(A_cols).T\n",
" b = [1,2,-2]\n",
" dim=2\n",
"elif case_number == 3:\n",
" A_cols = [[1,0,1], [0,1,0], [0,1,0]]\n",
" A = np.array(A_cols).T\n",
" b = [-1,2,1]\n",
" dim=3\n",
"elif case_number == 4:\n",
" A_cols = [[1,-1,0,1,-1], [0,1,1,1,0], [1,-1,1,0,1]]\n",
" A = np.array(A_cols).T\n",
" b = [0,2,0,1,4]\n",
" dim=3\n",
"else:\n",
" print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
" f\"Numéros de cas disponibles: [1,2,3,4]\")\n",
"\n",
"step = 0\n",
"VectorsList = [A_cols]"
]
},
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Aide\n",
- "\n",
- "Pour calculer la projection orthogonal de $b$ sur $Im(A)$, il peut être utile de dériver une base ortogonale (ou orthonormée) pour ce dernier. Vous pouvez utiliser la cellule suivante pour exécuter l'algorithme interactif de Gram-Schmidt.\n",
- "\n",
- "#### Instructions\n",
- "\n",
- "Pour utiliser la méthode interactive de Gram-Schmidt, procédez comme suit:\n",
- "\n",
- "1. Insérez le numéro de dossier souhaité dans la cellule suivante\n",
- "2. Exécutez la cellule appelée \"SÉLECTION DES PARAMÈTRES\" pour sélectionner le type d'opération et les coefficients nécessaires\n",
- "3. Exécutez la cellule appelée \"EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\" pour exécuter l'étape de l'algorithme de Gram-Schmidt avec les paramètres précédemment sélectionnés\n",
- "\n",
- "En outre:\n",
- "\n",
- "1. Vous pouvez annuler une opération en sélectionnant le bouton \"Revert\".\n",
- "\n",
- "2. Si les coefficients insérés sont incorrects, vous pouvez essayer avec de nouvelles valeurs sans effectuer une opération \"Revert\".\n",
- "\n",
- "3. Les coefficients qui ne sont pas liés à l'opération sélectionnée peuvent être définis sur n'importe quelle valeur, car ils ne sont pas utilisés dans le code."
- ]
- },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# SÉLECTION DES PARAMÈTRES\n",
"print(f\"Current vectors: {A_cols}\")\n",
"norm_coeff, proj_coeffs, operation, step_number = al.manual_GS(dim=dim)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# EXÉCUTER L'ÉTAPE DE L'ALGORITHME GRAM-SCHMIDT\n",
"A_cols = al.interactive_gram_schmidt(norm_coeff, proj_coeffs,\n",
" operation, step_number, \n",
" A_cols.copy(), VectorsList)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Aide\n",
"\n",
"Pour résoudre le système linéaire, vous pouvez tirer parti des cellules interactives suivantes qui permettent d'appliquer la méthode d'élimitation de Gauss. Notez que vous devez **entrer la valeur trouvée pour la projection de $\\boldsymbol{b}$ sur l'espace image de $\\boldsymbol{A}$ dans la première ligne!**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"proj_b = [[0], [2], [0]] # INSEREZ ICI LA VALEUR TROUVÉE POUR LA PROJECTION DE b SUR Im (A)\n",
"al.printA(A, proj_b)\n",
"[i,j,r,alpha]= al.manualEch(A,proj_b)\n",
"m=np.concatenate((A,proj_b), axis=1)\n",
"MatriceList=[A]\n",
"RhSList=[proj_b]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m=al.echelonnage(i,j,r,alpha,A,m,MatriceList,RhSList)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# INSEREZ ICI LA SOLUTION\n",
"x,y,z = sp.symbols('x, y, z')\n",
"sol = sp.sets.FiniteSet((0,y,2-y))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ÉVALUATION DE LA SOLUTION\n",
"corrections.Ex1_Chapitre9_12(sol, case_nb=case_number)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercice 2\n",
"\n",
"Considérez les systèmes linéaires suivants, avec forme $Ax = b$. Calculez leur solution au sens des moindres carrés en résolvant le système linéaire $A^TAx=A^Tb$.\n",
"\n",
"1. $\\quad A = \\begin{pmatrix}0 & 1 \\\\ 0 & -1\\end{pmatrix} \\qquad \\qquad b = \\begin{pmatrix}0 \\\\ 2\\end{pmatrix}$\n",
"2. $\\quad A = \\begin{pmatrix}0 & 1 \\\\ 1 & 1 \\\\ 1 & -2\\end{pmatrix} \\qquad \\qquad \\ \\ b = \\begin{pmatrix}1 \\\\ 1 \\\\ 0\\end{pmatrix}$\n",
"3. $\\quad A = \\begin{pmatrix}0 & 1 & 0\\\\ 1 & 0 & 1 \\\\ -2 & 0 & 0\\end{pmatrix} \\qquad \\quad b = \\begin{pmatrix}0 \\\\ 2 \\\\ -1\\end{pmatrix}$\n",
"4. $\\quad A = \\begin{pmatrix}1 & 0 & 0\\\\ 1 & -1 & -1 \\\\ 0 & 1 & 0 \\\\ 1 & 0 & 0 \\\\ 0 & 1 & -1\\end{pmatrix} \\qquad \\ \\ b = \\begin{pmatrix}2 \\\\ 0 \\\\ 0 \\\\ -1 \\\\ -1\\end{pmatrix}$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
+ "# SÉLECTION DU CAS\n",
"case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
+ "# INITIALISATION DES VARIABLES\n",
"if case_number == 1:\n",
" A_cols = [[0,0], [1,-1]]\n",
" A = np.array(A_cols).T\n",
" b = [[0], [2]]\n",
"elif case_number == 2:\n",
" A_cols = [[0,1,1], [1,1,-2]]\n",
" A = np.array(A_cols).T\n",
" b = [[1], [1], [0]]\n",
"elif case_number == 3:\n",
" A_cols = [[0,1,-2], [1,0,0], [0,1,0]]\n",
" A = np.array(A_cols).T\n",
" b = [[0], [2], [-1]]\n",
"elif case_number == 4:\n",
" A_cols = [[1,1,0,1,0], [0,-1,1,0,1], [0,-1,0,0,-1]]\n",
" A = np.array(A_cols).T\n",
" b = [2,0,0,-1,-1]\n",
"else:\n",
" print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
" f\"Numéros de cas disponibles: [1,2,3,4]\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Aide\n",
"\n",
"Pour résoudre le système linéaire, vous pouvez tirer parti des cellules interactives suivantes qui permettent d'appliquer la méthode d'élimitation de Gauss."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"al.printA(A.T@A, A.T@np.array(b))\n",
"[i,j,r,alpha]= al.manualEch(A.T@A, A.T@np.array(b))\n",
"m=np.concatenate((A.T@A,A.T@np.array(b)), axis=1)\n",
"MatriceList=[A.T@A]\n",
"RhSList=[A.T@np.array(b)]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m=al.echelonnage(i,j,r,alpha,A.T@A,m,MatriceList,RhSList)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# INSEREZ ICI LA SOLUTION\n",
"x,y,z = sp.symbols('x, y, z')\n",
"sol = sp.sets.FiniteSet((x,-1)) "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ÉVALUATION DE LA SOLUTION\n",
"corrections.Ex2_Chapitre9_12(sol, case_nb=case_number)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercice 3\n",
"\n",
"Un scénario important dans lequel le calcul des solutions de systèmes linéaires au sens des moindres carrés est utilisé est celui de la **régression linéaire**. Supposons que l'on donne $N$ mesures de certaines quantités $\\left\\{\\left\\{x_j^i\\right\\}_{j=1}^K, y^i\\right\\}_{i=1}^N$; les variables $x$ sont appelées variables explicatives et $K$ est leur nombre. Par exemple, si $K=1$, ils peuvent être le poids ($x_1$) et la taille ($y$) de $N$ personnes différentes; si $K=2$ ils peuvent être le poids ($x_1$), la longueur des pieds ($x_2$) et la taille ($y$) de $N$ personnes différentes.\n",
"\n",
"L'objectif est de déterminer la meilleure relation linéaire possible entre ces grandeurs, c'est-à-dire de déterminer les meilleures valeurs possibles pour les coefficients $\\left\\{c_j\\right\\}_{j=0}^K$. En termes mathématiques, cela revient à résoudre le problème de minimisation suivant $$ \\left\\{c_j\\right\\}_{j=0}^K = \\underset{\\tilde{c}_0, \\dots, \\tilde{c}_k}{argmin} \\sum\\limits_{i=1}^N \\left(y^i - \\tilde{c}_0 - \\sum\\limits_{j=1}^K \\tilde{c}_j x_j^i \\right)^2$$ \n",
"\n",
"S'il existe une relation linéaire entre les données, alors $$y^i = c_0 + \\sum\\limits_{j=1}^K c_j x_j^i \\quad \\forall \\ i \\in \\{1, \\dots, N\\}$$ Cela implique que les coefficients $\\left\\{c_j\\right\\}_{j=0}^K$ sont des solutions au système linéaire suivant:\n",
"\n",
"\\begin{equation}\n",
"\\begin{pmatrix}\n",
"1 & x_1^1 & x_2^1 & \\dots & x_K^1 \\\\\n",
"1 & x_1^2 & x_2^2 & \\dots & x_K^2 \\\\ \n",
"\\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
"1 & x_1^N & x_2^N & \\dots & x_K^N\n",
"\\end{pmatrix}\n",
"\\begin{pmatrix}\n",
"c_0 \\\\ c_1 \\\\ c_2 \\\\ \\vdots \\\\ c_K\n",
"\\end{pmatrix} = \n",
"\\begin{pmatrix}\n",
"y^1 \\\\ y^2 \\\\ \\vdots \\\\ y^N\n",
"\\end{pmatrix}\n",
"\\end{equation}\n",
"\n",
"Quoi qu'il en soit, comme $N$ dans les applications du monde réel est beaucoup plus grand que $K$, il est très probable que ce système n'admette aucune solution. Ainsi, c'est une approche commun de recourir au calcul d'une solution au sens des moindres carrés; on peut en fait prouver que la solution au sens des moindres carrés est égal à la solution au problème de minimisation quadratique susmentionné, dont dérive le nom de \"moindres carrés\".\n",
"\n",
"### Instructions\n",
"e but de l'exercice est de montrer un scénario de cas réel où des solutions des moindres carrés aux systèmes linéaires sont employées; ainsi aucun calcul numérique n'est requis et peu de quantités doivent être correctement insérées.\n",
"\n",
"1. **GÉNÉRATION DE DONNÉES**: la cellule appelée \"GÉNÉRATION DE DONNÉES\" est responsable de la génération des données. En particulier, la méthode \"Ex3_Chapitre9_12_generate_data\" génère les données en superposant du bruit gaussien blanc à des données linéairement dépendantes. Les deux premiers arguments d'entrée régulent l'intensité du bruit. L'argument d'entrée appelée \"K\" définit le nombre de variables explicatives; les seules valeurs disponibles sont K = 1 et K = 2, de sorte que les données peuvent être visualisées via des nuages de points. Essayez les deux! Finalement, les variables X et Y stockent la matrice de gauche et le vecteur de droite du système linéaire précédemment introduit.\n",
"\n",
"2. **INSERTION DE SOLUTION**: la cellule appelée \"INSERTION DE SOLUTION\" vous permet de saisir les valeurs de la matrice de gauche M et du vecteur de droite f, définissant le système linéaire à résoudre afin de calculer la solution souhaitée. Dans ce but, nous rappelons que, étant donné deux matrices A, B:\n",
" * A.T $\\rightarrow$ calcule la transposée de A\n",
" * A @ B $\\rightarrow$ calcule le produit matriciel entre A et B\n",
" * A * B $\\rightarrow$ calcule le produit élément par élément entre A et B\n",
"\n",
"\n",
"3. **VISUALISATION DE LA SOLUTION**: la cellule appelée \"VISUALISATION DE LA SOLUTION\" vous permet de visualiser la solution au problème donnée."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# GÉNÉRATION DE DONNÉES\n",
- "data, fig = corrections.Ex3_Chapitre9_12_generate_data(0.15, 0.075, case_nb=1)\n",
+ "data, fig = corrections.Ex3_Chapitre9_12_generate_data(0.15, 0.075, K=2)\n",
"X = data[0]\n",
"Y = data[1]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# INSERTION DE SOLUTION\n",
- "M = X # !! inserez ici la matrice !!\n",
- "f = Y # !! inserez ici le vecteur de droit !!\n",
+ "M = X.T @ X # !! inserez ici la matrice !!\n",
+ "f = X.T @ Y # !! inserez ici le vecteur de droit !!\n",
"sys = M, f"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# VISUALISATION DE LA SOLUTION\n",
"corrections.Ex3_Chapitre9_12(sys, data, fig)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Passez au notebook du chapitre 9.13-9.14: La factorisation QR: application à la résolution d'un système au sens du moindres carrées](./9.13-9.14%20La%20factorisation%20QR%20-%20application%20à%20la%20résolution%20d'un%20système%20au%20sens%20des%20moindres%20carrées.ipynb)"
]
}
],
"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.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.13-9.14 La factorisation QR - application \303\240 la r\303\251solution d'un syst\303\250me au sens des moindres carr\303\251es.ipynb" "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.13-9.14 La factorisation QR - application \303\240 la r\303\251solution d'un syst\303\250me au sens des moindres carr\303\251es.ipynb"
index b37cce5..992f09b 100644
--- "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.13-9.14 La factorisation QR - application \303\240 la r\303\251solution d'un syst\303\250me au sens des moindres carr\303\251es.ipynb"
+++ "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.13-9.14 La factorisation QR - application \303\240 la r\303\251solution d'un syst\303\250me au sens des moindres carr\303\251es.ipynb"
@@ -1,74 +1,371 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Concept(s)-clé(s) et théorie**\n",
"\n",
"## Définition 1\n",
"Soit $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ une matrice dont les colonnes sont linéairement indépendantes (vues comme vecteurs de $\\mathbb{R}^m$). Alors il existe une factorisation du type $A = QR$, où $Q$ est une matrice $m \\times n$ dont les colonnes forment une base orthonormée de l'espace colonnes de $A$ et $R$ est une matrice $n \\times n$ triangulaire supériore, inversible, dont les coefficients diagonaux sont strictement positifs.\n",
"\n",
"## Algorithme 1\n",
"Soit $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ une matrice dont les colonnes sont linéairement indépendantes (vues comme vecteurs de $\\mathbb{R}^m$). Afin de déterminer une factorisation $QR$ de $A$, on procède comme suit:\n",
"1. Poser $\\mathcal{C} = \\left( c_1, \\dots, c_n \\right)$, qui ici forme une base de l'espace colonnes $W$ de $A$\n",
"2. A l'aide du procède de Gram-Schmidt, calculer une base orthoormée $\\mathcal{B} = \\left( w_1, \\dots, w_n \\right)$ de $W$\n",
"3. Définir $Q \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ comme étant la matrice dont la $i$-ème colonne est $w_i$\n",
- "4. Pour tout $1 \\leq k \\leq n$, écrire $c_k = r_{1k}w_1 + r_{2k}w_2 + \\dots + r_{kk}w_k + 0w_{k+1} + \\dots + 0w_{n}$. On supposerà que $r_{ii} \\geq 0$, quitte à replacer $w_i$ par $-w_i$). Poser alors $r_k = \\begin{pmatrix} r_{1k} & r_{2k} & \\dots & r_{kk} & 0 & \\dots & 0 \\end{pmatrix}^T$\n",
+ "4. Pour tout $1 \\leq k \\leq n$, écrire $c_k = r_{1k}w_1 + r_{2k}w_2 + \\dots + r_{kk}w_k + 0w_{k+1} + \\dots + 0w_{n}$. On supposerà que $r_{ii} \\geq 0$, quitte à replacer $w_i$ par $-w_i$. Poser alors $r_k = \\begin{pmatrix} r_{1k} & r_{2k} & \\dots & r_{kk} & 0 & \\dots & 0 \\end{pmatrix}^T$\n",
"5. Définir $R \\in \\mathcal{M}_{n \\times n}(\\mathbb{R})$ comme étant la matrice dont la $i$-ème colonne est $r_i$.\n",
"\n",
+ "Donc:\n",
+ "\n",
+ "$$ Q = \\left( \\begin{array}{@{}c|c|c|c@{}} w_1 & w_2 & \\dots & w_n \\end{array}\\right) \\qquad \n",
+ "R = \\begin{pmatrix} \\langle c_1, w_1 \\rangle & \\langle c_2, w_1 \\rangle & \\langle c_3, w_1 \\rangle & \\dots & \\langle c_n, w_1 \\rangle \\\\\n",
+ "0 & \\langle c_2, w_2 \\rangle & \\langle c_3, w_2 \\rangle & \\dots & \\langle c_n, w_2 \\rangle \\\\ \n",
+ "0 & 0 & \\langle c_3, w_3 \\rangle & \\dots & \\langle c_n, w_2 \\rangle \\\\ \n",
+ "\\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\ \n",
+ "0 & 0 & 0 & \\dots & \\langle c_n, w_n \\rangle \\end{pmatrix}$$\n",
+ "\n",
"## Proposition 1\n",
"Soit $Q \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ une matrice dont les colonnes forment une base orthonormée de l'espace des colonnes $W$ de $Q$. Alors \n",
"\n",
"$$QQ^Tb = proj_Wb$$ \n",
"\n",
"pour tout $b \\in \\mathcal{M}_{m \\times 1}(\\mathbb{R})$.\n",
"\n",
"## Proposition 2\n",
- "Soit $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ une matrice dont les colonnes sont linéairement indépendantes et soit $A = QR$ une factorisation $QR$ de $A$. Alors pour tout $b \\in \\mathcal{M}_{m \\times 1}(\\mathbb{R})$, l'equation $AX=b$ admet une unique solution au sens du moindres carrées, sonnée par la formule\n",
+ "Soit $A \\in \\mathcal{M}_{m \\times n}(\\mathbb{R})$ une matrice dont les colonnes sont linéairement indépendantes et soit $A = QR$ une factorisation $QR$ de $A$. Alors pour tout $b \\in \\mathcal{M}_{m \\times 1}(\\mathbb{R})$, l'equation $Ax=b$ admet une unique solution au sens du moindres carrées, donnée par la formule\n",
+ "\n",
+ "$$ \\hat{x} = R^{-1} Q^T b$$\n",
+ "\n",
+ "Également, la solution du système au sens du moindres carrée peut être calculée en résolvant le système linéaire traingulaire supérieur suivant\n",
+ "\n",
+ "$$ R\\hat{x} = Q^T b$$\n",
"\n",
- "$$ \\hat{X} = R^{-1} Q^T b$$"
+ "\n",
+ "\n",
+ "En utilisant la méthode de substitution vers l'arrière, il n'est alors pas nécessaire de dériver explicitement l'espression de l'inverse de R."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exercises et Examples"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import Librairie.AL_Fct as al\n",
"import Corrections.corrections as corrections\n",
"import numpy as np\n",
- "import sympy as sp"
+ "import sympy as sp\n",
+ "import time\n",
+ "from scipy.linalg import solve_triangular, lu"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Exercice 1\n",
+ "Considérez les matrices suivantes. En tirant parti des cellules interactives suivantes, appliquez l'algorithme de Gram-Schmidt et dérivez leur factorisation QR.\n",
+ "\n",
+ "1. $\\qquad A_1 = \\begin{pmatrix} 1 & 2 & 0 \\\\ 0 & 1 & 1 \\\\ 1 & 0 & 1 \\end{pmatrix}$\n",
+ "2. $\\qquad A_2 = \\begin{pmatrix} 1 & 1 \\\\ 0 & 2 \\\\ 0 & -2 \\\\ 0 & -1 \\end{pmatrix}$\n",
+ "3. $\\qquad A_3 = \\begin{pmatrix} -3 & 1 & 0 \\\\ 1 & 1 & 1 \\\\ 5 & 0 & 2 \\\\ -1 & -1 & 0 \\end{pmatrix}$ \n",
+ "4. $\\qquad A_4 = \\begin{pmatrix} -4 & 20 & 35 & 5 \\\\ -4 & -30 & -15 & 55 \\\\ -8 & 40 & -80 & -65 \\\\ 23 & -15 & 30 & 15 \\end{pmatrix}$"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Instructions\n",
+ "\n",
+ "Pour utiliser la méthode interactive pour la factorisation QR, procédez comme suit:\n",
+ "\n",
+ "1. Insérez le numéro de cas souhaité dans la cellule appelèe \"SÉLECTION DU CAS\". Exécutez le cellules appelées \"SÉLECTION DU CAS\" et \"INITIALISATION DES VARIABLES\"\n",
+ "2. Exécutez la cellule appelée \"SÉLECTION DES PARAMÈTRES\" pour insérer les coefficients necessaires\n",
+ "3. Exécutez la cellule appelée \"EXÉCUTER L'ÉTAPE DE L'ALGORITHME\" pour exécuter l'étape de l'algorithme avec les paramètres précédemment sélectionnés\n",
+ "4. Répétez les étapes 2 et 3 jusqu'à ce que la factorisation soit terminée"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# SÉLECTION DU CAS\n",
+ "case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# INITIALISATION DES VARIABLES\n",
+ "if case_number == 1: \n",
+ " A = np.array([[1,2,0], [0,1,1], [1,0,1]])\n",
+ "elif case_number == 2:\n",
+ " A = np.array([[1,1], [0,2], [0,-2], [0,-1]])\n",
+ "elif case_number == 3:\n",
+ " A = np.array([[-3,1,0], [1,1,1], [5,0,2], [-1,-1,0]])\n",
+ "elif case_number == 4:\n",
+ " A = np.array([[-4,20,35,5], [-4,-30,-15,55], [-8,40,-80,-65], [23,-15,30,15]])\n",
+ "else:\n",
+ " print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
+ " f\"Numéros de cas disponibles: [1,2,3,4]\")\n",
+ " \n",
+ "Q = np.full(A.shape, np.nan)\n",
+ "R = np.full((A.shape[1],A.shape[1]), np.nan)\n",
+ "QList = [Q]\n",
+ "RList = [R]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# SÉLECTION DES PARAMÈTRES\n",
+ "norm_coeff, proj_coeffs = al.manual_QR(QList[-1], RList[-1], A)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# EXÉCUTER L'ÉTAPE DE L'ALGORITHME\n",
+ "al.interactive_QR(norm_coeff, proj_coeffs, QList, RList, A)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "8/25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Exercice 2\n",
+ "\n",
+ "Considérez les systèmes linéaires suivants, avec forme $Ax=b$. Calculez leur solution au sens des moindres carrés en tirant parti de la factorisation QR de A. \n",
+ "\n",
+ "1. $\\quad A = \\begin{pmatrix}1 & 1 \\\\ 1 & -1\\end{pmatrix} \\qquad \\qquad \\ \\ b = \\begin{pmatrix}1 \\\\ -1\\end{pmatrix}$\n",
+ "2. $\\quad A = \\begin{pmatrix}-2 & 1 \\\\ 1 & 3 \\\\ 2 & 0\\end{pmatrix} \\qquad \\qquad \\quad b = \\begin{pmatrix}1 \\\\ 2 \\\\ 0\\end{pmatrix}$\n",
+ "3. $\\quad A = \\begin{pmatrix}1 & 0 & 0\\\\ 2 & 0 & 1 \\\\ 0 & 4 & 0 \\\\ 0 & 3 & 2 \\\\ 2 & 0 & 2 \\end{pmatrix} \\qquad \\qquad \\ b = \\begin{pmatrix}0 \\\\ 1 \\\\ 0 \\\\ -1 \\\\ 1 \\end{pmatrix}$\n",
+ "4. $\\quad A = \\begin{pmatrix}5 & 0 & 0\\\\ -1 & -2 & -2 \\\\ 1 & 1 & 2 \\\\ 3 & 2 & 0 \\\\ 0 & 0 & 1\\end{pmatrix} \\qquad \\ \\ b = \\begin{pmatrix}0 \\\\ 2 \\\\ 0 \\\\ 1 \\\\ 4\\end{pmatrix}$\n",
+ "\n",
+ "### !! ATTENTION !!\n",
+ "Si $A$ ne correspond pas aux exigences de la *Définition 1*, insérez **None** pour les valeurs des $Q$, $R$ et de la solution $\\hat{x}$ au système linéaire donnée au sens des moindres carrés!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# SÉLECTION DU CAS\n",
+ "case_number = 1 # CHOISISSEZ LE NUMÉRO DE CAS ICI ET EXECUTEZ LA CELLULE SUIVANTE!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# INITIALISATION DES VARIABLES\n",
+ "if case_number == 1:\n",
+ " A_cols = [[1,1], [1,-1]]\n",
+ " A = np.array(A_cols).T\n",
+ " b = [1,-1]\n",
+ " dim=2\n",
+ "elif case_number == 2:\n",
+ " A_cols = [[-2,1,2], [1,3,0]]\n",
+ " A = np.array(A_cols).T\n",
+ " b = [1,2,0]\n",
+ " dim=2\n",
+ "elif case_number == 3:\n",
+ " A_cols = [[1,2,0,0,2], [0,0,4,3,0], [0,1,0,2,2]]\n",
+ " A = np.array(A_cols).T\n",
+ " b = [0,1,0, -1, 1]\n",
+ " dim=3\n",
+ "elif case_number == 4:\n",
+ " A_cols = [[5,-1,1,3,0], [0,-2,1,2,0], [0,-2,2,0,1]]\n",
+ " A = np.array(A_cols).T\n",
+ " b = [0,2,0,1,4]\n",
+ " dim=3\n",
+ "else:\n",
+ " print(f\"{case_number} n'est pas un numéro de cas valide!\" \n",
+ " f\"Numéros de cas disponibles: [1,2,3,4]\")\n",
+ "\n",
+ "step = 0\n",
+ "VectorsList = [A_cols]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Aide\n",
+ "\n",
+ "Pour calculer la factorisation QR de $A$, vous pouvez exécutez la cellule suivant. Si $A$ ne correspond pas aux exigences de la *Définition 1*, supprimez le résultat de cette cellule et insérez **None** comme réponse dans la suivante."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Q1, R1 = al.unique_qr(A)\n",
+ "al.printA(Q1, name=\"Q\")\n",
+ "al.printA(R1, name=\"R\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Aide\n",
+ "\n",
+ "Pour calculer la solution du système linéaire $R\\hat{x}=Q^Tb$, plutôt que de dériver explicitement l'expression pour l'inverse de $R$, utilisez la **méthode de substitution vers l'arrière**!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# INSÉREZ LES VALEURS DE Q,R ET DE LA SOLUTION AU SYSTÈME AU SENS DES MOINS CARREÉS\n",
+ "Q = Q1 # None\n",
+ "R = R1 # None\n",
+ "sol = sp.sets.FiniteSet((0, 0, 0)) # None"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "corrections.Ex2Chapitre9_13_14(Q, R, sol, case_nb=case_number)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Exemple 1\n",
+ "\n",
+ "Dans le chapitre 2, tout en considérant la décomposition LU, nous avons signalé qu'un cas d'utilisation très important est celui où plusieurs systèmes linéaires, présentant tous la même matrice de gauche mais différents vecteurs de droite, doivent être résolus.\n",
+ "\n",
+ "De la même manière, la factorisation QR est extrêmement utile lorsque plusieurs systèmes linéaires doivent être résolus au sens des moindres carrés. En effet, une fois la factorisation effectuée, il suffit de résoudre un seul système linéaire triangulaire supérieur (i.e. $R\\hat{x} = Q^Tb$) pour dériver la solution souhaitée.\n",
+ "\n",
+ "Pour des systèmes suffisamment grands, cette approche s'avère plus rapide à la fois que la solution directe des différents systèmes (c'est-à-dire résoudre à chaque étape $A^TAx = A^Tb$, avec $A^TA$ étant calculé une fois pour toutes au début) et que l'application de la décomposition LU (dans ce cas, utilisée avec pivotement partiel pour gagner en performances supplémentaires!) à la matrice $A^TA$.\n",
+ "\n",
+ "**Exécutez la cellule suivante et évaluez les différences de performances ... cela peut prendre quelques secondes**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "M, N = 1000, 500 # dimension des systèmes linéaires à résoudre au sens des moindres carrés\n",
+ "Nt = 10000 # nombre de systèmes linéaires à résoudre au sens des moindres carrés\n",
+ "A = np.random.rand(M, N);\n",
+ "A_ls = A.T @ A\n",
+ "\n",
+ "print(\"----------------------------Factorisation QR----------------------------------\")\n",
+ "start = time.time()\n",
+ "Q, R = np.linalg.qr(A, mode=\"reduced\")\n",
+ "time_qr = time.time() - start\n",
+ "print(\"Temps d'exécution: % f s\" %(time_qr))\n",
+ "\n",
+ "print(\"----------------------------Factorisation LU--------------------------------\")\n",
+ "start = time.time()\n",
+ "P, L, U = lu(A_ls)\n",
+ "time_lu = time.time() - start\n",
+ "print(\"Temps d'exécution: % f s\" %(time_lu))\n",
+ "\n",
+ "print(\"\\n-------Résolution du systèmes linéaires au sens des moindres carreés--------\")\n",
+ "\n",
+ "# résoudre sans utiliser les factorisations QR et LU\n",
+ "start = time.time()\n",
+ "for cnt in range(Nt):\n",
+ " b = np.random.rand(M,1)\n",
+ " x = np.linalg.solve(A_ls, A.T@b)\n",
+ "time_no_fact = time.time() - start\n",
+ "print(\"Sans factorisation: temps d'exécution: % f s\" %time_no_fact)\n",
+ "\n",
+ "# résoudre en utilisant la factorisation LU\n",
+ "start = time.time()\n",
+ "for cnt in range(Nt):\n",
+ " b = np.random.rand(M,1)\n",
+ " y = solve_triangular(L, P@(A.T@b))\n",
+ " x = solve_triangular(U, y)\n",
+ "time_lu += time.time() - start\n",
+ "\n",
+ "print(\"Avec factorisation LU: temps d'exécution: % f s\" % time_lu)\n",
+ "\n",
+ "# résoudre en utilisant la factorisation QR\n",
+ "start = time.time()\n",
+ "for cnt in range(Nt):\n",
+ " b = np.random.rand(M,1)\n",
+ " x = solve_triangular(R, Q.T@b)\n",
+ "time_qr += time.time() - start\n",
+ "\n",
+ "print(\"Avec factorisation QR: temps d'exécution: % f s\" %time_qr)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Vous pouvez comparer les temps d'exécution pour différentes tailles de matrice (c'est-à-dire changer les paramètres M et N) et pour un nombre différent de systèmes lineaires (c'est-à-dire changer le paramètre N_t)**"
]
}
],
"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.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.2 - Produit scalaires, d\303\251finitions, exemples.ipynb" "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.2 - Produit scalaires, d\303\251finitions, exemples.ipynb"
index a5faaea..4dfbaa5 100644
--- "a/Chapitre 9 - Produits scalaires et espaces euclidens/9.2 - Produit scalaires, d\303\251finitions, exemples.ipynb"
+++ "b/Chapitre 9 - Produits scalaires et espaces euclidens/9.2 - Produit scalaires, d\303\251finitions, exemples.ipynb"
@@ -1,253 +1,193 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Concept(s)-clé(s) et théorie**\n",
"\n",
"## Définition 1 - Produit Scalaire\n",
"Soit $V$ un $\\mathbb{R}$-espace vectoriel. Un **produit scalaire** sur $V$ est une application qui fait correspondre à chaque paire ordonnée $(u,v) \\in V \\times V$ un nombre réel, noté $\\langle u, v \\rangle \\in \\mathbb{R}$, telle que les conditions suivantes soient vérifiées, pour tous $u,v,w \\in V, \\alpha \\in \\mathbb{R}$:\n",
"\n",
"1. *Symmétrie*: $\\langle u,v \\rangle = \\langle v, u \\rangle$\n",
"2. *Additivité*: $\\langle u+v, w \\rangle = \\langle u,w \\rangle + \\langle v,w \\rangle$\n",
"3. *Bilinearité (combinè avec 2)*: $\\langle \\alpha u, v \\rangle = \\alpha \\langle u,v \\rangle = \\langle u, \\alpha v \\rangle$\n",
"4. *Definié Positivité*: $\\langle u,u \\rangle \\geq 0 \\ \\forall u \\in V$ et si $\\langle u,u \\rangle = 0$ alors $u=0$.\n",
"\n",
"## Définition 2 - Espace Euclidien\n",
"Un $\\mathbb{R}$-espace vectoriel *de dimension finie* muni d'un produit scalaire s'appelle un **espace euclidien**.\n",
"\n",
"## Définition 3 - Orthogonalité\n",
"Soiet $V$ un $\\mathbb{R}$-espace vectoriel muni d'un produit scalaire $\\langle \\cdot,\\cdot \\rangle$ et $u,v \\in V$. On dit que $u$ et $v$ sont **orthogonaux** si $\\langle u,v \\rangle = 0$. \n",
"\n",
"## Example 1 \n",
"Un example de produit scalaire dans $V = \\mathcal{M}_{n \\times n}(\\mathbb{R})$ est: $$ \\langle A,B \\rangle = Trace(A^TB)$$ ou la trace d'un matrice carée de dimension $n$ est definie comme suit: $$Trace(A) = \\sum\\limits_{i=1}^n a_{ii}$$\n",
"\n",
"## Example 2\n",
"Un example de produit scalaire dans $V = \\mathcal{C}^0([a;b], \\mathbb{R}) =: \\{f: [a;b] \\rightarrow \\mathbb{R} : f \\ fonction \\ continue\\}$ (avec $[a;b]$ un intervalle de $\\mathbb{R}$) est: $$ \\langle f,g \\rangle = \\int_a^b f(x)g(x) \\ dx$$ où $\\int_a^b f(x) \\ dx$ désigne l'intégrale de Riemann de $f$ dans l'intervalle $[a;b]$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exercises et Exemples"
]
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
+ "outputs": [],
"source": [
"import Librairie.AL_Fct as al\n",
"import Corrections.corrections as corrections\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 1\n",
"Considérez les couples de $\\mathbb{R}$-espaces vectoriels et d'opérateurs suivants et marquez ceux des déclarations suivantes qui sont corrects.\n",
"\n",
"1. $\\big(\\mathbf{V}, \\langle a, b \\rangle\\big) = \\big(\\mathbb{R}^3, \\ a_1b_1 - a_1b_2 - a_2b_1 + a_2b_2 - a_2b_3 - a_3b_2 + 2a_3b_3\\big)$\n",
"2. $\\big(\\mathbf{V}, \\langle a, b \\rangle\\big) = \\big(\\mathbb{R}^3, \\ a^TMb\\big) \\qquad$ with $M = \\begin{pmatrix} 2 & 0 & 1\\\\ 0 & 2 & -1 \\\\ -1 & -1 & 2 \\end{pmatrix}$\n",
"3. $\\big(\\mathbf{V}, \\langle a, b\\rangle\\big) = \\big(\\mathbb{P}^2(\\mathbb{R}), \\ 2c^a_0c^b_0 - c^a_0c^b_2 + c^a_1c^b_1 - c^a_2c^b_0 + c^a_2c^b_2\\big) \\qquad$ with $a(x) =: c^a_2 x^2 + c^a_1 x + c^a_0 \\ $ and $ \\ b(x) =: c^b_2x^2 + c^b_1 x + c^b_0$\n",
"4. $\\big(\\mathbf{V}, \\langle a, b\\rangle\\big) = \\big(\\mathbb{P}^3(\\mathbb{R}), \\ 2c^a_0c^b_0 - c^a_0c^b_2 + c^a_1c^b_1 - c^a_2c^b_0 + c^a_2c^b_2\\big) \\qquad$ with $a(x) =: c^a_3 x^3 + c^a_2 x^2 + c^a_1 x + c^a_0 \\ $ and $ \\ b(x) =: c^a_3 x^3 + c^b_2x^2 + c^b_1 x + c^b_0$\n",
"5. $\\big(\\mathbf{V}, \\langle a, b\\rangle\\big) = \\big(\\mathcal{C}^1([x_0, x_1]; \\mathbb{R}), \\ a(x_0)b(x_0) + \\int_{x_0}^{x_1} a'(x)b'(x) \\ dx \\big)$\n",
"6. $\\big(\\mathbf{V}, \\langle a, b\\rangle\\big) = \\big(\\mathcal{C}^2([x_0, x_1]; \\mathbb{R}), \\ a(x_0)b(x_0) + \\int_{x_0}^{x_1} a''(x)b''(x) \\ dx \\big)$\n",
"\n",
"### Remarques\n",
"- $\\mathbb{P}^n(\\mathbb{R})$ désigne l'ensemble des polynômes a valeurs réelles de degré au plus $n$, qui peuvent alors être exprimés de manière unique en termes de $n+1$ coefficients scalaires\n",
"- $\\mathcal{C}^n([x_0, x_1]; \\mathbb{R})$ désigne l'ensemble des fonctions à valeurs réelles qui sont $n$-fois différenciables, avec toutes les dérivées continues, sur l'intervalle $[x_0; x_1]$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex1Chapitre9_2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 2\n",
"Étant donné les couples de matrices suivants, déterminez celles qui sont orthogonales par rapport au produit scalaire défini via l'opérateur de trace\n",
"\n",
"1. $ \\qquad A = \\begin{pmatrix} 1 & 2 & 0 \\\\ 0 & -1 & -1 \\\\ 1 & 3 & 1 \\end{pmatrix} \\qquad \\quad \\ \\ \n",
" B = \\begin{pmatrix} 1 & -1 & 4 \\\\ 3 & 1 & -2 \\\\ 1 & 0 & -1 \\end{pmatrix}$ \n",
"2. $ \\qquad A = \\begin{pmatrix} 0 & 2 \\\\ -1 & 3 \\end{pmatrix} \\qquad \\qquad \\quad\n",
" B = \\begin{pmatrix} 3 & 1 \\\\ 1 & -1 \\end{pmatrix}$\n",
"3. $ \\qquad A = \\begin{pmatrix} 0 & 1 & 3 & 0 \\\\ 1 & 0 & 1 & 0 \\\\ -1 & -2 & 2 & 1 \\\\ 3 & 4 & 1 & 2 \\end{pmatrix} \\qquad\n",
" B = \\begin{pmatrix} 3 & 1 & -1 & 2 \\\\ 2 & 2 & 0 & 1 \\\\ -1 & 1 & -1 & 3 \\\\ -1 & 1 & 1 & -1 \\end{pmatrix}$\n",
"4. $ \\qquad A = \\begin{pmatrix} 1 & -3 \\\\ 2 & 1 \\end{pmatrix} \\qquad \\qquad \\quad\n",
" B = \\begin{pmatrix} 1 & -2 \\\\ -2 & 1 \\end{pmatrix}$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"corrections.Ex2Chapitre9_2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise 3\n",
"Étant donné les couples de fonctions suivants, déterminez s'ils sont orthogonaux par rapport au produit scalaire donné\n",
"\n",
"1. $\\qquad f(x) = 1 - x^2; \\quad g(x) = -(x-1)^2 \\qquad \\quad with: \\quad \\langle f, g \\rangle = c^f_0c^g_0 + c^f_1c^g_1 + c^f_2c^g_2$\n",
"2. $\\qquad f(x) = 1 - x^2; \\quad g(x) = -(x-1)^2 \\qquad \\quad with: \\quad \\langle f, g \\rangle = \\int_{-1}^{1} f(x)g(x) \\ dx$\n",
"3. $\\qquad f(x) = x - \\dfrac{1}{2}; \\quad g(x) = x^2 - x - \\dfrac{1}{6} \\qquad \\ \\ with: \\quad \\langle f, g \\rangle = 2c^f_0c^g_0 - c^f_0c^g_0 + 2c^f_1c^g_1 - c^f_1c^g_2 - c^f_2c^g_0 - c^f_2c^g_1 + 2c^f_2c^g_2$\n",
"4. $\\qquad f(x) = x - \\dfrac{1}{2}; \\quad g(x) = x^2 - x - \\dfrac{1}{6} \\qquad \\ \\ with: \\quad \\langle f, g \\rangle = \\int_0^1 f(x)g(x) \\ dx$\n",
"5. $\\qquad f(x) = \\sin(x); \\quad \\ g(x) = \\cos(x) \\qquad \\qquad \\ \\ with: \\quad \\langle f,g \\rangle = \\int_{-\\pi}^{\\pi} f(x)g(x) \\ dx$\n",
"6. $\\qquad f(x) = \\sin(x); \\quad \\ g(x) = \\cos(x) \\qquad \\qquad \\ \\ with: \\quad \\langle f,g \\rangle = \\int_{0}^{\\pi / 2} f(x)g(x) \\ dx$"
]
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Cliquer sur CTRL pour sélectionner plusieurs réponses\n"
- ]
- },
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "daffcdc93f9841568bb81cbcff6cbcad",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "interactive(children=(SelectMultiple(description='Les fonctions sont orthogonales dans les cas:', layout=Layou…"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
+ "outputs": [],
"source": [
"corrections.Ex3Chapitre9_2()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Aide**: Vous pouvez vous aider en exécutant les cellules suivantes, qui permettent de tracer les 3 couples de fonctions considérés dans l'exercice 3"
]
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "