Tracer des fonctions "compliquées" en tex

adrien2019
Modifié (July 2022) dans LaTeX
Bonjour à tous
Je cherche à tracer en tex des fonctions ayant des définitions "compliquées". Il y a en particulier deux fonctions auxquelles je m'intéresse:
1) La fonction $\varphi: t \mapsto \sum\limits_{n=1}^{+ \infty} \frac{(-1)^n \cos (2^n t)}{2^n}$.
2) La fonction de Thomae : elle vaut $0$ sur les irrationnels et $\frac{1}{q}$ en $x = \frac{p}{q}$, où $\frac{p}{q}$ est la représentation irréductible de $x$ lorsque $x$ est rationnel.
Quelqu'un sait-il comment faire cela de façon simple en tex (il est par exemple hors de question de tracer des points un par un pour la seconde fonction, ni d'écrire une par une les fonctions à sommer jusqu'à un nombre assez grand pour la première fonction) ?
Je vous remercie d'avance pour votre aide !

Réponses

  • Bonjour,
    J'aurais justement attaqué le problème par ce que tu qualifies de "hors de question". Je connais mal le tracé de fonctions en tex mais j'imagine qu'il fonctionne plus ou moins comme Matplotlib en Python (au pire, fais ton graphique en Python et insère la figure en Tex qui va avec). Dans ce cas, l'outil trace bien la fonction point par point. Pour ces 2 fonctions, un calcul point par point ne me semble pas du tout délirant, je m'explique :
    1) La somme converge relativement vite, tu peux majorer la valeur absolue du reste par $\frac{1}{2^n}$. Tu auras donc juste besoin des 20 premiers termes pour avoir une précision à $10^{-6}$ : c'est tout à fait gérable pour un ordi.
    2) Ici, tu peux tracer ta fonction en plaçant directement les points $(p/q,1/q)$ sur ton graphique. Il faut donc une manière d'énumérer un bon nombre de rationnels, par exemple pour $1 \leqslant p \leqslant q \leqslant N$ (la fonction est 1-périodique). La densité ne sera pas uniforme mais, pour $N$ assez grand, ça ne se verra pas sur le graphique. Inutile de tracer ta fonction sur les irrationnels, tu verras la fonction nulle se dessiner sous ton nuage de points rationnels automatiquement.
  • Bonjour,
    Peut-être qu'avec une boucle du style "\foreach \i in {0,1,...,3}{+(-1)^\i * cos(2^\i*\x)/2^\i }" on peut faire calculer la somme par LaTeX. Mais je ne sais pas comment le mettre en pratique (ni si ça peut marcher).
  • Pour ce genre de fonctions je crois qu'il vaut mieux importer un pdf tracé avec un logiciel dédié, par ex. Sage.
  • Le problème d'importer des images d'autres logiciels c'est de se retrouver avec des polices de caractères différentes. Ca fait bien moche.
  • Guego
    Modifié (July 2022)
    Ce que je fais quand j'ai des fonctions un peu compliquées à tracer : je fais les calculs avec Sage ou Python, ou Maple. J'exporte la liste des coordonnées des points dans un fichier, et ensuite, j'utilise tikz pour faire le graphique et l'intégrer dans mon document LaTeX (on peut lui dire de tracer une courbe passant par les points listés dans un fichier externe).
  • J'avais fait grosso modo comme toi @Guego une fois pour tracer en tikz le flocon de von Koch. J'avais fait calculer à Python la liste des points à relier, qu'il retournait sous forme d'un texte en syntaxe tikz, puis copié-collé le résultat dans le fichier ".tex".
  • Sage est capable de mettre des textes dans la polices standard de LaTeX. Sinon on peut faire une figure sans texte et les mettre après dans un environnement tikz.
  • Joaopa a dit :
    Le problème d'importer des images d'autres logiciels c'est de se retrouver avec des polices de caractères différentes. Ca fait bien moche.
    On peut utiliser matplotlib et sauvegarder au format pgf : https://matplotlib.org/stable/tutorials/text/pgf.html
  • adrien2019
    Modifié (July 2022)
    Merci pour vos réponses !
    J'ai déjà les tracés des figures dans d'autres logiciels, mais je cherche à les reproduire en tex pour avoir un document assez "self contained", et de plus, comme l'a dit @Joaopa , je me retrouve souvent avec des polices différentes ou des caractères plus gros que dans le document tex (et c'est alors toujours un bazar pour trouver le bon "scale" pour remettre les caractères de la figure à la taille exacte.
    Est-ce qu'il existe une instruction "somme" pour pouvoir sommer des fonctions de 1 à un certain nombre sans avoir à les écrire une par une à la main?
  • JavierT
    Modifié (July 2022)
    adrien2019 a dit :
    Est-ce qu'il existe une instruction "somme" pour pouvoir sommer des fonctions de 1 à un certain nombre sans avoir à les écrire une par une à la main?
    Oui avec l'instruction Sum de pstricks-add
    \Sum( <index name>,<start>,<step>,<end>,<function> )
    soit ici pour tracer entre -10 et 10
    \psplot{-10}{10}{Sum(i,1,10,(-1)^i*cos(x*2^i)/(2^i)}
  • Benoit RIVET
    Modifié (July 2022)
    Exemple : le fichier python
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    sns.set_style("darkgrid")
    plt.ion()
    
    def f(x : float) -> float:
        """
        Paramètre
        ---------
        x : réel
        
        Résultat
        --------
        y : réel
            Valeur approchée de sum (-1)**n*cos(2**n*x)/2**n
        """
        y = sum((-1)**n*np.cos(2**n*x)/2**n for n in range(1, 50))
        return y
    
    plt.figure("Graphe", figsize = [6.4, 4.8])
    X = np.linspace(-np.pi, np.pi, 1 + 2**10)
    Y = f(X)
    plt.plot(X, Y, color = 'C0',
        label = r'$y = \sum_{n = 1}^{+\infty}\frac{(-1)^{n}\cos\left(2^{n}x\right)}{2^{n}}$')
    plt.xlim(-np.pi, np.pi)
    plt.ylim(-0.5, 1)
    plt.legend()
    plt.savefig('graphe.pgf', bbox_inches = 'tight')
    

    Insertion dans un fichier tex (ici pour produire une figure autonome au format pdf avec lualatex et des polices personnalisées) :

    \documentclass[12pt]{standalone}
    \usepackage{tikz, xcolor, fontspec, fouriernc}
    \setsansfont[Scale = 0.92]{texgyreschola}
    
    \begin{document}
    \input{graphe.pgf}
    \end{document}
  • Guego
    Modifié (July 2022)
    Un exemple de ce que je pourrais faire (avec une autre fonction) :
    code python :
    
    import math
    
    f=open("fichier.txt","w")
    
    for i in range(100):
    	x=-1.1+2.2*i/100
    	y=math.erf(x)
    	f.write(str(x)+"\t"+str(y)+"\n")
    
    f.close()
    
    Et ensuite, on fait un joli graphique avec tikz :
    
    \begin{center}
    \definecolor{gris}{rgb}{0.75,0.75,0.75}
    \begin{tikzpicture}[x=4.0cm,y=4.0cm]
    % Quadrillage
    \draw[color=gris,dash pattern=on 2pt off 2pt, xstep=2.0cm,ystep=2.0cm] (-1.1,-1.1) grid (1.1,1.1);
    % Axes
    \draw[->,color=black] (-1.1,0) -- (1.1,0);
    \foreach \x in {-1,-0.5,0.5,1}<br>\draw[shift={(\x,0)},color=black] (0pt,2pt) -- (0pt,-2pt) node[below] {\footnotesize $\x$};
    \draw[->,color=black] (0,-1.1) -- (0,1.1);
    \foreach \y in {-1,-0.5,0.5,1}<br>\draw[shift={(0,\y)},color=black] (2pt,0pt) -- (-2pt,0pt) node[left] {\footnotesize $\y$};
    \draw[color=black] (0pt,-10pt) node[right] {\footnotesize $0$};
    % Graphique
    \draw[color=red, smooth] plot file {fichier.txt};
    % Légende
    \draw[color=red] (0.5,0.6) node[rotate=40, scale=0.8]{$y=erf(x)$};
    \end{tikzpicture}
    \end{center}
    Ce qui donne le résultat en pièce jointe

    Édit : je ne comprends pas pourquoi à chaque fois que je poste du code, ça me met tout sur une seule ligne.
    Édit 2 : merci Math Coss
  • Math Coss
    Modifié (July 2022)
    Astuce : cliquer sur le bouton < / > (tout à droite de la barre d'outils) avant de copier-coller entre les balises "code", de sorte que les retours chariot soient pris littéralement et pas transformés en balises "br". Exemple :
    import math
    
    f=open("fichier.txt","w")
    
    for i in range(100):
    	x=-1.1+2.2*i/100
    	y=math.erf(x)
    	f.write(str(x)+"\t"+str(y)+"\n")
    
    f.close()<br>
    et...
    \begin{center}
    \definecolor{gris}{rgb}{0.75,0.75,0.75}
    \begin{tikzpicture}[x=4.0cm,y=4.0cm]
    % Quadrillage
    \draw [color=gris,dash pattern=on 2pt off 2pt, xstep=2.0cm,ystep=2.0cm] (-1.1,-1.1) grid (1.1,1.1);
    % Axes
    \draw[->,color=black] (-1.1,0) -- (1.1,0);
    \foreach \x in {-1,-0.5,0.5,1}
    \draw[shift={(\x,0)},color=black] (0pt,2pt) -- (0pt,-2pt) node[below] {\footnotesize $\x$};
    \draw[->,color=black] (0,-1.1) -- (0,1.1);
    \foreach \y in {-1,-0.5,0.5,1}
    \draw[shift={(0,\y)},color=black] (2pt,0pt) -- (-2pt,0pt) node[left] {\footnotesize $\y$};
    \draw[color=black] (0pt,-10pt) node[right] {\footnotesize $0$};
    \clip(-1.1,-1.1) rectangle (1.1,1.1);
    % Graphique
    \draw[color=red, smooth] plot file {fichier.txt};
    % Légende
    \draw[color=red] (0.5,0.6) node[rotate=40, scale=0.8]{$y=erf(x)$};
    \end{tikzpicture}
    \end{center}

  • [Utilisateur supprimé]
    Modifié (July 2022)
    Vu que notre ami @adrien2019 ne précise pas quel $\TeX$ il utilise, je propose (pour la postérité) une réponse à la fois simple, rapide et efficace à l'aide de ConTeXt et MetaPost (adaptable assez facilement en LaTeX pour ceux qui utilisent lua(la)tex):
    \startTEXpage
      \startluacode
        function MP.phi(t)
            total = 0.0
    
            for n=1,32 do
                total = total + (-1)^n*math.cos(2^n*t)/2^n
            end
    
            return total
        end
      \stopluacode
    
      \startMPcode
        begingroup ;
    
        vardef phi(expr t) = MP.phi(t) enddef ;
        draw function(1, "x", "phi(x)", 0, 10, .001) ;
    
        endgroup ;
      \stopMPcode
    \stopTEXpage
    
    où function s'utilise comme suit:
    function(1, "x", "f(x)", XMIN, XMAX, STEP)

    Pour plus de détails, voir page 238 et suivantes dans le manuel MetaFun.


    De plus, pour peu que soit utilisé ConTeXt LMTX ou luameta(la)tex, il est possible de rendre le tout encore plus "simple" (en quelque sorte car la grande force de luametafun est surtout de permettre la réalisation de différentes sortes de graphes: 2D, 3D, histogrammes, surfaces... voir dans le manuel dont le lien est plus bas):
    \startTEXpage
      \startluacode
        function MP.phi(t)
            total = 0.0
    
            for n=1,32 do
                total = total + (-1)^n*math.cos(2^n*t)/2^n
            end
    
            return total
        end
      \stopluacode
    
      \startMPcode
        draw lmt_function [
          xmin = 0,  xmax = 10, xstep = .001,
          ymin = -1, ymax = 1,
    
          code = "MP.phi(x)",
        ];
      \stopMPcode
    \stopTEXpage
    
    Pour en savoir plus, voir le manuel luametafun.

  • MetaFun, c'est le fun ?
  • [Utilisateur supprimé]
    Modifié (July 2022)
    Je trouve que les morceaux de code que j’ai posté le prouve bien, oui. En tout cas comparé à ces hacks du démon qu’il faut réaliser avec TikZ. :)
    Attention toutefois. TikZ a aussi ses avantages, ainsi selon les scénarios celui-ci sera peut-être plus simple d’utilisation que MetaPost/MetaFun, même si en général ils sont tout aussi puissant l’un que l’autre et permettent de faire à peu près les mêmes choses.
  • adrien2019
    Modifié (July 2022)
    Merci pour vos réponses !
    J'essaye de trouver des solutions utilisant exclusivement Tikz (sans importer de graphe réalisé à l'aide d'un logiciel extérieur). J'ai importé le package pstricks-add, mais je n'ai pas réussi à utiliser la fonction "sum" (sans doute parce que je n'ai pas encore lu tout le manuel d'utilisation du package, et que je ne sais donc pas quelle(s) balise(s) mettre autour de la fonction). J'ai tenté d'écrire "à la main" les 20 premières fonctions, mais j'ai assez vite reçu un message d'erreur me disant "dimension too large". Voici le code (sans doute non minimal, puisque j'ai laissé un certain nombre de packages que j'utilise d'habitude et qui ne sont peut-être pas tous utiles ici):
    \documentclass[a4paper,8pt]{article}
    \usepackage[utf8]{inputenc}
    \usepackage[T1]{fontenc}
    \usepackage[french]{babel}
    \usepackage{amsfonts}
    \usepackage{amssymb}
    \usepackage{caption}
    \usepackage{graphicx}
    \usepackage{pstricks}
    \usepackage{pstricks-add}
    \usepackage{tikz}
    \usepackage{amsthm}
    \usepackage{mathtools}
    \usepackage{nameref}
    \usepackage{hyperref}
    \usepackage{xcolor}
    \usepackage{tikz}
    \usetikzlibrary{decorations.pathreplacing, calligraphy, tikzmark, matrix, fit, positioning,calc}
    \usepackage[left=2.8cm,right=2.9cm,margin=2cm]{geometry}
    \setlength{\parindent}{0pt}
    \begin{document}
    
    \begin{figure}\centering
    \begin{tikzpicture}
    \draw[step=1cm, gray, very thin] (-6, -3) (6, 3);
    \draw[very thick, ->] (-6,0) -- (6,0) node[below left]{$x$};
    \draw[very thick, ->] (0,-3) -- (0,3) node[left]{$y$};
    
    \draw[blue, very thick] [domain=-6:6,samples=600] plot (\x,{cos(\x r) - cos(2*\x r)/2 + cos(4*\x r)/4 - cos (8*\x r)/8 + cos (16*\x r)/16 - cos (32*\x r)/32});
    
    \end{tikzpicture} \captionsetup{labelformat=empty} \caption{Graphe de $\varphi$}\vspace{-1em} \end{figure} \end{document}
    Tex m'arrête à $2^n = 32$... quelqu'un sait-il comment aller plus loin, ou quelqu'un peut-il m'indiquer comment utiliser la fonction sum de @JavierT à l'intérieur de ce document ?
    Je vais également essayer de lire la documentation de MetaFun indiquée par @dp (j'utilise TexMaker, "compilation rapide" si c'était sa question).
  • [Utilisateur supprimé]
    Modifié (July 2022)
    Je n'ai pas spécialement posé de question. C'est plus simplement que tu n'as pas précisé si tu utilises Plain TeX, Eplain, LaTeX ou ConTeXt, qui (grossièrement) bien qu'ayant tous pour objectif de produire des documents hautement qualitatifs, emploient des approches légèrement différentes (bien qu'étant tous basés sur Plain TeX de Donald E. Knuth).
    Dans mon cas, je t'ai proposé une solution qui utilise ConTeXt (et c'est "natif", donc ça fonctionne out-of-the-box -- oui, ça veut dire qu'il n'y a pas à se prendre la tête avec les paquets comme avec LaTeX.) qui ne fonctionnera pas sur LaTeX, tout du moins avec pdfLaTeX ou XeLaTeX par exemple. En revanche, ça devrait fonctionner avec des adaptations sur lua(la)TeX avec (notamment) les packages luacode et luamplib. Il est aussi possible de générer les fichiers "à côté" avec le binaire mpost une fois ConTeXt installé.
    Bref, plein solutions s'offrent à toi. Il ne te reste plus qu'à choisir celle qui répond le mieux à tes besoins.
  • [Utilisateur supprimé]
    Modifié (July 2022)
    Je me dis que ça pourrait être intéressant de montrer ce qu'il est possible de faire simplement et rapidement dans ce cas de figure avec ConTeXt et MetaPost [voir ce document pour commencer avec ConTeXt et ce document pour commencer avec MetaPost (il existe une version française de ce dernier mais ayant 20 ans d'age)].

    Commençons par réaliser une petite macro qui sera bien utile (écrite plus pour la compréhension que l’efficacité). Cette macro va nous servir à afficher un repère orthonormal $(0, \vec{\imath}, \vec{\jmath})$ où une unité de longueur correspond à $1$ centimètre.
    def xyaxis(expr xmin, xmax, ymin, ymax) =
      path x, y ;
      x := (xmin*cm, 0cm) -- (xmax*cm, 0cm) ;
      y := (0cm, ymin*cm) -- (0cm, ymax*cm) ;
    
      % Grille d'arrière plan "grisée"
      drawoptions(dashed withdots withcolor .75white) ;
      for xx = ceiling xmin upto floor xmax: draw y shifted (xx*cm,0) ; endfor
      for yy = ceiling ymin upto floor ymax: draw x shifted (0,yy*cm) ; endfor
    
      % Axes x et y orthonormaux
      drawoptions(withpen pencircle scaled .5pt) ;
      %% origin
      draw textext.ulft("$O$") shifted origin shifted (-0.15cm,0.15cm) ;
      %% x axis
      drawarrow x scaled 1 ; draw textext.rt  ("$x$") shifted (xmax*cm+0.15cm,0cm) ;
      %% y axis
      drawarrow y scaled 1 ; draw textext.top ("$y$") shifted (0cm,ymax*cm+0.15cm) ;
    
      % Graduation
      for xx = ceiling xmin upto floor xmax:
        if     xx=xmax:
        elseif xx=0:
        else: draw (xx*cm, 0cm) -- (xx*cm, -0.15cm) ;
              draw textext.bot(xx) shifted (xx*cm,-0.30cm) ;
        fi
      endfor
      for yy = ceiling ymin upto floor ymax:
        if     yy=ymax:
        elseif yy=0:
        else: draw (0cm,yy*cm) -- (-0.15cm, yy*cm) ;
              draw textext.lft(yy) shifted (-0.30cm,yy*cm) ;
        fi
      endfor
    enddef ;
    
    Après avoir "importé" la macro (depuis un autre fichier ou simplement copier-coller), on réécrit mon code précédent (avec quelques modifications).
    On commence donc au début par créer la fonction $\varphi$ en lua. Pourquoi lua ? Eh bien, parce que ConTeXt utilise LuaTeX qui permet (parmi tant d'autres choses [voir les manuels de luatex et de luametatex]) de gérer du code écrit en lua. Ici on s'en sert pour générer les valeurs numériques d'une fonction mais on pourrait tout aussi bien s'en servir pour diagonaliser une matrice ou encore effectuer les divisions euclidiennes de polynômes. En réalité, il est possible de l'utiliser, notamment, pour automatiser tout ce que vous pouvez imaginer vouloir automatiser dans vos documents $\TeX$ [voir le manuel ConTeXt Lua Documents].
    S'en suit la génération du graphe. On commence évidement par afficher notre repère orthonormal à l'aide de la macro précédente. Pour ce qui est du graphe de $\varphi: t \mapsto \sum\limits_{n=1}^{+ \infty} \frac{(-1)^n \cos (2^n t)}{2^n}$ en lui-même, j'ai été assez gourmand en ressources ici, étant donné que j'ai décidé de générer deux fois plusieurs valeurs $\varphi(x)$ pour $x\in[-6,6]$. Ça fonctionne bien et rapidement ici car c'est la seule fonction affichée mais dans les faits avec un document plus long, comprenant plusieurs dizaines ou centaines de pages, il serait beaucoup judicieux de ne pas faire ça et générer les parties en pointillées uniquement pour les valeurs extrêmes de l'intervalle.
    \startTEXpage
      \startluacode
        function MP.phi(t)
            total = 0.0
            for n=1,32 do    total = total + (-1)^n*math.cos(2^n*t)/2^n    end
            return total
        end
      \stopluacode
    
    
      \startMPcode
        begingroup ;
    
        % On utilise la macro précédemment introduite pour
        % afficher le repère orthonormal dans lequel la
        % fonction va prendre place.
        xyaxis(-7.5,7.5,-1.5,1.5) ;
    
        % On "importe" la fonction lua dans MetaPost.
        % Étape importante car `function` ne semble pas
        % accepter directement des fonctions lua.
        vardef phi(expr t) = MP.phi(t) enddef ;
    
        draw function(1, "x", "phi(x)", -7.5, 7.5, .001)      % On commence par générer une première fois la fonction
            scaled 1cm                                        % où 1 "unité" vaut 1cm
            dashed evenly                                     % en pointillées
            withpen pencircle scaled .15pt                    % avec une courbe pas trop grosse
            withcolor darkred ;                               % de couleur rouge
    
        draw function(1, "x", "phi(x)", -6, 6, .001)          % On génère de nouveau la fonction
            scaled 1cm                                        % où 1 "unité" vaut 1cm
            withpen pencircle scaled .15pt                    % avec une courbe pas trop grosse
            withcolor darkred ;                               % de couleur rouge
    
        endgroup ;
      \stopMPcode
    \stopTEXpage
    
    Le tout permet finalement d'obtenir cette représentation de la fonction $\varphi: t \mapsto \sum\limits_{n=1}^{+ \infty} \frac{(-1)^n \cos (2^n t)}{2^n}$:

    Enfin, évidement, ça fonctionne affreusement bien et en changeant simplement l'expression de $\varphi$ dans le code lua, par exemple pour $\phi: t \mapsto \sum\limits_{n=1}^{+ \infty} \frac{(-1)^n \textcolor{red}{\sin} (2^n t)}{2^n}$, on obtient très facilement (après une simple recompilation):
    Je joins en pièce jointe le fichier minimal tel qu'il devrait être écrit pour donner ce rendu. À compiler avec ConTeXt comme suit
    $ context phi.tex
    le résultat se trouvera alors dans le fichier phi.pdf.


Connectez-vous ou Inscrivez-vous pour répondre.