diff --git a/Chapitre 9 - Produits scalaires et espaces euclidens/Corrections/corrections.py b/Chapitre 9 - Produits scalaires et espaces euclidens/Corrections/corrections.py index 9c3cb54..43e0794 100644 --- a/Chapitre 9 - Produits scalaires et espaces euclidens/Corrections/corrections.py +++ b/Chapitre 9 - Produits scalaires et espaces euclidens/Corrections/corrections.py @@ -1,1701 +1,1773 @@ import sys sys.path.insert(0, './../') import numpy as np import sympy as sp import matplotlib.pyplot as plt from scipy.linalg import null_space from itertools import chain import ipywidgets as widgets import plotly import plotly.graph_objs as go from ipywidgets import interact_manual, Layout, widgets, HBox from IPython.display import display, Latex from sympy.solvers.solveset import linsolve from Librairie.AL_Fct import get_couple_vectors_info_2D, get_couple_matrices_info, get_couple_functions_info from Librairie.AL_Fct import couple_functions_plotter, compute_expansion_coefficients, vector_plot_2D, vector_plot_3D from Librairie.AL_Fct import project, project_on_subspace, isDiag, plot_line, plot_plane, scatterPlot -from Librairie.AL_Fct import Plot2DSys, Plot3DSys, compare_sympy_FiniteSets +from Librairie.AL_Fct import Plot2DSys, Plot3DSys, compare_sympy_FiniteSets, unique_qr def Ex1Chapitre9_1(): """Provides the correction of exercise 1 of notebook 9_1 """ a = widgets.Checkbox( value=False, description=r'$$\qquad u_1 \cdot u_2 \cdot [\dots] \cdot u_n = u_n \cdot [\dots] \cdot u_2 \cdot u_1$$', disabled=False, layout=Layout(width='90%', height='50px') ) b = widgets.Checkbox( value=False, description=r'$$\qquad (u_1 + u_2) \cdot (u_1 + u_2) = ||u_1|| + ||u_2|| + 2 (u_1 \cdot u_2)$$', disabled=False, layout=Layout(width='90%', height='50px') ) c = widgets.Checkbox( value=False, description=r"$$ \qquad \left(\sum\limits_{i=1}^nu_i\right) \cdot \left(\sum\limits_{i=1}^nu_i\right) = " r"\sum\limits_{i=1}^n ||u_i||^2 + 2\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}u_i \cdot u_j$$", disabled=False, layout=Layout(width='100%', height='80px') ) d = widgets.Checkbox( value=False, description=r'\begin{align*}' r'\qquad (\lambda_1u_1 + \lambda_2u_2) \cdot (\lambda_3u_3 + \lambda_4u_4) =&' r'\lambda_1\lambda_3(u_1 \cdot u_3) + \lambda_1\lambda_4(u_1 \cdot u_4) \ + \\' r' \qquad \quad & \lambda_2\lambda_3(u_2 \cdot u_3) + \lambda_2\lambda_4(u_2 \cdot u_4)' r'\end{align*}', disabled=False, layout=Layout(width='90%', height='80px') ) e = widgets.Checkbox( value=False, description=r'$$\qquad u_1 \cdot u_2 = 0 \implies u_1 \perp u_2 \ \forall \ u_1, u_2 \in \mathbb{R}^2$$', disabled=False, layout=Layout(width='80%', height='50px') ) def correction(a, b, c, d, e): if a and not b and c and d and not e: display(Latex(r"C'est correct! En particulier: " "1. Vrai, en raison de la symétrie du produit scalaire. " "2. Faux, car les normes du côté gauche doivent être au carré. " "3. Vrai, en raison de l'additivité (la formule peut être prouvée par induction. Essaie! " "4. Vrai, en raison de la bilinéarité du produit scalaire. " "5. Faux, car il faut préciser que $u_1$ et $u_2$ ne peuvent pas être des vecteurs nuls.")) else: display(Latex("C'est faux.")) interact_manual(correction, a=a, b=b, c=c, d=d, e=e) return def Ex2Chapitre9_1(vec1, vec2): """Provides the correction of exercise 2 of notebook 9_1 """ display(Latex("Insérez les valeurs des quantités listées ci-dessous. " "Entrez les valeurs avec 4 chiffres après la virgule! Si l'angle n'est pas défini, entrez -999!")) norm_u = widgets.FloatText( value=0.0, step=0.0001, description='||u||:', disabled=False ) norm_v = widgets.FloatText( value=0.0, step=0.0001, description='||v||:', disabled=False ) u_dot_v = widgets.FloatText( value=0.0, step=0.0001, description=r'$u \cdot v$:', disabled=False ) theta_u_v = widgets.FloatText( value=0.0, step=0.0001, description=r'$\Delta\theta$', disabled=False ) solution_button = widgets.Button(description='Solution', disabled=True) box = HBox(children=[solution_button]) out = widgets.Output() @out.capture() def solution(e): out.clear_output() get_couple_vectors_info_2D(vec1, vec2, show=True, return_results=False) display(norm_u) display(norm_v) display(u_dot_v) display(theta_u_v) norm_u_true, norm_v_true, u_dot_v_true, theta_u_v_true = get_couple_vectors_info_2D(vec1, vec2, show=False, return_results=True) def f(): threshold = 1e-4 u_correct, v_correct, u_dot_v_correct, theta_correct = tuple(np.zeros((4, 1)).astype(bool)) if np.abs(norm_u.value - norm_u_true) < threshold: display(Latex(r"La norme de $u$ est correcte!")) u_correct = True else: display(Latex(f"La norme de $u$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(norm_v.value - norm_v_true) < threshold: display(Latex(r"La norme de $v$ est correcte!")) v_correct = True else: display(Latex(f"La norme de $v$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(u_dot_v.value - u_dot_v_true) < threshold: display(Latex(r"Le produit scalaire est correct!")) u_dot_v_correct = True else: display(Latex(f"Le produit scalaire n'est pas correct! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if not np.isnan(theta_u_v_true) and np.abs(theta_u_v.value - theta_u_v_true) < threshold: display(Latex(r"L'angle est correct!")) theta_correct = True elif np.isnan(theta_u_v_true) and theta_u_v.value == -999: display(Latex(r"Correct! L'angle n'est pas defini!")) theta_correct = True else: display(Latex(f"L'angle n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if u_correct and v_correct and u_dot_v_correct and theta_correct: display(Latex(r"RÉSULTATS")) get_couple_vectors_info_2D(vec1, vec2, show=True, return_results=False) else: solution_button.disabled = False interact_manual(f) solution_button.on_click(solution) display(box) display(out) return def Ex3Chapitre9_1(case=1): """Provides the correction of exercise 3 of notebook 9_1 :param case: number of the case. Available: {1,2,3}. Defaults to 1 :type case: int """ fig = plt.figure(figsize=(5, 5)) ax = fig.gca() origin = [0], [0] if case == 1: theta = np.hstack((np.linspace(0, np.pi / 4, 100), np.linspace(5 * np.pi / 4, 3 * np.pi / 2, 100))) x1 = 2 * np.cos(theta) x2 = 2 * np.sin(theta) limit = 2.5 for i in range(2): plt.plot(x1[i * 100: (i + 1) * 100], x2[i * 100: (i + 1) * 100], '-', color='r', linewidth=2) elif case == 2: theta_ref = np.linspace(0, 2 * np.pi, 13) theta = np.hstack( [np.linspace(theta_ref[i], theta_ref[i + 1], 50) if not i % 2 else np.array([]) for i in range(12)]) vals = [1, 3, 5, 7, 9, 11] x1 = np.hstack([vals[i] * np.cos(theta[i * 50:(i + 1) * 50]) for i in range(6)]) x2 = np.hstack([vals[i] * np.sin(theta[i * 50:(i + 1) * 50]) for i in range(6)]) limit = 12 for i in range(6): plt.plot(x1[i * 50:(i + 1) * 50], x2[i * 50:(i + 1) * 50], '-', color='r', linewidth=2) elif case == 3: theta = np.linspace(0, 8 * np.pi, 800) vals = np.logspace(-2, 0, 800) x1 = vals * np.cos(theta) x2 = vals * np.sin(theta) limit = 1.2 plt.plot(x1, x2, '-', color='r', linewidth=2) else: raise ValueError(f"Le cas {case} n'est pas défini. Cas disponibles: {1, 2, 3}") ax.plot(*origin, marker='o', color='b') ax.set_xlim(-limit, limit) ax.set_ylim(-limit, limit) ax.grid() # Move left y-axis and bottom x-axis to centre, passing through (0,0) ax.spines['left'].set_position('center') ax.spines['bottom'].set_position('center') # Eliminate upper and right axes ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') # Show ticks in the left and lower axes only ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') plt.show() if case == 1: a = widgets.Checkbox( value=False, description=r"L'ensemble peut être exprimé comme: " r" $$\qquad S=\Bigl\{v \in \mathbb{R}^2: ||v||=2 \land \theta \ \% \ \pi \in " r"\Bigl[0; \dfrac{\pi}{4}\Bigr]\Bigr\}$$", disabled=False, layout=Layout(width='80%', height='60px') ) b = widgets.Checkbox( value=False, description=r"L'ensemble peut être exprimé comme: " r" $$\qquad S=\Bigl\{v \in \mathbb{R}^2: ||v||=2 \land \Bigl\lfloor\dfrac{\theta}{\pi}\Bigr\rfloor " r"in [0; 1]\Bigr\}$$", disabled=False, layout=Layout(width='80%', height='60px') ) c = widgets.Checkbox( value=False, description=r"$v = \Bigl[\dfrac{\sqrt{2}}{2}; \dfrac{\sqrt{2}}{2}\Bigr]$ appartient à l'ensemble", disabled=False, layout=Layout(width='80%', height='50px') ) d = widgets.Checkbox( value=False, description=r"$v = -\dfrac{1}{2}\Bigl[\sqrt{2}; \sqrt{6}\Bigr]$ appartient à l'ensemble", disabled=False, layout=Layout(width='80%', height='50px') ) def correction(a, b, c, d): if a and not b and not c and d: display( (Latex(r"C'est correct! En effet l'ensemble S est fait d'éléments de norme égale à 2 $ et d'angles " r"dans $\Bigl[0; \dfrac{\pi}{4}\Bigr]$ ou dans " r"$\Bigl[\dfrac{5\pi}{4}; \dfrac{3\pi}{2}\Bigr]$, ce qui est indiqué par la réponse 1. " r"La réponse 2 est erronée car le calcul de $\Bigl\lceil\dfrac{\theta}{\pi}\Bigr\rceil$" r"n'a aucun sens dans ce contexte. Enfin, " r"$v_1 = \Bigl[\dfrac{\sqrt{2}}{2}; \dfrac{\sqrt{2}}{2}\Bigr]$ n'appartient pas à l'ensemble " r"puisque sa norme est égale à $1$ et non à $2$ (réponse 3); inversement " r"$v_2 = -\dfrac{1}{2}\Bigl[\sqrt{2}; \sqrt{6}\Bigr]$ fait partie à l'ensemble,appartenant " r" à l'arc dans le troisième quadrant (réponse 4)."))) else: display(Latex("C'est faux.")) elif case == 2: a = widgets.Checkbox( value=False, description=r"L'ensemble peut être exprimé comme: " r"$$\qquad S= \Bigl\{v \in \mathbb{R}^2: \Bigl\lceil \dfrac{\theta}{\pi / 6} \Bigr\rceil \ is \ odd \ \land ||v|| = \Bigl\lfloor \dfrac{\theta}{\pi / 6} \Bigr\rfloor \Bigr\}$$", disabled=False, layout=Layout(width='90%', height='60px') ) b = widgets.Checkbox( value=False, description=r"L'ensemble peut être exprimé comme: " r"$$\qquad S= \Bigl\{v \in \mathbb{R}^2: \Bigl\lfloor \dfrac{\theta}{\pi / 6} \Bigr\rfloor \ is \ even \ \land ||v|| = \Bigl\lceil \dfrac{\theta}{\pi / 6} \Bigr\rceil \Bigr\}$$", disabled=False, layout=Layout(width='90%', height='60px') ) c = widgets.Checkbox( value=False, description=r"$v = \dfrac{1}{2}\Bigl[\sqrt{3}; 3\Bigr]$ appartient à l'ensemble", disabled=False, layout=Layout(width='100%', height='60px') ) d = widgets.Checkbox( value=False, description=r"$v = \Bigl[-\sqrt{3}; \sqrt{3}\Bigr]$ appartient à l'ensemble", disabled=False, layout=Layout(width='100%', height='60px') ) def correction(a, b, c, d): if not a and b and c and not d: display((Latex( r"C'est correct! En effet l'ensemble est fait par tous les éléments tels que la partie entière de " r"a division de leur angle (supposée être en radians et en $[0;2\pi]$ par " r"$\dfrac{\pi}{6}$ est pair et avec norme égale à" r"$\Bigl\lceil\dfrac{\theta}{\pi / 6}\Bigr\rceil$ (réponse 2). La réponse 1 est erronée car" r"la norme des éléments de l'ensemble est égale à $\Bigl\lceil\dfrac{\theta}{\pi / 6}\Bigr\rceil$" r"et non à $\Bigl\lfloor\dfrac{\theta}{\pi / 6}\Bigr\rfloor$; la condition '" r"$\Bigl\lceil\dfrac{\theta}{\pi / 6}\Bigr\rceil \ is \ odd$' est plutôt vrai. Enfin, " r"$v_1 = \dfrac{1}{2}\bigl[\sqrt{3}; 3\bigr]$ appartient à S (c'est l'élément le plus à droite " r"du deuxième arc du premier quadrant) (réponse 3), tandis que" r" $v_2 = \bigl[-\sqrt{3}; \sqrt{3}\bigr]$ ne fait pas partie de S puisque sa norme est égale à $3$, " r"et il devrait être égal à $5$ pour faire partie de l'arc dans le deuxième quadrant (réponse 4)."))) else: display(Latex("C'est faux.")) elif case == 3: a = widgets.Checkbox( value=False, description=r"L'élément de l'ensemble $S$ avec la norme maximale a une norme égale à $1$", disabled=False, layout=Layout(width='90%', height='50px') ) b = widgets.Checkbox( value=False, description=r'Ce qui suit est valable: ' r'$$ \qquad \nexists \ v_1, v_2 \in S: v_1 \cdot v_2 = 0 \land ||v_1|| = ||v_2||$$', disabled=False, layout=Layout(width='90%', height='50px') ) c = widgets.Checkbox( value=False, description=r"Ce qui suit est valable: " r"$$ \qquad \forall \alpha \in [0;2\pi] \ \exists v \in S: \theta = \alpha$$", disabled=False, layout=Layout(width='100%', height='50px') ) d = widgets.Checkbox( value=False, description=r"Tous les couples d'élémets de S sont une base de $\mathbb{R}^2$", disabled=False, layout=Layout(width='100%', height='50px') ) def correction(a, b, c, d): if a and b and c and not d: display((Latex( r"C'est correct! En effet, cet ensemble est en fait constitué de vecteurs dont les angles s'étendent sur " r"$[0;8\pi]$ et dont les normes augmentent logarithmiquement de $10^{-2}$ (if $\theta = 0$) à " r"$10$ if $\theta = 8\pi$. Pour cette raison, l'élément avec la norme maximale a une norme égale à " r"$1$ (answer 1) et tous les angles dans $[0; 2\pi]$ sont représentés par un vecteur dans l'ensemble " r"(answer 3). (réponse 3). De plus, l'ensemble ne contient aucun couple d'éléments orthogonaux " r"et qui ont la même norme (en fait, il ne contient pas deux éléments avec le même " r"norme du tout!) (réponse 2). Au lieu de cela, tout couple d'éléments ayant le même angle est " r"dépend linéairement et ne peut donc pas être une base de $\mathbb{R}^2$ (réponse 4)."))) else: display(Latex("C'est faux.")) else: raise ValueError(f"Le cas {case} n'est pas défini. Cas disponibles: {1, 2, 3}") interact_manual(correction, a=a, b=b, c=c, d=d) return def Ex1Chapitre9_2(): """Provides the correction to exercise 1 of notebook 9_2 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans1 = widgets.SelectMultiple( options=['1', '2', '3', '4', '5', '6'], description="L'opérateur est un produit scalaire dans les cas:", style=style, layout=Layout(width='70%', height='120px'), disabled=False, ) ans2 = widgets.SelectMultiple( options=['1', '2', '3', '4', '5', '6'], description=r"L' $\mathbb{R}$-espace vectoriel, équipé avec l'operateur donné, " r"est un espace Euclidien dans le cas", style=style, layout=Layout(width='70%', height='120px'), disabled=False, ) def correction(ans1, ans2): res_ans = np.zeros(2).astype(bool) res_ans[0] = '1' not in ans1 and '2' in ans1 and '3' in ans1 and \ '4' not in ans1 and '5' in ans1 and '6' not in ans1 res_ans[1] = '1' not in ans2 and '2' in ans2 and '3' in ans2 and \ '4' not in ans2 and '5' not in ans2 and '6' not in ans2 if res_ans.all(): display(Latex(r"C'est correct! En effet: CAS 1: l'opérateur n'est pas un produit scalaire " r"parce qu'il n'est pas défini positif; donc l'espace vectoriel ne peut pas être " r"un espace Euclidien. CAS 2: l'opérateur est en fait un produit scalaire (" r"il s'avère respecter toutes les propriétés requises); en plus l'espace vectoriel " r"à une dimension finie (3), et c'est donc un espace Euclidien une fois équipé " r"avec l'opérateur donné. CAS 3: l'opérateur est un produit scalaire; l'espace vectoriel " r"à une dimension finie (3, égal au nombre de coefficients caractérisants) " r"et c'est donc un espace Euclidien s'il est équipé de l'opérateur donné. CAS 4: l'opérateur " r"est le même que dans le cas 2, mais dans ce cas ce n'est PAS un produit scalaire car " r"il n'est pas défini positif (notez, en effet, que chaque polynôme de degré égal à 3 " r"à un produit scalaire nul avec lui-même, bien qu'il n'est pas le polynôme nul); " r"donc l'espace vectoriel ne peut alors pas être un espace Eucliden. " r"CAS 5: l'opérateur donné définit un produit scalaire. En particulier, definiè " r"positivitè vient du fait que, si $\langle a, a \rangle = 0$, alors $a(x_0) = 0$ " r"et $a'(x)^2=0 \ \forall x \in [x_0; x_1]$; en conséquence $a$ doit être constant dans $[x_0; x_1]$ " r"et égal à $0$ en $x_0$. En raison de la continuité, cela implique qu'en fait $a \equiv 0$, comme " r"souhaité. Cependant, la dimensionnalité de l'espace vectoriel est infinie; il ne peut donc pas " r"être un espace Euclidien. CAS 6: l'opérateur ne définit pas un produit scalaire car il n'est pas " r"défini positif; en effet, si $\langle a, a \rangle=0$ alors $a$ doit être $0$ à $x_0$ et il doit avoir " r"dérivée seconde nulle dans $[x_0; x_1]$. Ainsi, toute fonction linéaire égale à $0$ à $x_0$ " r"conduirait à avoir $ \langle a, a \rangle=0$, ce qui prouve que l'opérateur n'est pas " r"défini positif. Facilement, l'espace vectoriel ne peut pas être un espace Euclidien, aussi parce que " r"sa dimension est infinie.")) else: display(Latex("C'est faux.")) if not res_ans[0]: display((Latex("La réponse à la première question est fausse"))) if not res_ans[1]: display((Latex("La réponse à la deuxième question est fausse"))) interact_manual(correction, ans1=ans1, ans2=ans2) return def Ex2Chapitre9_2(): """Provides the correction to exercise 2 of notebook 9_2 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans = widgets.SelectMultiple( options=['1', '2', '3', '4'], description='Les matrices sont orthogonales dans les cas:', style=style, layout=Layout(width='40%', height='80px'), disabled=False, ) def correction(ans): res_ans = '1' in ans and '2' not in ans and \ '3' in ans and '4' not in ans if res_ans: display(Latex(r"C'est correct!")) else: display(Latex("C'est faux.")) interact_manual(correction, ans=ans) return def Ex3Chapitre9_2(): """Provides the correction to exercise 3 of notebook 9_2 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans = widgets.SelectMultiple( options=['1', '2', '3', '4', '5', '6'], description='Les fonctions sont orthogonales dans les cas:', style=style, layout=Layout(width='60%', height='110px'), disabled=False, ) def correction(ans): res_ans = '1' in ans and '2' not in ans and '3' not in ans and \ '4' in ans and '5' in ans and '6' not in ans if res_ans: display(Latex(r"C'est correct!")) else: display(Latex("C'est faux.")) interact_manual(correction, ans=ans) return def Ex3Chapitre9_2_plotter(case_nb=1): """Helper function that allows to plot the functions involved in Ex 3 of notebook 9_2 :param case_nb: test case number. Defaults to 1 :type case_nb: int """ if case_nb in {1, 2}: f = lambda x: 1 - x ** 2 g = lambda x: -(x - 1) ** 2 x0 = -1 x1 = 1 elif case_nb in {3, 4}: f = lambda x: x - 1 / 2 g = lambda x: x ** 2 - x - 1 / 6 x0 = 0 x1 = 1 elif case_nb in {5, 6}: f = lambda x: np.sin(x) g = lambda x: np.cos(x) x0 = -np.pi if case_nb == 5 else 0 x1 = np.pi if case_nb == 5 else np.pi / 2 else: raise ValueError(f"Cas test nombre {case_nb} n'est pas disponible. " f"Cas disponibles: [1,2,3,4,5,6]") couple_functions_plotter(f, g, [x0, x1], pi_formatting=case_nb in {5,6}) return def Ex1Chapitre9_3_4(): """Provides the correction to exercise 1 of notebook 9_3_4 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans = widgets.SelectMultiple( options=['1', '2', '3', '4', '5', '6'], description='La déclaration est vraie dans les cas:', style=style, layout=Layout(width='40%', height='110px'), disabled=False, ) def correction(ans): res_ans = '1' in ans and '2' not in ans and '3' not in ans and \ '4' in ans and '5' not in ans and '6' in ans if res_ans: display(Latex(r"C'est correct! En effet, l'énoncé 1 correspond au lemme de Titu et c'est un " r"corollaire classique de l'inégalité Cauchy-Schwarz, qui peut être prouvé par l'exécution de les " r"substitutions $\tilde{u_i} = \dfrac{u_i}{\sqrt{u_i}}$ et $\tilde{v_i} = \sqrt{v_i}$, pour " r"$i \in \{1,\dots, n\}$. " r"L'énoncé 2 est faux, car l'inégalité traingulaire se maintient avec un " r"signe d'égalité si les vecteurs sont orthogonaux (théorème de Pythagore), mais l' inegalité de " r"Cauchy-Schwarz l'inégalité le fait si les vecteurs sont soit parallèles soit antiparallèles, " r"c'est-à-dire si le cosinus de l'angle entre les vecteurs est égal à 1 ou -1. " r"L'énoncé 3 est faux puisque, dans ce cas, il peut être prouvé par de simples calculs que le " r" cosinus de l'angle entre les deux vecteurs est egal a $0$, de sorte que l'angle est égal à " r"$\pi/2$ et les vecteurs sont orthogonaux." r"L'énoncé 4 est vrai, puisque toutes les fonctions appartenant à la famille $\mathcal{F}_1$ " r"sont mutuellement orthogonal par rapport au produit scalaire donné." r"L'énoncé 5, au contraire, est faux, puisque les monômes pairs et impairs sont toujours " r"orthogonaux sur un intervalle symétrique à 0$, mais deux monomiaux impairs et deux monomiaux " r"pairs ne le sont pas. " r"FEnfin, l'énoncé 6 est vraie, puisque on peut facilement vérifier que les quatre matrices " r"sont mutuellement orthogonales, par rapport au produit scalaire défini via l'opérateur de trace.")) else: display(Latex("C'est faux.")) interact_manual(correction, ans=ans) return def Ex2Chapitre9_3_4(case_nb=1): """Provides the correction to exercise 2 of notebook 9_3_4 :param case_nb: test case number. Defaults to 1 :type case_nb: int """ if case_nb == 1: A = np.array([[-1, 2, 1], [0, 1, -1], [-2, 3, 0]]) B = np.array([[0, -1, 2], [1, 3, -2], [1, 1, 1]]) elif case_nb == 2: A = np.array([[1, -1], [0, 3]]) B = np.array([[0, 2], [1, -1]]) elif case_nb == 3: A = np.array([[0, 1, 3, 0], [1, 0, 1, 0], [-1, -2, 2, 1], [3, 4, 1, 2]]) B = np.array([[3, 1, -1, 2], [2, 2, 0, 1], [-1, 1, -1, 3], [-1, 1, 1, -1]]) elif case_nb == 4: A = np.array([[1, 0], [-1, 3]]) B = np.array([[-1, -5], [2, -3]]) else: raise ValueError(f"Cas test nombre {case_nb} n'est pas disponible. " f"Cas disponibles: [1,2,3,4]") res = get_couple_matrices_info(A, B) display(Latex("Insérez les valeurs des quantités listées ci-dessous. " "Entrez les valeurs avec 4 chiffres après la virgule! Si l'angle n'est pas défini, entrez -999!")) style = {'description_width': 'initial'} angle = widgets.FloatText( value=0.0, step=0.0001, style=style, description=r'$\theta$:', disabled=False ) LHS = widgets.FloatText( value=0.0, step=0.0001, style=style, description='$||A+B||^2$', disabled=False ) RHS = widgets.FloatText( value=0.0, step=0.0001, style=style, description=r'$||A||^2 + ||B||^2$', disabled=False ) solution_button = widgets.Button(description='Solution', disabled=True) box = HBox(children=[solution_button]) out = widgets.Output() @out.capture() def solution(e): out.clear_output() get_couple_matrices_info(A, B, show=True) display(angle) display(LHS) display(RHS) def f(): threshold = 1e-4 if (np.isnan(res['angle']) and angle.value == -999.0) or \ (np.abs(angle.value - res['angle'] * 180.0 / np.pi) <= threshold): display(Latex(r"L'angle est correcte!")) theta_correct = True else: theta_correct = False display(Latex(f"La'angle n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(LHS.value - res['norm_sum'] ** 2) <= threshold: display(Latex(r"$||A+B||^2$ est correcte!")) LHS_correct = True else: LHS_correct = False display(Latex(f"$||A+B||^2$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(RHS.value - res['norm_1'] ** 2 - res['norm_2'] ** 2) <= threshold: display(Latex(r"$||A||^2 + ||B||^2$ est correcte!")) RHS_correct = True else: RHS_correct = False display(Latex(f"$||A||^2 + ||B||^2$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if theta_correct and LHS_correct and RHS_correct: display(Latex("C'est tout correct!")) else: solution_button.disabled = False display(Latex("Il y a quelques erreurs. " "N'oubliez pas d'insérer tous les résultats avec 4 chiffres après la virgule")) return interact_manual(f) solution_button.on_click(solution) display(box) display(out) return def Ex3Chapitre9_3_4(case_nb=1): """Provides the correction to exercise 3 of notebook 9_3_4 :param case_nb: test case number. Defaults to 1 :type case_nb: int """ if case_nb == 1: f = lambda x: x g = lambda x: np.exp(-np.abs(x)) x0 = -1 x1 = 1 elif case_nb == 2: f = lambda x: x ** 3 + 0.5 g = lambda x: x ** 2 - x x0 = 0 x1 = 1 elif case_nb == 3: f = lambda x: np.sin(np.abs(2 * x)) g = lambda x: np.cos(np.abs(x + np.pi / 2)) x0 = -np.pi / 2 x1 = np.pi / 2 elif case_nb == 4: f = lambda x: np.sin(np.abs(2 * x)) g = lambda x: np.cos(np.abs(x + np.pi / 2)) x0 = 0 x1 = np.pi / 2 else: raise ValueError(f"Cas test nombre {case_nb} n'est pas disponible. " f"Cas disponibles: [1,2,3,4]") res = get_couple_functions_info(f, g, [x0, x1]) display(Latex("Insérez les valeurs des quantités listées ci-dessous. " "Entrez les valeurs avec 4 chiffres après la virgule! Si l'angle n'est pas défini, entrez -999!")) style = {'description_width': 'initial'} angle = widgets.FloatText( value=0.0, step=0.0001, style=style, description=r'$\theta$:', disabled=False ) LHS = widgets.FloatText( value=0.0, step=0.0001, style=style, description='$||f+g||^2$', disabled=False ) RHS = widgets.FloatText( value=0.0, step=0.0001, style=style, description=r'$||f||^2 + ||g||^2$', disabled=False ) solution_button = widgets.Button(description='Solution', disabled=True) box = HBox(children=[solution_button]) out = widgets.Output() @out.capture() def solution(e): out.clear_output() if case_nb == 1: f = lambda x: x g = lambda x: np.exp(-np.abs(x)) x0 = -1 x1 = 1 elif case_nb == 2: f = lambda x: x ** 3 + 0.5 g = lambda x: x ** 2 - x x0 = 0 x1 = 1 elif case_nb == 3: f = lambda x: np.sin(np.abs(2 * x)) g = lambda x: np.cos(np.abs(x + np.pi / 2)) x0 = -np.pi / 2 x1 = np.pi / 2 elif case_nb == 4: f = lambda x: np.sin(np.abs(2 * x)) g = lambda x: np.cos(np.abs(x + np.pi / 2)) x0 = 0 x1 = np.pi / 2 get_couple_functions_info(f, g, [x0, x1], show=True) couple_functions_plotter(f, g, [x0, x1], pi_formatting=case_nb in {3,4}) display(angle) display(LHS) display(RHS) def f(): threshold = 1e-4 if (np.isnan(res['angle']) and angle.value == -999.0) or \ (np.abs(angle.value - res['angle'] * 180.0 / np.pi) <= threshold): display(Latex(r"L'angle est correcte!")) theta_correct = True else: theta_correct = False display(Latex(f"La'angle n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(LHS.value - res['norm_sum'] ** 2) <= threshold: display(Latex(r"$||f+g||^2$ est correcte!")) LHS_correct = True else: LHS_correct = False display(Latex(f"$||f+g||^2$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if np.abs(RHS.value - res['norm_1'] ** 2 - res['norm_2'] ** 2) <= threshold: display(Latex(r"$||f||^2 + ||g||^2$ est correcte!")) RHS_correct = True else: RHS_correct = False display(Latex(f"$||f||^2 + ||g||^2$ n'est pas correcte! L'erreur est supérieure au seuil de {threshold}. " f"Entrez les valeurs avec 4 chiffres après la virgule!")) if theta_correct and LHS_correct and RHS_correct: display(Latex("C'est tout correct!")) else: solution_button.disabled = False display(Latex("Il y a quelques erreurs. " "N'oubliez pas d'insérer tous les résultats avec 4 chiffres après la virgule")) return interact_manual(f) solution_button.on_click(solution) display(box) display(out) return def Ex1Chapitre9_5(): """Provides the correction to exercise 1 of notebook 9_5 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans1 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 1:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans2 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 2:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans3 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 3:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans4 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 4:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) def correction(ans1, ans2, ans3, ans4): res_ans = np.zeros(4).astype(bool) res_ans[0] = 'Famille Orthogonale' not in ans1 and 'Famille Orthonormale' not in ans1 and \ 'Base Orthogonale' not in ans1 and 'Base Orthonormale' not in ans1 res_ans[1] = 'Famille Orthogonale' in ans2 and 'Famille Orthonormale' in ans2 and \ 'Base Orthogonale' in ans2 and 'Base Orthonormale' in ans2 res_ans[2] = 'Famille Orthogonale' in ans3 and 'Famille Orthonormale' not in ans3 and \ 'Base Orthogonale' in ans3 and 'Base Orthonormale' not in ans3 res_ans[3] = 'Famille Orthogonale' in ans4 and 'Famille Orthonormale' not in ans4 and \ 'Base Orthogonale' in ans4 and 'Base Orthonormale' not in ans4 if res_ans.all(): display(Latex(r"C'est correct! " r"CAS 1: une famille de trois vecteurs dans un espace de dimension $2$ ne peut pas être " r"orthonormal et donc elle ne peut pas être une base; toutes les options sont fausses. " r"CAS 2: par rapport au produit scalaire non-usuel défini via la matrice $A$ symétrique et " r"défini positive, cette famille de vecteurs est en fait une base orthonormée; " r"en effet tous les produits scalaires entre les différents éléments de la famille sont égaux " r"à $0$ et les normes de tous les vecteurs (toujours calculés par rapport au produit scalaire " r"non-usuel donnèe) sont égaux à $1$. " r"CAS 3: c'est une famille de $4$ vecteurs orthogonaux (et donc linéairement indépendants!) " r"dans $\mathbb{R}^4$, donc c'est à la fois une famille orthogonale et une base orthogonale; " r"de​toute façon aucune des vecteurs a la norme unitaire et il n'y a donc pas d'orthonormalité. " r"CAS 4: il s'agit d'une famille de $3$ vecteurs orthogonaux (et donc linéairement " r"indépendants!) dans un sous-espace de dimension $5$; donc, c'est une famille orthogonale " r"(et aussi orthonormal), mais elle ne peut pas être une base.")) else: display(Latex("C'est faux.")) wrong_indices = np.where(res_ans == False)[0] right_indices = np.setdiff1d(np.arange(4), wrong_indices) display(Latex(f"Cas correctes: {right_indices+1}")) display(Latex(f"Cas mauvais: {wrong_indices+1}")) interact_manual(correction, ans1=ans1, ans2=ans2, ans3=ans3, ans4=ans4) return def Ex2Chapitre9_5(): """Provides the correction to exercise 2 of notebook 9_5 """ print("Cliquer sur CTRL pour sélectionner plusieurs réponses") style = {'description_width': 'initial'} ans1 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 1:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans2 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 2:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans3 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 3:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) ans4 = widgets.SelectMultiple( options=['Famille Orthogonale', 'Famille Orthonormale', 'Base Orthogonale', 'Base Orthonormale'], description='Cas 4:', style=style, layout=Layout(width='60%', height='80px'), disabled=False, ) def correction(ans1, ans2, ans3, ans4): res_ans = np.zeros(4).astype(bool) res_ans[0] = 'Famille Orthogonale' not in ans1 and 'Famille Orthonormale' not in ans1 and \ 'Base Orthogonale' not in ans1 and 'Base Orthonormale' not in ans1 res_ans[1] = 'Famille Orthogonale' in ans2 and 'Famille Orthonormale' not in ans2 and \ 'Base Orthogonale' not in ans2 and 'Base Orthonormale' not in ans2 res_ans[2] = 'Famille Orthogonale' in ans3 and 'Famille Orthonormale' in ans3 and \ 'Base Orthogonale' in ans3 and 'Base Orthonormale' in ans3 res_ans[3] = 'Famille Orthogonale' in ans4 and 'Famille Orthonormale' not in ans4 and \ 'Base Orthogonale' in ans4 and 'Base Orthonormale' not in ans4 if res_ans.all(): display(Latex(r"C'est correct! CASE 1: CAS 1: c'est la base du monôme standard de $\mathbb{P}^4(\mathbb{R})$; " r"de toute façon, il n'est ni orthogonal ni orthonormé par rapport au produit scalaire usuel. " r"CAS 2: c'est une famille de $4$ vecteurs dans un sous-espace de dimension $5$, donc il ne " r"peut pas être une base; de toute façon ses éléments sont orthogonaux entre eux, donc c'est " r"une famille orthogonale (non orthonormé, car aucune des normes des vecteurs est ègale à $1$)" r"CAS 3: c'est une famille de $3$ vecteurs orthogonaux, tous avec norme unitaire, dans un " r"sous-espace de dimension $3$; il s'agit donc d'une famille et d'une base orthogonales et " r"orthonormées. Remarque que tous les calculs doivent être effectués par rapport au produit" r"scalaire non-usuel donnèe, caractérisé par la fonction de pondération $w(x)=1-x^2$." r"CAS 4: c'est la base dite de Fourier et elle est constituée d'un nombre infini de modes, " r"dont la fréquence augmente jusqu'à l'infini (étant infinie la dimension du espace vectoriel " r"$V = \mathcal{C}\left([-\pi;\pi]; \mathbb{R}\right)$); de plus, tous les éléments " r"sont orthogonales entre elles, mais leurs normes sont égales à $\sqrt{2\pi}$ (pour la " r"fonction constante) et à $\sqrt{\ pi}$ (pour toutes les autres fonctions trigonométriques), " r"donc la base est orthogonale mais non orthonormal.")) else: display(Latex("C'est faux.")) wrong_indices = np.where(res_ans == False)[0] right_indices = np.setdiff1d(np.arange(4), wrong_indices) display(Latex(f"Cas correctes: {right_indices+1}")) display(Latex(f"Cas mauvais: {wrong_indices+1}")) interact_manual(correction, ans1=ans1, ans2=ans2, ans3=ans3, ans4=ans4) return def Ex3Chapitre9_5(ans, case_nb=1): """Provides the correction to exercise 2 of notebook 9_5 :param ans: answer of the user :type ans: numpy.ndarray or list :param case_nb: number of the test case. It defaults to 1 :type case_nb: int """ int_limits = None weight_function = None W = None if case_nb == 1: B = [[2,0,1], [0,1,-1], [1,1,0]] v = [1,1,1] elif case_nb == 2: B = [[2,0,1,1], [0,1,-1,1], [1,0,-1,-1], [0,2,1,-1]] v = [-1,0,2,-2] elif case_nb == 3: B = [lambda x: 1.0, lambda x: x-1/2, lambda x: x**2-x+1/6, lambda x: x**3 - 3/2*x**2 + 3/5*x -1/20] v = lambda x: x**3 - x**2 + x - 1 int_limits = [0,1] elif case_nb == 4: B = [lambda x: np.sqrt(3)/2, lambda x: np.sqrt(15/2)*x, lambda x: np.sqrt(7)*5/7*x**2 - np.sqrt(7)/14] v = lambda x: x**2 + x + 1 int_limits = [-1,1] weight_function = lambda x: 1 - x**2 else: raise ValueError(f"Le cas numéro {case_nb} n'est pas disponible") v_B = compute_expansion_coefficients(B, v, int_limits=int_limits, weight_function=weight_function, W=W) solution_button = widgets.Button(description='Solution', disabled=True) box = HBox(children=[solution_button]) out = widgets.Output() @out.capture() def solution(e): out.clear_output() display(Latex(f"Solution: {v_B}")) if case_nb == 4: display(Latex(r"C'est le cas dans lequel la norme de $v$ peut être calculée rapidement. En effet, " r"base donnée est orthonormée, donc la norme de $v$ est égale à celle du vecteur exprimant " r"ses coordonnées par rapport à $\mathcal{B}$ et il peut être dérivé sans effectuer aucune " r"intégration numérique!")) threshold = 1e-4 if np.linalg.norm(ans-v_B) <= threshold: display(Latex("C'est correct!")) else: solution_button.disabled = False display(Latex("C'est faux! N'oubliez pas d'insérer tous les résultats avec 4 chiffres après la virgule")) solution_button.on_click(solution) display(box) display(out) return def Ex1Chapitre9_6_7(ans, case_nb=1): """Provides the correction to exercise 1 of notebook 9_6_7 :param ans: answer of the user :type ans: numpy.ndarray or list or function :param case_nb: number of the test case. It defaults to 1 :type case_nb: int """ W = None int_limits = None x = sp.Symbol('x') weight_function = 1 + 0*x if case_nb == 1: u = [-1, 2] v = [1, 1] W = np.eye(2) elif case_nb == 2: u = [1, 2, 1] v = [0, 1, -1] W = np.array([[2, 0, -1], [0, 1, 0], [-1, 0, 2]]) elif case_nb == 3: u = [-1, 0, 1, 0, 1] v = [0, 1, -1, 1, 0] W = np.eye(5) elif case_nb == 4: u = x**2 - 1 v = x**1 int_limits = [0, 1] elif case_nb == 5: u = x**2 - x v = x + 1 int_limits = [-1, 1] weight_function = 1 - x**2 else: raise ValueError(f"Le cas numéro {case_nb} n'est pas disponible") proj_u_v = project(u, v, W=W, int_limits=int_limits, weight_function=weight_function) if type(ans) is list: ans = np.array(ans) threshold = 1e-4 if case_nb in {1, 2, 3}: try: assert type(ans) is list or type(ans) is np.ndarray and len(ans) == len(proj_u_v) except AssertionError: raise ValueError(f"La réponse doit être une liste ou un numpy array 1D de longueur {len(proj_u_v)}") res = (np.abs(ans - proj_u_v) <= threshold).all() elif case_nb in {4, 5}: try: assert isinstance(ans, sp.Basic) except AssertionError: raise ValueError(f"La réponse doit être une fonction univariée!") xx = np.linspace(int_limits[0], int_limits[1], 1000) res = (np.abs(np.vectorize(sp.lambdify(x, ans - proj_u_v, "numpy"))(xx)) <= threshold).all() else: res = False if res: display(Latex("C'est correct!")) if case_nb == 1: vector_plot_2D([u, v, proj_u_v.tolist(), (np.array(u) - proj_u_v).tolist()], orig=[[0, 0], [0, 0], [0, 0], proj_u_v.tolist()], labels=['u', 'v', 'proj_v(u)', 'u-proj_v(u)']) elif case_nb == 2: vector_plot_3D([u, v, proj_u_v.tolist(), np.array(u - proj_u_v).tolist()], orig=[[0, 0, 0], [0, 0, 0], [0, 0, 0], proj_u_v.tolist()], labels=['u', 'v', 'proj_v(u)', 'u-proj_v(u)']) else: display(Latex(f"Aucune représentation graphique n'est disponible pour le cas de test numéro {case_nb}")) else: display(Latex("C'est faux! N'oubliez pas d'insérer tous les résultats numerique avec 4 chiffres " "après la virgule")) if case_nb == 1: vector_plot_2D([u, v, ans.tolist()], orig=[[0, 0], [0, 0], [0, 0]], labels=['u', 'v', 'ans', 'u-ans']) elif case_nb == 2: vector_plot_3D([u, v, ans.tolist()], orig=[[0, 0, 0], [0, 0, 0], [0, 0, 0]], labels=['u', 'v', 'ans', 'u-ans']) else: display(Latex(f"Aucune représentation graphique n'est disponible pour le cas de test numéro {case_nb}")) if case_nb in {1, 2, 3}: orth = np.inner(u - ans, np.dot(W, v)) elif case_nb in {4, 5}: orth = sp.integrate(weight_function * (u - ans) * v, (x, int_limits[0], int_limits[1])) display(Latex(f"Le produit scalaire entre 'v' et la différence entre 'u' et sa projection doit être égal à " f"zéro, mais il vaut {orth} dans ce cas!")) return def Ex1Chapitre9_8_9(proj1, proj2, case_nb=1): """Provides the correction to exercise 1 of Notebook 9.8-9.9 :param proj1: projection onto W :type proj1: list or numpy.ndarray :param proj2: projection onto the orthogonal complement of W in V :type proj2: list or numy.ndarray :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ if type(proj1) is list: proj1 = np.array(proj1) if type(proj2) is list: proj2= np.array(proj2) if case_nb == 1: S = [[1, 1]] v = [-1, 0] elif case_nb == 2: S = [[0, 1], [-1, 1]] v = [-3, 5] elif case_nb == 3: S = [[1, 2, 1]] v = [1, 0, 1] elif case_nb == 4: S = [[1, -1, 1], [0, 1, 1]] v = [-1, 0, 1] else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") true_proj_1 = project_on_subspace(v, S, W=None, plot=False, show=False) true_proj_2 = np.array(v) - true_proj_1 try: assert proj1.shape == true_proj_1.shape and proj2.shape == true_proj_2.shape except AssertionError: raise ValueError(f"Les projections doivent être des vecteurs 1D de longueur {true_proj_1.shape[0]}, " f"mais le premier a forme {proj1.shape[0]} " f"et la deuxième a forme {proj2.shape[0]}") if np.linalg.norm(proj1 - true_proj_1) <= 1e-4 and np.linalg.norm(proj2 - true_proj_2) <= 1e-4: display(Latex("C'est correct!")) _ = project_on_subspace(v, S, W=None, plot=True, show=True) else: display(Latex(f"C'est faux! En particulier, les normes $l^2$ des erreurs sur les deux projections sont " f"{np.linalg.norm(proj1 - true_proj_1)} and {np.linalg.norm(proj2 - true_proj_2)}.")) return def Ex2Chapitre9_8_9(proj1, proj2, int_limits, case_nb=1): """Provides the correction to exercise 2 of Notebook 9.8-9.9 :param proj1: projection onto W :type proj1: sympy.function :param proj2: projection onto the ortogonal complement of W in V :type proj2: sympy.function :param int_limits: integration limits :type int_limits: list[float] :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ x = sp.Symbol('x') if case_nb == 1: S = [1 + 0 * x, x] v = x**2 elif case_nb == 2: S = [1 + 0 * x, x] v = x ** 2 elif case_nb == 3: S = [1 + 0 * x, x ** 2, x ** 3] v = x ** 2 + x elif case_nb == 4: S = [x ** 2, x ** 3] v = 1 + x + x ** 2 + x ** 3 else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") true_proj_1 = project_on_subspace(v, S, int_limits=int_limits, plot=False, show=False) true_proj_2 = np.array(v) - true_proj_1 try: assert isinstance(proj1, sp.Basic) and isinstance(proj2, sp.Basic) except AssertionError: raise ValueError(f"Les réponses doivent être fonctions univariées!") xx = np.linspace(int_limits[0], int_limits[1], 1000) res1 = (np.abs(np.vectorize(sp.lambdify(x, proj1 - true_proj_1, "numpy"))(xx)) <= 1e-4).all() res2 = (np.abs(np.vectorize(sp.lambdify(x, proj2 - true_proj_2, "numpy"))(xx)) <= 1e-4).all() if res1 and res2: display(Latex("C'est correct!")) _ = project_on_subspace(v, S, W=None, plot=True, show=True) else: display(Latex(f"C'est faux!")) return def Ex3Chapitre9_8_9(basis, case_nb=1): """Provides the correction to exercice 3 of notebook 9.8-9.9 :param basis: orthonormal basis for the subspace, entered by the user :type basis: list[list] or numpy.ndarray :param case_nb: case number. Defaults to 1 :type case_nb: int """ is_correct = True if case_nb == 1: mat = [[1, 2], [0, 1]] elif case_nb == 2: mat = [[1, -1]] elif case_nb == 3: mat = [[1, 2, 1], [0, 1, 0], [-2, 0, -2]] elif case_nb == 4: mat = [[-1, 1, 2]] else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") dim = len(basis) elem_dims = [len(basis[i]) for i in range(dim)] true_elem_dim = len(mat[0]) true_basis = (np.array(null_space(mat))).T true_dim = len(true_basis) if not basis and not true_basis: display(Latex("C'est correct! Le sous-espace $W$ coincide avec $\mathbb{R}^n$, donc $W^\perp$ ne contien " "que le vecteur nul.")) return if type(basis) is list: basis = np.array(basis) try: assert type(basis) is np.ndarray and len(basis.shape) == 2 except AssertionError: raise TypeError("The entered basis must be either a 2D numpy array or a list of lists") if not all([elem_dims[i] == true_elem_dim for i in range(dim)]): display(Latex(f"C'est faux! Tous les éléments de la base doivent avoir dimension {true_elem_dim}!")) return if dim != true_dim: display(Latex("C'est faux! La dimension de la base donnée est fausse!")) return if not isDiag(basis.dot(basis.T)): display(Latex("C'est faux! La base doit être orthogonale!")) return i = 0 while i in range(dim) and is_correct: proj = project_on_subspace(basis[i], true_basis, plot=False, show=False) if np.linalg.norm(basis[i] - proj) >= 1e-4: is_correct = False i += 1 i = 0 while i in range(true_dim) and is_correct: true_proj = project_on_subspace(true_basis[i], basis, plot=False, show=False) if np.linalg.norm(true_basis[i] - true_proj) >= 1e-4: is_correct = False i += 1 if is_correct: display(Latex("C'est correct!")) else: display(Latex("C'est faux!")) if len(mat[0]) == 2 and len(basis) == 1: fig = vector_plot_2D(list(chain(*[mat, basis])), labels=list(chain(*[[f'$w_{i}$' for i in range(len(mat))], [f'$w^\perp_{i}$' for i in range(len(basis))]])), show=False) fig = plot_line([mat[0]], data=list(fig.data), layout=fig.layout, label=r'$W$') fig = plot_line(basis, data=list(fig.data), layout=fig.layout, label=r'$W^\perp$') plotly.offline.iplot(fig) elif len(mat[0]) == 3: fig = vector_plot_3D(list(chain(*[mat, basis])), labels=list(chain(*[[f'$w_{i}$' for i in range(len(mat))], [f'$w^\perp_{i}$' for i in range(len(basis))]])), show=False) if len(basis) == 1: _, inds = sp.Matrix(mat).T.rref() fig = plot_plane(np.array(mat)[inds, :], color='rgb(0,255,255)', data=list(fig.data), layout=fig.layout, label=r'$W$') fig = plot_line(basis, data=list(fig.data), layout=fig.layout, label=r'$W^\perp$') elif len(basis) == 2: fig = plot_line([mat[0]], data=list(fig.data), layout=fig.layout, label=r'$W$') fig = plot_plane(basis, color='rgb(0,255,255)', data=list(fig.data), layout=fig.layout, label=r'$W^\perp$') plotly.offline.iplot(fig) return def Ex1Chapitre9_10_11(proj, case_nb=1): """Provides the correction to exercise 1 of Notebook 9.10-9.11 :param proj: projection onto W :type proj: list or numpy.ndarray :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ if type(proj) is list: proj = np.array(proj) if case_nb == 1: S = [[1, -2]] v = [-2, 1] elif case_nb == 2: S = [[0, 1, 0], [1, -1, 0]] v = [-3, 2, 1] elif case_nb == 3: S = [[1, 2, -1, -2], [0, 1, 0, -1]] v = [0, -1, 1, -1] else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3]") true_proj = project_on_subspace(v, S, W=None, plot=False, show=False) try: assert proj.shape == true_proj.shape except AssertionError: raise ValueError(f"La meilleure approximation quadratique dois être un vecteur 1D de longueur " f"{true_proj.shape[0]}, mais il a forme {proj.shape[0]}") if np.linalg.norm(proj - true_proj) <= 1e-4: display(Latex("C'est correct!")) _ = project_on_subspace(v, S, W=None, plot=True, show=True) else: display(Latex(f"C'est faux! En particulier, la norme $l^2$ de l'erreur est " f"{np.linalg.norm(proj - true_proj)}")) return def Ex2Chapitre9_10_11(proj, int_limits, case_nb=1): """Provides the correction to exercise 2 of Notebook 9.10-9.11 :param proj: projection onto W :type proj: sympy.function :param int_limits: integration limits :type int_limits: list[float] :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ x = sp.Symbol('x') if case_nb == 1: S = [1 + 0 * x, x] v = sp.Abs(x) elif case_nb == 2: S = [1 + 0 * x, x, x**2] v = sp.Abs(x) elif case_nb == 3: S = [1 + 0 * x, x, x ** 2] v = sp.sin(x) elif case_nb == 4: S = [1 + 0 * x, x, x ** 2, x ** 3] v = sp.exp(x) else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") true_proj = project_on_subspace(v, S, int_limits=int_limits, plot=False, show=False) try: assert isinstance(proj, sp.Basic) except AssertionError: raise ValueError(f"Les réponses doivent être fonctions univariées!") xx = np.linspace(int_limits[0], int_limits[1], 1000) res = (np.abs(np.vectorize(sp.lambdify(x, proj - true_proj, "numpy"))(xx)) <= 1e-4).all() if res: display(Latex("C'est correct!")) _ = project_on_subspace(v, S, int_limits=int_limits, plot=True, show=True) else: display(Latex(f"C'est faux!")) return def Ex1_Chapitre9_12(sol, case_nb=1): """Provides the correction to exercise 1 of Notebook 9.12 :param sol: solution to the system, in LS sense :type sol: sp.FiniteSet :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ x, y, z = sp.symbols('x, y, z') if case_nb == 1: A = np.array([[1, 0], [1, 0]]) b = np.array([1, 3]) elif case_nb == 2: A = np.array([[1, 1], [1, -1], [2, 0]]) b = np.array([1, 2, -2]) elif case_nb == 3: A = np.array([[1, 0, 0], [0, 1, 1], [1, 0, 0]]) b = np.array([-1, 2, 1]) elif case_nb == 4: A = np.array([[1, 0, 1], [-1, 1, -1], [0, 1, 1], [1, 1, 0], [-1, 0, 1]]) b = np.array([0, 2, 0, 1, 4]) else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") sys = sp.Matrix(A.T@A), sp.Matrix(A.T@b) sol_true = linsolve(sys, x, y) if case_nb in {1,2} else linsolve(sys, x, y, z) is_correct = compare_sympy_FiniteSets(sol, sol_true) if is_correct: display(Latex("C'est correct!")) if case_nb in {1,2}: Plot2DSys(-7, 7, 15, A.tolist(), b.tolist(), with_sol=True, with_sol_lstsq=True) else: Plot3DSys(-7, 7, 15, A, b, with_sol=True, with_sol_lstsq=True) else: display(Latex("C'est faux!")) return def Ex2_Chapitre9_12(sol, case_nb=1): """Provides the correction to exercise 2 of Notebook 9.12 :param sol: solution to the system, in LS sense :type sol: sp.FiniteSet :param case_nb: number of the test case. Defaults to 1 :type case_nb: int """ x, y, z = sp.symbols('x, y, z') if case_nb == 1: A = np.array([[0, 1], [0, -1]]) b = np.array([0, 2]) elif case_nb == 2: A = np.array([[0, 1], [1, 1], [1, -2]]) b = np.array([1, 1, 0]) elif case_nb == 3: A = np.array([[0, 1, 0], [1, 0, 1], [-2, 0, 0]]) b = np.array([0, 2, -1]) elif case_nb == 4: A = np.array([[1, 0, 0], [1, -1, -1], [0, 1, 0], [1, 0, 0], [0, 1, -1]]) b = np.array([2, 0, 0, -1, -1]) else: raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" f"Numéros de cas disponibles: [1,2,3,4]") sys = sp.Matrix(A.T @ A), sp.Matrix(A.T @ b) sol_true = linsolve(sys, x, y) if case_nb in {1,2} else linsolve(sys, x, y, z) is_correct = compare_sympy_FiniteSets(sol, sol_true) if is_correct: display(Latex("C'est correct!")) if case_nb in {1,2}: Plot2DSys(-7, 7, 15, A.tolist(), b.tolist(), with_sol=True, with_sol_lstsq=True) else: Plot3DSys(-7, 7, 15, A.tolist(), b.tolist(), with_sol=True, with_sol_lstsq=True) else: display(Latex("C'est faux!")) return def Ex3_Chapitre9_12_generate_data(noise_scale_1, noise_scale_2, num_points=251, plot=True, K=1): """Generates the data used in exercise 3 of notebook 9.12 :param noise_scale_1: first noise scale parameter, representing the scale factor on the standard deviation of the first uncorrelated gaussian noise component :type noise_scale_1: float :param noise_scale_2: second noise component scale parameter, representing the scale factor on the standard deviation of the second correlated noise component :type noise_scale_2: float :param num_points: number of datapoints to be generated. Defaults to 251 :type num_points: int :param plot: if True, a scatter plot of the data (2D or 3D depending on the case number) is displayed. Defaults to True. :type plot: bool :param K: number of the case. Defaults to 1 :type K: int :return: generated data and figure, if 'plot' is set to True :rtype: Union(list[np.ndarray], plotly.Figure) or list[np.ndarray] """ try: assert K in {1, 2} except AssertionError: raise ValueError(f"{K} n'est pas un numéro valide!" f"Numéros disponibles: [1,2]") x1 = np.random.uniform(-1, 1, num_points) x2 = np.random.uniform(-1, 1, num_points) if K == 2 else None ones = np.ones_like(x1) X = np.hstack((np.expand_dims(ones, 1), np.expand_dims(x1, 1))) if K == 2: X = np.hstack((X, np.expand_dims(x2, 1))) coeffs = np.random.uniform(-2, 2, 2 if K == 1 else 3) y = coeffs[0] + coeffs[1] * x1 + (coeffs[2] * x2 if K == 2 else 0) Y = y + np.mean(y) * noise_scale_1 * np.random.normal(0, 1, num_points) + \ noise_scale_2 * np.random.normal(-1, 1, num_points) * np.abs(y) if plot: fig = scatterPlot(x1=x1, x2=x2, y=Y) data = [X, Y] if plot: return data, fig else: return data def Ex3_Chapitre9_12(sys, data, fig): """Provides the solution to exercise 3 of notebook 9.12 :param sys: matrix and right-hand side of the linear system to be solved :type sys: tuple(np.ndarray, np.ndarray) or tuple(list[list], list) or tuple(sp.Matrix, sp.Matrix) :param data: data on which doing linear regression via Least-Squares :type data: tuple(np.ndarray, np.ndarray) or list[np.ndarray, np.ndarray] :param fig: figure containing the data scatter plot, to be completed with the LS solution :type fig: plotly.Figure """ + if not isinstance(sys[0], sp.Matrix) or not isinstance(sys[1], sp.Matrix): sys = list(sys) if not isinstance(sys[0], sp.Matrix): sys[0] = sp.Matrix(sys[0]) if not isinstance(sys[1], sp.Matrix): sys[1] = sp.Matrix(sys[1]) sys = tuple(sys) X = data[0] Y = data[1] c0, c1, c2 = sp.symbols('c0, c1, c2') if X.shape[1] == 2: sol = linsolve(sys, c0, c1) elif X.shape[1] == 3: sol = linsolve(sys, c0, c1, c2) else: raise ValueError(f"La matrice de données X doit avoir 2 ou 3 colonnes, alors qu'ici elle en a {X.shape[1]}") print(f"Coefficients de la solution entrée: {sol.args[0]}") true_M = sp.Matrix(X.T@X) true_f = sp.Matrix(X.T@Y) true_sys = true_M, true_f if X.shape[1] == 2: true_sol = linsolve(true_sys, c0, c1) elif X.shape[1] == 3: true_sol = linsolve(true_sys, c0, c1, c2) else: raise ValueError(f"La matrice de données X doit avoir 2 ou 3 colonnes, alors qu'ici elle en a {X.shape[1]}") print(f"Coefficients de la solution: {true_sol.args[0]}") is_correct = compare_sympy_FiniteSets(sol, true_sol) x1 = np.linspace(-1, 1, 501) x2 = np.linspace(-1, 1, 501) if X.shape[1] == 3: x1Grid, x2Grid = np.meshgrid(x1, x2) fig_data = list(fig.data) fig_layout = fig.layout red = 'rgb(255, 0, 0)' green = 'rgb(0, 255, 0)' colorscale_red = [[i, red] for i in np.linspace(0, 1, 11)] colorscale_green = [[i, green] for i in np.linspace(0, 1, 11)] if is_correct: display(Latex("C'est correct!")) if X.shape[1] == 2: trace1 = go.Scatter(x=x1, y=float(sol.args[0][0]) + float(sol.args[0][1]) * x1, mode='lines', line=dict(color=red), name='Solution') elif X.shape[1] == 3: trace1 = go.Surface(x=x1Grid, y=x2Grid, z=float(sol.args[0][0]) + float(sol.args[0][1]) * x1Grid + float(sol.args[0][2]) * x2Grid, opacity=0.6, colorscale=colorscale_red, showscale=False, showlegend=True, name='Solution') else: display(Latex("C'est faux!")) do_second_plot = False if X.shape[1] == 2: trace1 = go.Scatter(x=x1, y=float(true_sol.args[0][0]) + float(true_sol.args[0][1]) * x1, mode='lines', line=dict(color=red), name='True Solution') if isinstance(sol, sp.sets.FiniteSet) and \ all([isinstance(sol.args[0][i], sp.Number) for i in range(len(sol.args[0]))]): trace2 = go.Scatter(x=x1, y=float(sol.args[0][0]) + float(sol.args[0][1]) * x1, mode='lines', line=dict(color=green), name='Your Solution') do_second_plot = True elif isinstance(sol, sp.sets.EmptySet): display(Latex("Le système entré n'admet aucune solution! " "Etes-vous sûr qu'il est écrit sous la forme des moindres carrés?")) else: display(Latex("Le système entré admet plusieurs solutions!")) elif X.shape[1] == 3: trace1 = go.Surface(x=x1Grid, y=x2Grid, z=float(true_sol.args[0][0]) + float(true_sol.args[0][1]) * x1Grid + float(true_sol.args[0][2]) * x2Grid, opacity=0.6, colorscale=colorscale_red, showscale=False, showlegend=True, name='True Solution') if isinstance(sol, sp.sets.FiniteSet) and \ all([isinstance(sol.args[0][i], sp.Number) for i in range(len(sol.args[0]))]): trace2 = go.Surface(x=x1Grid, y=x2Grid, z=float(sol.args[0][0]) + float(sol.args[0][1]) * x1Grid + float(sol.args[0][2]) * x2Grid, opacity=0.6, colorscale=colorscale_green, showscale=False, showlegend=True, name='Your Solution') do_second_plot = True elif isinstance(sol, sp.sets.EmptySet): display(Latex("Le système entré n'admet aucune solution! " "Etes-vous sûr qu'il est écrit sous la forme des moindres carrés?")) else: display(Latex("Le système entré admet plusieurs solutions!")) fig_data.append(trace1) if not is_correct and do_second_plot: fig_data.append(trace2) new_fig = go.Figure(data=fig_data, layout=fig_layout) plotly.offline.iplot(new_fig) - return \ No newline at end of file + return + + +def Ex2Chapitre9_13_14(Q, R, sol, case_nb=1): + """Provides the solution to exercise 2 of notebook 9.13-9.14 + + :param Q: orthogonal matrix of the factorisation + :type Q: list[list] or np.ndarray + :param R: superior triangular matrix of the factorisation + :type R: list[list] or np.ndarray + :param sol: LS solution to the linear system + :type sol: sp.sets.FiniteSet or sp.sets.EmptySet + :param case_nb: current case number. Defaults to 1 + :type case_nb: int + """ + + if not isinstance(Q, np.ndarray): + Q = np.array(Q) + if not isinstance(R, np.ndarray): + R = np.array(R) + + x, y, z = sp.symbols('x, y, z') + + if case_nb == 1: + A = np.array([[1, 1], [1, -1]]) + b = np.array([1, -1]) + elif case_nb == 2: + A = np.array([[-2, 1], [1, 3], [2, 0]]) + b = np.array([1, 2, 0]) + elif case_nb == 3: + A = np.array([[1, 0, 0], [2, 0, 1], [0, 4, 0], [0, 3, 2], [2, 0, 2]]) + b = np.array([0, 1, 0, -1, 1]) + elif case_nb == 4: + A = np.array([[5, 0, 0], [-1, -2, -2], [1, 1, 2], [3, 2, 0], [0, 0, 1]]) + b = np.array([0, 2, 0, 1, 4]) + else: + raise ValueError(f"{case_nb} n'est pas un numéro de cas valide!" + f"Numéros de cas disponibles: [1,2,3,4]") + + try: + Q_true, R_true = unique_qr(A, only_valid=True) + except ValueError: + if Q is None and R is None and x is None: + display(Latex("C'est correct! La matrice $A$ ne correspond pas aux exigences qui garantissent l'existence " + "d'une factorisation QR 'valide'!")) + else: + display(Latex("C'est faux! En effet, la matrice $A$ ne correspond pas aux exigences qui garantissent " + "l'existence d'une factorisation QR 'valide'; vous devez donc entrer 'None' pour les valeurs " + "des $Q$, $R$ et de la solution $\hat{x}$ au système linéaire au sens des moindres carrés.")) + return + + Q_correct = np.linalg.norm(Q - Q_true) < 1e-4 + R_correct = np.linalg.norm(R - R_true) < 1e-4 + + sys = sp.Matrix(R_true), sp.Matrix(Q_true.T @ b) + sol_true = linsolve(sys, x, y) if case_nb in {1, 2} else linsolve(sys, x, y, z) + sol_correct = compare_sympy_FiniteSets(sol, sol_true) + + if Q_correct and R_correct and sol_correct: + display(Latex("C'est correct!")) + if case_nb in {1,2}: + Plot2DSys(-7, 7, 15, A.tolist(), b.tolist(), with_sol=True, with_sol_lstsq=True) + else: + Plot3DSys(-7, 7, 15, A.tolist(), b.tolist(), with_sol=True, with_sol_lstsq=True) + elif not Q_correct or not R_correct: + display(Latex("C'est faux! La factoristation QR de A n'est pas correct!")) + elif not sol_correct: + display(Latex("C'est faux! La factorisation QR de $A$ est correcte, mais la solution du système linéaire au " + "le sens des moindres carrés n'est pas!")) + + return +