initial commit
parent
5bdfe14600
commit
04ebd642fe
@ -1,11 +1,14 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
|
||||
Version 2, December 2004
|
||||
|
||||
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
|
||||
|
||||
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 @@
|
||||
version = '0.6'
|
@ -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 où 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…
Reference in New Issue