initial commit

main
ddenoncin 2 years ago
parent 5bdfe14600
commit 04ebd642fe

@ -1,11 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004 Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. Everyone is permitted to copy and distribute verbatim or modified copies of
this license document, and changing it is allowed as long as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO. 0. You just DO WHAT THE FUCK YOU WANT TO.

@ -1,2 +1,16 @@
# etude_fonction # Étude fonctions
Permet de générer automatiquement le code LaTeX permettant l'étude d'une fonction.
## Utilisation
Exécuter en ligne de commande le script ``etude_fonction`` qui vous demandera un dossier dans lequel mettre les sources LaTeX ainsi que l'expression de la fonction à écrire au format ``Python`` (préfixer par ``sp`` les fonctions usuelles) puis les intervalles sur lesquels la fonction est définie, dérivable et sur lequel vous voulez son tableau de variation. Les intervalles doivent être de la forme ``[a,b]`` ou ``]a,b[`` ou ``[a,b[`` ou ``]a,b]`` avec ``a`` et ``b`` convertibles en flottant ou bien les symboles ``-oo`` ou ``oo``.
Vous obtiendrez alors le ``main.tex`` du dossier voulu que vous pourrez compiler. Pour ajouter des informations, éditer simplement le fichier ``example.tex``. Pour éditer l'en-tête, il suffit d'éditer le fichier ``Utils/02_config_paquets.tex``.
## Contributions
Toute contribution est la bienvenue, et peut m'être adressée à l'adresse <math@denoncin.fr>
## Licence
[WTFPL](http://www.wtfpl.net/)

@ -0,0 +1,5 @@
## TODO
Faire une documentation correcte...
Écrire des tests ?

@ -0,0 +1 @@
from etude_fonction.etude_fonction import *

@ -0,0 +1,92 @@
import os
import libcurses as curses
from sys import argv,exit
import documentex
from subprocess import call
from pylatex import Document, Section, Itemize, Command, Math, NoEscape
from pylatex.base_classes import Arguments
from . import lib_etude_fonction as libef
import libargfunc as libargv
def etude_fonction():
if len(argv) != 2:
print("usage : etude_fonction nom_dossier")
exit(1)
dossier = argv[1]
ok = False
while not ok:
expression = curses.saisie_message_std('f(x)= ? ')
expression, ok = libargv.sympify(expression)
if not ok:
curses.afficher_message_std(expression)
# en sortie, expression est une expression sympy utilisable
ok = False
while not ok:
intervalle_definition = curses.saisie_message_std('intervalle de définition ? ')
intervalle_definition, ok = libargv.sympify_intervalle(intervalle_definition)
if not ok:
curses.afficher_message_std(intervalle_definition)
ok = False
while not ok:
intervalle_etude = curses.saisie_message_std("intervalle d'étude (taper entrée si c'est le même que le précédent) ? ")
intervalle_etude, ok = libargv.sympify_intervalle2(intervalle_etude)
if intervalle_etude == '' and ok:
intervalle_etude = intervalle_definition
if not ok:
curses.afficher_message_std(intervalle_etude)
ok = False
while not ok:
intervalle_derivabilite = curses.saisie_message_std("intervalle de dérivabilité (taper entrée si c'est le même que l'intervalle de définition) ? ")
intervalle_derivabilite, ok = libargv.sympify_intervalle2(intervalle_derivabilite)
if intervalle_derivabilite == '' and ok:
intervalle_derivabilite = intervalle_definition
if not ok:
curses.afficher_message_std(intervalle_derivabilite)
#(1+sqrt(1+24*x**2))/(6*x)#x*ln(1+1/x)
#intervalle_definition = Interval.open(0,oo)
#intervalle_etude = intervalle_definition
# valeurs utilisées pour tracer le graphe de la fonction
xmin = -10
xmax = 10
ymin = -15
ymax = 15
f=libef.Fonction(expression,intervalle_definition)
df = f.dx(intervalle_derivabilite)
call(['documentex',dossier,'doc'])
curr_dir = os.getcwd()
os.chdir(dossier)
f.graphe(xmin,xmax,ymin,ymax)
os.chdir(curr_dir)
doc=Section("Étude d'une fonction")
doc.append("Étudions la fonction donnée par ")
mapsto = Command('mapsto')
fonction = Math(escape=False)
doc.append(f.latex_print())
doc.append(NoEscape(r"Cette fonction est dérivable sur $"+libef.latex(intervalle_derivabilite)+"$ et sa dérivée est la fonction "))
doc.append(df.latex_print())
tableau_variations = f.tikztab(intervalle_etude)
if tableau_variations != '':
doc.append("On en déduit le tableau de variations suivant")
doc.append(f.tikztab(intervalle_etude))
doc.append("Du tableau de variation on en déduit les informations suivantes ")
with doc.create(Itemize()) as itemize:
itemize.add_item(NoEscape("<+vos commentaires sur le tableau de variation ici+> "))
else:
doc.append(r"<+malheureusement sympy.solveset et sympy.solve sont incapables de trouver les points critiques de cette fonction pour une raison inconnue.+>"+NoEscape("\n"))
doc.append("Le graphique de la fonction est donné sur la figure ")
numero=str(1)
doc.append(Command('ref',arguments=Arguments('fig:'+numero)))
doc.append(f.display_graphe('graphe de la fonction $x\mapsto '+f.latex()+'$',numero))
doc.generate_tex(os.path.join(dossier,'example'))

@ -0,0 +1,344 @@
import matplotlib
matplotlib.use('Agg')
from pylatex import Command, NoEscape, Math
from pylatex.base_classes import Arguments, Environment, Options
from sympy.abc import x
from sympy import oo,latex,solveset,simplify,lambdify,S,FiniteSet,EmptySet,Complement,sign,limit,solve,factor
import matplotlib.pyplot as plt
from numpy import linspace
#classes utiles pour les environnements latex utilisés
class Array(Environment):
escape=False
class Displaymath(Environment):
escape=False
class Figure(Environment):
escape=False
class Tikzpicture(Environment):
escape=False
#fonctions auxiliaires utiles
def order(set_sol):
"""
retourne une liste ordonnée à partir d'un FiniteSet
"""
sol=[]
n=len(list(set_sol))
setsol=set_sol
while n!= 0:
sol.append(setsol.inf)
setsol=Complement(setsol,FiniteSet(setsol.inf))
n-=1
return sol
def float_or_tex(expr):
"""
Permet de décider si dans le tableau de variation on utilise uniquement une expression approchée de la valeur de la fonction au point ou si on donne le code latex avec éventuellement une approximation pour le tracé
"""
tex = latex(expr)
if len(str(tex))>20:
return '\\sim '+latex(round(float(expr),2))
else:
try:
if expr==int(expr):
return tex
else:
return '\\scriptstyle{'+tex+'}\\simeq '+latex(round(float(expr),2))
except:
if expr==oo:
return '+'+tex
else:
return tex
class Fonction:
"""
Classe de fonction permettant de produire du code latex automatisé pour son etude. Une fonction est la donnée d'une expression en x, d'un Interval (classe d'objets Sympy) de départ et par défaut l'Interval d'arrivée est S.Reals.
"""
def __init__(self,expr,s,t=S.Reals):
self.expr = expr
self.s = s
self.t = t
def __str__(self):
# strings_to_replace = {'log':'ln','asin':'arcsin','acos':'arccos','atan':'arctan'}
expression = str(self.expr)
# for string in strings_to_replace:
# expression = expression.replace(string,strings_to_replace[string])
return expression
def latex(self):
"""
Expression latex de la fonction
"""
# strings_to_replace = {'log':'ln','asin':'arcsin','acos':'arccos','atan':'arctan'}
expression = latex(self.expr)
# for string in strings_to_replace:
# expression=expression.replace(string,strings_to_replace[string])
return expression
def subs(self,symbol,symbolremp):
return Fonction(self.expr.subs(symbol,symbolremp),self.s,self.t)
def latex_source(self):
"""
Expression latex de l'ensemble de départ
"""
if self.s.inf==-oo and self.s.sup==oo:
return latex(S.Reals)
else:
return latex(self.s)
def latex_target(self):
"""
Expression latex de l'ensemble d'arrivée
"""
return latex(self.t)
def latex_inline(self):
"""
Expression latex en ligne de la fonction
"""
fonction = Math(inline=True,escape=False)
fonction.append(latex(x))
fonction.append(Command('mapsto'))
fonction.append(latex(self.expr))
return fonction
def latex_print(self):
"""
Expression latex complète de la fonction en environnement displaymath
"""
mapsto = Command('mapsto')
arrow = Command('rightarrow')
fonction = Displaymath()
with fonction.create(Array(arguments=Arguments('ccc'))) as fun:
fun.append(self.latex_source())
fun.append(r' & ')
fun.append(arrow)
fun.append(r' & ')
fun.append(self.latex_target())
fun.append(r'\\')
fun.append(r'x')
fun.append(r' & ')
fun.append(mapsto)
fun.append(r' & ')
fun.append(latex(self.expr))
return fonction
def dx(self,s):
"""
Retourne la dérivée de la fonction comme objet Fonction, il faut préciser le domaine de dérivabilité.
"""
return Fonction(self.expr.diff(x).factor().simplify(),s,self.t)
def critique(self,s):
"""
Retourne les points critiques de la fonction lorsque c'est possible, et -1 si l'ensemble n'est pas un FiniteSet
"""
try:
zeros = solveset(self.dx(s).expr,x,s)
if type(zeros)==FiniteSet or zeros==EmptySet():
return zeros
elif type(zeros)==ConditionSet:
raise TypeError
else:
return -1
except:
try:
zeros = solve(self.dx(s).expr,x)
zeros=FiniteSet(*zeros).intersection(s)
return zeros
except:
return -1
def graphe(self,xmin,xmax,ymin,ymax,n=1000):
"""
trace le graphique de la courbe et le sauvegarde dans le dossier courant sous le nom courbe.pdf
"""
ax = plt.gca()
#plt.axis((xmin,xmax,ymin,ymax))
#plt.axes().set_aspect('equal')
ax.axis((xmin,xmax,ymin,ymax))
ax.set_aspect('equal')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
fx=lambdify((x),self.expr,'numpy')
T=linspace(xmin,xmax,n)
X=[fx(t) for t in T]
plt.plot(T,X)
plt.savefig("courbe.pdf")
def display_graphe(self,titre,numero,position='H'):
graphique = Figure(options=Options('H'))
graphique.append(r'\centering')
graphique.append(Command('includegraphics',arguments=Arguments('courbe.pdf')))
arg = Arguments(NoEscape(titre+r'\label{fig:'+numero+'}'))
# arg.append(Command('label',arguments=Arguments('fig:'+numero)))
c = Command('caption',arguments=arg)
graphique.append(c)
return graphique
def bornes_intervalle_et_pc(self,s):
"""
retourne les bornes de l'intervalle s ainsi que les points critiques de self sur s s'il y en a. Permet de construire le tableau de variation de la fonction si possible. Retourne -1 si il y a une infinité de points critiques si Sympy n'arrive pas à les calculer
"""
try:
return self.critique(s).union(FiniteSet(s.inf)).union(FiniteSet(s.sup))
except:
return -1
def intervalle_tabvar(self,s):
"""
donne le code latex de la première ligne d'un tableau de variation de f
"""
try:
set_sol=self.bornes_intervalle_et_pc(s)
set_sol=order(set_sol)
sol=''
for t in set_sol:
if t == oo:
sol+='$'
sol+='+'+latex(t)
sol+='$'
elif t == set_sol[-1]:
sol+='$'
sol+=latex(t)
sol+='$'
else:
sol+='$'
sol+=latex(t)
sol+='$,'
return sol
except:
return -1
def signe_to_tex(self,sol,eps,op):
"""
fonction auxiliaire pour remplir un tableau de signes
"""
if op=='+':
if sign(self.expr.subs(x,sol+eps))==1:
return '+'
else:
return '-'
elif op=='-':
if sign(self.expr.subs(x,sol-eps))==1:
return '+'
else:
return '-'
def signe(self,eps,sol1,sol2,set_sol):
"""
fonction auxiliaire pour remplir un tableau de signes. Signe prend en argument la fonction et renvoie les signes de la dérivée !
"""
if sol1 in set_sol:
if sign(self.expr.subs(x,sol1+eps))==1:
return '0,+,'
else:
return '0,-,'
elif sol1 not in set_sol:
if sol1==-oo:
if sol2==oo:
return self.signe_to_tex(0,eps,'+')+','+self.signe_to_tex(0,eps,'+')+','
else:
return self.signe_to_tex(sol2,eps,'-')+','+self.signe_to_tex(sol2,eps,'-')+','
else:
return self.signe_to_tex(sol1,eps,'+')+','+self.signe_to_tex(sol1,eps,'+')+','
def signes(self,s,eps=10**(-1)):
"""
fonction permettant de remplir avec tikz un tableau de signes : on trouve les points critiques et on établit un tableau de signe en remplissant deux cases de suite grâce à la fonction signe en partant d'un bord de l'intervalle d'étude ou d'un point critique.
"""
try:
set_sol=order(self.bornes_intervalle_et_pc(s))
points_critiques = order(self.critique(s))
signx=''
n=len(set_sol)
for i in range(n-1):
sol1=set_sol[i]
sol2=set_sol[i+1]
signx+=self.dx(s).signe(eps,sol1,sol2,points_critiques)
sol=set_sol[n-1]
if sol in points_critiques:
signx+='0'
return signx
except:
return -1
def tikztabinit(self,s,eps=10**(-1)):
intervalle = self.intervalle_tabvar(s)
if intervalle!=-1:
return Command('tkzTabInit',arguments=Arguments(NoEscape(r"$x$/1,$f'(x)$/1,$f$/1"),NoEscape(self.intervalle_tabvar(s))))
else:
return ''
def tikztabsignes(self,s,eps=10**(-1)):
"""
Permet de remplir la ligne des signes d'un tableau de variations de self
"""
intervalle = self.intervalle_tabvar(s)
if intervalle!=-1:
return Command('tkzTabLine',arguments=Arguments(NoEscape(self.signes(s,eps))))
else:
return ''
def variation(self,s,sol1,sol2,critiques,eps=10**(-1)):
"""
donne le code latex pour un élément de la ligne du tableau de variations d'une des fonctions coordonnées
"""
lim = limit(self.expr,x,sol1)
tex_lim = float_or_tex(lim)
dfun = self.dx(s)
if '+' in dfun.signe(eps,sol1,sol2,critiques):
# if lim not in [-oo,oo]:
# return '-/$'+tex_lim+'$,'
# elif lim in [-oo,oo]:
return '-/$'+tex_lim+'$,'
elif '-' in dfun.signe(eps,sol1,sol2,critiques):
# if lim not in [-oo,oo]:
# return '+/$'+tex_lim+'$,'
# elif lim in [-oo,oo]:
return '+/$'+tex_lim+'$,'
def variations(self,s,eps=10**(-1)):
"""
donne le code latex pour la ligne du tableau de variations d'une des fonctions de coordonnées en utilisant la fonction variation
"""
var=''
set_sol = order(self.bornes_intervalle_et_pc(s))
if set_sol==-1:
set_sol = [s.inf,s.sup]
n=len(set_sol)
points_critiques = order(self.critique(s))
for i in range(n-1):
sol1=set_sol[i]
sol2=set_sol[i+1]
var+=self.variation(s,sol1,sol2,points_critiques,eps)
sol1=set_sol[n-2]
sol2=set_sol[n-1]
lim = limit(self.expr,x,sol2,'-')
dfun = self.dx(s)
tex_lim = float_or_tex(lim)
if '+' in dfun.signe(eps,sol1,sol2,points_critiques):
# if lim not in [-oo,oo]:
var+='+/$'+tex_lim+'$'
# elif lim in [-oo,oo]:
# if lim==oo:
# var+='+/$+'+tex_lim+'$'
# else:
# var+='+/$'+tex_lim+'$'
elif '-' in dfun.signe(eps,sol1,sol2,points_critiques):
# if lim not in [-oo,oo]:
var+='-/$'+tex_lim+'$'
# elif lim in [-oo,oo]:
# var+='-/$-'+tex_lim+'$'
return var
def tikztabvar(self,s,eps=10**(-1)):
"""
Permet de remplir la ligne des variations d'un tableau de variations de self
"""
intervalle = self.intervalle_tabvar(s)
if intervalle!=-1:
return Command('tkzTabVar',arguments=Arguments(NoEscape(self.variations(s,eps))))
else:
return ''
def tikztab(self,s,eps=10**(-1)):
"""
Donne le code Tikz du tableau de variations de self
"""
intervalle = self.intervalle_tabvar(s)
if intervalle!=-1:
figure = Figure(options=Options('H'))
figure.append(Command('centering'))
tikz = Tikzpicture()
intervalle_etude = s
with figure.create(tikz) as fig:
fig.append(self.tikztabinit(intervalle_etude))
fig.append(self.tikztabsignes(intervalle_etude))
fig.append(self.tikztabvar(intervalle_etude))
return figure
else:
return ''

@ -0,0 +1,30 @@
from setuptools import setup, find_packages
from etude_fonction.constantes_etude_fonction import version
setup(
name="etude_fonction",
version=version,
packages=find_packages(),
include_package_data=True,
entry_points={
'console_scripts': [
'etude_fonction = etude_fonction.etude_fonction:etude_fonction',
]
},
install_requires=["libcurses",
"libargfunc",
"documentex",
"sympy",
"pylatex",
"matplotlib",
"numpy",
],
author="David Denoncin",
author_email="math@denoncin.fr",
url="math.denoncin.fr",
description="Génération de corrigés d'étude de fonctions",
classifiers=[
"Licence :: WTFPL"
]
)
Loading…
Cancel
Save