Issue rendering a latex formula

Dear all,

this is my first post here, but I’m not new to python neither to matplotlib, however today I found an issue that I’m not able to fix by myself.

The minimal example is:

import matplotlib
from matplotlib import pylab as plt

matplotlib.rc('text', usetex = True)
matplotlib.rc('font', **{'family' : "sans-serif"})
params= {'text.latex.preamble' : [r'\usepackage{amsmath}']}
plt.rcParams.update(params)


formula = r"""\begin{equation}
    \begin{aligned}
        y = & +0.00000 \cdot 1  \\ 
            & -132.08690 \cdot x_0  \\ 
            & -8.49824 \cdot x_1  \\ 
            & +1.23479 \cdot x_0^2  \\ 
            & +5.34608 \cdot x_0 \cdot x_1  \\ 
            & -45.48402 \cdot x_1^2  \\ 
            & -0.00381 \cdot x_0^3  \\ 
            & -0.02472 \cdot x_0^2 \cdot x_1  \\ 
            & -0.05395 \cdot x_0 \cdot x_1^2  \\ 
            & +2.86417 \cdot x_1^3
    \end{aligned}
\end{equation}"""

figc, ax = plt.subplots(figsize=(12, 9), tight_layout=True)
ax.text(
    0.5,
    0.5,
    formula,
    horizontalalignment="right",
    verticalalignment="center",
    transform=ax.transAxes,
    fontsize=8,
)
figc.savefig("test1.png")

If I try to execute the code it fails with the following error:

