#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 17 19:11:52 2022
@author: jlebovits
"""
from __future__ import division
import sys
import random
from copy import deepcopy
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_lineaire_bis
from src.scripts.Mes_fctions.Mes_fctions_d_alg_lineaire_bis import *
import src.scripts.Mes_fctions.Mes_fctions_generalistes_bis
from src.scripts.Mes_fctions.Mes_fctions_generalistes_bis import *
import src.scripts.Mes_fctions.Mes_fctions_utilitaires
from src.scripts.Mes_fctions.Mes_fctions_utilitaires import _pxs_add_letter
from sympy import *
from src.scripts.pxs_runtime import get_pxs_lang, myst
[docs]
def pxsl_par(expr, minus = False, add = False):
"""
Wraps a LaTeX expression in parentheses if it starts with a minus sign or if ti is an Add.
Parameters
----------
expr : sympy expression or numeric
The expression to be displayed.
Returns
-------
myst : LaTeX-formatted object (via the myst function)
The LaTeX string, wrapped in parentheses if negative or Add.
"""
config_standard = pxs_config()
# Vérifie si l'expression, convertie en chaîne, commence par un signe '-'
# Cela permet d'identifier les expressions négatives (par exemple -x, -3, -sin(x)...)
if minus and add:
if str(expr).startswith('-') or expr.could_extract_minus_sign() or isinstance(expr, Add):
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
elif minus:
if str(expr).startswith('-') or expr.could_extract_minus_sign():
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
elif add:
if isinstance(expr, Add):
# Si l'expression est négative, on ajoute des parenthèses autour de son affichage LaTeX
# Exemple : -x → \left(-x\right)
# myst() est ici utilisée pour insérer la version LaTeX dynamique de l'expression
return myst(
r"""\left(\py{latex(expr, **config_standard)}\right) """,
globals(), locals()
)
# Si l'expression n’est pas négative, on la renvoie simplement en LaTeX sans parenthèses
return myst(
r"""\py{latex(expr, **config_standard)} """,
globals(), locals()
)
def _pxsl_choose_udv(sol, u, du, v, dv, var = Symbol('x')):
"""
Generates bilingual LaTeX text (English/French) explaining the choice of
functions u and v' in an integration by parts setup.
Parameters
----------
u : sympy expression
Function chosen as u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function chosen as v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
Returns
-------
str
A LaTeX-formatted bilingual text describing u, v', and their derivatives.
"""
config_standard = pxs_config()
text = ""
# Introduction : phrase bilingue annonçant le choix de u et v'
text = myst(r"""
\en{with}\fr{On choisit $u$ et $v'$ tels que :}
""", globals(), locals())
# Bloc d'équations affichant les expressions de u(x) et v'(x)
text += myst(r"""
\en{\begin{equation*}
&u = \py{latex(u, **config_standard)}&
&\textrm{and}&
&v' = \py{latex(dv, **config_standard)}&
\end{equation*}}
\fr{\begin{equation*}
&u(\py{var}) = \py{latex(u, **config_standard)}&
&\textrm{et}&
&v'(\py{var}) = \py{latex(dv, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Transition bilingue vers les dérivées et primitives correspondantes
text += myst(r"""
\en{gives}\fr{On en déduit donc}
""")
# Bloc d'équations affichant u'(x) et v(x)
text += myst(r"""
\en{\begin{equation*}
&u' = \py{latex(du, **config_standard)}&
&\textrm{and}&
&v = \py{latex(v, **config_standard)}.&
\end{equation*}}
\fr{\begin{equation*}
&u'(\py{var}) = \py{latex(du, **config_standard)}&
&\textrm{et}&
&v(\py{var}) = \py{latex(v, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Retourne le texte LaTeX complet (mélange de phrases et équations)
sol["choice"] = text
return text
def _pxsl_choose_vdu(sol, u, du, v, dv, var = Symbol('x')):
"""
Generates bilingual LaTeX text (English/French) explaining the choice of
functions u' and v in an integration by parts setup.
Parameters
----------
u : sympy expression
Function corresponding to u(x).
du : sympy expression
Derivative of u(x), i.e., u'(x).
v : sympy expression
Function chosen as v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
Returns
-------
str
A LaTeX-formatted bilingual text describing u', v, and their corresponding
functions and derivatives.
"""
config_standard = pxs_config()
text = ""
# Introduction : phrase bilingue annonçant le choix de u' et v
text = myst(r"""
\en{with}\fr{On choisit $u'$ et $v$ tels que :}
""", globals(), locals())
# Bloc d'équations affichant u'(x) et v(x)
text += myst(r"""
\en{\begin{equation*}
&u' = \py{latex(du, **config_standard)}&
&\textrm{and}&
&v = \py{latex(v, **config_standard)}&
\end{equation*}}
\fr{\begin{equation*}
&u'(\py{var}) = \py{latex(du, **config_standard)}&
&\textrm{et}&
&v(\py{var}) = \py{latex(v, **config_standard)}.&
\end{equation*}}
""", globals(), locals())
# Transition bilingue vers les primitives et dérivées correspondantes
text += myst(r"""
\en{gives}\fr{On en déduit donc}
""")
# Bloc d’équations affichant u(x) et v'(x)
text += myst(r"""
\en{\begin{equation*}
&u = \py{latex(u, **config_standard)}&
&\textrm{and}&
&v' = \py{latex(dv, **config_standard)}.&
\end{equation*} }
\fr{\begin{equation*}
&u(\py{var}) = \py{latex(u, **config_standard)}&
&\textrm{et}&
&v'(\py{var}) = \py{latex(dv, **config_standard)}.&
\end{equation*} }
""", globals(), locals())
# Retourne le texte LaTeX complet (phrases bilingues et équations)
sol["choice"] = text
return text
def _pxsl_sentence1(sol, u, du, v, dv, type_int, var = Symbol('x'), bl = None, br = None, intf = None):
"""
Generates the bilingual LaTeX formula showing the integration by parts
resolution in the case of an *indefinite integral* (primitive).
Depending on the type of pair chosen ("udv" or "vdu"),
the function builds the appropriate LaTeX equation for:
∫ u·v' dx = u·v − ∫ u'·v dx
or
∫ u'·v dx = u·v − ∫ u·v' dx
Parameters
----------
u : sympy expression
Function u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
type_int : str
Type of integration by parts ("udv" or "vdu").
Returns
-------
str
A LaTeX-formatted bilingual text representing the integration by parts formula.
"""
# Cas où l'utilisateur a fourni (u, dv)
# Construction de la formule : ∫u·v' dx = u·v − ∫u'·v dx
if type_int == "udv":
text = myst(r"""
\begin{equation*}
\label{eq}
\py{intf} \py{pxsl_par(u, add = True)}\cdot \py{pxsl_par(dv, add = True, minus = True)} \ d\py{var}
&= \py{bl}\py{pxsl_par(u, add = True)}\cdot \py{pxsl_par(v, add = True, minus = True)}\py{br} - \py{intf} \py{pxsl_par(du, add = True)} \cdot \py{pxsl_par(v, add = True, minus = True)} \ d\py{var}\\
""", globals(), locals())
# Cas où l'utilisateur a fourni (du, v)
# Construction de la formule : ∫u'·v dx = u·v − ∫u·v' dx
if type_int == "vdu":
text = myst(r"""
\begin{equation*}
\label{eq}
\py{intf} \py{pxsl_par(du, add = True)}\cdot \py{pxsl_par(v, add = True, minus = True)} \ d\py{var}
&=\py{bl} \py{pxsl_par(u, add = True)}\cdot \py{pxsl_par(v, add = True, minus = True)} \py{br}- \py{intf} \py{pxsl_par(u, add = true)} \cdot \py{pxsl_par(dv, add = True, minus = True)} \ d\py{var}\\
""", globals(), locals())
# Retourne le texte LaTeX de la résolution par parties
sol["sentence1"] = text
return text
def _pxsl_sentence2(sol, uv, expr, nb_IBP, a = None, b = None, var = Symbol('x')):
"""
Builds LaTeX fragments for the integration by parts process when handling
sign changes and coefficients, with or without integration bounds.
Parameters
----------
uv : sympy expression
The product term u·v from integration by parts.
expr : sympy expression
The remaining integral expression after applying integration by parts.
nb_IBP : int
The number of successive integrations by parts already applied (used to adjust display).
a, b : numeric or symbolic, optional
Lower and upper bounds of integration. If None, the integral is indefinite.
Returns
-------
tuple
text : LaTeX string representing intermediate and simplified steps
of the integration by parts process, with correct sign and coefficient handling.
"""
config_standard = pxs_config()
# Si les bornes a et b ne sont pas données → intégrale indéfinie
bl, br, intf = _pxs_bounds(a, b)
# Extraction du coefficient numérique de expr (partie indépendante de x)
# puis gestion du signe et de la valeur absolue
expr = factor(-simplify(-expr))
if a is None and b is None:
text = myst(r"""
&= \py{bl}\py{latex(uv, **config_standard)}\py{br}
""", globals(), locals())
sol["uv"] = uv
else:
text = myst(r"""
&= \py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a), minus = True, add = True)}
""", globals(), locals())
sol["uv"] = uv.subs(var,b) - uv.subs(var,a)
text += myst(r""" \py{pxsl_latex_coefficient(-pxs_separate_factors(expr, var)[0], sign = True)}\py{intf} \py{pxsl_par(pxs_separate_factors(expr, var)[1], add = True, minus = True)} \ d\py{var}
""", globals(), locals())
sol["coeff"] = pxs_separate_factors(expr, var)[0]
if nb_IBP == 2 and a is not None and b is not None:
if uv.subs(var,a).could_extract_minus_sign():
text += myst(r"""
\\&=\py{latex(uv.subs(var,b) - uv.subs(var,a), **config_standard)} \py{pxsl_latex_coefficient(pxs_separate_factors(expr, var)[0], sign = True)}\py{intf} \py{pxsl_par(pxs_separate_factors(expr, var)[1], add = True, minus = True)} \ d\py{var}
""", globals(), locals())
sol["sentence2"] = text
return text, pxs_separate_factors(expr, var)[1], sol
sol["sentence2"] = text
return text, expr, sol
def _pxsl_explain(sol, u, du, v, dv, type_int, nb_IBP, a = None, b = None, var = Symbol('x'), bl = None, br = None, intf = None):
"""
Builds a bilingual LaTeX explanation for the integration by parts process,
using either the primitive form or the definite integral form.
The function combines:
- the core integration by parts equation (via _pxsl_resolution_prim or _pxsl_resolution_int)
- the corresponding minus/plus term adjustments (via _pxsl_minus)
depending on the integration type ("udv" or "vdu") and the presence of bounds.
Parameters
----------
u : sympy expression
Function u(x).
du : sympy expression
Derivative of u(x).
v : sympy expression
Function v(x).
dv : sympy expression
Derivative of v(x), i.e., v'(x).
type_int : str
Integration by parts type: "udv" for (u, dv) or "vdu" for (du, v).
nb_IBP : int
Number of integration by parts steps performed (for formatting purposes).
a, b : numeric or symbolic, optional
Integration bounds. If None, the integral is indefinite.
Returns
-------
tuple
text : LaTeX string representing the detailed explanation
and simplified version of the integration by parts process.
"""
# Étape 1 : construire la résolution pour une primitive
text = _pxsl_sentence1(sol, u, du, v, dv, type_int, var, bl, br, intf)
# Étape 2 : ajouter les termes avec le bon signe selon le type d’intégration
if type_int == "udv":
txt, expr, sol = _pxsl_sentence2(sol, u*v, v*du, nb_IBP, a = a, b = b, var = var)
text += txt
else:
txt, expr, sol = _pxsl_sentence2(sol, u*v, u*dv, nb_IBP, a = a, b = b, var = var)
text += txt
# Retourne le texte complet et sa version simplifiée
return text, expr, sol
def _pxsl_conclude(sol, uv, expr, origin_int, var = Symbol('x')):
"""
Generates the final bilingual LaTeX step that concludes the integration by parts
process, displaying the complete evaluated primitive with constant C.
Parameters
----------
uv : sympy expression
The product term u·v obtained during integration by parts.
expr : sympy expression
The remaining part of the expression (integral to be evaluated or simplified).
origin_int : sympy expression
The original integral expression (used for final verification and display).
Returns
-------
str
A LaTeX-formatted bilingual text showing the final result of the integration,
including the constant of integration.
"""
config_standard = pxs_config()
expr = factor(-simplify(-expr))
# Extraction du coefficient numérique de l’expression (partie indépendante de x)
# Gestion du signe et des cas particuliers (valeurs 1 ou -1 ignorées)
if pxs_separate_factors(expr, var)[0] < 0 and pxs_separate_factors(expr, var)[0] != -1:
coeff = myst(r"""\py{latex(-pxs_separate_factors(expr, var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
elif pxs_separate_factors(expr, var)[0] > 0 and pxs_separate_factors(expr, var)[0] != 1:
coeff = myst(r"""\py{latex(pxs_separate_factors(expr, var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
else:
coeff = myst(r""" """, globals(), locals())
# Si l’expression commence par un signe négatif
if str(expr).startswith('-'):
# Cas où le signe devient "plus" après simplification
text = myst(r""" \\&=
\py{latex(uv, **config_standard)} + \py{coeff}\py{dot} \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], x), add = True, minus = True)} + C
""", globals(), locals())
else:
# Cas standard : on garde le signe négatif devant le terme intégré
text = myst(r""" \\&=
\py{latex(uv, **config_standard)} - \py{coeff}\py{dot}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var), add = True, minus = True)} + C
""", globals(), locals())
# Ajout de la ligne finale donnant la primitive complète de l’intégrale d’origine
text += myst(r"""\\
& = \py{latex(integrate(origin_int, var), **config_standard)} + C, \textrm{ \en{with}\fr{où} }C\textrm{ \en{a real constant}\fr{est une constante réelle}}.
\end{equation*}
""", globals(), locals())
# Retourne le texte LaTeX complet de conclusion
sol["conclude"] = text
return text
def _pxsl_conclude_int(sol, uv, expr, origin_int, a, b, var = Symbol('x')):
"""
Generates the final bilingual LaTeX step concluding the integration by parts
process for a *definite integral* between bounds a and b.
This function evaluates and formats the expression:
[u·v]_a^b ± coefficient × [∫f(x)dx]_a^b
and shows the fully evaluated result of the original definite integral.
Parameters
----------
uv : sympy expression
The product term u·v obtained during integration by parts.
expr : sympy expression
The remaining part of the expression (integral to be evaluated or simplified).
origin_int : sympy expression
The original integral expression (used for final comparison and display).
a, b : numeric or symbolic
Lower and upper bounds of the definite integral.
Returns
-------
str
A LaTeX-formatted bilingual text showing the final step of the definite
integration by parts, with all evaluations and signs displayed correctly.
"""
config_standard = pxs_config()
bl, br, intf = _pxs_bounds(a, b)
expr = factor(-simplify(-expr))
# Définit les crochets et notations bilingues pour les bornes d’intégration
# Extraction du coefficient numérique et gestion du signe
# On ajoute aussi les symboles nécessaires selon la présence d’un coefficient
if pxs_separate_factors(expr, var)[0] < 0 and pxs_separate_factors(expr, var)[0] != -1:
coeff = myst(r"""\py{latex(-pxs_separate_factors(expr, var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
l_par = myst(r"""\left( """)
r_par = myst(r"""\right) """)
elif pxs_separate_factors(expr, var)[0] > 0 and pxs_separate_factors(expr, var)[0] != 1:
coeff = myst(r"""\py{latex(pxs_separate_factors(expr, var)[0], **config_standard)} """, globals(), locals())
dot = myst(r"""\cdot """)
l_par = myst(r"""\left( """)
r_par = myst(r"""\right) """)
else:
coeff = myst(r""" """, globals(), locals())
dot = myst(r""" """)
l_par = myst(r""" """)
r_par = myst(r""" """)
text = ""
# Si l’expression commence par un signe négatif
if expr.could_extract_minus_sign():
# Cas où le signe devient "plus" après simplification
if isinstance(uv.subs(var,a), Add):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)}""", globals(), locals())
for term in uv.subs(var,a).as_ordered_terms():
if term.could_extract_minus_sign():
text += myst(r""" + \py{latex(-term, **config_standard)}""", globals(), locals())
else:
text += myst(r""" - \py{latex(abs(term), **config_standard)}""", globals(), locals())
text += myst(r""" + \py{coeff} \py{dot}\py{bl}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var), minus = True)}\py{br}
""", globals(), locals())
text += myst(r""" + \py{coeff} \py{dot}\py{l_par}\py{latex(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), add = True, minus = True)}\py{r_par}
""", globals(), locals())
elif uv.subs(var,a).could_extract_minus_sign():
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} + \py{latex(-uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{bl}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var), minus = True)}\py{br}
""", globals(), locals())
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} + \py{latex(-uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{l_par}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b), minus = True, add = True)} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), minus = True, add = True)}\py{r_par}
""", globals(), locals())
else:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b) - uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{bl}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var), minus = True)}\py{br}
""", globals(), locals())
text += myst(r"""
\\&=\py{latex(uv.subs(var,b) - uv.subs(var,a), **config_standard)} + \py{coeff} \py{dot}\py{l_par}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b))} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), add = True, minus = True)}\py{r_par}
""", globals(), locals())
else:
# Cas standard : le signe reste négatif devant la deuxième intégrale
text = myst(r""" \\&=
\py{latex(uv.subs(var,b), **config_standard)} - \py{pxsl_par(uv.subs(var,a), add = True, minus = True)} - \py{coeff}\py{dot}\py{bl}\py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var), minus = True)}\py{br}""", globals(), locals())
if isinstance(uv.subs(var,a), Add):
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)}""", globals(), locals())
for term in uv.subs(var,a).as_ordered_terms():
if term.could_extract_minus_sign():
text += myst(r""" + \py{latex(-term, **config_standard)}""", globals(), locals())
else:
text += myst(r""" - \py{latex(term, **config_standard)}""", globals(), locals())
text += myst(r""" - \py{coeff} \py{dot}\left(\py{latex(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), minus = True, add = True)}\right)
""", globals(), locals())
elif uv.subs(var,a).could_extract_minus_sign():
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} + \py{latex(-uv.subs(var,a), **config_standard)} - \py{coeff} \py{dot}\left(\py{latex(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), add = True, minus = True)}\right)
""", globals(), locals())
else:
text += myst(r"""
\\&=\py{latex(uv.subs(var,b), **config_standard)} - \py{latex(uv.subs(var,a), **config_standard)} - \py{coeff} \py{dot}\left(\py{latex(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,b), **config_standard)} - \py{pxsl_par(integrate(pxs_separate_factors(expr, var)[1], var).subs(var,a), add = True, minus = True)}\right)
""", globals(), locals())
# Ligne finale : égalité avec l’intégrale définie complète d’origine
text += myst(r"""\\
& =\py{latex(integrate(origin_int, (var, a, b)), **config_standard)}.
\end{equation*}
""", globals(), locals())
# Retourne le texte LaTeX complet de conclusion (forme avec bornes)
sol["conclude"] = text
return text
def _pxs_bounds(a, b):
config_standard = pxs_config()
if a is None and b is None:
bl = myst(r""" """)
br = myst(r""" """)
intf = myst(r""" \int """)
else:
bl = myst(r"""\en{}\fr{\left[} """, globals(), locals())
br = myst(r"""\en{\bigg|_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}}\fr{\right]_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}}} """, globals(), locals())
intf = myst(r""" \int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} """, globals(), locals())
return bl, br, intf
def _pxs_explain_IBP(sol, var = Symbol('x'), f1 = None, f2 = None, type_int = None, nb_IBP = 1, a = None, b = None):
"""
Builds a complete bilingual LaTeX correction text for the Integration by Parts (IBP) process,
handling both single and double applications, and supporting definite or indefinite integrals.
Depending on the number of integrations by parts (`nb_IBP`) and the type (`type_int`),
this function calls all the appropriate sub-functions:
- _pxs_resolve_pairs → identifies (u, du, v, dv)
- _pxsl_choose_udv/vdu → generates the introduction LaTeX text
- _pxsl_explain → generates the main formula
- _pxsl_conclude/_int → final step (primitive or definite)
- recursively calls itself if nb_IBP = 2
Parameters
----------
x : sympy Symbol
The integration variable.
f1, f2 : sympy expressions
The two parts of the integrand provided by the user.
type_int : str
Integration by parts type: "udv" for (u, dv) or "vdu" for (du, v).
nb_IBP : int, optional
Number of integration by parts steps (1 or 2). Default is 1.
a, b : numeric or symbolic, optional
Lower and upper bounds of integration (if given, produces a definite integral).
Returns
-------
str
A LaTeX-formatted bilingual text that fully explains the integration by parts
procedure, with all symbolic steps, coefficients, and final results.
"""
config_standard = pxs_config()
text = ""
# --- Définition des éléments de mise en forme selon la présence de bornes ---
bl, br, intf = _pxs_bounds(a, b)
# === Première intégration par parties ===
# Retrouver u, du, v et dv en fonction des informations de départ
if type_int == "udv":
u, du, v, dv = f1, diff(f1, var), integrate(f2, var), f2
if type_int == "vdu":
u, du, v, dv = integrate(f2, var), f2, f1, diff(f1, var)
# Introduction selon le type choisi (u,dv) ou (du,v)
if type_int == "udv":
text += _pxsl_choose_udv(sol, u, du, v, dv, var)
else:
text += _pxsl_choose_vdu(sol, u, du, v, dv, var)
# Phrase bilingue avant l’application de la formule
text += myst(r"""
\en{So integration by parts gives us:}\fr{En appliquant la formule, on obtient : }""")
# Construction de la formule selon la présence ou non de bornes
txt, expr, sol = _pxsl_explain(sol, u, du, v, dv, type_int, nb_IBP, var = var, a = a, b = b, bl = bl, br = br, intf = intf)
text += txt
# === Conclusion si une seule intégration par parties ===
if nb_IBP == 1 and a is None and b is None:
if type_int == "udv":
text += _pxsl_conclude(sol, u*v, v*du, u*dv, var = var)
if type_int == "vdu":
text += _pxsl_conclude(sol, u*v, u*dv, v*du, var = var)
elif nb_IBP == 1:
if type_int == "udv":
text += _pxsl_conclude_int(sol, u*v, v*du, u*dv, a, b, var = var)
if type_int == "vdu":
text += _pxsl_conclude_int(sol, u*v, u*dv, v*du, a, b, var = var)
# === Cas où une deuxième intégration par parties est demandée (primitive) ===
if nb_IBP == 2 :
text += myst(r"""
\end{equation*}
""")
# Retourne le texte LaTeX complet expliquant les étapes de l’IBP
return text, u, du, v, dv, expr, sol
[docs]
def pxs_explain_IBP(var = Symbol('x'), f1 = None, f2 = None, type_int = "udv", a = None, b = None, nb_IBP = 1, intro = True, conclude = True, link = "https://app.pyxiscience.com/teacher/modules/7e0b271d-92f9-11f0-a777-0e37881c19a9/chapter/258e5825-9de5-11f0-a5a8-0e37881c19a9#quotient-fini#IBP2"):
"""
Creates a complete bilingual LaTeX explanation for computing an integral
using the Integration by Parts (IBP) method.
This is the main public-facing function that introduces the IBP concept,
calls the internal recursive explanation generator `_pxs_explain_IBP`, and
concludes by showing the final boxed result of the integral (definite or
indefinite).
Parameters
----------
var : sympy Symbol
The integration variable.
f1, f2 : sympy expressions
The two parts of the integrand (used as u and dv or du and v).
type_int : str, optional
Type of integration by parts ("udv" or "vdu"). Default is "udv".
a, b : numeric or symbolic, optional
Lower and upper bounds of the integral. If None, it’s treated as an
indefinite integral.
nb_IBP : int, optional
Number of integrations by parts to perform (1 or 2). Default is 1.
Returns
-------
str
A bilingual LaTeX-formatted text containing the full reasoning,
step-by-step explanation, and final boxed conclusion of the IBP process.
"""
config_standard = pxs_config()
sol = {}
# Introduction bilingue avec lien interactif vers le chapitre concerné
if intro:
text = myst(fr"""
```{fr}
On calcule cette intégrale en réalisant une [intégration par parties]({{link}}) :
```
```{en}
Using [integration by parts]({{link}})
```
""", globals(), locals())
else:
text = myst(r""" """)
sol["intro"] = text
# Appel de la fonction principale interne qui rédige les étapes détaillées
txt, u, du, v, dv, expr, sol = _pxs_explain_IBP(sol, var, f1, f2, type_int, nb_IBP, a, b)
text += txt
sol["u"], sol["du"], sol["v"], sol["dv"] = u, du, v, dv
sol["a"], sol["b"], sol["var"] = a, b, var
# === Cas (u, dv) sans bornes ===
if conclude and type_int == "udv" and a is None and b is None and nb_IBP == 1:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int \py{latex(u*dv, **config_standard)} \;d\py{var} = \py{latex(integrate(u*dv, var), **config_standard)}} + C$, \en{with}\fr{où} $C$ \en{a real constant}\fr{est une constante réelle}.}
\end{equation*}""", globals(), locals())
elif type_int == "udv" and a is None and b is None and nb_IBP == 1:
sol["int"] = integrate(u*dv, var)
# === Cas (u, dv) avec bornes ===
elif conclude and type_int == "udv" and nb_IBP == 1:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(u*dv, **config_standard)} \;d\py{var} = \py{latex(integrate(u*dv, (var, a, b)), **config_standard)}}$}
\end{equation*}""", globals(), locals())
sol["int"] = integrate(u*dv, (var, a, b))
elif type_int == "udv" and nb_IBP == 1:
sol["int"] = integrate(u*dv, (var, a, b))
# === Cas (du, v) sans bornes ===
if conclude and type_int == "vdu" and a is None and b is None and nb_IBP == 1:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int \py{latex(v*du, **config_standard)} \;d\py{var} = \py{latex(integrate(v*du, var), **config_standard)}} + C$, \en{with}\fr{où} $C$ \en{a real constant}\fr{est une constante réelle}.}
\end{equation*}""", globals(), locals())
sol["int"] = integrate(v*du, var)
elif type_int == "vdu" and a is None and b is None and nb_IBP == 1:
sol["int"] = integrate(v*du, var)
# === Cas (du, v) avec bornes ===
elif conclude and type_int == "vdu" and nb_IBP == 1:
text += myst(r"""
\en{Thus,}\fr{On a donc montré :}
\begin{equation*}
\fbox{$\displaystyle{\int_{\py{latex(a, **config_standard)}}^{\py{latex(b, **config_standard)}} \py{latex(v*du, **config_standard)}\;d\py{var} = \py{latex(integrate(v*du, (var, a, b)), **config_standard)}}$}
\end{equation*}""", globals(), locals())
sol["int"] = integrate(v*du, (var, a, b))
elif type_int == "vdu" and nb_IBP == 1:
sol["int"] = integrate(v*du, (var, a, b))
sol["text"] = text
sol["expr"] = expr
# Retourne le texte complet, incluant l’introduction, le déroulé et la conclusion
return sol
[docs]
def pxsl_final_sentence(sol, a, b, var, mult, *args):
config_standard = pxs_config()
bl, br, intf = _pxs_bounds(a, b)
final_sol = myst(r"""
\begin{equation*}
\py{intf}\py{latex(args[0]["u"]*args[0]["dv"],**config_standard)}\,d\py{var}
""", globals(), locals())
for i in range(len(args) - 1):
if args[i]["coeff"] == -1 and (args[i+1]["int"].could_extract_minus_sign() or latex(args[i+1]["int"], **config_standard).startswith('-')):
final_sol += myst(r"""
&= \py{latex(args[i]["uv"], **config_standard)}\py{latex(args[i+1]["int"], **config_standard)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" +C """, globals(), locals())
if Add(args[i]["uv"], args[i+1]["int"], evaluate = False) != args[i]["uv"] + args[i+1]["int"]:
final_sol += myst(r"""
\\ &= \py{latex(integrate(args[0]["u"]*args[0]["dv"], (var, a, b)), **config_standard)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" +C """, globals(), locals())
elif args[i]["coeff"] == -1:
final_sol += myst(r"""
&=\py{latex(args[i]["uv"], **config_standard)}+\py{latex(args[i+1]["int"], **config_standard)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" + C """, globals(), locals())
if Add(args[i]["uv"], args[i+1]["int"], evaluate = False) != args[i]["uv"] + args[i+1]["int"]:
final_sol += myst(r"""
\\&= \py{latex(integrate(args[0]["u"]*args[0]["dv"], (var, a, b)), **config_standard)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" + C """, globals(), locals())
else :
mult2 = mult if args[i]["coeff"] != 1 else myst(r""" """)
final_sol += myst(r"""
&= \py{latex(args[i]["uv"], **config_standard)}\py{pxsl_latex_coefficient(-args[i]["coeff"], sign = True)}\py{mult2}\py{pxsl_par(args[i+1]["int"], add = True, minus = True)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" + C """, globals(), locals())
final_sol += myst(r"""
\\&= \py{latex(integrate(args[0]["u"]*args[0]["dv"], (var, a, b)), **config_standard)}
""", globals(), locals())
if a is None and b is None:
final_sol += myst(r""" + C, \textrm{ \en{with}\fr{où} }C\textrm{ \en{a real constant}\fr{est une constante réelle}}. """, globals(), locals())
final_sol += myst(r""" \end{equation*}""")
sol["final_sentence"] = final_sol
return final_sol
[docs]
def pxsl_partial_decomp(
num: list = [],
den: list = [],
var=Symbol('x'),
mul_symbol=myst(r""" \cdot"""),
method = "simple"
) -> dict:
"""
Construct the symbolic structure of a partial fraction decomposition.
The function analyzes the denominator factors, generates the appropriate
elementary fractions (including powers for repeated factors), assigns
symbolic coefficients (A, B, C, ...), and builds the associated identity
equation.
Parameters
----------
num : list, optional
List representing the numerator (kept for consistency, not expanded here).
den : list, optional
List of denominator factors (SymPy expressions).
var : Symbol, optional
Main symbolic variable (default is ``Symbol('x')``).
mul_symbol : Any, optional
Multiplication symbol used in LaTeX rendering.
method : str, optional
Decomposition method: "simple" or "advanced" (default is "simple").
If "simple", one letter by term, if "advanced", linear terms for irreducible quadratics.
Returns
-------
dict
A dictionary containing:
- num : list
- den : list
- letters : list of str
- elem_list : list of denominator elements
- var : Symbol
- expr : symbolic sum of elementary fractions
- identity : simplified identity after clearing denominators
Examples
--------
Basic usage (distinct linear factors)
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> sol = pxsl_partial_decomp(den=[x, x - 1])
>>> sol["letters"]
['A', 'B']
>>> sol["expr"]
A/x + B/(x - 1)
Repeated factor (powers are generated automatically)
>>> sol = pxsl_partial_decomp(den=[x, (x - 1)**2])
>>> sol["letters"]
['A', 'B', 'C']
>>> sol["expr"]
A/x + B/(x - 1) + C/(x - 1)**2
Mix of repeated and distinct factors
>>> sol = pxsl_partial_decomp(den=[(x + 2)**3, (2*x - 1)])
>>> sol["letters"]
['A', 'B', 'C', 'D']
>>> sol["expr"]
A/(x + 2) + B/(x + 2)**2 + C/(x + 2)**3 + D/(2*x - 1)
Advanced mode (irreducible quadratic gets a linear numerator)
>>> sol = pxsl_partial_decomp(den=[x**2 + 1], method="advanced")
>>> sol["letters"]
['A', 'B']
>>> sol["expr"]
(A*x + B)/(x**2 + 1)
Advanced mode with a mix (quadratic + repeated linear factor)
>>> sol = pxsl_partial_decomp(den=[x**2 + 1, (x - 3)**2], method="advanced")
>>> sol["letters"]
['A', 'B', 'C', 'D']
>>> sol["expr"]
(A*x + B)/(x**2 + 1) + C/(x - 3) + D/(x - 3)**2
Identity after clearing denominators (useful to solve for coefficients)
>>> sol = pxsl_partial_decomp(den=[x, x - 1])
>>> sol["identity"]
A*(x - 1) + B*x
"""
sol = {
"num": num,
"den": den,
"letters": [],
"expr": 0,
"var": var,
}
for elem in sol["den"]:
try:
poly = Poly(elem, sol["var"])
except:
pass
if isinstance(poly, Poly) and poly.degree() >= 2 and not pxs_is_factorable(poly) and method != "simple":
_pxs_add_letter(sol)
_pxs_add_letter(sol)
sol["expr"] += (Symbol(sol["letters"][-2])*sol["var"] + Symbol(sol["letters"][-1])) / poly
elif isinstance(elem, Pow) and elem.args[1] >= 2:
for n in range(1, elem.exp + 1):
_pxs_add_letter(sol)
sol["expr"] += Symbol(sol["letters"][-1]) / elem.base**n
else:
_pxs_add_letter(sol)
sol["expr"] += Symbol(sol["letters"][-1]) / elem
sol["identity"] = simplify(sol["expr"] * Mul(*sol["den"]))
return sol
[docs]
def pxsl_decomp_sol(sol: dict, x_0, mul_symbol: str = "", par: bool = False) -> None:
"""
Generate and store the LaTeX representation of the decomposition identity
evaluated at a specific value of the variable.
The result is stored directly in the solution dictionary under a dynamically
generated key.
Parameters
----------
sol : dict
Dictionary produced by ``pxsl_partial_decomp``.
x_0 : int or float
Value substituted for the main variable.
mul_symbol : str, optional
Multiplication symbol used in LaTeX output.
par : bool, optional
If True, negative values of `x_0` are enclosed in parentheses.
Returns
-------
None
The dictionary is modified in place by adding a LaTeX string.
Examples
--------
>>> pxsl_decomp_sol(sol, 2)
>>> "expr_2" in sol
True
"""
var_str = (
myst(r"""\py{str(x_0)}""", globals(), locals())
if x_0 >= 0 or par is False
else myst(r"""\left(\py{str(x_0)}\right)""", globals(), locals())
)
sol["expr" + str(x_0)] = latex(
sol["identity"],
symbol_names={sol["var"]: var_str},
mul_symbol=mul_symbol,
)