16

Is there a way to easily compute the erf function (or the cumulative distribution function of the normal law) in LaTeX?

Currently, I use pgf to make computation, but I did not find a way to compute erf using pgf.

I would be happy to use any package that is available to compute erf, or any custom solution to compute that function.

Xoff
  • 1,691

7 Answers7

22

Based on this answer.

\documentclass{standalone}
\usepackage{tikz}
\makeatletter
\pgfmathdeclarefunction{erf}{1}{%
  \begingroup
    \pgfmathparse{#1 > 0 ? 1 : -1}%
    \edef\sign{\pgfmathresult}%
    \pgfmathparse{abs(#1)}%
    \edef\x{\pgfmathresult}%
    \pgfmathparse{1/(1+0.3275911*\x)}%
    \edef\t{\pgfmathresult}%
    \pgfmathparse{%
      1 - (((((1.061405429*\t -1.453152027)*\t) + 1.421413741)*\t 
      -0.284496736)*\t + 0.254829592)*\t*exp(-(\x*\x))}%
    \edef\y{\pgfmathresult}%
    \pgfmathparse{(\sign)*\y}%
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup
}
\makeatother
\begin{document}
\begin{tikzpicture}[yscale = 3]
  \draw[very thick,->] (-5,0) -- node[at end,below] {$x$}(5,0);
  \draw[very thick,->] (0,-1) -- node[below left] {$0$} node[at end,
  left] {$erf(x)$} (0,1);
  \draw[red,thick] plot[domain=-5:5,samples=200] (\x,{erf(\x)});
\end{tikzpicture}
\end{document}

enter image description here

cjorssen
  • 10,032
  • 4
  • 36
  • 126
16

Using the same approximation idea as cjorssen (I tried the Taylor series as Qrrbrbirlbel suggested but it's pretty hopeless to get a decent approximation this way) I rewrote the function without using low-level PGF. Because we have so many 2D plots here already, I'll just use my 3D plot that I had already.

\documentclass{standalone}
\usepackage{pgfplots}
\usepackage{tikz}
\pgfplotsset{
colormap={bluewhite}{ color(0cm)=(rgb:red,18;green,64;blue,171); color(1cm)=(white)}
}
\begin{document}
\begin{tikzpicture}[
    declare function={erf(\x)=%
      (1+(e^(-(\x*\x))*(-265.057+abs(\x)*(-135.065+abs(\x)%
      *(-59.646+(-6.84727-0.777889*abs(\x))*abs(\x)))))%
      /(3.05259+abs(\x))^5)*(\x>0?1:-1);},
    declare function={erf2(\x,\y)=erf(\x)+erf(\y);}
]
\begin{axis}[
    small,
    colormap name=bluewhite,
    width=\textwidth,
    enlargelimits=false,
    grid=major,
    domain=-3:3,
    y domain=-3:3,
    samples=33,
    unit vector ratio*=1 1 1,
    view={20}{20},
    colorbar,
    colorbar style={
        at={(1,-.15)},
        anchor=south west,
        height=0.25*\pgfkeysvalueof{/pgfplots/parent axis height},
    }
]
\addplot3 [surf,shader=faceted] {erf2(x,y)};
\end{axis}
\end{tikzpicture}
\end{document}

3D plot of erf(x)+erf(y)

The approximation has a maximum error of 1.5·10-7 (source).

Thanks to Jake for spotting and fixing the wrong syntax I first had in this code.

Christian
  • 19,238
12

For precise values, I recommend externalizing the calculation, here gnuplot is used.

Code (needs --shell-escape enabled)

\documentclass{article}
\usepackage{amsmath,pgfmath,pgffor}
\makeatletter
\def\qrr@split@result#1 #2\@qrr@split@result{\edef\erfInput{#1}\edef\erfResult{#2}}
\newcommand*{\gnuplotErf}[2][\jobname.eval]{%
    \immediate\write18{gnuplot -e "set print '#1'; print #2, erf(#2);"}%
    \everyeof{\noexpand}
    \edef\qrr@temp{\@@input #1 }%
    \expandafter\qrr@split@result\qrr@temp\@qrr@split@result
}
\makeatother
\DeclareMathOperator{\erf}{erf}
\begin{document}
\foreach \x in {-50,...,50}{%
\pgfmathparse{\x/50}%
\gnuplotErf{\x/50.}%
$ x = \pgfmathresult = \erfInput, \erf(x) = \erfResult$\par
}
\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • 3
    Here is a comparison of cjorssen’s (good!) answer and gnuplot’s values – Qrrbrbirlbel Apr 07 '13 at 20:36
  • I get a strange error: runsystem(gnuplot -e "set print 'erf.eval'; print -50/50., erf(-50/50.);")...quotation error in system command (tl2012, debian)? Never had it before. – cjorssen Apr 07 '13 at 20:55
  • 2
    It's fixed in the CVS version but pgfmath,pgffor is not enough to compile in v2.10 official release. One needs to load pgf,pgffor to get everything otherwise pgfkeys etc. don't work. There was a dependency leak which was fixed some time ago in the CVS version. – percusse Apr 07 '13 at 21:31
8

A little late, but here's a solution using the sagetex package, which gives you the power of the (free) computer algebra system known as Sage. Sage has an error function command which, according to the documentation is 1-erf. This code

\documentclass{article}
\usepackage{sagetex}
\begin{document}
\pagestyle{empty}
Sage has an error function command that's equal to $1-\mbox{erf}$. Here's     $\sage{error_fcn(2)}$ and 
$\sage{1-error_fcn(2)}$. There is an erf command but it doesn't seem to be   working properly 
in my older version of Sage. The formatting in Sage goes like this:   $\sage{n(error_fcn(2),digits=6)}$ 
and $\sage{n((1-error_fcn(2)),digits=11)}$. 
\begin{sagesilent}
g(x)=erf(x)
q=plot(g,x,-3,3,color='red')
\end{sagesilent}

This is a plot of $\mbox{erf}(x)$. \begin{center} \sageplot[scale=.35]{q} \end{center} \end{document}

creates this output: enter image description here

DJP
  • 12,451
  • I did not find a way to get this package with texlive. Did I miss something ? – Xoff Apr 10 '13 at 16:00
  • You need to install Sage on your computer. The download link is here. Once you installed you just need to copy sagetex.sty to the place where you latex your document: see here and here for more details. Sagetex and TexLive mentioned at 2nd "here" link. – DJP Apr 10 '13 at 22:51
7

Error function erf(x) computation and figure anatomy (axes,legends and labels) have been rendered in three approaches.

  1. Fully gnuplot
  2. pgfplots invokes gnuplot
  3. Fully Matlab

Already there are good answers for example by Qrrbrbirlbel and cjorssen, both exploit pgfmath at macro level.

1. Fully gnuplot

Error function erf(x) computation in gnuplot with axes, legends and labels rendered in gnuplot epslatex terminal. The gnuplot terminal output is embedded automatically with gnuplottex package. terminal=pdf does not render Math labels hence epslatex terminal was used.

\documentclass[preview=true,12pt]{standalone}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{gnuplottex}
\begin{document} 
\begin{gnuplot}[terminal=epslatex,terminaloptions=color]
  set grid
  set size square
  set key left 
  set title 'Error function in gnuplot  $ erf(x) = \frac{2}{\sqrt{\pi}} \int_{0}^{x}e^{-t^{2}}\, dt$'
  set samples 50
  set xlabel "$x$"
  set ylabel "$erf(x)$"
  plot [-3:3] [-1:1] erf(x) title 'gnuplot' linetype 1 linewidth 3
\end{gnuplot}
\end{document}

1) gnuplot output figure enter image description here

2. pgfplots invokes gnuplot

Error function erf(x) computation in gnuplot invoked by pgfplots and axes, legends, labels are rendered by pgfplots

\documentclass[preview=true,12pt]{standalone}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{pgfplots}
\pgfplotsset{compat=1.8}
\begin{document} 
\begin{tikzpicture}
\begin{axis}[xlabel=$x$,ylabel=$erf(x)$,title= {Error function in pgfplots $erf(x)=\frac{2}{\sqrt{\pi}}\int_{0}^{x}e^{-t^{2}}\, dt$},legend style={draw=none},legend pos=north west,grid=major,enlargelimits=false]
\addplot [domain=-3:3,samples=50,red,no markers] gnuplot[id=erf]{erf(x)};
% Note: \addplot function { gnuplot code } is alias for \addplot gnuplot { gnuplot code };
\legend{pgfplots-gnuplot}
\end{axis}
\end{tikzpicture}
\end{document}

2. pgfplots(gnuplot backend)output figure

enter image description here

3) Fully Matlab

Error function $erf(x)$ computation in Matlab with axes,legends,labels rendered using matlabfrag(psfrag tag based) and mlf2pdf functions.

Note: Fonts are frozen in PDF figure unlike the above approaches, but can be changed in mlf2pdf.m before generating them.

**erf(x) Matlab Script using mlf2pdf(matlabfrag as backend) to generate pdf **

clear all
clc
% Plotting section
    set(0,'DefaultFigureColor','w','DefaultTextFontName','Times','DefaultTextFontSize',12,'DefaultTextFontWeight','bold','DefaultAxesFontName','Times','DefaultAxesFontSize',12,'DefaultAxesFontWeight','bold','DefaultLineLineWidth',2,'DefaultLineMarkerSize',8);

% x and y data
x=linspace(-3,3,50);
y=erf(x);

figure(1);plot(x,y,'r');
grid on
axis([-3 3 -1 1]);
xlabel('$x$','Interpreter','none');
ylabel('$erf(x)$','Interpreter','none');
legend('Matlab');legend('boxoff');
title('Error function in Matlab $erf(x)=\frac{2}{\sqrt{\pi}}\int_{0}^{x}e^{-t^{2}}\, dt$','Interpreter','none');
mlf2pdf(gcf,'error-func-fig');

3. Output Figure enter image description here

gnuplot 4.4,pgfplots 1.8 and pdflatex -shell-escape engine were used.

  • I went ahead with the second implementation here. I had to figure out something very simple but that wasn't working at the beginning. You have to call pdflatex with a special option like this: pdflatex -shell-escape yourfile.tex; also, you need to have gnuplot installed, which I didn't know I didn't have. Other than that. A great solution. – Wilmer E. Henao May 05 '16 at 18:48
4

You can also use the FFI in LuaJITTeX (and LuaTeX ≥ 1.0.3) to access the erf function in libm directly. The performance and accuracy are simply amazing!

\documentclass{article}

\usepackage{amsmath}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}

\directlua{
local ffi = require("ffi")
ffi.cdef[[
double erf(double arg);
]]
}

\pgfmathdeclarefunction{erf}{1}{%
  \edef\pgfmathresult{%
    \directlua{tex.print(ffi.C.erf(\pgfmathfloatvalueof{#1}))}%
  }%
}

\DeclareMathOperator\erf{erf}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
    no marks, samples=101,
    xlabel={$x$},
    ylabel={$\erf(x)$},
    ]
    \addplot {erf(x)};
  \end{axis}
\end{tikzpicture}

\end{document}

enter image description here

Henri Menke
  • 109,596
  • Really nice trick! One naive question: how does luatex/ffi knows where to look for (here libm)? – cjorssen Jan 22 '19 at 15:49
  • 1
    @cjorssen The FFI just resolves symbols like a compiler. Because libm is statically linked into LuaTeX (at least in vanilla TeX Live), you can access it via ffi.C. If the symbol comes from a DSO, you have to load it first using ffi.load and then resolve the symbol name through this handle. You can have a look at these two answers of mine for examples: https://tex.stackexchange.com/a/403794, https://tex.stackexchange.com/a/408014 – Henri Menke Jan 22 '19 at 20:18
  • Hi, can you write it to LuaLatex??? thanks!! – Alejandro Munoz Ossa Jan 24 '21 at 20:38
2

PSTricks solution, using package pst-ode, command \pstODEsolve.

The integral is thought of as an initial value problem and solved (integrated) numerically between 0 and x using RKF45 method with automatic stepsize control.

enter image description here

\documentclass[pstricks,border=5pt,12pt]{standalone}
\usepackage{pst-ode,pst-plot}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% erf
% #1: x
% #2: PS variable for result list
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\erf(#1)#2{%
  \pstODEsolve[algebraicAll]{retVal}{ %`retval' takes initial value and erf(#1)
    2/sqrt(Pi)*y[0] %post-multiply result of integration with 2/sqrt(Pi)
  }{0}{#1}{ % integration interval
    2 % two output points (at t=0 and t=#1)
  }{
    0.0 % initial value at t=0
  }{Euler^(-t^2)} %integrand
  %
  %  initialise, if necessary, empty solution list given as arg #2
  \pstVerb{/#2 where {pop}{/#2 {} def} ifelse}
  %  From `retVal', we throw away result at t=0, and append x (arg #1) and erf(x) to our
  %  solution list (arg #2):
  \pstVerb{/#2 [#2 #1 retVal exch pop] cvx def}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

%solve function integral, 121 plotpoints: -3, -2.95, ..., 2.95, 3
\multido{\nx=-3.00+0.05}{121}{
  \erf(\nx){erf} %result appended to solution list `erf'
}

%plot result
\begin{pspicture}(-1.5,-1)(0.2,6.2)
  \psset{xAxisLabel=$x$,yAxisLabel=$\mathrm{erf}(x)$,xAxisLabelPos={c,-0.3},yAxisLabelPos={-1,c}}
  \begin{psgraph}[axesstyle=frame,Oy=-1,Ox=-3,Dx=1,Dy=0.5](-3,-1)(-3,-1)(3,1){8cm}{6cm}
  \listplot[linecolor=red]{erf}
  \end{psgraph}
\end{pspicture}
\end{document}
AlexG
  • 54,894