/usr/lib/python3.9/_collections_abc.py:940: MatplotlibDeprecationWarning: Support for setting the 'text.latex.preamble' or 'pgf.preamble' rcParam to a list of strings is deprecated since 3.3 and will be removed two minor releases later; set it to a single string instead.
  self[key] = other[key]
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/matplotlib/texmanager.py", line 275, in _run_checked_subprocess
    report = subprocess.check_output(command,
  File "/usr/lib/python3.9/subprocess.py", line 424, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
  File "/usr/lib/python3.9/subprocess.py", line 528, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['latex', '-interaction=nonstopmode', '--halt-on-error', '/home/pietro/.cache/matplotlib/tex.cache/14e4c18300cd7f9c6f074f9a35f6827c.tex']' returned non-zero exit status 1.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/pietro/src/synapsees/lab/test1.py", line 42, in <module>
    figc.savefig("test1.png")
  File "/usr/lib/python3.9/site-packages/matplotlib/figure.py", line 2311, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/usr/lib/python3.9/site-packages/matplotlib/backends/backend_qt5agg.py", line 81, in print_figure
    super().print_figure(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/matplotlib/backend_bases.py", line 2210, in print_figure
    result = print_method(
  File "/usr/lib/python3.9/site-packages/matplotlib/backend_bases.py", line 1639, in wrapper
    return func(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py", line 509, in print_png
    FigureCanvasAgg.draw(self)
  File "/usr/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py", line 407, in draw
    self.figure.draw(self.renderer)
  File "/usr/lib/python3.9/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/lib/python3.9/site-packages/matplotlib/figure.py", line 1857, in draw
    self.tight_layout(**self._tight_parameters)
  File "/usr/lib/python3.9/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
    return func(*inner_args, **inner_kwargs)
  File "/usr/lib/python3.9/site-packages/matplotlib/figure.py", line 2613, in tight_layout
    kwargs = get_tight_layout_figure(
  File "/usr/lib/python3.9/site-packages/matplotlib/tight_layout.py", line 303, in get_tight_layout_figure
    kwargs = auto_adjust_subplotpars(fig, renderer,
  File "/usr/lib/python3.9/site-packages/matplotlib/tight_layout.py", line 84, in auto_adjust_subplotpars
    bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
  File "/usr/lib/python3.9/site-packages/matplotlib/axes/_base.py", line 4200, in get_tightbbox
    bbox = a.get_tightbbox(renderer)
  File "/usr/lib/python3.9/site-packages/matplotlib/artist.py", line 278, in get_tightbbox
    bbox = self.get_window_extent(renderer)
  File "/usr/lib/python3.9/site-packages/matplotlib/text.py", line 902, in get_window_extent
    bbox, info, descent = self._get_layout(self._renderer)
  File "/usr/lib/python3.9/site-packages/matplotlib/text.py", line 295, in _get_layout
    w, h, d = renderer.get_text_width_height_descent(
  File "/usr/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py", line 227, in get_text_width_height_descent
    w, h, d = texmanager.get_text_width_height_descent(
  File "/usr/lib/python3.9/site-packages/matplotlib/texmanager.py", line 423, in get_text_width_height_descent
    dvifile = self.make_dvi(tex, fontsize)
  File "/usr/lib/python3.9/site-packages/matplotlib/texmanager.py", line 309, in make_dvi
    self._run_checked_subprocess(
  File "/usr/lib/python3.9/site-packages/matplotlib/texmanager.py", line 283, in _run_checked_subprocess
    raise RuntimeError(
RuntimeError: latex was not able to process the following string:
b'\\\\begin{equation}'

Here is the full report generated by latex:
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/Arch Linux) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(/home/pietro/.cache/matplotlib/tex.cache/14e4c18300cd7f9c6f074f9a35f6827c.tex
LaTeX2e <2020-10-01> patch level 2
L3 programming layer <2020-12-03> xparse <2020-03-03>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texmf-dist/tex/latex/type1cm/type1cm.sty)
(/usr/share/texmf-dist/tex/latex/cm-super/type1ec.sty
(/usr/share/texmf-dist/tex/latex/base/t1cmr.fd))
(/usr/share/texmf-dist/tex/latex/base/inputenc.sty)
(/usr/share/texmf-dist/tex/latex/geometry/geometry.sty
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty)
(/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty
(/usr/share/texmf-dist/tex/generic/iftex/iftex.sty))

Package geometry Warning: Over-specification in `h'-direction.
    `width' (5058.9pt) is ignored.


Package geometry Warning: Over-specification in `v'-direction.
    `height' (5058.9pt) is ignored.

) (/usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty
For additional information on amsmath, use the `?' option.
(/usr/share/texmf-dist/tex/latex/amsmath/amstext.sty
(/usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty))
(/usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty)
(/usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty))
(/usr/share/texmf-dist/tex/latex/base/textcomp.sty)
(/usr/share/texmf-dist/tex/latex/l3backend/l3backend-dvips.def)
No file 14e4c18300cd7f9c6f074f9a35f6827c.aux.
*geometry* driver: auto-detecting
*geometry* detected driver: dvips
! Extra }, or forgotten $.
l.19 {\sffamily \begin{equation}}
                                 
No pages of output.

Note that if I compile the following latex document, it works without problems:

\documentclass{article}
\usepackage{amsmath}

\begin{document}

\begin{equation}
	\begin{aligned}
		y = & +0.00000 \cdot 1  \\ 
		& -132.08690 \cdot x_0  \\ 
		& -8.49824 \cdot x_1  \\ 
		& +1.23479 \cdot x_0^2  \\ 
		& +5.34608 \cdot x_0 \cdot x_1  \\ 
		& -45.48402 \cdot x_1^2  \\ 
		& -0.00381 \cdot x_0^3  \\ 
		& -0.02472 \cdot x_0^2 \cdot x_1  \\ 
		& -0.05395 \cdot x_0 \cdot x_1^2  \\ 
		& +2.86417 \cdot x_1^3
	\end{aligned}
\end{equation}

\end{document}

I’ve tried several combinations with r"", bytes, simple unicode, but I did not succed.
Any hint?

TL;DR

  • there is a workaround (no equation number, no '\n' in the input
  • this exposed at least 3 bugs

When we process the input string we do so line-at-a-time which which results in us passing a file like


\documentclass{article}
\newcommand{\mathdefault}[1]{#1}
\usepackage{type1cm}
\usepackage{type1ec}
\usepackage[utf8]{inputenc}
\DeclareUnicodeCharacter{2212}{\ensuremath{-}}
\usepackage[papersize=72in, margin=1in]{geometry}
['\\usepackage{amsmath}']
\makeatletter\@ifpackageloaded{textcomp}{}{\usepackage{textcomp}}\makeatother
\pagestyle{empty}
\begin{document}
% The empty hbox ensures that a page is printed even for empty inputs, except
% when using psfrag which gets confused by it.
\fontsize{8.000000}{10.000000}%
\ifdefined\psfrag\else\hbox{}\fi%
{\sffamily \begin{equation}}
\end{document}

off to LaTeX which is clearly invalid. I think the '\\\\begin' in the error message is a bit of a red-herring (which is the result of a couple levels of escaping not printing well).

If I change your input to

# note this takes a string not a list, that seems like a validation bug!
params= {'text.latex.preamble' : r'\usepackage{amsmath}'}
plt.rcParams.update(params)

# this is one line with no \n
formula = ''.join(r"""\begin{equation}
    \begin{aligned}
        y = & +0.00000 \cdot 1  \\ 
            & -132.08690 \cdot x_0  \\ 
            & -8.49824 \cdot x_1  \\ 
            & +1.23479 \cdot x_0^2  \\ 
            & +5.34608 \cdot x_0 \cdot x_1  \\ 
            & -45.48402 \cdot x_1^2  \\ 
            & -0.00381 \cdot x_0^3  \\ 
            & -0.02472 \cdot x_0^2 \cdot x_1  \\ 
            & -0.05395 \cdot x_0 \cdot x_1^2  \\ 
            & +2.86417 \cdot x_1^3
    \end{aligned}
\end{equation}""".split('\n'))


I get it to run without error, but all I see in the output is ‘(1)’

The generated latex is


\documentclass{article}
\newcommand{\mathdefault}[1]{#1}
\usepackage{type1cm}
\usepackage{type1ec}
\usepackage[utf8]{inputenc}
\DeclareUnicodeCharacter{2212}{\ensuremath{-}}
\usepackage[papersize=72in, margin=1in]{geometry}
\usepackage{amsmath}
\makeatletter\@ifpackageloaded{textcomp}{}{\usepackage{textcomp}}\makeatother
\pagestyle{empty}
\begin{document}
% The empty hbox ensures that a page is printed even for empty inputs, except
% when using psfrag which gets confused by it.
\fontsize{8.000000}{10.000000}%
\ifdefined\psfrag\else\hbox{}\fi%
{\sffamily \begin{equation}    \begin{aligned}        y = & +0.00000 \cdot 1  \\            & -132.08690 \cdot x_0  \\            & -8.49824 \cdot x_1  \\            & +1.23479 \cdot x_0^2  \\            & +5.34608 \cdot x_0 \cdot x_1  \\            & -45.48402 \cdot x_1^2  \\            & -0.00381 \cdot x_0^3  \\            & -0.02472 \cdot x_0^2 \cdot x_1  \\            & -0.05395 \cdot x_0 \cdot x_1^2  \\            & +2.86417 \cdot x_1^3    \end{aligned}\end{equation}}
\end{document}

which does render correctly with pdflatex.

If I change to

formula = ''.join(r"""\begin{equation*}
    \begin{aligned}
        y = & +0.00000 \cdot 1  \\ 
            & -132.08690 \cdot x_0  \\ 
            & -8.49824 \cdot x_1  \\ 
            & +1.23479 \cdot x_0^2  \\ 
            & +5.34608 \cdot x_0 \cdot x_1  \\ 
            & -45.48402 \cdot x_1^2  \\ 
            & -0.00381 \cdot x_0^3  \\ 
            & -0.02472 \cdot x_0^2 \cdot x_1  \\ 
            & -0.05395 \cdot x_0 \cdot x_1^2  \\ 
            & +2.86417 \cdot x_1^3
    \end{aligned}
\end{equation*}""".split('\n'))


it renders!


I think this has exposed 3 bug:

  1. we take a list in rcparams when we should only take a string (or we miss handle the list)
  2. we need to document that all latex must be forced to a single line (no \n)
  3. something is wrong with processing output with equation numbers

The equation number issue arises because we are rendering the equation on a tex document with an gigantic page size (as we don’t know a priori what size the equation will be), but that means the equation number gets pushed very very far to the right wrt. the rest of the equation, and things simply get clipped out of the figure. I guess the “easy” way out is to add a call to \newgeometry in rcParams["text.latex.preamble"] to fix the tex rendering canvas size…