Source code for Mes_fctions_d_alg_lineaire_bis

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 17 19:11:52 2022

@author: jlebovits
"""
from __future__ import division
import sys
from copy import deepcopy
from src.scripts.pxs_runtime import myst, get_pxs_lang
from sympy.printing.latex import LatexPrinter
from re import sub

# import src.scripts.Mes_fctions.Mes_fctions_deterministes
# from src.scripts.Mes_fctions.Mes_fctions_deterministes import *

# import src.scripts.Mes_fctions.Mes_fctions_generalistes
# from src.scripts.Mes_fctions.Mes_fctions_generalistes import *

# import src.scripts.Mes_fctions.Mes_fctions_probabilistes
# from src.scripts.Mes_fctions.Mes_fctions_probabilistes import *

# import src.scripts.Mes_fctions.Mes_fctions_d_ecriture_Latex
# from src.scripts.Mes_fctions.Mes_fctions_d_ecriture_Latex import *

# import src.scripts.Mes_fctions.Mes_fctions_d_alg_generale
# from src.scripts.Mes_fctions.Mes_fctions_d_alg_generale import *


from sympy import *
import functools as fct
import math as m
import random as rd
import numpy as np
from re import sub, match
from src.scripts.Mes_fctions.Mes_fctions_d_alg_lineaire import randmatrixdiagonale, zeros

from src.scripts.Mes_fctions.Classes_Extensions import pxs_Poly

# from src.scripts.Mes_fctions.Mes_fctions_probabilistes_bis import pxsl_res_num
from sympy.stats import P, E, variance, Die, Normal, DiscreteUniform, Bernoulli, sample, Binomial, density,  Normal, sample_iter, given


## ============== Copiées depuis Mes_fctions_generalistes_bis ==============

[docs] def pxs_config(mul_symbol: str = "") -> dict: """ Build a configuration dictionary for LaTeX rendering, depending on the current pyxisciences language settings. The language is retrieved using `get_pxs_lang()` and affects some formatting options, such as the decimal separator. Parameters ---------- mul_symbol : str, optional Multiplication symbol to be used in LaTeX output (default is ""). Returns ------- dict A dictionary containing LaTeX configuration options, including: - ln_notation : bool - mul_symbol : str - order : str - decimal_separator : str - inv_trig_style : str Examples -------- >>> pxs_config() {'ln_notation': True, 'mul_symbol': '', 'order': 'lex', ...} """ pxs_lang = get_pxs_lang() if pxs_lang == 'fr': return { "ln_notation": True, "mul_symbol": mul_symbol, "order": "lex", "decimal_separator": "comma", "inv_trig_style": "full", } else: return { "ln_notation": True, "mul_symbol": mul_symbol, "order": "lex", "decimal_separator": "dot", "inv_trig_style": "full", }
[docs] def simplify_plus_minus(txt): separators = r'(?:\s|\\;|\\displaystyle)*' return sub(r'\+' + separators + r'-', '-', txt)
[docs] def pxsl_add(*args, zeros = False): config_standard = pxs_config() ln_notation = config_standard["ln_notation"] mul_symbol = config_standard["mul_symbol"] dec_sep = config_standard["decimal_separator"] inv_trig = config_standard["inv_trig_style"] terms = [sympify(x) for x in args if (zeros or x)] if terms == []: terms = [sympify(0)] expr = Add(*terms, evaluate = False) return LatexPrinter(dict(order = "none", ln_notation = ln_notation, mul_symbol = mul_symbol, decimal_separator = dec_sep, inv_trig_style = inv_trig))._print_Add(expr)
[docs] def pxs_randint(mini, maxi, exclude = []): """ Returns a random integer between mini and maxi avoiding the element(s) in exclude. Exclude can be an integer or a collection of integers. """ if isinstance(exclude, int): exclude = exclude, st = set(range(mini, maxi + 1)) - set(exclude) return rd.choice(list(st))
## ==============
[docs] def pxsl_pow(x, n=1, opt=0, displaystyle=True): """ Fr : Fonction permettant d'écrire le nombre x entouré de parenthèses lorsqu'il est négatif ou irrationnel avec deux termes (par ex : 1+sqrt(2) ou 3sqrt(2)) Ne fonctionne pas pour des valeurs numériques non simplifiées (par ex : 1+3 ou 3*3/2) En : Function that writes the number x surrounded by parentheses when it is negative or irrational with two terms (e.g.: 1+sqrt(2) or 3sqrt(2)) Does not work for unsimplified numerical values (e.g.: 1+3 or 3*3/2) Version 5 --------- 13/02/25 Vérification ------ Auteur : Delphine Vérificateurs : ?? Paramètres ---------- x : nombre ou expression La base à élever à la puissance n n : int, optional L'exposant (défaut: 1) opt : int, optional Option de formatage (défaut: 0) 0: formatage standard 1: simplifie l'affichage pour x=1, x=0 ou n=1 2: simplifie davantage et renvoie une chaîne vide pour x=0 displaystyle : bool, optional Si True, utilise \displaystyle pour les fractions (défaut: False) Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- pxsl_sum_matrix, pxsl_prod_scalar_matrix, pxsl_prod_matrix, pxsl_pow_matrix """ # Préparation de l'expression LaTeX selon le mode displaystyle if displaystyle: latex_x = r"\displaystyle " + latex(x) else: latex_x = latex(x) # Cas où x est une expression (Add ou Mul) ou nombre négatif: if isinstance(x, Add) or isinstance(x, Mul) : if n == 1 : return myst(r"""\left(\py{latex_x}\right)""", globals(), locals()) else: return myst(r"""\left(\py{latex_x}\right)^{\py{n}}""", globals(), locals()) # Cas où x est un Rational elif isinstance(x,Rational) and x.q!=1: if n == 1 : # Pas de parenthèses quand n=1 if x < 0: # si la fraction est négative il faut des parenthèses return myst(r"""\left(\py{latex_x}\right)""", globals(), locals()) else: return myst(r"""\py{latex_x}""", globals(), locals()) else: # Parenthèses quand n différent de 1 return myst(r"""\left(\py{latex_x}\right)^{\py{n}}""", globals(), locals()) # Cas où x est un Symbol: elif isinstance(x,Symbol): if n == 1: return myst(r"""\py{latex_x}""", globals(), locals()) else: return myst(r"""\py{latex_x}^{\py{n}}""", globals(), locals()) # Cas où x est strictement négatif elif x<0: if n == 1: return myst(r"""\left(\py{latex_x}\right)""", globals(), locals()) else: return myst(r"""\left(\py{latex_x}\right)^{\py{n}}""", globals(), locals()) # Cas où x est un nombre positif ou nul else: # Option 0: formatage standard if opt == 0: if n == 1: return myst(r"""\py{latex_x}""", globals(), locals()) else: return myst(r"""\py{latex_x}^{\py{n}}""", globals(), locals()) # Option 1: simplifie pour x=0, x=1 ou n=1 elif opt == 1: if x == 1 or x == 0 or n == 1: return myst(r"""\py{latex_x}""", globals(), locals()) else: return myst(r"""\py{latex_x}^{\py{n}}""", globals(), locals()) # Option 2: simplifie davantage, chaîne vide pour x=0 else: # opt == 2 ou autres valeurs if x == 0: return myst(r""" """, globals(), locals()) elif x == 1 or n == 1: return myst(r"""\py{latex_x}""", globals(), locals()) else: return myst(r"""\py{latex_x}^{\py{n}}""", globals(), locals())
################ EXEMPLES ################## # pxsl_pow(3,2) retourne l'écriture latex de 3^{2} # pxsl_pow(-3,2) retourne l'écriture latex de \left(-3\right)^{2} # pxsl_pow(-3+sqrt(2),3) retourne l'écriture latex de \left(-3+\sqrt{2}\right)^{3} # pxsl_pow(-3*sqrt(2),3) retourne l'écriture latex de \left(-3\sqrt{2}\right)^{3} # pxsl_pow(3,Symbol('n')) retourne l'écriture latex de 3^{n} # pxsl_pow(1,'n') retourne l'écriture latex de 1^n # pxsl_pow(0,Symbol('n')) retourne l'écriture latex de 0^n # # x=Symbol('x') # y=Symbol('y') # pxsl_pow(x+y,Symbol('n')) retourne l'écriture latex de \left(x+y\right)^{n} # # x=Symbol('x') # y=Symbol('y') # pxsl_pow(x*y,Symbol('n')) retourne l'écriture latex de \left(x y\right)^{n}
[docs] def pxsl_matrix(A, sepG="(", sepD=")", display=False, res_num=False): """ Return a LaTeX representation of a matrix with right-aligned entries. This function converts a matrix into a nicely formatted LaTeX matrix. By default, entries are displayed as raw values. Optional display modes allow symbolic LaTeX rendering or numerical result formatting. Parameters ---------- A : Matrix The matrix to be displayed. sepG : str, optional Left delimiter of the matrix (default "("). In English mode, it is automatically replaced by "[". sepD : str, optional Right delimiter of the matrix (default ")"). In English mode, it is automatically replaced by "]". display : bool, optional If True, matrix entries are rendered in LaTeX format. res_num : bool, optional If True (and `display=True`), entries are displayed as numerical results. Returns ------- str A LaTeX string representing the formatted matrix. Examples -------- Basic usage with raw values: >>> from sympy import Matrix >>> A = Matrix([[1, 2], [3, 4]]) >>> pxsl_matrix(A) '\\\\left(\\begin{array}{rr}1 & 2\\\\[0.3em]3 & 4\\end{array}\\\\right)' Using custom delimiters: >>> pxsl_matrix(A, sepG='[', sepD=']') '\\\\left[\\begin{array}{rr}1 & 2\\\\[0.3em]3 & 4\\end{array}\\\\right]' Displaying symbolic expressions in LaTeX: >>> from sympy import symbols >>> x = symbols('x') >>> B = Matrix([[x, x**2], [1/x, 2]]) >>> pxsl_matrix(B, display=True) '\\\\left(\\begin{array}{rr}x & x^{2}\\\\[0.3em]\\frac{1}{x} & 2\\end{array}\\\\right)' Displaying numerical results (after evaluation): >>> from sympy import Rational >>> C = Matrix([[Rational(1, 2), Rational(3, 4)], [1, 2]]) >>> pxsl_matrix(C, display=True, res_num=True) '\\\\left(\\begin{array}{rr}0.5 & 0.75\\\\[0.3em]1 & 2\\end{array}\\\\right)' """ [n,p]=A.shape pxs_lang = get_pxs_lang() config_standard = pxs_config() if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left{{sepG}}\begin{array}{{{'r'*p}}}""",globals(),locals()) # lazy import to avoid circular import if display and res_num: from src.scripts.Mes_fctions.Mes_fctions_generalistes_bis import pxsl_num for i in range(n): if display: if res_num: expr+=myst(r"""{{pxsl_num(A[i,0])}}""",globals(),locals()) else: expr+=myst(r"""{{latex(A[i,0], **config_standard)}}""",globals(),locals()) else: expr+=myst(r""" {{A[i,0]}}""",globals(),locals()) for j in range(p-1): if display: if res_num: expr+=myst(r""" &{{pxsl_num(A[i,1+j])}}""",globals(),locals()) else: expr+=myst(r""" &{{latex(A[i,1+j], **config_standard)}}""",globals(),locals()) else: expr+=myst(r""" &{{A[i,1+j]}}""",globals(),locals()) expr+=myst(r"""\\[0.3em]""") expr+=myst(r"""\end{array}\right{{sepD}}""",globals(),locals()) return expr
################ EXEMPLES ################## # pxsl_matrix(Matrix([[1,2,3],[2,-3,4]])) retourne # l'expression latex : \left(\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right) # soit la matrice (entourée de parenthèses) : # 1 2 3 # 2 -3 4 # pxsl_matrix(Matrix([[1,2,3],[2,-3,4]]),'|','|') retourne # l'expression latex : \left|\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right| # soit le déterminant (matrice entourée de |) : # 1 2 3 # 2 -3 4 # on peut aussi utiliser une forme de séparateur d'un côté et un autre de l'autre côté
[docs] def pxsl_mat(A, sepG="(", sepD=")", display=False, frac = False, res_num=False, color = "blue", row = "", col = "", union = True, order = True, dec = 4): """ Return a LaTeX representation of a SymPy matrix. The function converts the entries of a matrix into a LaTeX array enclosed between customizable delimiters. It can display entries in normal mode, display mode, fractional LaTeX form, numerical approximation, and can highlight a row, a column, or their intersection. Parameters ---------- A : sympy.Matrix Matrix to be converted into LaTeX. sepG : str, optional Left delimiter. Default is ``"("``. In English mode, the default parenthesis is automatically replaced by ``"["``. sepD : str, optional Right delimiter. Default is ``")"``. In English mode, the default parenthesis is automatically replaced by ``"]"``. display : bool, optional If ``True``, each entry is written in display style using ``\ds``. This is useful for entries containing fractions, sums, powers, or other expressions that are more readable in display mode. Default is ``False``. frac : bool, optional If ``True``, entries are rendered using SymPy's ``latex`` function. This is useful when entries are rational numbers or symbolic expressions. Default is ``False``. res_num : bool, optional If ``True``, entries are rendered as numerical approximations using ``pxsl_num``. This option has priority over ``display`` and ``frac``. Default is ``False``. color : str, optional LaTeX color used to highlight selected entries. Default is ``"blue"``. row : int or str, optional Index of the row to highlight. Row indices start at ``0``. If no row should be highlighted, keep the default value ``""``. col : int or str, optional Index of the column to highlight. Column indices start at ``0``. If no column should be highlighted, keep the default value ``""``. order : bool, optional If True, the order of the terms is preserved when coefficients are litteral expressions. Default is True union : bool, optional Determines how ``row`` and ``col`` are combined when both are given. - If ``True``, all entries in the selected row or in the selected column are highlighted. - If ``False``, only the entry at the intersection of the selected row and selected column is highlighted. Default is ``True``. Returns ------- str A LaTeX string representing the matrix. Examples -------- Basic matrix with default delimiters: >>> from sympy import Matrix >>> A = Matrix([[1, 2], [3, 4]]) >>> pxsl_matrix(A) '\\left(\\begin{array}{rr} 1 & 2\\\\[6pt] 3 & 4\\\\[6pt]\\end{array}\\right)' Use custom delimiters: >>> pxsl_matrix(A, sepG="[", sepD="]") '\\left[\\begin{array}{rr} 1 & 2\\\\[6pt] 3 & 4\\\\[6pt]\\end{array}\\right]' Display fractions using LaTeX formatting: >>> from sympy import Rational >>> B = Matrix([[Rational(1, 2), Rational(2, 3)], [Rational(3, 4), 1]]) >>> pxsl_matrix(B, frac=True) '\\left(\\begin{array}{rr}\\frac{1}{2} & \\frac{2}{3}\\\\[6pt]\\frac{3}{4} & 1\\\\[6pt]\\end{array}\\right)' Use display style for larger entries: >>> pxsl_matrix(B, display=True) '\\left(\\begin{array}{rr}\\ds \\frac{1}{2} &\\ds \\frac{2}{3}\\\\[10pt]\\ds \\frac{3}{4} &\\ds 1\\\\[10pt]\\end{array}\\right)' Display numerical approximations: >>> pxsl_matrix(B, res_num=True) '\\left(\\begin{array}{rr}0.5 & 0.67\\\\[6pt]0.75 & 1\\\\[6pt]\\end{array}\\right)' Highlight a row: >>> pxsl_matrix(A, row=1) '\\left(\\begin{array}{rr} 1 & 2\\\\[6pt] \\textcolor{blue}{3} &\\textcolor{blue}{4}\\\\[6pt]\\end{array}\\right)' Highlight a column: >>> pxsl_matrix(A, col=0, color="red") '\\left(\\begin{array}{rr} \\textcolor{red}{1} & 2\\\\[6pt] \\textcolor{red}{3} & 4\\\\[6pt]\\end{array}\\right)' Highlight the union of a row and a column: >>> pxsl_matrix(A, row=0, col=1, color="purple", union=True) '\\left(\\begin{array}{rr}\\textcolor{purple}{1} &\\textcolor{purple}{2}\\\\[6pt] 3 &\\textcolor{purple}{4}\\\\[6pt]\\end{array}\\right)' Highlight only the intersection of a row and a column: >>> pxsl_matrix(A, row=0, col=1, color="purple", union=False) '\\left(\\begin{array}{rr} 1 &\\textcolor{purple}{2}\\\\[6pt] 3 & 4\\\\[6pt]\\end{array}\\right)' Notes ----- The priority order for rendering entries is: 1. ``res_num=True``: numerical approximation with ``pxsl_num``; 2. ``display=True``: LaTeX display style with ``\ds``; 3. ``frac=True``: LaTeX formatting with SymPy's ``latex``; 4. otherwise: raw entry display. Row and column indices start at ``0``. """ [n,p]=A.shape pxs_lang = get_pxs_lang() config_standard = pxs_config() if order: config_standard["order"] = "none" if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left\py{sepG}\begin{array}{\py{'r'*p}}""",globals(),locals()) # lazy import to avoid circular import if res_num: from src.scripts.Mes_fctions.Mes_fctions_generalistes_bis import pxsl_num for i in range(n): res = False if res_num and union and (row == i or col == 0): expr+=myst(r"""\textcolor{{{color}}}{{{pxsl_num(A[i,0], dec = dec)}}}""",globals(),locals()) res = True elif res_num and not union and (row == i and col == 0): expr+=myst(r"""\textcolor{{{color}}}{{{pxsl_num(A[i,0], dec = dec)}}}""",globals(),locals()) res = True elif res_num: expr+=myst(r"""{{pxsl_num(A[i,0], dec = dec)}}""",globals(),locals()) res = True if display and union and (row == i or col == 0): expr+=myst(r"""\ds \textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) res = True elif display and not union and (row == i and col == 0): expr+=myst(r"""\ds \textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) res = True elif display: expr+=myst(r"""\ds {{latex(A[i,0], **config_standard)}}""",globals(),locals()) res = True if frac and not res: if union and (row == i or col == 0): expr+=myst(r"""\textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) elif not union and (row == i and col == 0): expr+=myst(r"""\textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) else: expr+=myst(r"""{{latex(A[i,0], **config_standard)}}""",globals(),locals()) elif isinstance(A[i, 0], Add) and not res: config_standard["fold_short_frac"] = True if union and(row == i or col == 0): expr+=myst(r""" \textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) elif not union and(row == i and col == 0): expr+=myst(r""" \textcolor{{{color}}}{{{latex(A[i,0], **config_standard)}}}""",globals(),locals()) else: expr+=myst(r""" {{latex(A[i,0], **config_standard)}}""",globals(),locals()) elif not res: if union and(row == i or col == 0): expr+=myst(r""" \textcolor{{{color}}}{{{A[i,0]}}}""",globals(),locals()) elif not union and(row == i and col == 0): expr+=myst(r""" \textcolor{{{color}}}{{{A[i,0]}}}""",globals(),locals()) else: expr+=myst(r""" {{A[i,0]}}""",globals(),locals()) for j in range(p-1): res = False if res_num and union and (row == i or col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{pxsl_num(A[i,1+j], dec = dec)}}}""",globals(),locals()) res = True elif res_num and not union and (row == i and col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{pxsl_num(A[i,1+j], dec = dec)}}}""",globals(),locals()) res = True elif res_num: expr+=myst(r""" &{{pxsl_num(A[i,1+j], dec = dec)}}""",globals(),locals()) res = True if display and union and (row == i or col == j+1): expr+=myst(r""" &\ds \textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) res = True elif display and not union and (row == i and col == j+1): expr+=myst(r""" &\ds \textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) res = True elif display: expr+=myst(r""" &\ds {{latex(A[i,1+j], **config_standard)}}""",globals(),locals()) res = True if frac and not res: if union and (row == i or col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) elif not union and (row == i and col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) else: expr+=myst(r""" &{{latex(A[i,1+j], **config_standard)}}""",globals(),locals()) elif isinstance(A[i, 1 + j], Add) and not res: config_standard["fold_short_frac"] = True if union and(row == i or col == j + 1): expr+=myst(r""" &\textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) elif not union and(row == i and col == j + 1): expr+=myst(r""" &\textcolor{{{color}}}{{{latex(A[i,1+j], **config_standard)}}}""",globals(),locals()) else: expr+=myst(r""" &{{latex(A[i,1+j], **config_standard)}}""",globals(),locals()) elif not res: if union and (row == i or col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{A[i,1+j]}}}""",globals(),locals()) elif not union and (row == i and col == j+1): expr+=myst(r""" &\textcolor{{{color}}}{{{A[i,1+j]}}}""",globals(),locals()) else: expr+=myst(r""" &{{A[i,1+j]}}""",globals(),locals()) if display: if i == n-1: expr+=myst(r""" """) else: expr+=myst(r"""\\[10pt]""") else: if i == n-1: expr+=myst(r""" """) else: expr+=myst(r"""\\[6pt]""") expr+=myst(r"""\end{array}\right\py{sepD}""",globals(),locals()) return expr
[docs] def pxsl_sum_matrix(A,B,s="+",sepG='(',sepD=')'): """ Fonction permettant d'afficher le détail de la somme (ou la différence) de deux matrices Version ------- 13/02/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- A : Matrix Première matrice de la somme B : Matrix Deuxième matrice de la somme s : str "+" par défaut pour réaliser une somme "-" pour réaliser une différence sepG : str délimiteur gauche de la matrice sepD : str délimiteur droit de la matrice Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- aucune fonction pyxiscience """ [n,p]=A.shape pxs_lang = get_pxs_lang() if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left\py{sepG}\begin{array}{\py{'c'*p}}""",globals(),locals()) for i in range(n): expr+=myst(r""" \py{A[i,0]}\py{s}""",globals(),locals())+pxsl_pow(B[i,0]) for j in range(p-1): expr+=myst(r""" &\py{A[i,1+j]}\py{s}""",globals(),locals())+pxsl_pow(B[i,1+j]) expr+=myst(r"""\\""") expr+=myst(r"""\end{array}\right\py{sepD}""",globals(),locals()) return expr
################ EXEMPLES ################## # pxsl_sum_matrix(Matrix([[-1,0,3],[2,3,4]]),Matrix([[2,3,5],[2,-3,4]])) retourne # l'expression latex : \left(\begin{array}{ccc} -1+2&0+3&3+5\2+2&3+\left(-3\right)&4+4\\end{array}\right) # soit la matrice : # -1+2 0+3 3+5 # 2+2 3+(-3) 4+4 # la commande pxsl_sum_matrix(Matrix([[-1,0,3],[2,3,4]]),Matrix([[2,3,5],[2,-3,4]]),"+") renvoie la même chose # pxsl_sum_matrix(Matrix([[-1,0,3],[2,3,4]]),Matrix([[2,3,5],[2,-3,4]]),"-") retourne # l'expression latex : \left(\begin{array}{-1-2&0-3&3-5\2-2&3-\left(-3\right)&4-4\\end{array}\right) # soit la matrice : # -1-2 0-3 3-5 # 2-2 3-(-3) 4-4
[docs] def pxsl_prod_scalar_matrix(lamb,A,mult="times",sepG='(',sepD=')'): """ Fonction permettant d'afficher le détail du produit entre un scalaire et une matrice Version ------- 13/02/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- lamb : float coefficient multiplicateur A : Matrix Matrice mult : str "times" par défaut, peut-être remplacé par "cdot" pour modifier le symbole multiplicatif sepG : str délimiteur gauche de la matrice sepD : str délimiteur droit de la matrice Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- aucune fonction pyxiscience """ [n,p]=A.shape pxs_lang = get_pxs_lang() if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left\py{sepG}\begin{array}{\py{'c'*p}}""",globals(),locals()) for i in range(n): expr+=myst(r"""\py{lamb}\\py{mult}""",globals(),locals())+pxsl_pow(A[i,0]) for j in range(p-1): expr+=myst(r"""&""")+myst(r"""\py{lamb}\\py{mult}""",globals(),locals())+pxsl_pow(A[i,1+j]) expr+=myst(r"""\\""") expr+=myst(r"""\end{array}\right\py{sepD}""",globals(),locals()) return expr
################ EXEMPLES ################## # pxsl_prod_scalar_matrix(2,Matrix([[1,2,-3],[2,3,4]])) retourne # l'expression latex : \left(\begin{array}{ccc} 2\times1&2\times2&2\times\left(-3\right)\2\times2&2\times3&2\times4\\end{array}\right) # donc la matrice : # 2 x 1 2 x 2 2 x (-3) # 2 x 2 2 x 3 2 x 4 # la commande pxsl_prod_scalar_matrix(2,Matrix([[1,2,-3],[2,3,4]]),"times") renvoie la même chose # pxsl_prod_scalar_matrix(-2,Matrix([[1,2,-3],[2,3,4]])) retourne # l'expression latex : \left(\begin{array}{ccc} -2\times1&-2\times2&-2\times\left(-3\right)\-2\times2&-2\times3&-2\times4\\end{array}\right) # soit la matrice : # -2 x 1 -2 x 2 -2 x (-3) # -2 x 2 -2 x 3 -2 x 4 # pxsl_prod_scalar_matrix(2,Matrix([[1,2,-3],[2,3,4]]),"cdot") retourne # l'expression latex : \left(\begin{array}{ccc} 2\cdot1&2\cdot2&2\cdot\left(-3\right)\2\cdot2&2\cdot3&2\cdot4\\end{array}\right) # soit la matrice : # -2.1 -2.2 -2.(-3) # -2.2 -2.3 -2.4 avec le . correspondant à la commande latex cdot # pxsl_prod_scalar_matrix('a',Matrix([[1,2,-3],[2,3,4]])) retourne # l'expression latex : \left(\begin{array}{ccc} a\times1&a\times2&a\times\left(-3\right)\a\times2&a\times3&a\times4\\end{array}\right) # soit la matrice : # a x 1 a x 2 a x (-3) # a x 2 a x 3 a x 4 # pxsl_prod_scalar_matrix(Symbol('a'),Matrix([[1,2,-3],[2,3,4]])) renvoie la même chose
[docs] def pxsl_prod_matrix(A,B,mult="times",sepG='(',sepD=')'): """ Fonction permettant d'afficher le détail du produit entre deux matrices Version ------- 13/02/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- A : Matrix Première matrice du produit B : Matrix Deuxième matrice du produit mult : str "times" par défaut, peut-être remplacé par "cdot" pour modifier le symbole multiplicatif sepG : str délimiteur gauche de la matrice sepD : str délimiteur droit de la matrice Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- aucune fonction pyxiscience """ [nA,pA]=A.shape [nB,pB]=B.shape pxs_lang = get_pxs_lang() if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left\py{sepG}\begin{array}{\py{'c'*pB}}""",globals(),locals()) for i in range(nA): expr+=myst(r"""\py{pxsl_pow(A[i,0])}\\py{mult}""",globals(),locals())+pxsl_pow(B[0,0]) for k in range(pA-1): expr+=myst(r"""+""")+pxsl_pow(A[i,1+k])+myst(r"""\\py{mult}""",globals(),locals())+pxsl_pow(B[1+k,0]) for j in range(pB-1): expr+=myst(r"""&""")+myst(r"""\py{pxsl_pow(A[i,0])}\\py{mult}""",globals(),locals())+pxsl_pow(B[0,1+j]) for k in range(pA-1): expr+=myst(r"""+""")+pxsl_pow(A[i,1+k])+myst(r"""\\py{mult}""",globals(),locals())+pxsl_pow(B[1+k,1+j]) expr+=myst(r"""\\[10pt]""") expr+=myst(r"""\end{array}\right\py{sepD}""",globals(),locals()) return expr
################ EXEMPLES ################## # pxsl_prod_matrix(Matrix([[-1,-2,3],[2,0,4]]),Matrix([[2,3,5],[2,-3,4],[2,3,4]])) retourne # l'expression latex : \left(\begin{array}{ccc} -1\times2+\left(-2\right)\times2+3\times2&-1\times3+\left(-2\right)\times\left(-3\right)+3\times3&-1\times5+\left(-2\right)\times4+3\times4\2\times2+0\times2+4\times2&2\times3+0\times\left(-3\right)+4\times3&2\times5+0\times4+4\times4\\end{array}\right) # donc la matrice : # -1x2+(-2)x2+3x2 -1x3+(-2)x(-3)+3x3 -1x5+(-2)x4+3x4 # 2x2+0x2+4x2 2x3+0x(-3)+4x3 2x5+0x4+4x4 # la commande pxsl_prod_matrix(Matrix([[-1,-2,3],[2,0,4]]),Matrix([[2,3,5],[2,-3,4],[2,3,4]]),"times") renvoie la même chose # pxsl_prod_matrix(Matrix([[-1,-2,3],[2,0,4]]),Matrix([[2,3,5],[2,-3,4],[2,3,4]]),"cdot") retourne la matrice : # -1.2+(-2).2+3.2 -1.3+(-2).(-3)+3.3 -1.5+(-2).4+3.4 # 2.2+0.2+4.2 2.3+0.(-3)+4.3 2.5+0.4+4.4 avec le . correspondant à la commande latex cdot
[docs] def pxs_system_simpl(n=3,N="",opt="sys",max_coef=2,limit_sum=15): """ Fonction permettant de créer les matrices A et B d'un système linéaire en s'assurant de la simplicité de la solution Par défaut, la matrice est de taille 3x3 et B un vecteur aléatoire, de composant entre 1 et 3, de dimension 3 La fonction est également utilisable pour générer A dans le cadre de l'inversion de matrice Version ------- 25/03/25 Vérification ------------ Auteur : Delphine Vérificateurs : Paramètres ---------- n : int Dimension de la matrice A N : Matrix Deuxième matrice du produit, solution du système opt : char "sys" : c'est un système, on renvoie A et B pour Ax=B sinon : on renvoie seulement A max_coef : int on tire les opérations à réaliser sur les coefficients entre 1 et max_coef limit_sum : int si les coefficients de A et B dépassent la valeur de limit_sum la simulation est relancée Retour ------ A,B retourne les deux matrices du système AX=B Fonction utilisée par --------------------- pxs_commute_matrix """ # La matrice est N est copiée pour ne pas modifier la matrice originale if N=="": N=Matrix([rd.randint(-3,3) for i in range(n)]) A,B=eye(n),N.copy() # La variable compte permet de compter le nombre d'éléments >15 en valeur absolue dans la matrice A # Si un élément est supérieur à 15 en valeur absolue, on recommence. La variable compte est initialisée à 1 par défaut compte=1 while compte!=0: A,B=eye(n),N.copy() # Tant que le nombre de 0 dans la matrice A est supérieur à la dimension -1, on continue # on autorise donc pas de 0 pour une matrice 2x2, on autorise un 0 pour une matrice 3x3 etc... while sum(1 for element in A if element == 0)>=n-1: # on tire aléatoirement les deux lignes impliquées dans la relation index=rd.sample([i for i in range(n)],2) # on tire les coefficients multiplicateurs lamb=[rd.choice([-1,1])*rd.randint(1,max_coef),rd.choice([-1,1])*rd.randint(1,max_coef)] # On aura par exemple L1=2*L1+3*L2 A[index[0],:]=lamb[0]*A[index[0],:]+lamb[1]*A[index[1],:] B[index[0],:]=lamb[0]*B[index[0],:]+lamb[1]*B[index[1],:] compte=sum(1 for element in A if abs(element) >= limit_sum)+sum(1 for element in B if abs(element) >= limit_sum) if opt=="sys": return A,B else: return A
################ EXEMPLES ################## # pxs_system_simpl() retournera par exemple les matrices : # 2 -4 -2 -4 # A= 2 -5 -2 B= -6 # 2 -4 -4 0 # pour la solution # 0 # x= 2 # -2
[docs] def pxsl_ax(a,x=Symbol('x'),sign=" ",frac=True): """ Fonction permettant d'afficher l'expression ax en fonction des valeurs de a Version ------- 23/09/25 Vérification ------------ Auteur : Delphine Vérificateurs : Paramètres ---------- a : numerique x : Symbol ('x' par défaut) si x=Symbol("val"), la valeur a est affichée dans tous les cas sign : str "" ou "+", "" par défaut, le symbole "+" indique qu'il faut écrire le signe '+' devant l'expression. frac : boolean True : fraction écrite en mode math False : fraction écrite a/b en ligne Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- pxsl_system_lin, pxsl_lines_op """ config_standard = pxs_config() # on règle le cas a nul en premier if a==0: return myst(r""" """) # on considère ensuite le cas a entier (qui est aussi considéré comme un Rational donc # il ne faut pas inverser les if) if isinstance(a,Integer): if a==1: return myst(r""" \py{sign} 1""",globals(),locals()) if x is None else myst(r""" \py{sign} \py{x}""",globals(),locals()) elif a==-1: return myst(r""" - 1""",globals(),locals()) if x is None else myst(r""" - \py{x}""",globals(),locals()) elif a<0: return myst(r""" \py{a}""",globals(),locals()) if x is None else myst(r""" \py{a}\py{x}""",globals(),locals()) else : return myst(r"""\py{sign} \py{a}""",globals(),locals()) if x is None else myst(r"""\py{sign} \py{a}\py{x}""",globals(),locals()) # on considère ensuite le cas a Rational if isinstance(a,Rational) and frac == True: if a<0: return myst(r""" -\frac{\py{abs(a.p)}}{\py{a.q}}""",globals(),locals()) if x is None else myst(r""" -\frac{\py{abs(a.p)}}{\py{a.q}}\py{x}""",globals(),locals()) else: return myst(r""" \py{sign}\frac{\py{a.p}}{\py{a.q}}""",globals(),locals()) if x is None else myst(r""" \py{sign}\frac{\py{a.p}}{\py{a.q}}\py{x}""",globals(),locals()) if isinstance(a,Rational) and frac == False: if a.p==-1: return myst(r""" - 1/\py{a.q}""",globals(),locals()) if x is None else myst(r""" - \py{x}/\py{a.q}""",globals(),locals()) elif a.p==1: return myst(r""" \py{sign}1/\py{a.q}""",globals(),locals()) if x is None else myst(r""" \py{sign}\py{x}/\py{a.q}""",globals(),locals()) elif a<0: return myst(r""" -\py{abs(a.p)}/\py{a.q}""",globals(),locals()) if x is None else myst(r""" -\py{abs(a.p)}\py{x}/\py{a.q}""",globals(),locals()) else: return myst(r""" \py{sign}\py{a.p}/\py{a.q}""",globals(),locals()) if x is None else myst(r""" \py{sign}\py{a.p}\py{x}/\py{a.q}""",globals(),locals()) # on ferme avec le traitement pour tout nombre car certains calculs envoient un int # non reconnu dans les conditionnels précédants if a==1: return myst(r""" \py{sign} 1""",globals(),locals()) if x is None else myst(r""" \py{sign} \py{x}""",globals(),locals()) elif a==-1: return myst(r""" - 1""",globals(),locals()) if x is None else myst(r""" - \py{x}""",globals(),locals()) if isinstance(a, Add): latex_a = latex(a, **config_standard) if x: return myst(r"""\py{sign} \left(\py{latex_a}\right)\py{x}""", globals(), locals()) elif a.could_extract_minus_sign() or match(r"\s*-", latex_a): txt = myst(r"""\py{sign} \py{latex_a}""", globals(), locals()) txt = sub(r'\+(?:\s|\\;|\\displaystyle)*-', '-', txt) return txt else: return myst(r"""\py{sign} \py{latex_a}""", globals(), locals()) try: if a<0: return myst(r""" \py{a}""",globals(),locals()) if x is None else myst(r""" \py{a}\py{x}""",globals(),locals()) else: return myst(r"""\py{sign} \py{a}""",globals(),locals()) if x is None else myst(r"""\py{sign} \py{a}\py{x}""",globals(),locals()) except: txt = myst(r"""\py{sign} \py{latex(a, **config_standard)}""",globals(),locals()) if x is None else myst(r"""\py{sign} \py{latex(a, **config_standard)}\py{x}""",globals(),locals()) txt = sub(r'\+(?:\s|\\;|\\displaystyle)*-', '-', txt) return txt
################ EXEMPLES ################## # pxsl_ax(2) retourne # l'expression latex : 2x # # pxsl_ax(2,Symbol('y')) retourne # l'expression latex : 2y # # pxsl_ax(2,Symbol('L_{'+str(1)+'}')) retourne # l'expression latex : 2L_{1} # # pxsl_ax(1,Symbol('L_{'+str(1)+'}')) retourne # l'expression latex : L_{1} # # pxsl_ax(0,Symbol('L_{'+str(1)+'}')) retourne ""
[docs] def pxsl_double_matrix(A,B,listeMat=[],opt='sep', display = False): """ Fonction permettant d'afficher un système linéaire Ax=B Version ------- 13/02/25 Vérification ------------ Auteur : Delphine Vérificateurs : Paramètres ---------- A : Matrix B : Matrix listeMat : liste permet d'envisager l'ajout de matrices supplémentaires opt : str ('sep' par défaut) permet de préciser la présentation des matrices 'sep' : les deux matrices sont présentées côte à côte entourées de parenthèses 'ext' : les deux matrices sont présentées en matrice étendue séparée par Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- pxsl_resol_system """ if opt=="sep": expr=myst(r"""\begin{array}{cc}""") expr+=pxsl_matrix(A, display = display)+pxsl_matrix(B, display = display)+myst(r"""\end{array}""") return expr else: expr=myst(r"""\begin{array}{c:c}""") expr+=pxsl_matrix(A,'(','.', display = display)+myst(r"""&""",globals(),locals())+pxsl_matrix(B,".",')', display = display)+myst(r"""\end{array}""") return expr
################ EXEMPLES ################## # pxsl_double_matrix(Matrix([[1,2,3],[2,-3,4]]),Matrix([[1,2,3],[2,-3,4]])) retourne # l'expression latex : \begin{array}{ccc}\left(\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right)&&\left(\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right)\end{array} # c'est-à-dire deux matrices entourées de parenthèses et placées l'une à côté de l'autre # pxsl_double_matrix(Matrix([[1,2,3],[2,-3,4]]),Matrix([[1,2,3],[2,-3,4]]),opt="sep") retourne la même chose # ATTENTION : ne pas oublier opt= car il y a un autre paramêtre au milieu (la liste) # # pxsl_double_matrix(Matrix([[1,2,3],[2,-3,4]]),Matrix([[1,2,3],[2,-3,4]]),opt="ext") retourne # l'expression latex : \begin{array}{c:c}\left(\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right.&\left.\begin{array}{rrr}1&2&3\2&-3&4\\end{array}\right)\end{array} # c'est-à-dire deux matrices réécrite en matrice étendue séparées par des pointillés # # EXTENSION A VENIR : écrire une opération entre les deux matrices, possibilités d'écrire plus de 2 matrices
[docs] def pxsl_system_lin(A, B, x = 'x', frac = True): """ Construct a LaTeX representation of a linear system. The function formats a linear system of equations defined by a coefficient matrix `A` and a right-hand side vector `B` into a LaTeX `array` environment. Each equation is written as a linear combination of symbolic variables followed by its corresponding constant term. Parameters ---------- A : Matrix Coefficient matrix of the linear system. B : Matrix Right-hand side vector of the system. x : str, optional Base name of the unknown variables (default is `"x"`), producing variables of the form `x_1, x_2, ..., x_n`. frac : bool, optional If True, coefficients are displayed as fractions when appropriate. If False, coefficients are displayed in a simplified inline form. Returns ------- Any A symbolic object representing the LaTeX code of the linear system formatted as an `array`. Examples -------- >>> pxsl_system_lin(A, B) '\\\\left\\{ \\\\begin{array}{rcl} ... \\\\end{array}\\\\right.' """ [n,p]=A.shape # Permet de créer le vecteur des x_i en fonction de la dimension de A vect_x=Matrix([Symbol(x+'_1')]) for i in range(p-1): vect_x=vect_x.row_join(Matrix([Symbol(x+'_'+str(i+2))])) expr=myst(r"""\left\lbrace \begin{array}{rcl} """) for i in range(n): if A[i, :].is_zero_matrix: expr += "0" # Gère l'affichage du premier terme non nul sans le '+' devant if A[i, 0] != 0: expr+=pxsl_ax(A[i,0],vect_x[0], frac = frac) sign="+" else: sign="" for j in range(1,p): if A[i,j]!=0: expr+=pxsl_ax(A[i,j],vect_x[j],sign, frac = frac) sign="+" rhs = myst(r"""\py{B[i].p}/\py{B[i].q}""", globals(), locals()) if (isinstance(B[i], Rational) and B[i].q != 1 and not frac) else latex(B[i]) expr+=myst(r""" &=&\py{rhs}""",globals(),locals())+myst(r"""\\[0.3em]""") expr+=myst(r"""\end{array}\right.""") return expr
################ EXEMPLES ################## # pxsl_system_lin(Matrix([[2,3],[1,4]]),Matrix([1,1])) renvoie # l'expression latex \left{ \begin{array}{rcr} 2x_1+ 3x_2&=&1\\ x_1+ 4x_2&=&1\\end{array}\right. # # pxsl_system_lin(Matrix([[2,3,0],[1,4,-1],[-2,3,5]]),Matrix([1,1,0])) renvoie # l'expression latex \left{ \begin{array}{rcr} 2x_1+ 3x_2&=&1\\ x_1+ 4x_2-x_3&=&1\\-2x_1+ 3x_2+ 5x_3&=&0\\end{array}\right.
[docs] def pxsl_lines_op(n, listOp, opt="sys", frac = True): """ Construct a LaTeX array describing elementary row (line) operations. The function generates a symbolic LaTeX representation of a sequence of elementary row operations applied to a system or a matrix. Each operation is displayed line by line using an `array` environment, with arrows and linear combinations formatted according to the current language settings (French or English). Parameters ---------- n : int Number of rows of the system or matrix. listOp : list List of elementary row operations. Each element of the list is expected to be a tuple of the form `(a, i, b, j)` representing an operation applied to row `i` using row `j`: - if `a == 0`, rows `i` and `j` are swapped; - otherwise, the operation corresponds to `row_i ← a * row_i + b * row_j`. Row indices are assumed to be 1-based. opt : str, optional Output option (currently kept for interface consistency; default is `"sys"`). frac : bool, optional If True, coefficients are displayed as fractions when appropriate. If False, coefficients are displayed in a simplified inline form. Returns ------- Any A symbolic object representing the LaTeX code of an `array` environment describing the row operations. Examples -------- >>> pxsl_lines_op( ... n=3, ... listOp=[(1, 1, -2, 2), (0, 2, 1, 3)] ... ) '\\\\begin{array}{ccc} ... \\\\end{array}' """ espace = "" expr = myst(r""" \begin{array}{ccc}""") pxs_lang = get_pxs_lang() for j in range(n): for i in range(len(listOp)): if j==listOp[i][1]-1: a,b=listOp[i][0],listOp[i][2] ind1,ind2=listOp[i][1],listOp[i][3] var1 = Symbol('L_{'+str(ind1)+'}') if pxs_lang == "fr" else Symbol('R_{'+str(ind1)+'}') var2 = Symbol('L_{'+str(ind2)+'}') if pxs_lang == "fr" else Symbol('R_{'+str(ind2)+'}') if a==0: expr+=myst(r""" \fr{L}\en{R}_{\py{ind1}}& \leftrightarrow &""",globals(),locals())+pxsl_ax(a,var1,"",frac = frac)+pxsl_ax(b,var2,"")+myst(r"""\py{espace}""",globals(),locals()) else: expr+=myst(r""" \fr{L}\en{R}_{\py{ind1}}& \leftarrow &""",globals(),locals())+pxsl_ax(a,var1,"",frac = frac)+pxsl_ax(b,var2,"+",frac = frac)+myst(r"""\py{espace}""",globals(),locals()) if i==len(listOp)-1: expr+=myst(r""" \\[0.3em]""") expr+=myst(r"""\end{array}""") return expr
################ EXEMPLES ################## # pxsl_lines_op(2,[2,1,3,2]) retourne # l'expression latex : \begin{array}{c}L_{1} \leftarrow 2L_{1}+ 3L_{2}\\\end{array}\ # # pxsl_lines_op(2,[0,1,3,2]) retourne # l'expression latex : \begin{array}{c}L_{1} \leftarrow 3L_{2}\\\end{array}\ # # pxsl_lines_op(2,[0,1,-1,2]) retourne # l'expression latex : \begin{array}{c}L_{1} \leftarrow -3L_{2}\\\end{array}\ #
[docs] def pxsl_resol_system(listA,listB=[],listOp=[],x='x',method="sys",view="sep", detail = "on"): """ Fonction qui permet d'écrire chaque étape de la résolution d'un problème impliquant des manipulations de lignes Version ------- 23/09/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- listA : list contient la liste des matrices A successives utilisées lors de la résolution listB : list contient la liste des vecteurs (système) ou matrice (inversion) B successives utilisées lors de la résolution listOp : liste de liste chaque liste de la liste contient 4 éléments [a, ind1,b,ind2] permettant de réaliser le calcul sur la ligne d'indice ind1 a*L_ind1+b*L_ind2 x : s.Symbol ('x' par défaut) utilisé comme variable dans le cas de la résolution d'un système method : str ('sys' par défaut) "sys" : résolution d'un système "mat" : inversion d'une matrice "ech" : échelonnage d'une matrice view : str ("sep" par défaut) "sep" : les deux matrices sont représentées côte à côté "ext" : représente la matrice étendue A|B Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- Aucune fonction pyxiscience """ listA, listB, listOp = pxs_regroupe_ligne(listA, listB, listOp) expr="" for i in range(len(listA)): if i==0: if method=="sys": expr=myst(r"""\begin{array}{cl} """)+myst(r"""&""")+ pxsl_system_lin(listA[i],listB[i],x)+myst(r"""\\ \\""") elif method=="ech": expr=myst(r"""\begin{array}{cc} """)+myst(r"""&""")+ pxsl_matrix(listA[i])+myst(r"""\\ \\""") else: expr=myst(r"""\begin{array}{cc} """)+myst(r"""&""")+ pxsl_double_matrix(listA[i],listB[i],opt=view)+myst(r""" \\ \\""") if i!=0: if detail == "on": if method=="sys": expr+=pxsl_lines_op(listA[0].shape[0],listOp[i])+myst(r""" & """)+pxsl_system_lin(listA[i],listB[i],x)+myst(r"""\\ \\""") elif method=="ech": expr+=pxsl_lines_op(listA[0].shape[0],listOp[i],opt="ech")+myst(r""" & """)+pxsl_matrix(listA[i])+myst(r"""\\ \\""") else: expr+=pxsl_lines_op(listA[0].shape[0],listOp[i])+myst(r""" & """)+pxsl_double_matrix(listA[i],listB[i],opt=view)+myst(r"""\\ \\""") else: if method=="sys": expr+=myst(r""" & """)+pxsl_system_lin(listA[i],listB[i],x)+myst(r"""\\ \\""") elif method=="ech": expr+=myst(r""" & """)+pxsl_matrix(listA[i])+myst(r"""\\ \\""") else: expr+=myst(r""" & """)+pxsl_double_matrix(listA[i],listB[i],opt=view)+myst(r"""\\ \\""") expr+=myst(r"""\end{array}""") return expr
################ EXEMPLES ################## # Soit les variables de départ : # listA=[Matrix([ [-4, 2], [ 1, -1]]), Matrix([ [-2, 1], [ 1, -1]]), Matrix([ [-2, 1], [ 0, -1]]), Matrix([ [-2, 0], [ 0, -1]]), Matrix([ [-1, 0], [ 0, -1]]), Matrix([ [1, 0], [0, -1]]), Matrix([ [1, 0], [0, 1]])] # listB=[Matrix([ [-6], [ 0]]), Matrix([ [-3], [ 0]]), Matrix([ [-3], [-3]]), Matrix([ [-6], [-3]]), Matrix([ [-3], [-3]]), Matrix([ [ 3], [-3]]), Matrix([ [3], [3]])] # listOp=[[0, 0, 0, 0], [Rational(1,2), 1, 0, 0], [2, 2, 1, 1], [1, 1, 1, 2], [Rational(1,2), 1, 0, 0], [-1, 1, 0, 0], [-1, 2, 0, 0]] # # pxsl_resol_system(listA,listB,listOp,method='sys') retourne l'expression latex pour représenter # la résolution du système. #
[docs] def pxs_reduce_pgcd(A, B, listA, listB, listOp): """ Fonction permettant de diviser lignes des matrices A et B lorsque leur pgcd est différent de 1 Version ------- 23/09/25 (modifié 14/10/25) Vérification ------------ Auteur : Delphine Vérificateurs : Paramètres ---------- A : Matrix B : Matrix listA : list liste de matrices, permet de retrouver les différentes transformations de la matrice A listB : list liste de matrices, permet de retrouver les différentes transformations de la matrice B listOp : list Chaque élément de la liste est une liste de 4 éléments : [facteur multiplicatif de la ligne i, indice de la ligne i, facteur multiplicatif de la ligne j, indice de la ligne j] Retour ------ liste, liste, liste retourne les listes actualisées de l'opération de permutation Fonction utilisée par --------------------- pxs_steps_invert_matrix """ [n, p] = A.shape for i in range(n): # Extraction des numérateurs pour la ligne i de A A_row_nums = [elem.numerator if isinstance(elem, Rational) else elem for elem in A[i, :]] # Extraction des numérateurs pour la ligne i de B B_row_nums = [elem.numerator if isinstance(elem, Rational) else elem for elem in B[i, :]] # Calcul du PGCD pour les numérateurs if len(A_row_nums) > 0 and len(B_row_nums) > 0: # Utiliser sympy_gcd pour gérer les objets sympy pg_A = A_row_nums[0] for val in A_row_nums[1:]: pg_A = gcd(pg_A, val) pg_B = B_row_nums[0] for val in B_row_nums[1:]: pg_B = gcd(pg_B, val) pg = gcd(pg_A, pg_B) # Vérifier si pg est différent de 1 if pg != 1 and pg != 0: # Division de la ligne par le PGCD A[i, :] = A[i, :] / pg B[i, :] = B[i, :] / pg # Mise à jour des listes listA.append(A.copy()) listB.append(B.copy()) for j in range(n): if j == i: # L'opération est L_{i+1} -> 1/pg * L_{i+1} d'où la liste [1/pg, i+1, 0, 0] try: listOp.append([Rational(1, pg), i+1, 0, 0]) except: listOp.append([1/ pg, i+1, 0, 0]) return listA, listB, listOp
################ EXEMPLES ################## # Exemple pour une matrice avec PGCD = 2 # pxs_reduce_pgcd(Matrix([[2,4,6],[1,2,3]]),Matrix([2,3,4]),[Matrix([[2,4,6],[1,2,3]])],[Matrix([2,3,4])],[[0,0,0,0]]) retourne # listA = [Matrix([ [2, 4, 6], [1, 2, 3]]), Matrix([ [1, 2, 3], [1, 2, 3]])] # listB = [Matrix([ [2], [3], [4]]), Matrix([ [1], [3], [4]])] # listOp = [[0, 0, 0, 0], [1/2, 1, 0, 0]] #
[docs] def pxs_steps_invert_matrix(A,B,x='x',method="sys",view="sep", detail = "on"): """ Fonction permettant de stocker toutes les étapes de la résolution d'un système ou l'inversion d'une matrice Version ------- 23/09/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- A : Matrix B : Matrix x : Symbol ('x' par défaut) liste de matrices, permet de retrouver les différentes transformations de la matrice A method : str "sys" : pour afficher la résolution d'un système listOp : list Chaque élément de la liste est une liste de 4 éléments : [facteur multiplicatif de la ligne i, indice de la ligne i, facteur multiplicatif de la ligne j, indice de la ligne j] Retour ------ liste, liste, liste retourne les listes actualisées de l'opération de permutation Fonction utilisée par --------------------- Aucune fonction pyxiscience """ [n,p]=A.shape nmin=min(n,p) listA,listB=[A.copy()],[B.copy()] listOp=[[0,0,0,0]] # On vérifie le pgcd de chaque ligne pour simplifier listA,listB,listOp=pxs_reduce_pgcd(A,B,listA,listB,listOp) for k in range(nmin): # On cherche la ligne pivot r=-1 for i in range(k,n): if r<0 and A[i,k]!=0: r=i if r>k: # On met la ligne pivot en haut si elle ne l'est pas A.row_swap(k, r) B.row_swap(k,r) listA.append(A.copy()) listB.append(B.copy()) for j in range(n): if j==r: listOp.append([0,r+1,1,1+k]) if r>=k: # On élimine les lignes differentes de k for j in range(n): if A[j,k]!=0 and j!=k: pg=gcd(A[k,k],A[j,k]) cpAjk=A[j,k] cpAkk=A[k,k] try: A[j,:]=abs(cpAkk)*A[j,:]/pg- abs(cpAjk)*A[k,:]/pg*np.sign( cpAjk* cpAkk) except: A[j,:]=cpAkk*A[j,:]/pg- cpAjk*A[k,:]/pg try: B[j,:]=abs(cpAkk)*B[j,:]/pg- abs(cpAjk)*B[k,:]/pg*np.sign( cpAjk* cpAkk) except: B[j,:]=cpAkk*B[j,:]/pg- cpAjk*B[k,:]/pg listA.append(A.copy()) listB.append(B.copy()) for l in range(n): if l==j: try: listOp.append([abs(cpAkk)/pg,j+1,-abs(cpAjk)/pg*np.sign( cpAjk* cpAkk),k+1]) except: listOp.append([cpAkk/pg,j+1,-cpAjk/pg,k+1]) # On vérifie le pgcd de chaque ligne pour simplifier listA,listB,listOp=pxs_reduce_pgcd(A,B,listA,listB,listOp) # On exprime les solutions sous la forme finale for i in range(nmin): if A[i,i]!=0 and A[i,i]!=1: for j in range(B.shape[1]): try: B[i,j]=Rational(B[i,j],A[i,i]) except: B[i,j]=B[i,j]/A[i,i] cpAi=A[i,i] A[i,:]=A[i,:]/cpAi listA.append(A.copy()) listB.append(B.copy()) # Stockage de l'opération for j in range(nmin): if j==i and cpAi!=0: try: listOp.append([Rational(1,cpAi),i+1,0,0]) except: listOp.append([1/cpAi,i+1,0,0]) elif j==i and cpAi == -1: listOp.append([-1,i+1,0,0]) expr = pxsl_print_operations([listA, listB], listOp, method, x, view, detail) return expr
################ EXEMPLES ################## # pxs_steps_invert_matrix(Matrix([[2,3,0],[1,4,-1],[-2,3,5]]),Matrix([1,1,0])) renvoie # l'expression latex qui permet de décrire toute la résolution du système. # # pxs_steps_invert_matrix(Matrix([[2,3,0],[1,4,-1],[-2,3,5]]),Matrix([[1,0,0],[0,1,0],[0,0,1]]),method="mat") renvoie # l'expression latex qui permet de décrire l'inversion de la matrice avec les matrices mises côte à côte. # # pxs_steps_invert_matrix(Matrix([[2,3,0],[1,4,-1],[-2,3,5]]),Matrix([[1,0,0],[0,1,0],[0,0,1]]),method="mat",view="ext") renvoie # l'expression latex qui permet de décrire l'inversion de la matrice avec la matrice étendue.
[docs] def pxs_LU_decomposition(A, view = "sep", detail = "on", name_matrix = " ", PLU = False): """ Details the steps of LU factorization for a square matrix A. Version ------- 26/12/25 Authors ------------ Author: Raphaël Checked by: Arguments ---------- A: Matrix, the matrix to factorize method: str, display option view: str, display option detail: str, "on" to get additional details name_matrix: str, name the matrix is referred as Returns ------ text (str), L (Matrix), U (Matrix) (if PLU = False) text (str), P (Matrix), L (Matrix), U (Matrix) (if PLU = True) text: steps of the computation (P,) L, U: Matrixes such that (P)A = LU if they exist. None, None otherwise Function used by --------------------- No pyxiscience function Examples -------- >>> A = Matrix([[1, 2, 1], [3, 10, 3], [-2, -8, 5]]) # LU factorization exists >>> resol, L, U = pxs_LU_decomposition(A.copy()) >>> B = Matrix([[1, 2, 1], [3, 6, -1], [1, 1, 1]]) # LU factorization does not exist >>> resol, P, L, U = pxs_LU_decomposition(A4.copy(), name_matrix = "B", PLU = True) # L, U = None, None """ pxs_lang = get_pxs_lang() [n,p] = A.shape # Check if square if n != p: err = "La matrice fournie doit être carrée" if pxs_lang == "fr" else "Input matrix must be square" raise ValueError(err) B = Matrix(np.eye(n).astype(np.int64)) listA, listB=[A.copy()], [B.copy()] if PLU: P = Matrix(np.eye(n).astype(np.int64)) listP = [P.copy()] listOp = [[0,0,0,0]] k = 0 ok = True while k < n - 1 and ok: # for each column except the last one and while it is possible if A[k, k] != 0: # Handle subdiagonal coefficients on the (k+1)-th column for j in range(k + 1, n): if A[j,k]!=0: coeff = A[j, k] / A[k, k] A[j, :] -= coeff * A[k, :] B[j, k] = coeff listA.append(A.copy()) listB.append(B.copy()) if PLU: listP.append(P.copy()) listOp.append([1, j + 1, -coeff, k + 1]) k += 1 # below : cases where the k-th pivot is 0 elif np.any(A[k+1:, k]): # else A[k:, k] == 0, all coeff under the pivot are 0, nothing to do if not PLU: # looking above for another line to use as a "pivot" r = k - 1 while r >= 0 and not (A[r, k] != 0 and not np.any(A[r, :k])): r -= 1 if r == -1: # nothing found ok = False else: # using r-th row to eliminate the subdiagonal coefficients on the (k+1)-th column for j in range(k + 1, n): if A[j,k]!=0: coeff = A[j, k] / A[r, k] A[j, :] -= coeff * A[r, :] B[j, r] = coeff listA.append(A.copy()) listB.append(B.copy()) listOp.append([1, j + 1, -coeff, r + 1]) k += 1 else: # PLU case, looking for a non-zero coeff below in order to swap lines r = np.where(np.ravel(A[k + 1:, k]) != 0)[0][0] + k + 1 # row of first non-zero coeff. on column k A.row_swap(k, r) B[k, :k], B[r, :k] = B[r, :k], B[k, :k] P.row_swap(k, r) listA.append(A.copy()) listB.append(B.copy()) listP.append(P.copy()) listOp.append([0, k + 1, 1, r + 1]) else: k += 1 list_mat = [listA, listB] if PLU: list_mat.append(listP) text = myst(r"""\begin{equation*}""", locals(), globals()) operations = pxsl_print_operations(list_mat, listOp, method = "mat", view = view, detail = detail) text += operations text += myst(r"""\end{equation*}""", locals(), globals()) if PLU: return {"all": text, "resol": operations, "P": listP[-1], "L": listB[-1], "U": listA[-1]} elif ok: # no permutation, and LU factorization exists return {"all": text, "resol": operations, "L": listB[-1], "U": listA[-1]} else: # no permutation, and LU factorization does not exist if pxs_lang == "fr": negative_conclusion = myst(r""" On ne peut pas poursuivre la réduction sans permutation de lignes, la matrice ${{name_matrix}}$ ne possède donc pas de décomposition $LU$.""", locals(), globals()) if pxs_lang == "en": negative_conclusion = myst(r""" A line permutation would be required at this stage, hence the matrix ${{name_matrix}}$ does not admit an $LU$ factorization.""", locals(), globals()) text += negative_conclusion return {"all": text, "resol": operations, "L": listB[-1], "U": listA[-1]}
[docs] def pxs_matrix_no_LU(p, no_first = True): """ Constructs a random sympy (p x p) matrix with integer coefficients, for which the LU decomposition without pivoting does not exist. Strategy: a random index k is chosen in {1, ..., p-1}, then the k-th leading principal submatrix A_k is made singular by replacing its last row with a linear combination of the k-1 previous rows. Coefficients of the full matrix are drawn from [-5, 5], except for the constructed row which may exceed these bounds. Parameters ---------- p : int Matrix size (must be >= 2). no_first : True for avoiding a 0 as first coefficient Returns ------- Matrix A sympy (p x p) matrix with no LU decomposition. """ if p < 2: raise ValueError("p must be an integer >= 2.") no_first = no_first and p > 2 # Randomly choose which leading principal minor to annihilate (1-based) k = rd.randint(1 + no_first, p - 1) coeffs = [[rd.randint(-5, 5) for _ in range(p)] for _ in range(p)] if k == 1: # Trivial case: force a[0][0] = 0 coeffs[0][0] = 0 else: # Make A_k singular by replacing its last row with a linear # combination of the k-1 previous rows. # Coefficients lambda are chosen in {-2, -1, 1, 2} to stay discrete. lambdas = [rd.choice([-2, -1, 1, 2]) for _ in range(k - 1)] for j in range(k): val = sum(lambdas[i] * coeffs[i][j] for i in range(k - 1)) coeffs[k - 1][j] = val return Matrix(coeffs)
[docs] def pxsl_print_operations(list_mat, listOp=[], method = "sys", x = "x", view="sep", detail = "on", frac = True): """ Displays each step of the resolution for a problem involving line operations. This function is meant to replace pxsl_resol_system Version ------- 06/01/26 Authors ------------ Auteur : Ronan - Delphine - Raphaël Vérificateurs : Arguments ---------- list_mat : list of lists each element is a list of successive matrices appearing in the resolution listOp : liste of lists each sublist contains 4 elements [a, ind1, b, ind2] describing the following operation: L(ind1) <- a * L_ind1 + b * L_ind2 x : s.Symbol ('x' par défaut) determines the name of the variables in the system method : str ('sys' by default) "sys" : system form "mat" : matrix form view : str ("sep" by default) "sep" : matrices are displayed side-by-side "ext" : extended matrix A1|A2|...|An frac : bool, optional If True, coefficients are displayed as fractions when appropriate. If False, coefficients are displayed in a simplified inline form. Returns ------ str Latex expression Function used by --------------------- pxs_steps_invert_matrix, pxs_LU_decomposition, pxs_compute_ech, pxs_compute_ech_reduite """ def __pxsl_multiple_matrix(list_mat, view, display = frac): n = len(list_mat) cols = "c" * n if view == "sep" else ":".join("c" * n) seps = [["(", ")"] if view == "sep" else [".", "."] for _ in range(n)] seps[0][0], seps[-1][1] = "(", ")" expr = myst(r"""\begin{array}{\py{cols}}""", locals(), globals()) expr += "&".join([pxsl_matrix(mat, sep[0], sep[1], display = display) for mat, sep in zip(list_mat, seps)]) expr += myst(r"""\end{array}""", locals(), globals()) return expr n = list_mat[0][0].shape[0] list_mat, listOp = pxs_regroupe_ligne(list_mat, listOp) if method == "sys": try: listA, listB = list_mat except: raise ValueError("list_mat must be of length 2 exactly when method is 'sys'") # First line if method=="sys": expr = myst(r"""\begin{array}{cl} """)+myst(r"""&""")+ pxsl_system_lin(listA[0],listB[0],x, frac = frac)+myst(r"""\\ \\""") else: expr = myst(r"""\begin{array}{cc} """)+myst(r"""&""")+ __pxsl_multiple_matrix([listX[0] for listX in list_mat], view = view) + myst(r""" \\ \\""") # other lines for i in range(1, len(list_mat[0])): printed_ops = pxsl_lines_op(n, listOp[i], frac = frac) if detail == "on" else " " if method=="sys": expr += myst(r"""\py{printed_ops} & """, locals(), globals()) + pxsl_system_lin(listA[i],listB[i],x, frac = frac)+myst(r"""\\ \\""") else: expr += myst(r"""\py{printed_ops} & """, locals(), globals()) + __pxsl_multiple_matrix([listX[i] for listX in list_mat], view = view) + myst(r"""\\ \\""") expr+=myst(r"""\end{array}""") return expr
##
[docs] def pxs_commute_matrix(n,opt=""): """ Fonction permettant de créer les matrices A, B et C de dimension n avec A et B commutantes et A et C non commutantes Version ------- 13/02/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- n : int Dimension de la matrice A opt: str "commut" : Envoie deux matrices qui commutent "noncommut" : Envoie deux matrices qui ne commutent pas autre : renvoie les trois matrices Retour ------ A,B retourne les deux matrices du système AX=B Fonction utilisée par --------------------- aucune fonction pyxiscience """ Nmax=3 A=ones(n)*10*n C=A.copy() while A*C==C*A or sum(1 for element in A if abs(element) >= 10*n)>=1 or sum(1 for element in C if abs(element) >= 10*n)>=1: # On construit les matrices diagonales de la décomposition P*A*Pinv P=pxs_system_simpl(n,eye(n),"mat") Pinv=P.inv() DiagA=randmatrixdiagonale(n,-Nmax,Nmax) DiagC=DiagA.copy() DiagC[0,n-1]=1 C=P*DiagC*Pinv*P.det() C=C/fct.reduce(m.gcd,C[:,:]) # on s'assure que A ne puisse pas commuter avec n'importe quelle matrice (ce qui arrive si les éléments de la diagonale sont tous égaux) # on s'assure également qu'on n'obtient pas la matrice nulle while DiagA==zeros(n) or DiagA[0,0]==DiagA[n-1,n-1]: DiagA=randmatrixdiagonale(n,-Nmax,Nmax) # on génère les matrices A et B commutantes A=P*DiagA*Pinv*P.det() A=A/fct.reduce(m.gcd,A[:,:]) B=C.copy()*10*n while A*B!=B*A or sum(1 for element in B if abs(element) >= 10*n)>=1: DiagB=randmatrixdiagonale(n,-Nmax,Nmax) while DiagB==zeros(n): DiagB=randmatrixdiagonale(n,-Nmax,Nmax) if DiagA[0,0]==DiagB[0,0]: DiagB[0,0]=DiagB[0,0]+1 B=P*DiagB*Pinv*P.det() B=B/fct.reduce(m.gcd,B[:,:]) if opt=="commute": return A,B if opt=="noncommute": return A,C else: return A,B,C
################ EXEMPLES ################## # pxs_commute_matrix(2) renvoie A, B et C de dimension 2x2 telles que A et B commutent et # A et C ne commutent pas. # Par exemple : # 3 4 5 8 5 2 # A = -2 -3 B = -4 -7 C = -2 1 # # pxs_commute_matrix(3) renvoie A, B et C de dimension 3x3 telles que A et B commutent et # A et C ne commutent pas. # Par exemple : # 0 1 1 4 -5 -5 0 3 3 # A = 4 3 -1 B = -8 1 5 C = 4 1 -3 # -4 -1 3 8 -7 -11 -4 1 5 # # pxs_commute_matrix(2,"commute") renvoie A, B dimension 2x2 telles que A et B commutent # Par exemple : # 3 4 5 8 # A = -2 -3 B = -4 -7 # # pxs_commute_matrix(3,"noncommute") renvoie A et C de dimension 3x3 telles que A et C ne commutent pas. # Par exemple : # 0 1 1 0 3 3 # A = 4 3 -1 C = 4 1 -3 # -4 -1 3 -4 1 5
[docs] def pxsl_pow_matrix(A,k,opt=0,sepG='(',sepD=')'): """ Fonction permettant d'écrire en latex une matrice dont tous les coefficients sont élevés à la même puissance Les puissances de 0 et 1 sont simplifiées, les valeurs sont centrées par défaut Version ------- 13/02/25 Vérification ------------ Auteur : Ronan - Delphine Vérificateurs : Paramètres ---------- A : Matrix k : float ou Symbol valeur de la puissance sepG : str délimiteur gauche de la matrice sepD : str délimiteur droit de la matrice Retour ------ str retourne l'expression en latex Fonction utilisée par --------------------- aucune fonction pyxiscience """ [n,q]=A.shape pxs_lang = get_pxs_lang() if pxs_lang == "en" and sepG=='(': sepG='[' if pxs_lang == "en" and sepD==')': sepD=']' expr=myst(r"""\left\py{sepG}\begin{array}{\py{'c'*q}}""",globals(),locals()) for i in range(n): expr=expr+pxsl_pow(A[i,0],k,opt) for j in range(q-1): expr=expr+myst(r""" &""")+pxsl_pow(A[i,1+j],k,opt) expr=expr+myst(r"""\\""") expr=expr+myst(r"""\end{array}\right\py{sepD}""",globals(),locals()) return expr
################ EXEMPLES ################## # pxsl_pow_matrix(Matrix([[2,0,1],[4,8,-4],[2,3,0]]),2) # renvoie l'expression latex \left(\begin{array}{ccc}2^{2}&0^2&1^2\4^{2}&8^{2}&\left(-4\right)^{2}\2^{2}&3^{2}&0^2\\end{array}\right) # c'est-à-dire # # 2^2 0^2 1^2 # 4^2 8^2 (-4)^2 # 2^2 3^2 0^2 # pxsl_pow_matrix(Matrix([[2,0,1],[4,8,-4],[2,3,0]]),2,1) # renvoie l'expression latex \left(\begin{array}{ccc}2^{2}&0&1\4^{2}&8^{2}&\left(-4\right)^{2}\2^{2}&3^{2}&0\\end{array}\right) # c'est-à-dire # # 2^2 0 1 # 4^2 8^2 (-4)^2 # 2^2 3^2 0
[docs] def pxs_regroupe_ligne(list_mat, listOp=[]): """ Fonction permettant de regrouper les lignes qui peuvent être écrites en une seule étape Version ------- 21/03/25 -> 06/01/26 (Raphaël) Vérification ------------ Auteur : Delphine Vérificateurs : Paramètres ---------- listA : list liste des étapes pour la matrice/système de départ listB : list liste des étapes pour la matrice miroir (inversion) ou membre droit (système) listOp : liste liste des opérations sur lignes Retour ------ listA, listB, listOp retourne les listes actualisées Fonction utilisée par --------------------- pxsl_resol_system, pxsl_print_operations """ if len(listOp) <= 1: return list_mat, listOp elif len(listOp) == 2: return list_mat, [listOp[0], [listOp[1]]] n, nb_etape = len(list_mat[0]), 1 listOp_bis = [listOp[0]] listOp_bis.append([listOp[1]]) list_mat_bis = [[listX[0]] for listX in list_mat] for i in range(1, n-1): if listOp[i][0] != 0 and all(sous_liste[1] !=listOp[i+1][1] for sous_liste in listOp_bis[nb_etape]) and all(sous_liste[1] !=listOp[i+1][3] for sous_liste in listOp_bis[nb_etape]): listOp_bis[nb_etape].append(listOp[i+1]) else: for j in range(len(list_mat)): list_mat_bis[j].append(list_mat[j][i]) nb_etape += 1 listOp_bis.append([listOp[i+1]]) if i == n-2: for j in range(len(list_mat)): list_mat_bis[j].append(list_mat[j][i+1]) if listOp_bis: return list_mat_bis, listOp_bis else: return list_mat, listOp
[docs] def pxs_compute_ech(A): """ Fonction permettant de stocker toutes les étapes de la construction d'une matrice échelonnée Paramètres ---------- A : Matrix Retour ------ liste, liste, liste retourne les listes actualisées de l'opération de permutation Fonction utilisée par --------------------- Aucune fonction pyxiscience """ [n,p]=A.shape nmin=min(n,p) listA=[A.copy()] listOp=[[0,0,0,0]] for k in range(nmin): # On cherche la ligne pivot r=-1 for i in range(k,n): if r<0 and A[i,k]!=0: r=i if r>k: # On met la ligne pivot en haut si elle ne l'est pas A.row_swap(k, r) listA.append(A.copy()) for j in range(n): if j==r: listOp.append([0,r+1,1,1+k]) if r>=k: # On élimine les lignes après k for j in range(k,n): if A[j,k]!=0 and j!=k: pg=m.gcd(A[k,k],A[j,k]) cpAjk=A[j,k] cpAkk=A[k,k] A[j,:]=abs(cpAkk)*A[j,:]/pg- abs(cpAjk)*A[k,:]/pg*np.sign( cpAjk* cpAkk) listA.append(A.copy()) for l in range(n): if l==j: listOp.append([abs(cpAkk)/pg,j+1,-abs(cpAjk)/pg*np.sign( cpAjk* cpAkk),k+1]) expr = pxsl_print_operations([listA], listOp = listOp, method = "mat") return expr
[docs] def pxs_compute_ech_reduite(A): """ Fonction transformant une matrice en forme échelonnée réduite en stockant chaque étape. Paramètres ---------- A : numpy.ndarray Matrice d'entrée Retour ------ listA : liste des matrices à chaque étape listOp : liste des opérations sous forme [a, i, b, j] avec : - a : coefficient multiplicatif pour L_i - i : numéro de la ligne affectée (1-based index) - b : coefficient multiplicatif pour L_j - j : numéro de la ligne utilisée (1-based index) """ [n, p] = A.shape listA = [A.copy()] listOp = [[0,0,0,0]] for k in range(min(n, p)): # Trouver la ligne pivot r = -1 for i in range(k, n): if A[i, k] != 0: r = i break if r == -1: continue # Si toute la colonne est nulle, on passe à la suivante # Échanger L_k et L_r si nécessaire if r != k: A.row_swap(k, r) # Échange des lignes listA.append(A.copy()) listOp.append([0, k + 1, 1, r + 1]) # Format imposé # Normaliser le pivot (L_k = L_k / pivot pour avoir 1) pivot = A[k, k] if pivot != 1: A[k, :] /= pivot listA.append(A.copy()) listOp.append([1 / pivot, k + 1, 0, 0]) # Division de la ligne par le pivot # Élimination en dessous et au-dessus for j in range(n): if j != k and A[j, k] != 0: facteur = A[j, k] A[j, :] -= facteur * A[k, :] listA.append(A.copy()) listOp.append([1, j + 1, -facteur, k + 1]) # Format imposé expr = pxsl_print_operations([listA], listOp = listOp, method = "mat") return expr
[docs] def randmatrixrect(p,q,a,b): """Returns a rectangular matrix with p rows and q columns such that every coefficient is a realization of of a discrete random variable on range(a, b) :returns: LaTeX bmatrix as a string """ M= eye(p,q) for i in range(p): for j in range(q): #print('(i,j) =', i,j,'\n') #M[i,j] = next(sample(DiscreteUniform('h', range(a, b)))) M[i,j] = sample(DiscreteUniform('h', range(a, b))) # M[i,j] = next(sample(DiscreteUniform('h', range(a, b)))) for i in range(p) for j in range(p)] return M
[docs] def pxs_invertible_matrix(n): for _ in range(100): entries = [[random.randint(-2, 2) for _ in range(n)] for _ in range(n)] M = Matrix(entries) if M.det() != 0: return M return Matrix.eye(n)
[docs] def pxs_diag_matrix(p,a,b): """ Returns a square diagonal matrix of size p such that every coefficient is a realization of a uniform discrete random variable, the range of which is range(a, b) """ D= eye(p) for i in range(p): for j in range(p): if j==i: D[i,j] = sample(DiscreteUniform('h', range(a, b))) else: D[i,j] = 0 return D
[docs] def pxs_triangular(p, a, b, diag = None, lower = False, inv = False): """ Returns a square triangular matrix of size p such that every coefficient is a realization of a uniform discrete random variable, the range of which is [[a, b]] The diagonal coefficients can be specified as a list with the optional <diag> argument. """ T = zeros(p) for i in range(p): for j in range(p): if j == i and diag: T[i, i] = diag[i] elif j == i and inv: T[i, i] = pxs_randint(a, b, 0) elif j >= i: T[i,j] = sample(DiscreteUniform('h', range(a, b + 1))) return T.T if lower else T
[docs] def pxs_construct_RREF(n = 3, p = 3, M = (1, 2, 3), min = -9, max = 9): """ Construct a matrix with a partial Row Reduced Echelon Form (RREF) structure based on a given pivot pattern. The function creates a matrix of shape `(n, p)` whose pivot positions are specified by the tuple `M`. Each element `m` of `M` indicates that the corresponding row has a pivot equal to 1 in column `m-1`. The remaining coefficients located to the right of the pivot and outside the pivot columns are filled with random integers between `min` and `max`. Parameters ---------- n : int, optional Number of rows of the matrix (default is 3). p : int, optional Number of columns of the matrix (default is 3). M : tuple or Matrix, optional - If `M` is a tuple, it represents the pivot positions (1-based indexing). - If `M` is a SymPy Matrix, it is copied and used as the initial matrix. min : int, optional Minimum value for the random coefficients (default is -9). max : int, optional Maximum value for the random coefficients (default is 9). Returns ------- Matrix A SymPy matrix of shape `(n, p)` that follows the structure imposed by `M`. Examples -------- >>> pxs_construct_RREF(n=3, p=4, M=(1, 3)) Matrix([ [1, 0, 0, a], [0, 0, 1, b], [0, 0, 0, 0] ]) >>> pxs_construct_RREF(n=2, p=3, M=(2,)) Matrix([ [0, 1, c], [0, 0, 0] ]) """ if isinstance(M, Matrix): A = M. copy() elif isinstance(M, tuple): A = zeros(n, p) for i, m in enumerate(M): A[i, m-1] = 1 for i in range(len(M)): for k in range(p): if k+1 > M[i] and k+1 not in M: A[i, k] = rd.randint(min, max) return A
[docs] def pxs_generate_sys(M = (1, 2, 3), n = 3, p = 3, N = "", opt = "sys", min = -9, max = 9): """ Generate a linear system associated with a matrix in (partial) RREF form. The function first constructs a matrix with a Row Reduced Echelon Form–like structure using `pxs_construct_RREF`, based on the pivot pattern `M`. A right-hand side vector is generated (or copied) and a simplifying transformation matrix is then applied to produce either the full linear system or only the transformed coefficient matrix. Parameters ---------- M : tuple or Matrix, optional - If `M` is a tuple, it specifies the pivot positions (1-based indexing) used to construct the RREF-like matrix. - If `M` is a SymPy Matrix, its shape defines the values of `n` and `p`. n : int, optional Number of rows of the system (default is 3). p : int, optional Number of columns of the coefficient matrix (default is 3). N : Matrix or str, optional Right-hand side vector of the system. If an empty string is provided, a random vector of length `n` with integer entries between `-3` and `3` is generated. opt : str, optional Output option: - `"sys"` returns both the transformed coefficient matrix and the transformed right-hand side vector. - Any other value returns only the transformed coefficient matrix. min : int, optional Minimum value for the random coefficients in the generated matrix (default is 9). max : int, optional Maximum value for the random coefficients in the generated matrix (default is 9). Returns ------- Matrix or tuple of Matrix - If `opt == "sys"`, returns a tuple `(A, B)` where `A` is the transformed coefficient matrix and `B` is the transformed right-hand side vector. - Otherwise, returns only the transformed coefficient matrix. Examples -------- >>> A, B = pxs_generate_sys(M=(1, 3), n=3, p=4) >>> A.shape (3, 4) >>> A = pxs_generate_sys(M=(2,), n=2, p=3, opt="mat") >>> A.shape (2, 3) """ # La matrice est N est copiée pour ne pas modifier la matrice originale if isinstance(M, Matrix): n, p = M.shape if N=="": N = Matrix([rd.randint(-3,3) for i in range(n)]) A, B = pxs_construct_RREF(n, p, M, min, max), N.copy() A1 = pxs_system_simpl(n = n, opt = "") if opt=="sys": return A1 * A, A1 * B else: return A1 * A
[docs] def pxs_repeat_generate_sys(M = (1, 2, 3), n = 3, p = 3, N = "", opt = "sys", min = -9, max = 9, backup = Matrix([[1, 1, 1], [1, 2, 3], [2, 3, 4]]), nb_iter = 10): for _ in range(nb_iter): res = pxs_generate_sys(M, n, p, N, opt, min, max) A = res[0] if opt == "sys" else res if not pxs_zero_column(A): return res return (backup, zeros(backup.rows, 1)) if opt == "sys" else backup
[docs] def pxs_gauss_jordan( A, B=None, x: str = "x", method: str = "sys", view: str = "sep", detail: str = "on", strict: bool = False, frac: bool = True, vectors: str = "col", short: bool = True ): """ Perform a Gauss–Jordan elimination and generate a formatted (LaTeX) output of all intermediate steps. The function applies the Gauss–Jordan algorithm to the linear system ``A * X = B`` (or to the reduction of ``A`` alone if ``B is None``). Each elementary row operation is recorded so that a detailed, step-by-step symbolic representation of the reduction process can be produced, typically for inclusion in a LaTeX document via the ``myst`` / ``pxsl_*`` utilities. Parameters ---------- A : Matrix Coefficient matrix of the linear system (SymPy ``Matrix``). B : Matrix or None, optional Right-hand side vector or matrix. If ``None``, the function only reduces ``A`` (the right-hand side is taken as a zero matrix of compatible size). x : str, optional Base name of the unknown variables used in the symbolic display (e.g. ``"x"`` produces ``x_1, x_2, ...``). Default is ``"x"``. method : str, optional Display method passed to the printing routine (typically ``"sys"`` to format the output as a linear system). Default is ``"sys"``. view : str, optional Visualization mode for intermediate steps (for example, separate or combined views of matrices and operations). Default is ``"sep"``. detail : str, optional Level of detail in the output: - ``"on"`` displays all elementary operations, - other values may reduce verbosity (depending on ``pxsl_print_operations``). Default is ``"on"``. strict : bool, optional Pivot selection strategy: - if ``True``, applies a strict Gauss–Jordan strategy by choosing, below the current row, the pivot with the largest absolute value in the column (partial pivoting); - if ``False``, chooses the first non-zero coefficient below the current row (simplified strategy). Default is ``False``. frac : bool, optional Controls the rendering of rational coefficients: - if ``True``, coefficients are displayed as fractions when appropriate, - if ``False``, coefficients may be displayed in a simplified inline form. Default is ``True``. vectors : str, optional Orientation of solution vectors in the display: - ``"col"`` for column vectors, - any other value for row vectors. Default is ``"col"``. Returns ------- Any A symbolic object representing the formatted output (typically a LaTeX string or structure produced via ``myst`` and ``pxsl_*`` utilities). Examples -------- Basic example (solving a square linear system): >>> from sympy import Matrix >>> A = Matrix([[1, 2], [3, 4]]) >>> B = Matrix([[5], [6]]) >>> out = pxs_gauss_jordan(A, B) >>> isinstance(out, str) or out is not None True Changing the variable base name (``x="u"`` produces ``u_1, u_2, ...``): >>> A = Matrix([[1, 1], [0, 1]]) >>> B = Matrix([[2], [3]]) >>> out = pxs_gauss_jordan(A, B, x="u") >>> isinstance(out, str) or out is not None True Pivot strategy: simplified vs strict (partial pivoting): >>> A = Matrix([[0, 1], [2, 3]]) >>> B = Matrix([[1], [1]]) >>> out1 = pxs_gauss_jordan(A, B, strict=False) # first non-zero pivot >>> out2 = pxs_gauss_jordan(A, B, strict=True) # largest |value| pivot >>> (out1 is not None) and (out2 is not None) True Reducing verbosity (if supported by the display routine): >>> A = Matrix([[1, 2], [3, 4]]) >>> B = Matrix([[5], [6]]) >>> out = pxs_gauss_jordan(A, B, detail="off") >>> out is not None True Skipping the final solution display (only reduction steps): >>> A = Matrix([[1, 2], [3, 4]]) >>> B = Matrix([[5], [6]]) >>> out = pxs_gauss_jordan(A, B, solve=False) >>> out is not None True Reducing ``A`` alone (``B=None``): >>> A = Matrix([[1, 2, 3], [2, 4, 6]]) >>> out = pxs_gauss_jordan(A) >>> out is not None True Solution set representation (vector orientation and span form): >>> A = Matrix([[1, 1, 0], [0, 0, 1]]) >>> B = Matrix([[2], [3]]) >>> out_col = pxs_gauss_jordan(A, B, vectors="col", span=True) >>> out_row = pxs_gauss_jordan(A, B, vectors="row", span=False) >>> (out_col is not None) and (out_row is not None) True """ def __check_incompatible(A, B): npA = np.array(A) npB = np.ravel(B) ind_zero_lines = np.where([not npA[i].any() for i in range(len(npA))])[0] return npB[ind_zero_lines].any() #def __print_solved(A, B, col_pivots, free_indices, x = "x", frac = True): #n,p = A.shape #r = len(col_pivots) #vect_x = Matrix([Symbol(x + "_" + str(j + 1)) for j in range(p)]) #expr = myst(r"""\left\{ \begin{array}{rcl} """) if r > 1 else myst(r"""\left. \begin{array}{rcl} """) #for i in range(r): #j = col_pivots[i] - 1 #expr += myst(r"""\py{vect_x[j]} & =& """, globals(), locals()) #sign = " " #if B[i] == 0 and A[i, j+1:].is_zero_matrix: #expr += "0" #if B[i]: #rhs = myst(r"""\py{B[i].p}/\py{B[i].q}""", globals(), locals()) if (isinstance(B[i], Rational) and B[i].q != 1 and not frac) else latex(B[i]) #expr += myst(r"""\py{rhs}""",globals(),locals()) #sign = "+" #for k in range(j+1, p): #if A[i, k] != 0: #expr += pxsl_ax(-A[i, k], vect_x[k], sign, frac = frac) #sign = "+" #expr += myst(r"""\\[0.3em]""") # displaying the list of free variables #if free_indices: #expr += latex(tuple(vect_x[j-1] for j in free_indices)) if len(free_indices) > 1 else myst(r"""\py{vect_x[list(free_indices)[0] - 1]}""", globals(), locals()) #set_r = myst(r"""\R^{\py{p-r}}""", globals(), locals()) if p - r > 1 else myst(r"""\R""") #expr += myst(r""" &\in &\py{set_r}\\""", globals(), locals()) #expr += myst(r"""\end{array}\right.""") #return expr def __print_solved(A, B, col_pivots, free_indices, x = "x", frac = True): n,p = A.shape r = len(col_pivots) vect_x = Matrix([Symbol(x + "_" + str(j + 1)) for j in range(p)]) expr = myst(r"""\left\{ \begin{array}{rcl} """) k = -1 for i in range(p): if i+1 in free_indices: expr += myst(r"""\py{latex(vect_x[i])} &=&\py{latex(vect_x[i])}\\""", globals(), locals()) continue k += 1 j = i sign = " " expr += myst(r"""\py{latex(vect_x[i])} &=& """, globals(), locals()) if B[k] == 0 and A[k, j+1:].is_zero_matrix: expr += myst(r"""0""", globals(), locals()) if B[k]: rhs = myst(r"""\py{B[k].p}/\py{B[k].q},""", globals(), locals()) if (isinstance(B[k], Rational) and B[k].q != 1 and not frac) else latex(B[k]) expr += myst(r"""\py{rhs}""",globals(),locals()) sign = "+" for l in range(j+1, p): if A[k, l] != 0: expr += myst(r"""\py{pxsl_ax(-A[k, l], vect_x[l], sign, frac = frac)}""", globals(), locals()) sign = "+" if i != p-1: expr += myst(r""" \\ """) # displaying the list of free variables #if free_indices: #for j in free_indices: #expr += myst(r"""\py{latex(vect_x[j-1])}""", globals(), locals()) expr += myst(r"""\end{array}\right.""") return expr def __print_param(A, B, col_pivots, free_indices, x = "x", frac = True): n,p = A.shape r = len(col_pivots) vect_x = Matrix([Symbol(x + "_" + str(j + 1)) for j in range(p)]) expr1 = myst(r"""\left( """) for i, v in enumerate(vect_x): if i != len(vect_x) -1: expr1 += myst(r"""\py{latex(v)},""", globals(), locals()) else: expr1 += myst(r"""\py{latex(v)}""", globals(), locals()) expr1 += myst(r""" \right) = ( """) k = -1 for i in range(p): if i+1 in free_indices: if i != p-1: expr1 += myst(r"""\py{latex(vect_x[i])},""", globals(), locals()) else: expr1 += myst(r"""\py{latex(vect_x[i])}""", globals(), locals()) continue k += 1 j = i sign = " " if B[k] == 0 and A[k, j+1:].is_zero_matrix: expr1 += myst(r"""0""", globals(), locals()) if B[k]: rhs = myst(r"""\py{B[k].p}/\py{B[k].q},""", globals(), locals()) if (isinstance(B[k], Rational) and B[k].q != 1 and not frac) else latex(B[k]) if k != p-1: expr1 += myst(r"""\py{rhs}""",globals(),locals()) else: expr1 += myst(r"""\py{rhs}""",globals(),locals()) sign = "+" for l in range(j+1, p): if A[k, l] != 0: expr1 += myst(r"""\py{pxsl_ax(-A[k, l], vect_x[l], sign, frac = frac)}""", globals(), locals()) sign = "+" if i != p-1: expr1 += myst(r""" , """) # displaying the list of free variables #if free_indices: #for j in free_indices: #expr += myst(r"""\py{latex(vect_x[j-1])}""", globals(), locals()) --> expr1 += myst(r""") """) expr2 = myst(r""" """) if free_indices: expr2 = latex(tuple(vect_x[j-1] for j in free_indices)) if len(free_indices) > 1 else myst(r"""\py{vect_x[list(free_indices)[0] - 1]}""", globals(), locals()) set_r = myst(r"""\R^{\py{p-r}}""", globals(), locals()) if p - r > 1 else myst(r"""\R""") expr2 += myst(r""" \in \py{set_r}""", globals(), locals()) return expr1, expr2 def __get_basis(A, B, col_pivots, free_indices, frac = True): n, p = A.shape canonical = eye(p) basis = [] for j in free_indices: lesser_pivots = [p-1 for p in col_pivots if p < j] nb = len(lesser_pivots) u = canonical[j-1, :] - sum([A[i, j-1] * canonical[pivot, :] for i, pivot in enumerate(lesser_pivots)], start = zeros(1, p)) basis.append(u) # particular solution: x0 = sum([B[i] * canonical[p - 1, :] for i, p in enumerate(col_pivots)], start = zeros(1, p)) return basis, x0 pxs_lang = get_pxs_lang() sol = {"sys": myst(r""" """), "param": myst(r""" """), "free_var": myst(r""" """)} [n, p] = A.shape no_rhs = B is None if no_rhs: B = zeros(n, 1) listA = [A.copy()] listB = [B.copy()] listOp = [[0,0,0,0]] r = -1 col_pivots = [] sol["resol"] = myst(r"""\begin{equation*}""", locals(), globals()) if solve else " " j = 0 go_on = True while j < p and r < n - 1 and go_on: # looking for the line to swap with: if strict: k = max(range(r+1, A.rows), key = lambda i: Abs(A[i, j])) # if strict = False, take the row of the first non zero coefficient if any elif np.any(A[r+1:, j]): k = np.where(np.ravel(A[r + 1:, j]) != 0)[0][0] + r + 1 else: # all coeffs under row r+1 are zero, nothing will be done anyway k = r + 1 if A[k, j] != 0: r += 1 if A[k, j] != 1: listOp.append([1 / A[k, j], k + 1, 0, 0]) B[k, :] = B[k, :] / A[k, j] A[k, :] = A[k, :] / A[k, j] A, B = Matrix(simplify(A)), Matrix(simplify(B)) listA.append(A.copy()) listB.append(B.copy()) col_pivots.append(j + 1) if k != r: A.row_swap(k, r) B.row_swap(k, r) A, B = Matrix(simplify(A)), Matrix(simplify(B)) listA.append(A.copy()) listB.append(B.copy()) listOp.append([0, r + 1, 1, k + 1]) for i in range(n): if i != r and A[i, j]: listOp.append([1, i + 1, -A[i, j], r + 1]) B[i, :] = B[i, :] - A[i, j] * B[r, :] A[i, :] = A[i, :] - A[i, j] * A[r, :] A, B = Matrix(simplify(A)), Matrix(simplify(B)) listA.append(A.copy()) listB.append(B.copy()) go_on = not (short and __check_incompatible(A, B)) j += 1 free_indices = set(range(1, p + 1)) - set(col_pivots) # will be useful for basis of solutions list_mat = [listA] if no_rhs else [listA, listB] sol["resol"] += pxsl_print_operations(list_mat, listOp = listOp, method = method, x = x, view = view, detail = detail, frac = frac) sol["resol"] += myst(r"""\end{equation*} """) #if len(col_pivots) > 1: #sol["resol"] += myst(r""" #On obtient donc le système équivalent suivant :""") if pxs_lang == "fr" else myst(r"""Hence we get the following equivalent system:""") #else: #sol["resol"] += myst(r""" #Le système est donc équivalent à :""") if pxs_lang == "fr" else myst(r"""Hence the system is equivalent to:""") --> # sol["sys"] = myst(r""" # \begin{equation*}""") consistent = not __check_incompatible(A, B) sol["sys"] = __print_solved(A, B, col_pivots, free_indices, x = x, frac = frac) if consistent else pxsl_system_lin(A, B, x = x, frac = frac) sol["param"], sol["free_var"] = __print_param(A, B, col_pivots, free_indices, x = x, frac = frac) # sol["sys"] += myst(r""" # \end{equation*}""") sol["A"] = A sol["B"] = B #expr += myst(r"""\\ \\""") #expr += __print_solved(A, B, col_pivots, free_indices, x = x, frac = frac) #expr += myst(r"""\end{equation*}""", locals(), globals()) # displaying the list of free variables vect_x_free = Matrix([Symbol(x + "_" + str(j)) for j in free_indices]) basis, x0 = __get_basis(A, B, col_pivots, free_indices, frac = frac) if vectors == "col": basis, x0 = [v.T for v in basis], x0.T # displaying the solutions as linear combinations of the basis vectors: mat_delim = "[" if pxs_lang == "en" else "(" if consistent: # sol["set"] = myst(r"""\begin{equation*} # \begin{align*} # \mathcal{S} &= \left\{""") sol["set"] = myst(r"""\left\{""") if not x0.is_zero_matrix or not free_indices: sol["set"] += latex(x0, mat_delim = mat_delim, fold_short_frac = not frac) if free_indices: sol["set"]+= " + " for l in range(len(basis)): vector_tex = latex(basis[l], mat_delim = mat_delim, fold_short_frac = not frac) sol["set"] += myst(r""" \py{vect_x_free[l]} . \py{vector_tex} \py{" + " if (l < len(basis) - 1) else " "}""", globals(), locals()) if free_indices: sol["set"] += myst(r""" ~ : ~ """) # displaying the list of free variables if free_indices: sol["set"] += latex(tuple(vect_x_free)) if len(vect_x_free) > 1 else myst(r"""\py{vect_x_free[0]}""", globals(), locals()) set_r = myst(r"""\R^{\py{p-r - 1}}""", globals(), locals()) if p - r -1 > 1 else myst(r"""\R""") sol["set"] += myst(r""" \in \py{set_r}""", globals(), locals()) # sol["set"] += myst(r"""\right\} # \end{align*} # \end{equation*}""") sol["set"] += myst(r"""\right\}""") sol["span"] = myst(r""" """) if free_indices: vepan = "Vect" if pxs_lang == "fr" else "Span" # sol["span"] = myst(r""" # \begin{equation*} # \begin{align*}""") if not x0.is_zero_matrix: sol["span"] += latex(x0, mat_delim = mat_delim, fold_short_frac = not frac) + " + " sol["span"] += myst(r"""\text{\py{vepan}}\left( """, globals(), locals()) # for l in range(len(basis)): # vector_tex = latex(basis[l], mat_delim = mat_delim, fold_short_frac = not frac) # expr += myst(r"""\py{vector_tex} \py{" , " if (l < len(basis) - 1) else " "}""", globals(), locals()) sol["span"] += " , ".join([latex(vector, mat_delim = mat_delim, fold_short_frac = not frac) for vector in basis]) sol["span"] += myst(r"""\right)""") # sol["span"] += myst(r""" # \end{align*} # \end{equation*}""") else: sol["set"], sol["span"] = myst(r"""\emptyset"""), myst(r"""\emptyset""") return sol
[docs] def pxs_colinear_rows(M, i, j): """ Test whether two rows of a matrix are colinear. Two rows are said to be colinear if one is a scalar multiple of the other. The test is performed by computing the rank of the matrix formed by the two rows. By convention, if at least one of the two rows is a zero row, the function returns ``False`` (zero rows are ignored). Parameters ---------- M : Matrix A SymPy matrix. i : int Index of the first row to test (0-based). j : int Index of the second row to test (0-based). Returns ------- bool ``True`` if rows ``i`` and ``j`` are colinear, ``False`` otherwise. Examples -------- Two proportional rows: >>> from sympy import Matrix >>> M = Matrix([[1, 2, 3], ... [2, 4, 6], ... [1, 0, 1]]) >>> pxs_colinear_rows(M, 0, 1) True Rows that are not colinear: >>> pxs_colinear_rows(M, 0, 2) False A zero row is ignored: >>> M = Matrix([[1, 2, 3], ... [0, 0, 0], ... [2, 4, 6]]) >>> pxs_colinear_rows(M, 0, 1) False Colinearity still detected with non-adjacent rows: >>> pxs_colinear_rows(M, 0, 2) True """ if M.row(i).is_zero or M.row(j).is_zero: return False # on ignore les lignes nulles ici return Matrix([M.row(i), M.row(j)]).rank() == 1
[docs] def pxs_break_colinearity(M, N, i, j, *, coef_range=(-3, 3)): """ Break the colinearity between two rows of a linear system using an elementary row operation. If rows ``i`` and ``j`` of the matrix ``M`` are colinear, the function replaces row ``i`` by a linear combination row_i ← a * row_i + b * row_k where ``k`` is a row index different from ``i`` and ``j``, and ``a`` and ``b`` are nonzero integers chosen randomly in ``coef_range``. The same operation is applied consistently to the right-hand side vector ``N`` so that the linear system remains equivalent. If rows ``i`` and ``j`` are not colinear, or if no suitable third row is available, the matrices are returned unchanged. Parameters ---------- M : Matrix Coefficient matrix of the linear system. N : Matrix Right-hand side column vector of the system. i : int Index of the first row (0-based). j : int Index of the second row (0-based). coef_range : tuple of int, optional Range ``(min, max)`` from which the integer coefficients ``a`` and ``b`` are drawn (default is ``(-3, 3)``). Zero is excluded. Returns ------- Matrix The modified coefficient matrix. Matrix The modified right-hand side vector. Examples -------- Breaking colinearity between two proportional rows: >>> from sympy import Matrix >>> M = Matrix([[1, 2, 3], ... [2, 4, 6], ... [1, 0, 1]]) >>> N = Matrix([1, 2, 0]) >>> M2, N2 = pxs_break_colinearity(M, N, 0, 1) The resulting system is equivalent, but rows 0 and 1 are no longer colinear: >>> from sympy import Matrix >>> Matrix([M2.row(0), M2.row(1)]).rank() == 1 False If the rows are not colinear, nothing is changed: >>> M = Matrix([[1, 2], ... [3, 4]]) >>> N = Matrix([1, 1]) >>> M2, N2 = pxs_break_colinearity(M, N, 0, 1) >>> M2 == M and N2 == N True If no suitable third row exists, the matrices are returned unchanged: >>> M = Matrix([[1, 2], ... [2, 4]]) >>> N = Matrix([1, 2]) >>> M2, N2 = pxs_break_colinearity(M, N, 0, 1) >>> M2 == M and N2 == N True """ M = M.copy() N = N.copy() n = M.rows if not pxs_colinear_rows(M, i, j): return M, N # rien à faire # choisir une ligne k différente de i et j candidates = [k for k in range(n) if k not in (i, j) and not M.row(k).is_zero] if not candidates: return M, N # pas de ligne exploitable k = rd.choice(candidates) # coefficients non nuls a = rd.choice([c for c in range(*coef_range) if c != 0]) b = rd.choice([c for c in range(*coef_range) if c != 0]) # opération élémentaire M.row_op(i, lambda v, col: a * v + b * M[k, col]) N.row_op(i, lambda v, col: a * v + b * N[k]) return M, N
[docs] def pxs_break_all_colinear_rows(A, B, max_iter=5): """ Remove colinearity between all pairs of rows of a linear system. The function repeatedly scans the coefficient matrix ``A`` for pairs of colinear rows. Whenever such a pair is found, an elementary row operation is applied (via :func:`pxs_break_colinearity`) to break the colinearity while preserving the solution set of the system. The process is repeated until no colinear row pairs remain, or until the maximum number of iterations is reached. Parameters ---------- A : Matrix Coefficient matrix of the linear system. B : Matrix Right-hand side column vector. max_iter : int, optional Maximum number of iterations allowed to remove colinearities (default is ``10``). Returns ------- Matrix The modified coefficient matrix with reduced row colinearity. Matrix The modified right-hand side vector. Examples -------- Removing colinearity between multiple rows: >>> from sympy import Matrix >>> A = Matrix([[1, 2, 3], ... [2, 4, 6], ... [3, 6, 9]]) >>> B = Matrix([1, 2, 3]) >>> A2, B2 = pxs_break_all_colinear_rows(A, B) After processing, no two nonzero rows are colinear: >>> any( ... Matrix([A2.row(i), A2.row(j)]).rank() == 1 ... for i in range(A2.rows) ... for j in range(i + 1, A2.rows) ... if not A2.row(i).is_zero and not A2.row(j).is_zero ... ) False If the matrix contains no colinear rows, it is returned unchanged: >>> A = Matrix([[1, 0], ... [0, 1]]) >>> B = Matrix([1, 1]) >>> A2, B2 = pxs_break_all_colinear_rows(A, B) >>> A2 == A and B2 == B True """ M = A.copy() N = B.copy() for _ in range(max_iter): changed = False for i in range(M.rows): for j in range(i + 1, M.rows): if pxs_colinear_rows(M, i, j): M, N = pxs_break_colinearity(M, N, i, j) changed = True break if changed: break if not changed: return M, N return M, N
[docs] def pxs_zero_column(A): """ Checks whether Matrix A has at least one zero column. Parameters ---------- A : Matrix Returns ------- bool : True if A has at least one zero column, False otherwise Examples -------- >>> A = Matrix( ... [[1, 0, 2], ... [2, 0, -3], ... [1, 0, 1]]) >>> pxs_zero_column(A) True >>> M = Matrix( ... [[1, 0, 2], ... [2, 1, -3], ... [1, 0, 1]]) >>> pxs_zero_column(M) False """ return np.any([A[:, j].is_zero_matrix for j in range(A.cols)])
## CALCULS DE DÉTERMINANTS
[docs] def pxs_determinant(A, detail = "on", **kwargs): """ Compute the determinant of a matrix using row reduction, with optional step-by-step output. The function reduces the matrix to upper-triangular form via row swaps and row scaling, tracking all intermediate matrices and operations. The determinant is then recovered from the diagonal of the final matrix, the number of row swaps performed, and the scaling factors applied. Parameters ---------- A : Matrix A square SymPy matrix. detail : str, optional If ``"on"`` (default), row operations are displayed next to each intermediate matrix. Any other value suppresses the operation labels. **kwargs Additional keyword arguments forwarded to SymPy's ``latex()`` function (e.g. ``mul_symbol``). Returns ------- dict A dictionary with the following keys: - ``"last"`` : Matrix The final row-reduced matrix. - ``"oper"`` : str A LaTeX string displaying all intermediate matrices and operations. - ``"swaps"`` : int The number of row swaps performed. - ``"exp"`` : str A LaTeX string for the unsimplified determinant expression (product of sign, scaling factors, and diagonal coefficients), or ``"0"`` if a zero pivot was encountered. - ``"sc_fact"`` : list The list of scaling factors extracted from pivot rows (as their inverses, i.e. the actual pivot values before scaling). - ``"val"`` : str A LaTeX string for the fully evaluated determinant value, or ``"0"`` if a zero pivot was encountered. - ``"all"`` : str A complete LaTeX string combining the reduction table and the final determinant computation. Examples -------- Full determinant computation with details: >>> A = Matrix([[0, 1], [3, 4]]) >>> result = pxs_determinant(A) >>> result["swaps"] 1 >>> result["val"] '-3' Singular matrix (zero pivot encountered): >>> A = Matrix([[1, 2], [2, 4]]) >>> result = pxs_determinant(A) >>> result["val"] '0' >>> result["exp"] '0' 3x3 matrix with scaling factors: >>> A = Matrix([[2, 4, 0], [1, 3, 1], [0, 1, 2]]) >>> result = pxs_determinant(A) >>> result["sc_fact"] [2] """ def __print_ops(listA, listOp, factors, **kwargs): n = listA[0].shape[0] [listA], listOp = pxs_regroupe_ligne([listA], listOp) swaps_txt = myst(r"""\text{swaps:}\quad """) if pxs_lang == "en" else myst(r"""échanges : """) factors_inv = [1 / x for x in factors] factors_txt = myst(r"""\text{factors:}\quad """) if pxs_lang == "en" else myst(r"""facteurs : """) r = 0 # nb of swaps f = 0 # index of factor # First line expr = myst(r"""\begin{array}{ccc} """)+myst(r"""&""")+ pxsl_matrix(listA[0]) + myst(r""" \\ \\""") # other lines for i in range(1, len(listA)): printed_ops = pxsl_lines_op(n, listOp[i], frac = frac) if detail == "on" else " " expr += myst(r"""{{printed_ops}} & """, locals(), globals()) + pxsl_matrix(listA[i]) + myst(r"""&""") do_swap = not all([op[0] for op in listOp[i]]) #do_scale = not all([op[2] for op in listOp[i]]) nb_scale = len([op for op in listOp[i] if op[2] == 0]) if do_swap and nb_scale: # both swap and scale r += 1 f += nb_scale current_factors = myst(r""" , """).join([latex(fac, **kwargs) for fac in factors_inv[:f]]) expr += myst(r"""\begin{array}{l} {{swaps_txt}} {{r}} \\ {{factors_txt}} \displaystyle {{current_factors}} \end{array}""", globals(), locals()) elif do_swap: # swap only r += 1 expr += myst(r"""{{swaps_txt}} {{r}}""", globals(), locals()) elif nb_scale: # scaling only f += nb_scale current_factors = myst(r""" , """).join([latex(fac, **kwargs) for fac in factors_inv[:f]]) expr += myst(r"""{{factors_txt}} \displaystyle {{current_factors}}""", globals(), locals()) expr += myst(r"""\\ \\""") expr+=myst(r"""\end{array}""") return expr def __writing(operations, expression, value, nb_swaps): text = myst(r"""\begin{equation*} {{operations}} \end{equation*} """, globals(), locals()) if expression: if nb_swaps: neg = " " nb_swaps_tex = myst(r"""${{latex(nb_swaps)}}$""", globals(), locals()) else: neg = "not" if pxs_lang == "en" else "n'" nb_swaps_tex = "aucun" if pxs_lang == "fr" else "any" if pxs_lang == "en": text += myst(r"""We have {{neg}} performed {{nb_swaps_tex}} row swap, hence: \begin{equation*} \begin{align*} \det {{pxsl_matrix(A)}} &= {{expression}} \\ &= {{value}} \,. \end{align*} \end{equation*} """, globals(), locals()) else: text += myst(r"""Nous {{neg}}avons effectué {{nb_swaps_tex}} échange de lignes, donc : \begin{equation*} \begin{align*} \det {{pxsl_matrix(A)}} &= {{expression}} \\ &= {{value}} \,. \end{align*} \end{equation*} """, globals(), locals()) else: if pxs_lang == "en": text += myst(r"""The last matrix above has a zero diagonal coefficient, hence: \begin{equation*} \det {{pxsl_matrix(A)}} = 0\,. \end{equation*}""") else: text +=myst(r"""La dernière matrice obtenue ci-dessus possède un coefficient diagonal nul, hence: \begin{equation*} \det {{pxsl_matrix(A)}} = 0\,. \end{equation*}""") return text pxs_lang = get_pxs_lang() n = A.rows listA=[A.copy()] listOp=[[0,0,0,0]] nb_swaps = 0 factors = [] for k in range(n - 1): if np.any(A[k:, k]): # swapping r = np.where(np.ravel(A[k:, k]) != 0)[0][0] + k if r != k: nb_swaps += 1 A.row_swap(k, r) listA.append(A.copy()) listOp.append([0, r + 1, 1, k + 1]) # setting 1 as pivot if A[k, k] != 1: factors.append(A[k, k]) listOp.append([1 / A[k, k], k + 1, 0, 0]) A[k, :] /= A[k, k] listA.append(A.copy()) # getting zeros under the pivot for j in range(k+1, n): if A[j, k] != 0: listOp.append([1, j+1, -A[j, k], k + 1]) A[j, :] = A[j, :] - A[j, k] * A[k, :] listA.append(A.copy()) else: # zero pivot, computation is over operations = pxsl_print_operations([listA], listOp = listOp, method = "mat") all_details = __writing(operations, None, None, nb_swaps) return {"last" : A, "oper" : operations, "swaps" : nb_swaps, "exp" : "0", "sc_fact" : factors, "val" : "0", "all" : all_details} np_diag = np.array(A)[range(n), range(n)] diag_coeffs = list(np_diag[np_diag != 1]) # non-ones diagonal coefficients of the last matrix if not diag_coeffs: diag_coeffs = [1] sign = [Pow(-1, nb_swaps, evaluate = False)] if nb_swaps else [] all_det_factors = sign + factors + diag_coeffs # operations = pxsl_print_operations([listA], listOp = listOp, method = "mat") operations = __print_ops(listA, listOp, factors, **kwargs) expression = latex(Mul(*all_det_factors, evaluate = False), **kwargs) value = latex(Mul(*[(-1) ** nb_swaps] + factors + diag_coeffs), **kwargs) all_details = __writing(operations, expression, value, nb_swaps) result = { "last" : A, "oper" : operations, "swaps" : nb_swaps, "exp" : expression, "sc_fact" : [1 / x for x in factors], "val" : value, "all" : all_details, } return result
[docs] def pxs_compute_determinant(A, smart = True, **kwargs): """ Compute the determinant of a matrix by cofactor expansion along a strategically chosen row or column, with full LaTeX output. At each recursive step, the function optionally selects the row or column with the fewest non-zero entries (``smart=True``) to minimise the number of non-zero terms in the expansion. The result is a complete LaTeX ``align*`` environment showing all intermediate steps down to 2x2 determinants. Parameters ---------- A : Matrix A square SymPy matrix. smart : bool, optional If ``True`` (default), at each step the row or column with the fewest non-zero entries is selected for elimination, and either row or column operations are applied accordingly. If ``False``, the first column is always used, with row operations only. **kwargs Additional keyword arguments forwarded to SymPy's ``latex()`` function. Returns ------- str A LaTeX string showing the full step-by-step determinant computation, from the original matrix down to the final scalar value. Examples -------- 3x3 matrix with smart expansion: >>> A = Matrix([[1, 0, 0], [2, 3, 1], [0, 4, 2]]) >>> print(pxs_compute_determinant(A)) # LaTeX output exploiting the zeros in the first row 3x3 matrix without smart expansion: >>> A = Matrix([[2, 1, 3], [0, 4, 1], [1, 2, 0]]) >>> print(pxs_compute_determinant(A, smart=False)) # LaTeX output always expanding along the first column """ def __rec_compute_determinant(A, smart, expr, values, **kwargs): def __latex_without_ones(list_terms, **kwargs): list_no_ones = [x for x in list_terms if x != 1] if list_no_ones: return latex(Mul(*list_no_ones, evaluate = False), **kwargs) if list_no_ones != [-1] else myst(r""" - """) else: return " " def __get_coeffs(vector): flat = np.array(vector).ravel() # flatten to get a 1d-array ones_ind = np.where(flat == 1)[0] # indices of the ones, if any indices = np.where(flat)[0] if len(ones_ind) > 0: # if any return ones_ind[0], indices else: return indices[0], indices def __det2x2(A, **kwargs): a, b, c, d = A ad = Mul(a, d, evaluate = not (a * d)) bc = Mul(b, c, evaluate = not (b * c)) return myst(r"""{{latex(ad, **kwargs)}} - {{latex(bc, **kwargs)}}""", globals(), locals()).replace("- -", "+") pxs_lang = get_pxs_lang() n = A.rows if n == 2: try: mult = mul_symbol except: mult = myst(r""" """) lpar = myst(r"""\left(""") if len(values) != values.count(1) else myst(r""" """) rpar = myst(r"""\right)""") if len(values) != values.count(1) else myst(r""" """) expr += myst(r""" &= {{__latex_without_ones(values, **kwargs)}} {{mult}} {{lpar}} {{__det2x2(A, **kwargs)}} {{rpar}} & \\ """, globals(), locals()) values.append(A.det()) final_value = Mul(*values) expr += myst(r""" &= {{latex(final_value, **kwargs)}} \,. """, globals(), locals()) return expr if n <= 1: values.append(A[0, 0]) final_value = Mul(*values) expr += myst(r"""\\ &= {{__latex_without_ones(values, **kwargs)}} \\ &= {{latex(final_value, **kwargs)}} \,. """, globals(), locals()) return expr if A[:, 0].is_zero_matrix: expr += myst(r"""\\ &= 0 \, . """) return expr operations = [] sign = 1 column_operations = False # replace row operations by column operations if needed if smart: nonzero_row = np.count_nonzero(A, axis = 1) nonzero_col = np.count_nonzero(A, axis = 0) row_min, col_min = min(nonzero_row), min(nonzero_col) if row_min < col_min: # The best is a row column_operations = True # in this case we perform column operations k = np.argmin(nonzero_row) j0, indices = __get_coeffs(A[k, :]) coeff = A[k, j0] # scaling if need be if coeff != 1: operations.append([1 / coeff, j0 + 1, 0, 0]) A[:, j0] /= coeff # getting zeros on the row for j in indices: if j != j0: operations.append([1, j + 1, -A[k, j], j0 + 1]) A[:, j] = A[:, j] - A[k, j] * A[:, j0] sign = (-1) ** (k + j0) else: # The best is a column k = np.argmin(nonzero_col) i0, indices = __get_coeffs(A[:, k]) coeff = A[i0, k] # scaling if need be if coeff != 1: operations.append([1 / coeff, i0 + 1, 0, 0]) A[i0, :] /= coeff # getting zeros on the row for i in indices: if i != i0: operations.append([1, i + 1, -A[i, k], i0 + 1]) A[i, :] = A[i, :] - A[i, k] * A[i0, :] sign = (-1) ** (i0 + k) else: # swapping if A[0, 0].is_zero: r = np.where(np.ravel(A[:, 0]) != 0)[0][0] sign = -1 A.row_swap(0, r) operations.append([0, r + 1, 1, 1]) coeff = A[0, 0] # scaling if need be if coeff != 1: operations.append([1 / coeff, 1, 0, 0]) A[0, :] /= coeff # getting zeros under the pivot for j in range(1, n): if A[j, 0] != 0: operations.append([1, j+1, -A[j, 0], 1]) A[j, :] = A[j, :] - A[j, 0] * A[0, :] minor = A.copy() if column_operations: # smart and column operations i, j = k, j0 elif smart: # smart and row operations i, j = i0, k else: # unsmart i, j = 0, 0 minor.row_del(i) minor.col_del(j) row_symb = "L" if pxs_lang == "fr" else "R" print_ops = pxsl_lines_op(n, operations).replace(row_symb, "C") if column_operations else pxsl_lines_op(n, operations) expr += myst(r""" &= {{__latex_without_ones(values + [1 if smart else sign, coeff], **kwargs)}} \det {{pxsl_matrix(A)}} & {{print_ops}} \\ """, globals(), locals()) values.append(sign * coeff) expr += myst(r""" &= {{__latex_without_ones(values, **kwargs)}} \det {{pxsl_matrix(minor)}} & \\ """, globals(), locals()) return __rec_compute_determinant(minor, smart, expr, values, **kwargs) begin = myst(r""" \begin{equation*} \begin{align*} \det {{pxsl_matrix(A)}} """, globals(), locals()) end = myst(r""" \end{align*} \end{equation*} """) return begin + __rec_compute_determinant(A, smart, " ", [], **kwargs) + end
[docs] def pxs_best_line(A): """ Identify the row or column of a matrix with the fewest non-zero entries. This is used to select the most efficient line for cofactor expansion. In case of a tie between the best row and the best column, the column is preferred. Parameters ---------- A : Matrix A square SymPy matrix. Returns ------- rc : str ``"r"`` if the best line is a row, ``"c"`` if it is a column. k : int The 1-based index of that row or column. Examples -------- A matrix whose first row has the fewest non-zero entries: >>> from sympy import Matrix >>> A = Matrix([[1, 0, 0], ... [2, 3, 1], ... [0, 4, 2]]) >>> pxs_best_line(A) ('r', 1) A matrix whose second column has the fewest non-zero entries: >>> A = Matrix([[1, 0, 3], ... [2, 0, 1], ... [0, 5, 2]]) >>> pxs_best_line(A) ('c', 2) Tie between a row and a column — the column is preferred: >>> A = Matrix([[1, 0, 3], ... [0, 2, 1], ... [4, 5, 6]]) >>> pxs_best_line(A) ('c', 1) """ nonzero_row = np.count_nonzero(A, axis = 1) nonzero_col = np.count_nonzero(A, axis = 0) row_min, col_min = min(nonzero_row), min(nonzero_col) if row_min < col_min: # The best is a row rc = "r" # in this case we perform column operations k = np.argmin(nonzero_row) + 1 else: rc = "c" k = np.argmin(nonzero_col) + 1 return rc, k
[docs] def pxs_expand_determinant(A, rc = "s", k = 1, color = "red", rc_min = None, k_min = None, **kwargs): """ Perform one step of cofactor expansion of a determinant along a chosen row or column, and return the resulting LaTeX expression and sub-problems. The expansion row or column can be specified explicitly, or selected automatically as the one with the fewest non-zero entries. The resulting minors can optionally have one of their own rows or columns highlighted in colour, indicating the line that will be used at the next expansion step. Parameters ---------- A : Matrix A square SymPy matrix. rc : str, optional ``"r"`` to expand along a row, ``"c"`` to expand along a column, or ``"s"`` (default) to select automatically the best line. k : int, optional 1-based index of the row or column to expand along. Used only when ``rc`` is ``"r"`` or ``"c"``. Defaults to ``1``. color : str, optional Color name to highlight the selected line inside each minor matrix. Defaults to ``"red"``. Pass ``None`` or ``""`` to disable highlighting. rc_min : str or None, optional ``"r"`` or ``"c"``, pre-specified choice of expansion direction for the minors. If ``None`` (default), the best line is selected automatically for each minor. k_min : int or None, optional 1-based index of the row or column to highlight inside each minor. If ``None`` (default), it is determined automatically. **kwargs Additional keyword arguments forwarded to SymPy's ``latex()`` function. Returns ------- dict A dictionary with the following keys: - ``"factors"`` : list of Expr The signed cofactor scalars ``(-1)^(i+j) * a_{ij}`` for each non-zero entry in the expansion line. - ``"minors"`` : list of Matrix The corresponding ``(n-1) x (n-1)`` minor matrices. - ``"expr"`` : str A LaTeX string of the full cofactor expansion (sum of factors times determinants of minors). - ``"rc"`` : str The direction actually used for expansion (``"r"`` or ``"c"``). - ``"index"`` : int The 1-based index of the row or column actually used. Examples -------- Automatic selection of the best line: >>> from sympy import Matrix >>> A = Matrix([[1, 0, 0], ... [2, 3, 4], ... [5, 6, 7]]) >>> result = pxs_expand_determinant(A) >>> result["rc"], result["index"] ('r', 1) >>> result["factors"] [1] Explicit expansion along the second column: >>> A = Matrix([[1, 2, 3], ... [0, 4, 0], ... [5, 6, 7]]) >>> result = pxs_expand_determinant(A, rc="c", k=2) >>> result["rc"], result["index"] ('c', 2) Disabling minor highlighting: >>> result = pxs_expand_determinant(A, color=None) >>> result["expr"] # LaTeX string, no color commands """ n = A.rows if rc == "s": rc, k = pxs_best_line(A) A = A.copy() if rc == "r" else A.T couples = [] M = A.copy() M.row_del(k - 1) list_factors, list_minors = [], [] get_rc_minor = rc_min is None or k_min is None for j in range(n): if A[k - 1, j]: factor = (-1) ** (j + k - 1) * A[k - 1, j] minor = M.copy() minor.col_del(j) if rc == "c": minor = minor.T if get_rc_minor: rc_min, k_min = pxs_best_line(minor) row_color = k_min - 1 if rc_min == "r" and color and n > 3 else "" # if n = 3 then minors are of order 2 hence colouring makes no sense col_color = k_min - 1 if rc_min == "c" and color and n > 3 else "" symb = Symbol(myst(r"""\det {{pxsl_mat(minor, color = color, row = row_color, col = col_color)}}""", globals(), locals())) if factor != 1: couples.append((factor, symb)) else: couples.append((symb,)) list_factors.append(factor) list_minors.append(minor) addition = Add(*[Mul(*prod, evaluate = False) for prod in couples], evaluate = False) kwargs_copy = kwargs.copy() kwargs_copy["order"] = "none" #expression = LatexPrinter(dict(order = "none"))._print_Add(addition) if len(couples) > 1 else latex(addition, **kwargs) expression = LatexPrinter(kwargs_copy)._print_Add(addition) if len(couples) > 1 else latex(addition, **kwargs_copy) result = { "factors" : list_factors, "minors" : list_minors, "expr" : expression, "rc" : rc, "index" : k } return result
# def pxs_expand_determinant_rec(A, info, depth): # # def __det2x2(A): # a, b, c, d = A # ad = Mul(a, d, evaluate = not (a * d)) # bc = Mul(b, c, evaluate = not (b * c)) # return myst(r"""\py{latex(ad)} - \py{latex(bc)}""", globals(), locals()).replace("- -", "+") # # def __convert_factor(factor): # if factor == 1: # return myst(r""" """) # elif factor == - 1: # return myst(r""" - """) # elif isinstance(factor, Add): # return myst(r""" \left( {{latex(factor)}} \right) """, globals(), locals()) # else: # return latex(factor) # # def __write_lin_comb(l_factors, r_factors): # # l_factors = [__convert_factor(fact) for fact in l_factors] # expression = " + ".join([myst(r"""{{l}} \left( {{r}} \right) """, globals(), locals()) for l, r in zip(l_factors, r_factors)]) # return simplify_plus_minus(expression) # # n = A.rows # if n == 1: # return latex(A[0]), info # if n == 2: # return __det2x2(A), info # # expd = pxs_expand_determinant(A) # if depth == 0: # info.append((expd["rc"], expd["index"])) # return expd["expr"], info # # l_factors = expd["factors"] # r_factors, sub_info = zip(*[pxs_expand_determinant_rec(minor, info, depth - 1) for minor in expd["minors"]]) # info.append(list(sub_info)) # return __write_lin_comb(l_factors, r_factors), info
[docs] def pxs_det_full_expand(A, nb_rep = None, rc = "s", k = 1, name = "A", color = "red", end = True, **kwargs): # name = False to avoid displaying initial matrix ; name = True to display the explicit matrix """ Produce a complete step-by-step cofactor expansion of a determinant, down to 2x2 determinants, as a LaTeX block. Starting from the full matrix, the function repeatedly applies ``pxs_expand_determinant`` to each minor, building a sequence of aligned LaTeX lines that shows the full expansion tree flattened into a single chain of equalities. At each level, a highlighted row or column inside the minor matrices indicates where the next expansion will be performed. Parameters ---------- A : Matrix A square SymPy matrix. nb_rep : int or None, optional Number of expansion steps to perform. Must be at most ``n - 2`` where ``n`` is the matrix size. Defaults to ``None``, meaning all steps are performed down to 2x2 minors. rc : str, optional ``"r"`` to always expand along a row, ``"c"`` along a column, or ``"s"`` (default) to select automatically at each step. k : int, optional 1-based index of the row or column to use when ``rc`` is ``"r"`` or ``"c"``. Defaults to ``1``. name : str, bool, or None, optional Controls how the left-hand side of the first equality is displayed. - A string (e.g. ``"A"``) : displayed as ``\\det A``. - ``True`` : the explicit matrix is rendered, with the expansion line highlighted. - ``False`` or ``None`` : no left-hand side is prepended. Defaults to ``"A"``. color : str, optional Color used to highlight the expansion row or column inside minor matrices. Defaults to ``"red"``. Pass ``None`` to disable. end : bool, optional If ``True`` (default), the expansion is completed all the way to the final numerical value. If ``False``, the last step stops at the 2x2 determinant expressions without evaluating them. **kwargs Additional keyword arguments forwarded to SymPy's ``latex()`` function. Returns ------- all_details : str A LaTeX string (without the surrounding ``equation*`` environment) showing the full expansion chain. list_lines : list of str The individual LaTeX lines of the expansion, including the left-hand side label if ``name`` is set. Examples -------- Full expansion of a 3x3 matrix: >>> from sympy import Matrix >>> A = Matrix([[1, 2, 3], ... [0, 4, 5], ... [1, 0, 6]]) >>> details, lines = pxs_det_full_expand(A) Expanding along the second row explicitly: >>> details, lines = pxs_det_full_expand(A, rc = "r", k = 2) Limiting the number of expansion steps to 1: >>> details, lines = pxs_det_full_expand(A, nb_rep = 1) Disabling the final numerical evaluation: >>> details, lines = pxs_det_full_expand(A, end = False) """ def __det2x2(A, **kwargs): a, b, c, d = A ad = Mul(a, d, evaluate = not (a * d)) bc = Mul(b, c, evaluate = not (b * c)) return myst(r"""{{latex(ad, **kwargs)}} - {{latex(bc, **kwargs)}}""", globals(), locals()).replace("- -", "+") def __convert_factor(factor, **kwargs): kwargs_copy = kwargs.copy() kwargs_copy["order"] = "none" if factor == 1: return myst(r""" """) elif factor == - 1: return myst(r""" - """) elif isinstance(factor, Add): return myst(r""" \left( {{latex(factor, **kwargs_copy)}} \right) """, globals(), locals()) else: return latex(factor, **kwargs_copy) def __write_lin_comb(l_factors, r_factors, r_par = None, show_mul = None, **kwargs): if r_par is None: r_par = [False] * len(r_factors) if show_mul is None: show_mul = [False] * len(r_factors) mul_symbol = kwargs.get("mul_symbol", "") if mul_symbol: muls = [mul_symbol if abs(l) != 1 else myst(r""" """) for l in l_factors] else: muls = [myst(r""" \cdot """) if boo else myst(r""" """) for boo in show_mul] l_factors = [__convert_factor(fact, **kwargs) for fact in l_factors] r_factors = [myst(r"""\left( {{r}} \right) """, globals(), locals()) if boo else r for r, boo in zip(r_factors, r_par)] expression = " + ".join([myst(r"""{{l}} {{x}} {{r}} """, globals(), locals()) for l, r, x in zip(l_factors, r_factors, muls)]) return simplify_plus_minus(expression) rc_min, k_min = (None, None) if rc == "s" else (rc, k) n = A.rows nb_rep = min(nb_rep, n - 2) if nb_rep else n - 2 list_lines = [] if n == 2: list_lines.append(__det2x2(A, **kwargs)) list_lines.append(latex(A.det(), **kwargs)) else: first_expd = pxs_expand_determinant(A, rc = rc, k = k, color = color, rc_min = rc_min, k_min = k_min, **kwargs) list_lines.append(first_expd["expr"]) factors, minors = first_expd["factors"], first_expd["minors"] for _ in range(1, nb_rep): sub_expds = [pxs_expand_determinant(minor, rc = rc, k = k, color = None, **kwargs) for minor in minors] sub_expr = [dico["expr"] for dico in sub_expds] sub_factors = [dico["factors"] for dico in sub_expds] sub_minors = [dico["minors"] for dico in sub_expds] r_par = [len(fact) > 0 and (len(fact) > 1 or fact[0].could_extract_minus_sign()) for fact in sub_factors] show_mul = [len(r_fact) == 0 or (len(r_fact) == 1 and (not rp) and abs(l_fact) != 1 and (not isinstance(l_fact, Add))) for l_fact, r_fact, rp in zip(factors, sub_factors, r_par)] # put a mul sign between factor and subexpr if needed old_factors = factors factors = [factors[i] * y for i in range(len(factors)) for y in sub_factors[i]] minors = sum(sub_minors, start = []) i0 = [i for i in range(len(sub_minors)) if sub_minors[i]][0] if len(minors) > 0 else None # index of first non-empty sublist in sub_minors (and sub_factors) if len(minors) > 1 or (len(minors) == 1 and old_factors[i0] != 1 and sub_factors[i0][0] != 1): # intermediary step needed # old_factors_nn = [x if y else myst(r""" """) for x, y in zip(old_factors, sub_factors)] # for showing 0 instead of 4 . 0 e.g list_lines.append(__write_lin_comb(old_factors, sub_expr, r_par, show_mul, **kwargs)) list_rck = [pxs_best_line(minor) for minor in minors] if rc == "s" else [(rc, k)] * len(minors) if color and len(minors) > 0 and minors[0].rows > 2: l_row_color = [kk - 1 if rrcc == "r" else "" for rrcc, kk in list_rck] l_col_color = [kk - 1 if rrcc == "c" else "" for rrcc, kk in list_rck] else: # if order 2 reached, no colouring l_row_color, l_col_color = [""] * len(minors), [""] * len(minors) # LA LIGNE CI-DESSOUS POSE UN PROBLÈME NON IDENTIFIÉ... #minors_tex = [myst(r"""\det {{pxsl_mat(minor, color = color, row = r, col = c)}} """, globals(), locals()) for minor, r, c in zip(minors, l_row_color, l_col_color)] # en remplacement de la ligne foireuse : minors_tex = [] for minor, r, c in zip(minors, l_row_color, l_col_color): mtext = myst(r"""\det {{pxsl_mat(minor, color = color, row = r, col = c)}} """, globals(), locals()) minors_tex.append(mtext) list_lines.append(__write_lin_comb(factors, minors_tex, **kwargs)) if end and nb_rep == n - 2: list_lines.append(__write_lin_comb(factors, [__det2x2(minor, **kwargs) for minor in minors], [True] * len(minors), **kwargs)) list_det = [minor.det() for minor in minors] #last_sum = " + ".join([latex(left * right) for left, right in zip(factors, list_det)]) last_sum = pxsl_add(*[left * right for left, right in zip(factors, list_det)], zeros = True) if len(factors) > 1: list_lines.append(last_sum) list_lines.append(latex(A.det(), **kwargs)) # all_details = myst(r""" # \begin{equation*} # \begin{align*} if isinstance(name, str): list_lines = [myst(r""" \det {{name}} """, globals(), locals())] + list_lines elif name: if rc == "s": rc, k = pxs_best_line(A) row_color = k - 1 if rc == "r" and color and n > 2 else "" col_color = k - 1 if rc == "c" and color and n > 2 else "" list_lines = [myst(r""" \det {{pxsl_mat(A, color = color, row = row_color, col = col_color)}} """, globals(), locals())] + list_lines # remove empty lines: list_lines = [line for line in list_lines if sub(r" *", "", line)] all_details = myst(r""" {{list_lines[0]}} &= """, globals(), locals()) all_details += myst(r""" \\ &= """, globals(), locals()).join(list_lines[1:]) # all_details += myst(r""" # \end{align*} # \end{equation*} # """, globals(), locals()) return all_details, list_lines
# === REMOVE WHEN pxs_full_expand NO LONGER USED === pxs_full_expand = pxs_det_full_expand # ==================================================
[docs] def pxs_char_poly(A, var = r" \lambda ", unit = False, rc = "s", k = 1, name = "A", color = "red", **kwargs): """ Produce a complete step-by-step cofactor expansion of a characteristic polynomial, down to 2x2 determinants, as a LaTeX block. Starting from the matrix ``A - var*I_n`` (or ``var*I_n - A`` if ``unit=True``), the function calls ``pxs_det_full_expand`` to build a sequence of aligned LaTeX lines showing the full expansion chain. The last line is replaced by the canonical factored form of the characteristic polynomial, computed via ``pxs_Poly``. Parameters ---------- A : Matrix A square SymPy matrix. var : str, optional LaTeX string for the characteristic variable. Defaults to ``r" \\lambda "``. unit : bool, optional If ``False`` (default), the characteristic matrix is ``A - var*I_n``. If ``True``, it is ``var*I_n - A``. rc : str, optional ``"r"`` to always expand along a row, ``"c"`` along a column, or ``"s"`` (default) to select automatically at each step. k : int, optional 1-based index of the row or column to use when ``rc`` is ``"r"`` or ``"c"``. Defaults to ``1``. name : str, bool, or None, optional Controls how the left-hand side of the first equality is displayed. - A string (e.g. ``"A"``): displayed as ``\\det(A - \\lambda I_n)`` or ``\\det(\\lambda I_n - A)`` depending on ``unit``. - ``True`` : the explicit characteristic matrix is rendered, with the expansion line highlighted. - ``False`` or ``None`` : no left-hand side is prepended. Defaults to ``"A"``. color : str, optional Color used to highlight the expansion row or column inside minor matrices. Defaults to ``"red"``. Pass ``None`` to disable. **kwargs Additional keyword arguments forwarded to SymPy's ``latex()`` function. Returns ------- all_details : str A LaTeX string (without the surrounding ``equation*`` environment) showing the full expansion chain, ending with the canonical form of the characteristic polynomial. cpoly : Poly The characteristic polynomial as a ``pxs_Poly`` object. Examples -------- Full expansion of the characteristic polynomial of a 3x3 matrix: >>> from sympy import Matrix >>> A = Matrix([[1, 2, 3], ... [0, 4, 5], ... [1, 0, 6]]) >>> details, cpoly = pxs_char_poly(A) Using the convention ``var*I_n - A`` instead: >>> details, cpoly = pxs_char_poly(A, unit=True) Expanding along the second row with a custom variable name: >>> details, cpoly = pxs_char_poly(A, var=r" \\mu ", rc="r", k=2) Disabling color highlighting: >>> details, cpoly = pxs_char_poly(A, color=None) """ n = A.rows assert n == A.cols, "The matrix must be square" var_sym = Symbol(var) M = var_sym * eye(n) - A if unit else A - var_sym * eye(n) _, list_lines = pxs_det_full_expand(M, nb_rep = None, rc = rc, k = k, name = name, color = color, end = True, **kwargs) cpoly = pxs_Poly(M.det(), var_sym) cpoly_tex = cpoly.pxsl_print() list_lines[-1] = cpoly_tex if isinstance(name, str): if unit: list_lines[0] = myst(r""" \det( {{var}} I_{{n}} - {{name}} ) """, globals(), locals()) else: list_lines[0] = myst(r""" \det( {{name}} - {{var}} I_{{n}} ) """, globals(), locals()) all_details = myst(r""" {{list_lines[0]}} &= """, globals(), locals()) + myst(r""" \\ &= """, globals(), locals()).join(list_lines[1:]) return all_details, cpoly
[docs] def pxs_randmatrixrect(p,q,a,b): """Returns a rectangular matrix with p rows and q columns such that every coefficient is a realization of of a discrete random variable on range(a, b) :returns: LaTeX bmatrix as a string """ M= eye(p,q) for i in range(p): for j in range(q): M[i,j] = sample(DiscreteUniform('h', range(a, b))) return M
[docs] def pxs_cptrzeros(M): """En: Counts the number of zero entries in the given matrix M. cpte le nombre de coefficients nuls dans une matrice donnée""" cptr = 0 p = M.cols q = M.rows for i in range(p): for j in range(q): if M[i,j] == 0: cptr = cptr + 1 return cptr
[docs] def pxs_matelement(p,q,i,j): """ En: Renvoie la matrice élémentaire de p lignes et q colonnes avec un 1 à la place i,j. Returns the elementary matrix with p rows and q columns with a 1 at position (i, j). Fr: Renvoie la matrice élémentaire de p lignes et q colonnes avec un 1 à la place i,j.""" if i > p or j>q: raise "l'indice de ligne ou de colonne est trop grand" else: M = Matrix(zeros(p, q)) M[i-1,j-1]=1 return M
[docs] def pxs_randmatrixInv(p,a,b,r): """En: Returns an invertible square matrix of size p, with r percent of non-zero entries (thus [r.p]+1 non-zero elements), and all entries are between a and b-1. Retourne une matrice carrée de taille p, inversible et dont le nbre de coefficients non nuls est de r pourcent (donc en fait [r.p]+1 éléments non nuls) et dont tous les coefficients sont compris entre a et b-1""" alphaprimeo = r*p*p/100 alphaprime = int(r*p*p/100) alphaprimeseconde = alphaprimeo -alphaprime H = eye(p) S = eye(p) if alphaprimeseconde < 1/2: alpha = alphaprime #print('alpha = ', alpha) else : alpha= alphaprime +1 #print('alpha = ', alpha) # if alphaprime == p**2: # alpha= p**2 #Nbre de coefficients qui devront être non nuls dans P # else : # alpha= int(r*p*p/100)+1 #Nbre de coefficients qui devront être non nuls dans P beta=p**2 - alpha #Nbre de coefficients qui devront être nuls dans P #print('beta = ', beta) if alpha < p: print('r is too small! Choose a greater value for r.') else : P = pxs_randmatrixrect(p,p,a,b) q= P.det() #print('(P,det(P)) = ', P, P.det()) #q= P.det()-q #print('q = ', q) rho = 0 while q == 0: P = pxs_randmatrixrect(p,p,a,b) q = P.det() #print('q = ', q) rho = rho+1 # A ce stade la matrice P est inversible # On veut que P ait exactement alpha coef non nuls et p^2-alpha nulles. #print('rho = ', rho) #Donc si rho = 0 c'est qu'on n'est pas rentré dans la boucle et que la matrice est inversible depuis le début #print('P = ', P, ' et det(P) = ', P.det()) cptrzeros = pxs_cptrzeros(P) # A ce stade cpt rzeros est égal au nombre de coefs nuls contenus dans P if cptrzeros == beta: H = P else : if cptrzeros < beta: #Donc ici la matrice P a trop peu de zéros, il faut en rajouter. while cptrzeros < beta: for i in range(p): if cptrzeros == beta: break else : for j in range(p): if cptrzeros == beta: break else : if P[i,j] != 0: S=P-P[i,j]*pxs_matelement(p,p,i+1,j+1) if S.det() != 0: P=S cptrzeros = pxs_cptrzeros(P) #print('P = ', P) #cptrzeros = cptrzeros +1 #print('cptrzeros = ', cptrzeros) H = P else : #Donc ici la matrice P a trop de zéros, il faut en retirer. while cptrzeros > beta: for i in range(p): if cptrzeros == beta: break else : for j in range(p): if cptrzeros == beta: break else : if P[i,j] == 0: S=P+pxs_matelement(p,p,i+1,j+1) if S.det() != 0: P=S cptrzeros = cptrzeros -1 H = P perm_rows = rd.sample([i for i in range(p)], p) perm_cols = rd.sample([i for i in range(p)], p) H = H[perm_rows, perm_cols] return H