Source code for Classes_in_progress


from sympy import *
import random as rd
from src.scripts.Mes_fctions.Classes_Extensions import *
from src.scripts.pxs_runtime import myst, get_pxs_lang

[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", }
# --- Gestion automatique des noms : nouveau système de nommage ---
[docs] class NameManager:
[docs] def __init__(self, start='f', skip_letters=('i', 'o', 'x', 'y', 'z')): """ start : lettre de départ (nom de la fonction originale) skip_letters : lettres à éviter dans la séquence """ self.skip_letters = set(skip_letters) self.start_letter = start # Séquence normale : u, v, g, h, j, k, l, m, n, p, q, r, s, t, w # (on évite i, o, x, y, z) all_letters = [chr(c) for c in range(ord('a'), ord('z') + 1)] normal_sequence = [] # D'abord u et v if 'u' not in self.skip_letters: normal_sequence.append('u') if 'v' not in self.skip_letters: normal_sequence.append('v') # Puis g jusqu'à t (en évitant i, o, et les lettres déjà utilisées) for letter in all_letters[6:20]: # g jusqu'à t if letter not in self.skip_letters and letter not in ['u', 'v']: normal_sequence.append(letter) # Ajouter w à la fin si pas dans skip_letters if 'w' not in self.skip_letters and 'w' not in normal_sequence: normal_sequence.append('w') # Gérer les cas spéciaux if start == 'u': # Si la fonction originale est u, la séquence est : v, w, g, h, j, k, l, ... self.sequence = ['v', 'w'] + [l for l in normal_sequence if l not in ['u', 'v', 'w']] elif start == 'v': # Si la fonction originale est v, la séquence est : u, w, g, h, j, k, l, ... self.sequence = ['u', 'w'] + [l for l in normal_sequence if l not in ['u', 'v', 'w']] else: # Cas normal : u, v, g, h, j, k, l, ... self.sequence = normal_sequence self.index = -1 # On commence à -1 pour que le premier next() donne le premier élément
[docs] def next(self): """Avance d'une lettre et la renvoie.""" self.index = (self.index + 1) % len(self.sequence) return self.sequence[self.index]
[docs] def current(self): """Renvoie la lettre courante (la fonction originale).""" return self.start_letter
def _pxsl_print_latex(steps, print_equation = True): """Formate la liste d'étapes en LaTeX.""" config_standard = pxs_config() if len(steps) == 1: if print_equation: return myst(r""" \begin{equation*} \py{latex(steps[0],**config_standard)} \end{equation*} """, globals(), locals()) else: return myst(r""" \py{latex(steps[0],**config_standard)} """, globals(), locals()) if len(steps) == 2: if print_equation: return myst(r""" \begin{equation*} \py{latex(steps[0],**config_standard)} = \py{latex(steps[1],**config_standard)} \end{equation*} """, globals(), locals()) else: return myst(r""" \py{latex(steps[0],**config_standard)} = \py{latex(steps[1],**config_standard)} """, globals(), locals()) lines = [myst(r""" \py{latex(steps[0],**config_standard)} &= \py{latex(steps[1],**config_standard)} """, globals(), locals())] for s in steps[2:]: lines.append(myst(r"""\\&= \py{latex(s,**config_standard)}""", globals(), locals())) body = myst(r""" \py{" ".join(lines)}""", globals(), locals()) if print_equation: return myst(r""" \begin{equation*} \begin{split} \py{body} \end{split} \end{equation*} """, globals(), locals()) else: return myst(r""" \py{body} """, globals(), locals()) # --- Utilitaires ---
[docs] def to_add_rest(args): return pxs_Add(*args) if args else S.Zero
[docs] def to_mul_rest(args): return pxs_Mul(*args) if args else S.One
[docs] def is_simple_linear(expr, variable): """Détecte une expression du type a*x + b ou a*x avec a,b constants (coeffs numériques).""" poly = expr.as_poly(variable) if poly is None: return False return poly.degree() <= 1 and all(c.is_number for c in poly.all_coeffs())
[docs] def is_polynomial_in(expr, variable): """Retourne True si expr est un polynôme (coeffs numériques) en 'variable'.""" return expr.as_poly(variable) is not None
[docs] def pxs_is_reference_function_diff(expr, variable): """ Retourne True si expr est une fonction de référence (éventuellement multipliée par une constante). Fonctions de référence : constante, x, x^n, 1/x, sqrt(x), e^x, ln(x), sin(x), cos(x), polynômes simples """ # Cas 1 : constante if expr.is_number: return True # Cas 2 : fonction simple if expr == variable: # x return True if isinstance(expr, Pow) and expr.base == variable and expr.exp.is_number: # x^n return True if isinstance(expr, exp) and expr.args[0] == variable: # e^x return True if isinstance(expr, log) and expr.args[0] == variable: # ln(x) return True if isinstance(expr, sin) and expr.args[0] == variable: # sin(x) return True if isinstance(expr, cos) and expr.args[0] == variable: # cos(x) return True # Cas 3 : polynôme simple (comme 2x+1, x-3, etc.) if expr.as_poly(variable) is not None: poly = expr.as_poly(variable) # C'est un polynôme avec coefficients numériques if all(coeff.is_number for coeff in poly.all_coeffs()): return True # Cas 4 : fonction multipliée par une constante (k*f(x)) if isinstance(expr, Mul): args = list(expr.args) # Chercher un facteur constant const_factors = [a for a in args if a.is_number] if const_factors: # Retirer les constantes et vérifier si le reste est une fonction de référence other_factors = [a for a in args if not a.is_number] if len(other_factors) == 1: return pxs_is_reference_function_diff(other_factors[0], variable) return False
[docs] def pxs_is_simple_function_diff(expr, variable): """ Retourne True si expr est une fonction simple : - fonction de référence - k × fonction de référence - somme de fonctions de référence (avec ou sans coefficients) """ # Cas 1 : fonction de référence directe if pxs_is_reference_function_diff(expr, variable): return True # Cas 2 : k × fonction de référence if isinstance(expr, Mul): args = list(expr.args) const_factors = [a for a in args if a.is_number] other_factors = [a for a in args if not a.is_number] if const_factors and len(other_factors) == 1: return pxs_is_reference_function_diff(other_factors[0], variable) # Cas 3 : somme de fonctions de référence (éventuellement avec coefficients) if isinstance(expr, Add): for term in expr.args: # Vérifier si le terme est une fonction de référence if pxs_is_reference_function_diff(term, variable): continue # Vérifier si c'est k × fonction de référence elif isinstance(term, Mul): has_const = any(a.is_number for a in term.args) non_const = [a for a in term.args if not a.is_number] if has_const and len(non_const) >= 1: # Vérifier si le produit des facteurs non constants est une fonction de référence if len(non_const) == 1: if pxs_is_reference_function_diff(non_const[0], variable): continue else: non_const_prod = Mul(*non_const) if pxs_is_reference_function_diff(non_const_prod, variable): continue # Si on arrive ici, le terme n'est pas acceptable return False # Tous les termes sont acceptables return True return False
[docs] def pxs_is_reference_function(expr, variable): """ Retourne True si expr est une fonction de référence (éventuellement multipliée par une constante). """ # Cas 1 : constante if expr.is_number: return True # Cas 2 : fonction simple if expr == variable: # x return True if isinstance(expr, Pow) and expr.base == variable and expr.exp.is_number: # x^n return True if isinstance(expr, exp): # e^x return True if isinstance(expr, log): # ln(x) return True if isinstance(expr, sin): # sin(x) return True if isinstance(expr, cos): # cos(x) return True # Cas 4 : fonction multipliée par une constante (k*f(x)) if isinstance(expr, Mul): args = list(expr.args) # Chercher un facteur constant const_factors = [a for a in args if a.is_number] if const_factors: # Retirer les constantes et vérifier si le reste est une fonction de référence other_factors = [a for a in args if not a.is_number] if len(other_factors) == 1: return pxs_is_reference_function(other_factors[0], variable) return False
[docs] def pxs_is_simple(expr, variable): """Détecte une expression du type a*x ou b avec a,b constants (coeffs numériques).""" poly = expr.as_poly(variable) if poly is None: return False return (poly.degree() <= 1 and all(c.is_number for c in poly.all_coeffs())) or isinstance(expr,exp) or isinstance(expr,log) or isinstance(expr,Pow) or isinstance(expr,sin) or isinstance(expr,cos) or isinstance(expr,tan)
[docs] def pxsl_print(expr, variable = None, detail = None, print_equation = True): """ """ simplified = None if detail is None: steps = [expr] terms = [] if isinstance(expr,Add): for term in expr.args: terms.append(term) simplified = Add(*terms) if set(steps[0].args) == set(simplified.args): simplified = expand(expr) else: simplified = expand(expr) if all(set(step.args) != set(simplified.args) for step in steps): steps.append(simplified) return _pxsl_print_latex(steps, print_equation) elif detail == "high": steps = dispatch(expr, variable, steps=[expr]) simplified = expand(expr) if all(set(step.args) != set(simplified.args) for step in steps): steps.append(simplified) return _pxsl_print_latex(steps, print_equation)
# Fonction utilitaire pour préserver l'ordre des termes
[docs] def preserve_order_add(*terms): """Crée une addition en préservant l'ordre des termes.""" if len(terms) == 0: return S.Zero elif len(terms) == 1: return terms[0] else: return SymPyAdd(*terms, evaluate=False)
# --- Découpe "logique" pour Add : avec les constantes à la fin---
[docs] def split_add_terms(expr): args = list(expr.args) if args and args[0].is_number and len(args) > 1: return args[1], to_add_rest([args[0]] + args[2:]) return args[0], to_add_rest(args[1:])
[docs] def split_mul_terms(expr): """ Découpe intelligemment un produit en deux facteurs. Si on a -x*f(x), on veut (-x, f(x)) et non (-1, x*f(x)) """ args = list(expr.args) # Cas spécial : si le premier argument est -1 et qu'il y a au moins 3 arguments # (par exemple -1 * x * ln(x)), on regroupe -1 avec le suivant if len(args) >= 3 and args[0] == S.NegativeOne: # Regrouper -1 avec le deuxième terme first_term = -args[1] # -1 * args[1] = -args[1] remaining_terms = args[2:] return first_term, to_mul_rest(remaining_terms) # Cas spécial : si on a k*x*f(x) où k est un nombre autre que -1 # on veut (k*x, f(x)) si x est la variable elif len(args) >= 3 and args[0].is_number and not args[0] == S.NegativeOne: # Si le deuxième terme est simple (comme x), on le groupe avec k first_term = args[0] * args[1] remaining_terms = args[2:] return first_term, to_mul_rest(remaining_terms) # Cas général : découper après le premier terme elif len(args) >= 2: return args[0], to_mul_rest(args[1:]) else: return expr, S.One
# --- Helpers pour l'étape "quotient" --- def _decompose_v_bases_exps(v): """Renvoie [(base, exp)] pour v (qui est un produit de puissances positives).""" if isinstance(v, Pow): return [(v.base, v.exp)] elif isinstance(v, Mul): out = [] for a in v.args: if isinstance(a, Pow): out.append((a.base, a.exp)) else: out.append((a, S.One)) return out else: return [(v, S.One)] def _squarefree_common_factor(v): """ Pour v = ∏ w_i^{n_i} avec n_i ∈ ℕ, renvoie g = ∏ w_i^{max(n_i-1,0)}. C'est le plus grand facteur commun de v et v' (utile pour "factoriser puis simplifier"). Si v contient des exposants non entiers (ex: 1/2), on ne prend rien pour ces facteurs. """ g = S.One for base, exp in _decompose_v_bases_exps(v): if getattr(exp, "is_integer", False) and exp.is_Integer and exp > 1: g *= base**(exp - 1) return g def _fmt_paren(expr): """Latex avec parenthèses si somme.""" config_standard = pxs_config() return f"\\left({latex(expr,**config_standard)}\\right)" if isinstance(expr, Add) else latex(expr,**config_standard) def _expanded_minus_str(term1, term2): """ Construit 'latex(expand_mul(term1),**config_standard) - latex(expand_mul(term2),**config_standard)' en distribuant le '-' terme à terme si term2 est une somme, pour obtenir p.ex. '2x+6-4x-6'. """ config_standard = pxs_config() t1 = expand_mul(term1) t2 = expand_mul(term2) s1 = latex(t1,**config_standard) if isinstance(t2, Add): parts = list(t2.as_ordered_terms()) s2 = " - " + " - ".join(latex(p,**config_standard) for p in parts) else: s2 = " - " + latex(t2,**config_standard) return s1 + s2 # --- Narration utilitaires (même rendu final) ---
[docs] def narr_header(name, variable): config_standard = pxs_config() return myst(r"""Calculons $\py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)})$. """, globals(), locals())
[docs] def narr_conclude(name, variable, expr): config_standard = pxs_config() derivative = diff(expr, variable) return myst(r""" Donc \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(derivative,**config_standard)} \end{equation*} """, globals(), locals())
[docs] def should_remove_times(expr): """ Détermine si on doit retirer le signe × dans l'expression finale. On retire × pour les cas finaux comme exp, sin, cos avec coefficients. """ # Si c'est une multiplication if isinstance(expr, Mul): args = list(expr.args) # Chercher si on a un coefficient numérique avec une fonction transcendante has_number = any(arg.is_number for arg in args) has_exp = any(isinstance(arg, exp) for arg in args) has_trig = any(isinstance(arg, (sin, cos)) for arg in args) # Pour exp, sin, cos on retire toujours le × avec un coefficient if has_number and (has_exp or has_trig): return True # Aussi pour les produits contenant ces fonctions for arg in args: if isinstance(arg, Mul) and any(isinstance(subarg, (exp, sin, cos)) for subarg in arg.args): return True return False
[docs] def narr_conclude_with_details(name, variable, formula_latex, simplified=None, detail = None): """ Affiche la conclusion avec la formule détaillée puis la simplification. formula_latex : la formule avec toutes les substitutions simplified : la forme simplifiée (optionnel) - peut être une chaîne LaTeX ou une expression SymPy """ config_standard = pxs_config() # Fonction pour nettoyer le × dans les cas appropriés def clean_times_if_needed(latex_str, expr=None): if expr and should_remove_times(expr): return latex_str.replace(" \\times ", " ").replace(" \\cdot ", " ") return latex_str if detail is None: if simplified is None: return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex.replace(" \\times ", " ").replace(" \\cdot ", " ")} \end{equation*} """, globals(), locals()) else: return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(simplified,**config_standard)} \end{equation*} """, globals(), locals()) else: # Si simplified est une chaîne (notre formule LaTeX construite manuellement) if simplified is not None and isinstance(simplified, str): # Comparer les chaînes après nettoyage formula_clean = formula_latex.replace(" ", "").replace("\\times", "").replace("\\cdot", "") simplified_clean = simplified.replace(" ", "").replace("\\times", "").replace("\\cdot", "") if formula_clean != simplified_clean: return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex} = \py{simplified} \end{equation*} """, globals(), locals()) else: return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex} \end{equation*} """, globals(), locals()) # Si simplified est une expression SymPy elif simplified is not None: # Obtenir la forme simplifiée finale final_simplified = simplified.simplify() final_latex = latex(final_simplified,**config_standard) # CORRECTION: Si on peut retirer le × directement, on le fait sur formula_latex if should_remove_times(final_simplified): # Retirer le × directement dans la formule détaillée formula_cleaned = formula_latex.replace(" \\times ", " ") return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_cleaned} \end{equation*} """, globals(), locals()) # Sinon, on continue avec la logique normale # Comparer les représentations LaTeX # Nettoyer les espaces pour une comparaison plus robuste formula_clean = formula_latex.replace(" ", "") final_clean = final_latex.replace(" ", "") if formula_clean != final_clean: # Les expressions sont différentes, on affiche les deux return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex} = \py{final_latex} \end{equation*} """, globals(), locals()) # Les expressions sont identiques ou simplified est None, on affiche seulement la formule return myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex} \end{equation*} """, globals(), locals())
[docs] def narr_deriv_or_recurse(expr, nm, variable, niveau, lhs=None, func_name=None, detail = None): """ Si expr est atomique ou linéaire simple: écrit une équation avec le lhs fourni, sinon descend récursivement via _dispatch_diff. func_name : nom de la fonction à utiliser (si None, on utilise nm.current()) """ config_standard = pxs_config() if expr.is_Atom or is_simple_linear(expr, variable): if lhs is None: lhs = "u'(x)" return myst(r""" \begin{equation*} \py{lhs} = \py{latex(diff(expr, variable),**config_standard)} \end{equation*} """, globals(), locals()) else: return _dispatch_diff(expr, nm, variable, niveau, override_name=func_name, detail= detail)
# --- Dispatcher ---
[docs] def expliquer_derivation(expr, variable=Symbol('x'), start_name='f', niveau='terminale', detail = None): """ niveau: 'premiere' | 'terminale' - 'premiere' : on détaille la somme terme à terme, et on explicite k*u - 'terminale' : polynômes en bloc, pas de détail pour k*u """ nm = NameManager(start=start_name) return _dispatch_diff(expr, nm, variable, niveau, detail = detail)
def _dispatch_diff(expr, nm, variable, niveau, override_name=None, detail = None): """ override_name : si fourni, utilise ce nom au lieu de nm.current() """ config_standard = pxs_config() if isinstance(expr, Add): return pxs_Add(*expr.args).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, Mul): return pxs_Mul(*expr.args).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, Pow): return pxs_Pow(expr.base, expr.exp).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, exp): return pxs_exp(expr.args[0]).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, log): return pxs_log(expr.args[0]).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, sin): return pxs_sin(expr.args[0]).pxsl_diff(nm, variable, niveau, override_name, detail) elif isinstance(expr, cos): return pxs_cos(expr.args[0]).pxsl_diff(nm, variable, niveau, override_name, detail) else: name = override_name if override_name else nm.current() return myst(r""" \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(diff(expr, variable),**config_standard)} """, globals(), locals())
[docs] def dispatch(expr, variable = None, steps=[]): config_standard = pxs_config() if isinstance(expr, Mul): steps = pxs_Mul(*expr.args).pxsl_print(variable, steps) elif isinstance(expr, Add): steps = pxs_Add(*expr.args).pxsl_print(variable, steps) else: steps.append(expr) return steps
[docs] class pxs_Add(Add):
[docs] def pxsl_print(self, variable=None, steps=[]): config_standard = pxs_config() simple = [] complex_terms = [] # Séparation des termes for term in self.args: if pxs_is_reference_function(term, variable): simple.append(term) else: complex_terms.append(term) # Étape 1 : Regroupement initial if simple: simple_sum = Add(*simple) if len(simple) > 1 else simple[0] steps.append(Add(simple_sum, *complex_terms)) # Étape 2 : Développement des termes complexes current_simple = Add(*simple) if simple else 0 for term in complex_terms: developed_steps = dispatch(term, variable, steps=[]) # Ajouter chaque étape de développement avec les termes simples for step in developed_steps[1:]: # Ignorer le terme original if current_simple != 0: steps.append(Add(current_simple, step)) else: steps.append(step) return steps
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() args = list(self.args) # --- traiter bloc polynômial même s'il est partiel --- # Cas 100% polynôme if self.as_poly(variable) is not None: phrase = narr_header(name, variable) phrase += myst(r""" On reconnaît un polynôme en $\py{latex(variable,**config_standard)}$. On applique la règle de dérivation pour les polynômes : \begin{equation*} (a_n x^n + \dots + a_1 x + a_0)' = n a_n x^{n-1} + \dots + a_1 \end{equation*} Ainsi : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(diff(self, variable))} \end{equation*} """, globals(), locals()) return phrase # Cas mixte : partition en trois catégories poly_terms, ref_terms, other_terms = [], [], [] for t in args: if is_polynomial_in(t, variable): poly_terms.append(t) elif pxs_is_reference_function_diff(t, variable): ref_terms.append(t) else: other_terms.append(t) # Si on a des termes polynomiaux et/ou de référence, on les groupe if poly_terms or ref_terms: # Combiner les termes "simples" (polynômes + fonctions de référence) simple_terms = poly_terms + ref_terms P = Add(*simple_terms) dP = diff(P, variable) name_P = nm.next() if other_terms: # Il y a aussi des termes complexes name_h = nm.next() H = Add(*other_terms) dH = diff(H, variable) phrase = narr_header(name, variable) # Description adaptée selon ce qu'on a trouvé if poly_terms and ref_terms: desc = "un polynôme et des fonctions usuelles" elif poly_terms: desc = "un polynôme" else: desc = "des fonctions usuelles" phrase += myst(r""" On décompose la somme et on repère """ + desc + r""" : $\py{latex(Symbol(name),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(Symbol(name_P),**config_standard)}(\py{latex(variable,**config_standard)}) + \py{latex(Symbol(name_h),**config_standard)}(\py{latex(variable,**config_standard)})$ avec \begin{equation*} \py{latex(Symbol(name_P),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(P,**config_standard)} \end{equation*} et \begin{equation*} \py{latex(Symbol(name_h),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(H,**config_standard)} \end{equation*} Par les règles de dérivation usuelles, on obtient directement : \begin{equation*} \py{latex(Symbol(name_P),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dP,**config_standard)} \end{equation*} """, globals(), locals()) phrase += _dispatch_diff(H, nm, variable, niveau, override_name=name_h, detail = detail) # Correction pour le problème du +-ln(x) # On vérifie si dH est négatif pour l'afficher correctement if detail is None: phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dP + dH,**config_standard)} \end{equation*} """, globals(), locals()) elif dH.could_extract_minus_sign(): phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dP,**config_standard)} - \py{latex(-dH,**config_standard)} = \py{latex(dP + dH,**config_standard)} \end{equation*} """, globals(), locals()) else: phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dP,**config_standard)} + \py{latex(dH,**config_standard)} = \py{latex(dP + dH,**config_standard)} \end{equation*} """, globals(), locals()) return phrase else: # Seulement des termes simples, pas besoin de décomposer phrase = narr_header(name, variable) if poly_terms and ref_terms: desc = "une somme de fonctions usuelles" elif poly_terms: desc = "un polynôme" else: desc = "une somme de fonctions usuelles" phrase += myst(r""" On reconnaît """ + desc + r""" en $\py{latex(variable,**config_standard)}$. En appliquant les règles de dérivation usuelles, on obtient directement : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(diff(self, variable),**config_standard)} \end{equation*} """, globals(), locals()) return phrase # --- pas de bloc poly détectable : somme g+h comme avant --- g, h = split_add_terms(self) name_g = nm.next() name_h = nm.next() phrase = narr_header(name, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\py{latex(Symbol(name_g),**config_standard)} + \py{latex(Symbol(name_h),**config_standard)}$ avec $\py{latex(Symbol(name_g),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(g,**config_standard)}$ et $\py{latex(Symbol(name_h),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(h,**config_standard)}$. On sait que $\big(\py{latex(Symbol(name_g),**config_standard)} + \py{latex(Symbol(name_h),**config_standard)}\big)'(\py{latex(variable,**config_standard)}) = \py{latex(Symbol(name_g),**config_standard)}'(\py{latex(variable,**config_standard)}) + \py{latex(Symbol(name_h),**config_standard)}'(\py{latex(variable,**config_standard)})$. """, globals(), locals()) phrase += narr_deriv_or_recurse( g, nm, variable, niveau, lhs=f"{latex(Symbol(name_g),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_g, detail = detail ) phrase += narr_deriv_or_recurse( h, nm, variable, niveau, lhs=f"{latex(Symbol(name_h),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_h, detail = detail ) dg, dh = diff(g, variable), diff(h, variable) # Construire la formule détaillée detailed_formula = f"{latex(dg,**config_standard)} + {latex(dh,**config_standard)}" # Construire la forme avec l'ordre préservé if dh.could_extract_minus_sign(): preserved_formula = f"{latex(dg,**config_standard)} - {latex(-dh,**config_standard)}" else: preserved_formula = f"{latex(dg,**config_standard)} + {latex(dh,**config_standard)}" # Calculer la vraie simplification final_simplified = dg + dh final_simplified_str = latex(final_simplified,**config_standard) # Vérifier si la simplification finale est différente if preserved_formula.replace(" ", "") != final_simplified_str.replace(" ", ""): # Il y a une simplification supplémentaire if detail is None: phrase += narr_conclude_with_details(name, variable, latex(diff(self,variable),**config_standard), simplified=None, detail = None) else: phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{detailed_formula} = \py{final_simplified_str} \end{equation*} """, globals(), locals()) else: phrase += narr_conclude_with_details(name, variable, detailed_formula, preserved_formula, detail) return phrase
[docs] class pxs_Mul(Mul):
[docs] def pxsl_print(self, variable = None, steps=[]): if all(isinstance(a, Add) for a in self.args): A_terms = Add.make_args(self.args[0]) B_terms = Add.make_args(self.args[1]) rest = self.args[2:] products = [Mul(a, b, evaluate=True) for a in A_terms for b in B_terms] steps.append(Mul(Add(*products, evaluate=False),*rest)) if set(steps[-1].args) != set(Mul(Add(*products),*rest).args): steps.append(Mul(Add(*products),*rest)) if rest != (): steps = pxs_Mul(Add(*products),*rest).pxsl_print(variable, steps) return steps
def _detect_quotient(self): """ Détecte des facteurs à puissance négative (numérique). u = produit des autres facteurs v = produit des base**(-exp) pour chaque exp < 0 (exposants rendus positifs). """ config_standard = pxs_config() args = list(self.args) neg_pow_factors, others = [], [] for f in args: if isinstance(f, Pow) and getattr(f.exp, "is_number", False) and f.exp.is_negative: neg_pow_factors.append(f) else: others.append(f) if neg_pow_factors: denom_parts = [p.base**(-p.exp) for p in neg_pow_factors] v = to_mul_rest(denom_parts) u = to_mul_rest(others) if others else S.One return True, u, v return False, None, None
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() # 1) Quotient direct si puissances négatives détectées is_q, u_q, v_q = self._detect_quotient() if is_q: u, v = u_q, v_q name_u = nm.next() name_v = nm.next() phrase = narr_header(name, variable) phrase += myst(r""" On reconnaît un quotient : $\py{latex(Symbol(name),**config_standard)} = \dfrac{\py{latex(Symbol(name_u),**config_standard)}}{\py{latex(Symbol(name_v),**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$ et $\py{latex(Symbol(name_v),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(v,**config_standard)}$. On sait que $\displaystyle{ \left(\frac{\py{latex(Symbol(name_u),**config_standard)}}{\py{latex(Symbol(name_v),**config_standard)}}\right)'(\py{latex(variable,**config_standard)}) = \frac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \py{latex(Symbol(name_v),**config_standard)}(\py{latex(variable,**config_standard)}) - \py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) \py{latex(Symbol(name_v),**config_standard)}'(\py{latex(variable,**config_standard)})}{\py{latex(Symbol(name_v),**config_standard)}^2(\py{latex(variable,**config_standard)})}} $""", globals(), locals()) # Calcul des dérivées du = diff(u, variable) dv = diff(v, variable) # Étape 1 : formule brute u'v - uv' / v² du_str = _fmt_paren(du) dv_str = _fmt_paren(dv) u_str = _fmt_paren(u) v_str = _fmt_paren(v) v2_str = _fmt_paren(v**2) step1_formula = ( rf"\frac{{{du_str} \times {v_str} - {u_str} \times {dv_str}}}{{{v2_str}}}" ) steps = [step1_formula] # Étape 2-3 : factoriser par un facteur commun g de v et v' g = _squarefree_common_factor(v) if g != S.One: v_over_g = simplify(v / g) dv_over_g = simplify(dv / g) g_str = _fmt_paren(g) step2_num = ( rf"{g_str} \times \left({du_str} \times {_fmt_paren(v_over_g)}" rf" - {u_str} \times {_fmt_paren(dv_over_g)}\right)" ) step2_formula = rf"\frac{{{step2_num}}}{{{v2_str}}}" steps.append(step2_formula) # Simplification par g den_after = simplify(v**2 / g) den_after_str = _fmt_paren(den_after) step3_num = ( rf"{du_str} \times {_fmt_paren(v_over_g)} - " rf"{u_str} \times {_fmt_paren(dv_over_g)}" ) step3_formula = rf"\frac{{{step3_num}}}{{{den_after_str}}}" steps.append(step3_formula) # Étape 4 : développement du numérateur uniquement step4_num = _expanded_minus_str(du * v_over_g, u * dv_over_g) step4_formula = rf"\frac{{{step4_num}}}{{{den_after_str}}}" steps.append(step4_formula) # Étape 5 : écriture finale simplifiée final_expr = simplify((du * v_over_g - u * dv_over_g) / den_after) step5_formula = latex(final_expr,**config_standard) steps.append(step5_formula) else: # Pas de facteur commun : on développe puis on simplifie den_after = v**2 den_after_str = _fmt_paren(den_after) raw_num = du * v - u * dv dev_num = expand_mul(raw_num) step2_formula = rf"\frac{{{latex(dev_num,**config_standard)}}}{{{den_after_str}}}" steps.append(step2_formula) final_expr = simplify(raw_num / den_after) step3_formula = latex(final_expr,**config_standard) steps.append(step3_formula) # Affichage final (on concatène toutes les étapes) chain = " = ".join(steps) if detail is None: phrase += narr_conclude_with_details(name, variable, latex(diff(self,variable),**config_standard), simplified=None, detail = None) else: phrase += myst(r""" Donc : \begin{equation*} \py{chain} \end{equation*} """, globals(), locals()) return phrase # 2) Cas particulier k*u (k numérique, u fonction de x) # On vérifie que c'est vraiment un coefficient numérique en facteur args = list(self.args) # Pour être un cas k*u, il faut : # - Exactement 2 arguments # - Le premier est un nombre # - Le second n'est pas un produit (sinon on a k*u*v qu'on veut traiter comme produit classique) if len(args) == 2 and args[0].is_number: k = args[0] core = args[1] if niveau == 'premiere': name_u = nm.next() phrase = narr_header(name, variable) # Vérifier si core est une fonction de référence if pxs_is_reference_function_diff(core, variable): # Fonction de référence, on fait court dcore = diff(core, variable) phrase += myst(r""" On reconnaît un produit $k \times u$ avec $k=\py{latex(k,**config_standard)}$ et $u(\py{latex(variable,**config_standard)})=\py{latex(core,**config_standard)}$. On sait que $(k \times u)' = k \times u'$ avec $u'(\py{latex(variable,**config_standard)}) = \py{latex(dcore,**config_standard)}$. """, globals(), locals()) else: # Pas une fonction de référence, on détaille phrase += myst(r""" On reconnaît un produit $k \times u$ avec $k=\py{latex(k,**config_standard)}$ et $u(\py{latex(variable,**config_standard)})=\py{latex(core,**config_standard)}$. On sait que : \begin{equation*} (k \times u)' = k \times u' \end{equation*} """, globals(), locals()) phrase += narr_deriv_or_recurse( core, nm, variable, niveau, lhs="u'(x)", func_name=name_u, detail = detail ) dcore = diff(core, variable) # Gérer les parenthèses pour dcore si c'est une somme dcore_str = f"\\left({latex(dcore,**config_standard)}\\right)" if isinstance(dcore, Add) else latex(dcore,**config_standard) # Formule détaillée : k × u' detailed_formula = f"{latex(k,**config_standard)} \\times {dcore_str}" simplified = k * dcore phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified, detail) return phrase else: # En terminale, on donne quand même une explication minimale phrase = narr_header(name, variable) phrase += myst(r""" La fonction est de la forme $k \times u$ avec $k = \py{latex(k,**config_standard)}$ et $u(\py{latex(variable,**config_standard)}) = \py{latex(core,**config_standard)}$. Par la règle $(k \times u)' = k \times u'$, on obtient directement : """, globals(), locals()) phrase += narr_conclude(name, variable, self) return phrase # 3) Produit « classique » (pas de quotient, pas de k*u avec k numérique en premier) u, v = split_mul_terms(self) name_u = nm.next() name_v = nm.next() phrase = narr_header(name, variable) # Vérifier si u et v sont des fonctions simples u_is_simple = pxs_is_simple_function_diff(u, variable) v_is_simple = pxs_is_simple_function_diff(v, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\py{latex(Symbol(name_u),**config_standard)} \times \py{latex(Symbol(name_v),**config_standard)}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$ et $\py{latex(Symbol(name_v),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(v,**config_standard)}$. On sait que $\big(\py{latex(Symbol(name_u),**config_standard)} \times \py{latex(Symbol(name_v),**config_standard)}\big)'(\py{latex(variable,**config_standard)}) = \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times \py{latex(Symbol(name_v),**config_standard)}(\py{latex(variable,**config_standard)}) + \py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) \times \py{latex(Symbol(name_v),**config_standard)}'(\py{latex(variable,**config_standard)})$""", globals(), locals()) if u_is_simple and v_is_simple: # Les deux sont simples, on donne directement les dérivées du = diff(u, variable) dv = diff(v, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$ et $\py{latex(Symbol(name_v),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dv,**config_standard)}$. """, globals(), locals()) elif u_is_simple and not v_is_simple: # u est simple, v ne l'est pas du = diff(u, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) phrase += _dispatch_diff(v, nm, variable, niveau, override_name=name_v, detail = detail) elif not u_is_simple and v_is_simple: # v est simple, u ne l'est pas dv = diff(v, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_v),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(dv,**config_standard)}$. On calcule $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})$ : """, globals(), locals()) phrase += _dispatch_diff(u, nm, variable, niveau, override_name=name_u, detail = detail) else: # Aucune n'est simple, on calcule les deux phrase += myst(r""". """, globals(), locals()) phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) phrase += narr_deriv_or_recurse( v, nm, variable, niveau, lhs=f"{latex(Symbol(name_v),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_v, detail = detail ) # Ajouter la conclusion détaillée du = diff(u, variable) dv = diff(v, variable) # Gérer les parenthèses pour toutes les expressions si nécessaire du_str = f"\\left({latex(du,**config_standard)}\\right)" if isinstance(du, Add) else latex(du,**config_standard) dv_str = f"\\left({latex(dv,**config_standard)}\\right)" if isinstance(dv, Add) else latex(dv,**config_standard) u_str = f"\\left({latex(u,**config_standard)}\\right)" if isinstance(u, Add) else latex(u,**config_standard) v_str = f"\\left({latex(v,**config_standard)}\\right)" if isinstance(v, Add) else latex(v,**config_standard) # Construire la formule détaillée : u'×v + u×v' detailed_formula = f"{du_str} \\times {v_str} + {u_str} \\times {dv_str}" # Calculer séparément chaque terme pour préserver l'ordre term1 = du * v term2 = u * dv # Développer les termes pour la forme intermédiaire term1_expanded = term1.expand() term2_expanded = term2.expand() # Construire la forme intermédiaire développée term1_str = latex(term1_expanded,**config_standard) term2_str = latex(term2_expanded,**config_standard) # CORRECTION pour expr15: Préserver l'ordre sin(x) + x*cos(x) # Si on a 1*sin(x), on veut juste sin(x) if term1_expanded == sin(variable) and u == S.One: term1_str = latex(sin(variable),**config_standard) # Si term2 est négatif, on l'affiche correctement if term2_expanded.could_extract_minus_sign(): simplified_formula = f"{term1_str} - {latex(-term2_expanded,**config_standard)}" else: simplified_formula = f"{term1_str} + {term2_str}" # Calculer la vraie simplification finale final_simplified = (term1_expanded + term2_expanded).simplify() # Vérifier si c'est une vraie simplification # On compare les chaînes LaTeX pour voir si elles sont différentes final_simplified_str = latex(final_simplified,**config_standard) # Nettoyer pour la comparaison simplified_clean = simplified_formula.replace(" ", "").replace("\\left(", "").replace("\\right)", "") final_clean = final_simplified_str.replace(" ", "").replace("\\left(", "").replace("\\right)", "") # Si on a "1 \times sin(x) + x \times cos(x)" et que la simplification donne "sin(x) + x cos(x)" # C'est une vraie simplification à montrer if "1 \\times" in detailed_formula and "1 \\times" not in simplified_formula: # On montre la simplification if detail is None: phrase += narr_conclude_with_details(name, variable, latex(diff(self,variable),**config_standard), simplified=None, detail = None) else: phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{detailed_formula} = \py{simplified_formula} \end{equation*} """, globals(), locals()) elif simplified_clean != final_clean: # Il y a une autre vraie simplification if detail is None: phrase += narr_conclude_with_details(name, variable, latex(diff(self,variable),**config_standard), simplified=None, detail = None) else: phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{detailed_formula} = \py{simplified_formula} = \py{final_simplified_str} \end{equation*} """, globals(), locals()) else: # Pas de simplification supplémentaire phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified_formula, detail) return phrase
# --- pxs_Pow ---
[docs] class pxs_Pow(Pow):
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() u = self.base n = self.exp name_u = nm.next() # Préfixe phrase = narr_header(name, variable) # Vérifier si u est une fonction de référence u_is_ref = pxs_is_reference_function_diff(u, variable) if n.is_integer and n > 0: # Calculer la chaîne pour u^(n-1) if n-1 == 1: # Si n-1 = 1, on n'affiche pas l'exposant power_formula = r"\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})" else: power_formula = r"\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})^{\py{latex(n-1,**config_standard)}}" if u_is_ref: du = diff(u, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(n,**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(n,**config_standard)}}\big)'(\py{latex(variable,**config_standard)}) = \py{latex(n,**config_standard)} \times \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times """ + power_formula + r"""$ avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(n,**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(n,**config_standard)}}\big)'(\py{latex(variable,**config_standard)}) = \py{latex(n,**config_standard)} \times \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times """ + power_formula + r"""$. """, globals(), locals()) elif n.is_Rational and n.q == 2: if u_is_ref: du = diff(u, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\sqrt{\py{latex(Symbol(name_u),**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\sqrt{\py{latex(Symbol(name_u),**config_standard)}}\big)' = \dfrac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})}{2\sqrt{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})}}$ avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\sqrt{\py{latex(Symbol(name_u),**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u)}$. On sait que \begin{equation*} \big(\sqrt{\py{latex(Symbol(name_u),**config_standard)}}\big)' = \frac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})}{2\sqrt{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})}} \end{equation*} """, globals(), locals()) elif n.is_number and n < 0: # Gérer spécialement le cas n = -1 if n == -1: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\dfrac{1}{\py{latex(Symbol(name_u),**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que \begin{equation*} \left(\frac{1}{\py{latex(Symbol(name_u),**config_standard)}}\right)' = -\frac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})}{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})^{2}} \end{equation*}""", globals(), locals()) else: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\dfrac{1}{\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(-n,**config_standard)}}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que \begin{equation*} \left(\frac{1}{\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(-n,**config_standard)}}}\right)' = \py{latex(n,**config_standard)} \times \frac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})}{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})^{\py{latex(-n+1,**config_standard)}}} \end{equation*}""", globals(), locals()) if u_is_ref: du = diff(u, variable) phrase += myst(r"""avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: phrase += myst(r""" """, globals(), locals()) else: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\py{latex(Symbol(name_u),**config_standard)}^{\py{latex(n,**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que \begin{equation*} \big(\py{latex(Symbol(name_u),**config_standard)}^{\alpha}\big)' = \alpha \times \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times \py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})^{\alpha-1} \end{equation*}""", globals(), locals()) if u_is_ref: du = diff(u, variable) phrase += myst(r"""avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: phrase += myst(r""" """, globals(), locals()) # Si u n'est pas une fonction de référence, on détaille son calcul if not u_is_ref: phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) # Conclusion détaillée selon le type de puissance du = diff(u, variable) # Gérer les parenthèses pour du si c'est une somme du_str = f"\\left({latex(du,**config_standard)}\\right)" if isinstance(du, Add) else latex(du,**config_standard) # Pour u^n, on met des parenthèses si u est une Add ET qu'on écrit u^n explicitement def format_power(base, exp): if exp == 1: # Si l'exposant est 1, on n'affiche pas l'exposant if isinstance(base, Add): return f"\\left({latex(base,**config_standard)}\\right)" else: return latex(base,**config_standard) elif isinstance(base, Add): return f"\\left({latex(base,**config_standard)}\\right)^{{{latex(exp,**config_standard)}}}" else: return latex(base**exp,**config_standard) if n.is_integer and n > 0: # n × u' × u^(n-1) detailed_formula = f"{latex(n,**config_standard)} \\times {du_str} \\times {format_power(u, n-1)}" elif n.is_Rational and n.q == 2: # u' / (2√u) - pas de parenthèses supplémentaires dans la racine detailed_formula = f"\\frac{{{du_str}}}{{2\\sqrt{{{latex(u,**config_standard)}}}}}" elif n.is_number and n < 0: # n × u' / u^(-n+1) if -n+1 == 1: # L'exposant vaut 1, on n'affiche pas l'exposant detailed_formula = f"{latex(n,**config_standard)} \\times \\frac{{{du_str}}}{{{latex(u,**config_standard)}}}" else: detailed_formula = f"{latex(n,**config_standard)} \\times \\frac{{{du_str}}}{{{format_power(u, -n+1)}}}" else: # α × u' × u^(α-1) detailed_formula = f"{latex(n,**config_standard)} \\times {du_str} \\times {format_power(u, n-1)}" simplified = diff(self, variable) phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified, detail) return phrase
# --- pxs_exp --- # --- pxs_exp (remplacement complet) ---
[docs] class pxs_exp(exp):
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() u = self.args[0] # exp a toujours un seul argument phrase = narr_header(name, variable) # Cas simple : e^x if u == variable: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est l'exponentielle de base $e$. On sait que la dérivée de $e^{\py{latex(variable,**config_standard)}}$ est $e^{\py{latex(variable,**config_standard)}}$. Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = e^{\py{latex(variable,**config_standard)}} \end{equation*} """, globals(), locals()) return phrase # Cas composé : e^{u(x)} name_u = nm.next() # On annonce la règle — sans jamais réécrire l'intérieur de l'exponentielle phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $e^{\py{latex(Symbol(name_u),**config_standard)}}$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(e^{\py{latex(Symbol(name_u),**config_standard)}}\big)'(\py{latex(variable,**config_standard)}) = \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})\,e^{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})}$. """, globals(), locals()) # Si u n'est pas "simple", on détaille son calcul (mais on ne touche pas à e^{u}) u_is_polynomial = is_polynomial_in(u, variable) if not (u_is_polynomial and niveau == 'terminale'): phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) # Conclusion : format strict (u') e^{u} SANS × et SANS manipulation de u du = diff(u, variable) # Parenthèses autour de u' si c'est une somme du_latex = f"\\left({latex(du,**config_standard)}\\right)" if isinstance(du, Add) else latex(du,**config_standard) exp_latex = f"e^{{{latex(u,**config_standard)}}}" # on n'altère jamais l'intérieur # On imprime UNE SEULE forme finale, sans chaîne d'égalités formula_latex = f"{du_latex}{' '}{exp_latex}" phrase += myst(r""" Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{formula_latex} \end{equation*} """, globals(), locals()) return phrase
# --- pxs_log ---
[docs] class pxs_log(log):
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() u = self.args[0] # log a toujours un seul argument phrase = narr_header(name, variable) # Cas simple : ln(x) if u == variable: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est le logarithme népérien. On sait que la dérivée de $\ln(\py{latex(variable,**config_standard)})$ est $\dfrac{1}{\py{latex(variable,**config_standard)}}$. Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \frac{1}{\py{latex(variable,**config_standard)}} \end{equation*} """, globals(), locals()) return phrase # Cas composé : ln(u(x)) name_u = nm.next() # Vérifier si u est un polynôme u_is_polynomial = is_polynomial_in(u, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\ln(\py{latex(Symbol(name_u),**config_standard)})$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\ln(\py{latex(Symbol(name_u),**config_standard)})\big)'(\py{latex(variable,**config_standard)}) = \dfrac{\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)})}{\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)})}$""", globals(), locals()) if u_is_polynomial and niveau == 'terminale': # En terminale, si u est un polynôme, on donne directement sa dérivée du = diff(u, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: # Sinon on détaille le calcul phrase += myst(r""". """, globals(), locals()) phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) # Conclusion avec gestion des parenthèses pour le numérateur du = diff(u, variable) # Si du est une somme (Add), on ajoute des parenthèses au numérateur if isinstance(du, Add): du_latex = f"\\left({latex(du,**config_standard)}\\right)" else: du_latex = latex(du,**config_standard) # Pour le dénominateur, pas de parenthèses supplémentaires u_latex = latex(u,**config_standard) # Formule détaillée : u'/u detailed_formula = f"\\frac{{{du_latex}}}{{{u_latex}}}" # Forme simplifiée simplified = diff(self, variable) phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified, detail) return phrase
# --- pxs_sin ---
[docs] class pxs_sin(sin):
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() u = self.args[0] # sin a toujours un seul argument phrase = narr_header(name, variable) # Cas simple : sin(x) if u == variable: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est la fonction sinus. On sait que la dérivée de $\sin(\py{latex(variable,**config_standard)})$ est $\cos(\py{latex(variable,**config_standard)})$. Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = \cos(\py{latex(variable,**config_standard)}) \end{equation*} """, globals(), locals()) return phrase # Cas composé : sin(u(x)) name_u = nm.next() # Vérifier si u est un polynôme u_is_polynomial = is_polynomial_in(u, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\sin(\py{latex(Symbol(name_u),**config_standard)})$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\sin(\py{latex(Symbol(name_u),**config_standard)})\big)'(\py{latex(variable,**config_standard)}) = \py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times \cos(\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}))$""", globals(), locals()) if u_is_polynomial and niveau == 'terminale': # En terminale, si u est un polynôme, on donne directement sa dérivée du = diff(u, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: # Sinon on détaille le calcul phrase += myst(r""". """, globals(), locals()) phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) # Conclusion avec gestion des parenthèses du = diff(u, variable) # Si du est une somme (Add), on ajoute des parenthèses if isinstance(du, Add): du_latex = f"\\left({latex(du,**config_standard)}\\right)" else: du_latex = latex(du,**config_standard) # Si u est une somme (Add), on ajoute des parenthèses dans cos(u) if isinstance(u, Add): cos_latex = f"\\cos\\left({latex(u,**config_standard)}\\right)" else: cos_latex = f"\\cos\\left({latex(u,**config_standard)}\\right)" # Formule détaillée : u' × cos(u) detailed_formula = f"{du_latex} \\times {cos_latex}" # Forme simplifiée simplified = diff(self, variable) phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified, detail) return phrase
# --- pxs_cos ---
[docs] class pxs_cos(cos):
[docs] def pxsl_diff(self, nm, variable, niveau, override_name=None, detail = None): config_standard = pxs_config() name = override_name if override_name else nm.current() u = self.args[0] # cos a toujours un seul argument phrase = narr_header(name, variable) # Cas simple : cos(x) if u == variable: phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est la fonction cosinus. On sait que la dérivée de $\cos(\py{latex(variable,**config_standard)})$ est $-\sin(\py{latex(variable,**config_standard)})$. Donc : \begin{equation*} \py{latex(Symbol(name),**config_standard)}'(\py{latex(variable,**config_standard)}) = -\sin(\py{latex(variable,**config_standard)}) \end{equation*} """, globals(), locals()) return phrase # Cas composé : cos(u(x)) name_u = nm.next() # Vérifier si u est un polynôme u_is_polynomial = is_polynomial_in(u, variable) phrase += myst(r""" La fonction $\py{latex(Symbol(name),**config_standard)}$ est de la forme $\cos(\py{latex(Symbol(name_u),**config_standard)})$ avec $\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}) = \py{latex(u,**config_standard)}$. On sait que $\big(\cos(\py{latex(Symbol(name_u),**config_standard)})\big)'(\py{latex(variable,**config_standard)}) = -\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) \times \sin(\py{latex(Symbol(name_u),**config_standard)}(\py{latex(variable,**config_standard)}))$""", globals(), locals()) if u_is_polynomial and niveau == 'terminale': # En terminale, si u est un polynôme, on donne directement sa dérivée du = diff(u, variable) phrase += myst(r""" avec $\py{latex(Symbol(name_u),**config_standard)}'(\py{latex(variable,**config_standard)}) = \py{latex(du,**config_standard)}$. """, globals(), locals()) else: # Sinon on détaille le calcul phrase += myst(r""". """, globals(), locals()) phrase += narr_deriv_or_recurse( u, nm, variable, niveau, lhs=f"{latex(Symbol(name_u),**config_standard)}'({latex(variable,**config_standard)})", func_name=name_u, detail = detail ) # Conclusion avec gestion des parenthèses et du signe négatif du = diff(u, variable) # Gérer les parenthèses pour du si c'est une somme if isinstance(du, Add): du_latex = f"\\left({latex(du,**config_standard)}\\right)" else: du_latex = latex(du,**config_standard) # Si u est une somme (Add), on ajoute des parenthèses dans sin(u) if isinstance(u, Add): sin_latex = f"\\sin\\left({latex(u,**config_standard)}\\right)" else: sin_latex = f"\\sin\\left({latex(u,**config_standard)}\\right)" # Formule détaillée : -u' × sin(u) detailed_formula = f"-{du_latex} \\times {sin_latex}" # Forme simplifiée simplified = diff(self, variable) phrase += narr_conclude_with_details(name, variable, detailed_formula, simplified, detail) return phrase
""" x = Symbol('x') # --- Add / Sommes --- ex1 = 3*x + 2 # affine simple ex2 = x**3 - 4*x + 7 # polynôme pur ex3 = x**2 + sin(x) + exp(x) # mélange poly + usuelles ex4 = -x + log(x) - 5 # signes + log simple # --- Mul / Produits --- ex5 = 4*sin(x) # k*u (k numérique) ex6 = -x*log(x) # signe devant, produit classique ex7 = (2*x+3)*cos(x) # produit poly * cos ex8 = (x-1)*(x+2) # produit de deux polynômes ex9 = (x+1)*exp(x) # produit * exp # --- Quotients (via puissances négatives) --- ex10 = (2*x+3)/(x+3)**2 # v=(x+3)^2 -> g=(x+3) ex11 = (x**2+1)/((x+1)*(x+2)) # v=(x+1)(x+2) -> pas de g ex12 = (3*x-1)/((x+1)**3*(x+2)**2) # v composite -> g=(x+1)^2*(x+2) ex13 = (sin(x)+1)/x # 1/x = x**(-1) ex14 = (x*cos(x))/(x+2)**4 # numérateur produit, v=(x+2)^4 # --- Puissances --- ex15 = (x+1)**5 # n entier >0 ex16 = sqrt(x+2) # n = 1/2 ex17 = (x-3)**(-2) # n négatif (≠ -1) ex18 = (2*x+1)**(-1) # n = -1 (cas spécial) # --- Exponentielles (ne pas toucher l’intérieur) --- ex19 = exp(2*x**2 + 3*x) # polynôme dans l’exposant ex20 = exp((x+1)*(x+2)) # produit dans l’exposant ex21 = exp(sin(x)) # non-polynôme dans l’exposant # --- Logarithmes --- ex22 = log(x) # simple ex23 = log((x+1)*(x+2)) # produit dans l’argument ex24 = log(x**2 + 3*x + 2) # polynôme dans l’argument # --- Sin/Cos (chaînes) --- ex25 = sin(2*x + 3) # affine dans sin ex26 = cos(x**2) # poly dans cos ex27 = sin((x+1)*(x+2)) # non-polynôme dans sin # --- Cas « mélange » + robustesse --- ex28 = x**4 + 3*x**2 - 2*x + 4 - x*log(x) + exp(x) # long mixte (ton expr6) ex29 = (x+1)**2 * (x+2)**-3 # produit * quotient (Pow négatif) ex30 = (2 - 3*x) * sin(x**2) # produit avec signe et chaîne ## Sans détails ### $f(x) = \py{latex(ex6)}$ \py{expliquer_derivation(ex6)} ______________________________________________________ ### $f(x) = \py{latex(ex7)}$ \py{expliquer_derivation(ex7, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex8)}$ \py{expliquer_derivation(ex8, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex9)}$ \py{expliquer_derivation(ex9, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex10)}$ \py{expliquer_derivation(ex10, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex11)}$ \py{expliquer_derivation(ex11, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex12)}$ \py{expliquer_derivation(ex12, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex13)}$ \py{expliquer_derivation(ex13, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex14)}$ \py{expliquer_derivation(ex14, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex23)}$ \py{expliquer_derivation(ex23, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex24)}$ \py{expliquer_derivation(ex24, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex27)}$ \py{expliquer_derivation(ex27, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex28)}$ \py{expliquer_derivation(ex28, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex29)}$ \py{expliquer_derivation(ex29, variable=x, start_name='f')} ______________________________________________________ ### $f(x) = \py{latex(ex30)}$ \py{expliquer_derivation(ex30, variable=x, start_name='f')} ______________________________________________________ ## Avec détails ### $f(x) = \py{latex(ex1)}$ \py{expliquer_derivation(ex1, variable=x, start_name='f', niveau='premiere', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex2)}$ \py{expliquer_derivation(ex2, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex3)}$ \py{expliquer_derivation(ex3, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex4,**config_standard)}$ \py{expliquer_derivation(ex4, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex5)}$ \py{expliquer_derivation(ex5, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex10)}$ \py{expliquer_derivation(ex10, variable=x, start_name='f', detail = "high")} ______________________________________________________ $f(x) = \py{latex(ex15)}$ \py{expliquer_derivation(ex15, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex16)}$ \py{expliquer_derivation(ex16, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex17)}$ \py{expliquer_derivation(ex17, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex18)}$ \py{expliquer_derivation(ex18, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex19)}$ \py{expliquer_derivation(ex19, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex20)}$ \py{expliquer_derivation(ex20, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex21)}$ \py{expliquer_derivation(ex21, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex22)}$ \py{expliquer_derivation(ex22, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex25)}$ \py{expliquer_derivation(ex25, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex26)}$ \py{expliquer_derivation(ex26, variable=x, start_name='f', detail = "high")} ______________________________________________________ ### $f(x) = \py{latex(ex29)}$ \py{expliquer_derivation(ex29, variable=x, start_name='f', detail = "high")} ______________________________________________________ """