2

My intent is to plot the first derivative of a function, following this approach:

f'(x) = (f(x + dx) - f(x))/dx.

Mine is a bit of a long function, so I would like to know if it is possible to save it in a macro (or something similar) and then recall it later as

\pgfmathsetmacro{\f}{\A * \b * ((exp(-\b*(x))/(\p-\b)) + (exp(-\p*(x))/(\b-\p)))}; 
produces the error 
*! Package PGF Math Error: Unknown function `x' (in '1* 0.7* ((exp(-0.7*(x))/(0.*

Minimal "working" example

\documentclass[12pt]{article}
\usepackage{tikz}
\usepackage{pgfplots}
\begin{document}
\begin{figure}
\centering
\begin{tikzpicture}
    \pgfmathsetmacro{\b}{0.7};
    \pgfmathsetmacro{\p}{0.4};
    \pgfmathsetmacro{\A}{1};
    %\pgfmathsetmacro{\f}{\A * \b * ((exp(-\b*(x))/(\p-\b)) + (exp(-\p*(x))/(\b-\p)))}; <-- failing attempt
    \begin{axis}[
    width=8cm, height=6cm,
    xmin=0.003, xmax=10,
    ymin=-0.2, ymax=0.8, 
    xtick=\empty, ytick=\empty,
    axis lines=middle,
    x label style={at={(axis cs:10,0)},anchor=west},
    y label style={at={(0,1)},above},
    xlabel={$t$}, ylabel={$r$},
    clip=false
    ]              
    \addplot [red, very thick, samples=100, smooth,domain=0.003:10]
        { \A * \b * ((exp(-\b*(x))/(\p-\b)) + (exp(-\p*(x))/(\b-\p))) }; %
    \end{axis}
    \end{tikzpicture}
\end{figure}
\end{document}

I wish could calculate derivative as

(\A * \b * ((exp(-\b*(x+0.01))/(\p-\b)) + (exp(-\p*(x+0.01))/(\b-\p))) - \f)/0.01

Or, were possible, with a simpler method

Yossi Gil
  • 15,951
user3713179
  • 635
  • 2
  • 11
  • There is a way of fake the derivative https://tex.stackexchange.com/a/193365/140722 – Black Mild Jul 21 '22 at 06:21
  • Also see https://tex.stackexchange.com/questions/540650/plot-construction-for-a-velocity-vs-position-graph-interpolation-for-phase-diag – Black Mild Jul 21 '22 at 06:23

1 Answers1

3

First, a note on this:

\pgfmathsetmacro{\b}{0.7};
\pgfmathsetmacro{\p}{0.4};
\pgfmathsetmacro{\A}{1};

There should be no ; here because these are not TikZ nor pgfplots statements like \path, \draw, \addplot, etc. These semicolons are the cause of the following error message (3 times) in the terminal output of TeX:

Missing character: There is no ; in font nullfont!

Also, while using \pgfmathsetmacro is correct, it is heavy machinery for simple constants written directly in decimal form. I propose to use \def for these, which will be equivalent but faster.

(Beware of single-letter control sequences though... macros like \c or \i have a standard definition in LaTeX; in certain circumstances, you could experience problems when overridding them. That said, local def. + mostly-computation-code like here is probably “not too unsafe”.).

pgfmath's declare function key

The tool you are looking for is /pgf/declare function (see Customizing the Mathematical Engine in the TikZ & PGF manual).

Using this tool and a dx step of 0.1, your idea gives:

\documentclass[tikz,border=2mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\begin{document} \begin{tikzpicture}[ declare function = { f(\x) = \A * \b * ((exp(-\b(\x))/(\p-\b)) + (exp(-\p(\x))/(\b-\p)));}, ] \def\b{0.7} \def\p{0.4} \def\A{1} \begin{axis}[ width=8cm, height=6cm, ymin=-0.15, ymax=0.7, axis lines=middle, x label style={at={(axis cs:10,0)}, anchor=west}, y label style={at={(0,1)}, above}, xlabel={$t$}, ylabel={$r$}, ] \addplot[red, very thick, samples=100, smooth, domain=0.003:10] { f(x) }; \addplot[blue, very thick, samples=100, smooth, domain=0.003:10] { (f(x + 0.1) - f(x)) / 0.1 }; \end{axis} \end{tikzpicture} \end{document}

enter image description here

If one uses the 0.01 step as in your question, the low accuracy of computations gives less pleasant results:

enter image description here

Using \fpeval for better numerical accuracy

One can obtain good results with the 0.01 step using \fpeval (not from pgfmath, and in particular doesn't use the fpu library). This technique is slower and it is more cumbersome to give a usable expression of the “derivative” this way, but the result looks good:

\documentclass[tikz,border=2mm]{standalone}
\usepackage{xfp}   % not needed if the LaTeX kernel is from 2022-06-01 or later
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\pgfmathdeclarefunction{g}{1}{% \begingroup \pgfmathfloattofixed{#1}% \let\x\pgfmathresult \edef\pgfmathresult{% \fpeval{ (\A * \b * ((exp(-\b(\x + 0.01))/(\p-\b)) + (exp(-\p(\x + 0.01))/(\b-\p))) - (\A * \b * ((exp(-\b(\x))/(\p-\b)) + (exp(-\p(\x))/(\b-\p))))) / 0.01}% }% \pgfmathsmuggle\pgfmathresult \endgroup }

\begin{document} \begin{tikzpicture}[ declare function = { f(\x) = \A * \b * ((exp(-\b(\x))/(\p-\b)) + (exp(-\p(\x))/(\b-\p)));}, ] \def\b{0.7} \def\p{0.4} \def\A{1} \begin{axis}[ width=8cm, height=6cm, ymin=-0.15, ymax=0.7, axis lines=middle, x label style={at={(axis cs:10,0)}, anchor=west}, y label style={at={(0,1)}, above}, xlabel={$t$}, ylabel={$r$}, ] \addplot[red, very thick, samples=100, smooth, domain=0.003:10] { f(x) }; \addplot[blue, very thick, samples=100, smooth, domain=0.003:10] { g(x) }; \end{axis} \end{tikzpicture} \end{document}

enter image description here

frougon
  • 24,283
  • 1
  • 32
  • 55
  • Nice! One last question: can I declare more than one function? – user3713179 Jul 20 '22 at 19:42
  • Yes, this is shown in the manual I pointed you to: you simply need to put a ; at the end of each definition: for instance, declare function = {f(\x) = ... ; g(\x) = ... ;}. Unrelated: in my latest edit, I've added an accurate solution with step 0.01. – frougon Jul 20 '22 at 19:47