mirror of https://github.com/Qiskit/qiskit.git
Add dual of mpl mathtext syntax for latex drawer labels (#3224)
* Add dual of mpl mathtext syntax for latex drawer labels
This commit adds a new feature to the latex drawer that adds support for
a similar syntax to matplotlib's mathtext to labels on the latex drawer.
In matplotlib if you specify a string between two dollar signs (for
example $\gamma$) that string will be parsed with mpl's native TeX
expression parser and that will be used to layout and render the string
per the TeX syntax. This uses the same syntax as mathtext, and enables
users to write raw latex syntax as gate labels between a pair of dollar
signs. All characters outside of the dollar signs are treated as utf8
and get encoded by pylatexenc to create valid latex, however all text
inside the dollar signs get passed verbatim to the output latex. This
enables users to mix and match having their unicode encoded for them and
manually passing latex
Fixes #3171
* Fix lint
* Use jupyter-execute instead of describing diagram
* Revert "Use jupyter-execute instead of describing diagram"
This reverts commit ab7bc18c39
. Running
the code example in jupyter-execute will require a latex distribution
with qcircuit to be installed. That adds a lot of run time to the doc
build and a non-obvious dependency. To avoid this, we'll just rely on
the code example and a description of the output.
This commit is contained in:
parent
a94e8a5b48
commit
fb87e7c2e2
|
@ -22,17 +22,11 @@ import json
|
|||
import math
|
||||
import re
|
||||
|
||||
try:
|
||||
from pylatexenc.latexencode import utf8tolatex
|
||||
|
||||
HAS_PYLATEX = True
|
||||
except ImportError:
|
||||
HAS_PYLATEX = False
|
||||
|
||||
import numpy as np
|
||||
from qiskit.visualization import qcstyle as _qcstyle
|
||||
from qiskit.visualization import exceptions
|
||||
from .tools.pi_check import pi_check
|
||||
from .utils import generate_latex_label
|
||||
|
||||
|
||||
class QCircuitImage:
|
||||
|
@ -63,11 +57,6 @@ class QCircuitImage:
|
|||
Raises:
|
||||
ImportError: If pylatexenc is not installed
|
||||
"""
|
||||
if not HAS_PYLATEX:
|
||||
raise ImportError('The latex and latex_source drawers need '
|
||||
'pylatexenc installed. Run "pip install '
|
||||
'pylatexenc" before using the latex or '
|
||||
'latex_source drawers.')
|
||||
# style sheet
|
||||
self._style = _qcstyle.BWStyle()
|
||||
if style:
|
||||
|
@ -374,7 +363,7 @@ class QCircuitImage:
|
|||
'b').zfill(self.cregs[if_reg])[::-1]
|
||||
if op.name not in ['measure', 'barrier', 'snapshot', 'load',
|
||||
'save', 'noise']:
|
||||
nm = utf8tolatex(op.name).replace(" ", "\\,")
|
||||
nm = generate_latex_label(op.name).replace(" ", "\\,")
|
||||
qarglist = op.qargs
|
||||
if aliases is not None:
|
||||
qarglist = map(lambda x: aliases[x], qarglist)
|
||||
|
|
|
@ -12,9 +12,14 @@
|
|||
# copyright notice, and modified files need to carry a notice indicating
|
||||
# that they have been altered from the originals.
|
||||
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
|
||||
"""Common visualization utilities."""
|
||||
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
|
||||
from qiskit.converters import circuit_to_dag
|
||||
from qiskit.visualization.exceptions import VisualizationError
|
||||
|
||||
|
@ -24,6 +29,36 @@ try:
|
|||
except ImportError:
|
||||
HAS_PIL = False
|
||||
|
||||
try:
|
||||
from pylatexenc.latexencode import utf8tolatex
|
||||
|
||||
HAS_PYLATEX = True
|
||||
except ImportError:
|
||||
HAS_PYLATEX = False
|
||||
|
||||
|
||||
def generate_latex_label(label):
|
||||
"""Convert a label to a valid latex string."""
|
||||
if not HAS_PYLATEX:
|
||||
raise ImportError('The latex and latex_source drawers need '
|
||||
'pylatexenc installed. Run "pip install '
|
||||
'pylatexenc" before using the latex or '
|
||||
'latex_source drawers.')
|
||||
|
||||
regex = re.compile(r"(?<!\\)\$(.*)(?<!\\)\$")
|
||||
match = regex.search(label)
|
||||
if not match:
|
||||
label = label.replace('\$', '$') # noqa
|
||||
return utf8tolatex(label)
|
||||
else:
|
||||
mathmode_string = match.group(1).replace('\$', '$') # noqa
|
||||
before_match = label[:match.start()]
|
||||
before_match = before_match.replace('\$', '$') # noqa
|
||||
after_match = label[match.end():]
|
||||
after_match = after_match.replace('\$', '$') # noqa
|
||||
return utf8tolatex(before_match) + mathmode_string + utf8tolatex(
|
||||
after_match)
|
||||
|
||||
|
||||
def _validate_input_state(quantum_state):
|
||||
"""Validates the input to state visualization functions.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
The ``latex`` output mode for ``qiskit.visualization.circuit_drawer()`` and
|
||||
the ``qiskit.circuit.QuantumCircuit.draw()`` method now has a mode to
|
||||
passthrough raw latex from gate labels. The syntax for doing this mirrors
|
||||
matplotlib's
|
||||
(mathtext mode)[https://matplotlib.org/tutorials/text/mathtext.html]
|
||||
syntax. Any portion of a label string between a pair of `$`s will be treated
|
||||
as raw latex and passed directly to the output latex. This can be used
|
||||
|
||||
Prior to this release all gate labels were run through a utf8 -> latex
|
||||
conversion to make sure that the output latex would compile the string as
|
||||
expected. This is still what happens for all portions of a label outside
|
||||
the `$`s. Also if you want to use a dollar sign in your label make sure
|
||||
you escape it in the label string (ie `'\$'`).
|
||||
|
||||
You can mix and match this passthrough with the utf8 -> latex conversion to
|
||||
create the exact label you want, for example::
|
||||
|
||||
from qiskit import circuit
|
||||
circ = circuit.QuantumCircuit(2)
|
||||
circ.h([0, 1])
|
||||
circ.append(circuit.Gate(name='α_gate', num_qubits=1, params=[0]), [0])
|
||||
circ.append(circuit.Gate(name='α_gate$_2$', num_qubits=1, params=[0]), [1])
|
||||
circ.append(circuit.Gate(name='\$α\$_gate', num_qubits=1, params=[0]), [1])
|
||||
circ.draw(output='latex')
|
||||
|
||||
will now render the first custom gate's label as ``α_gate``, the second
|
||||
will be ``α_gate`` with a 2 subscript, and the last custom gate's label
|
||||
will be ``$α$_gate``.
|
|
@ -12,6 +12,8 @@
|
|||
# copyright notice, and modified files need to carry a notice indicating
|
||||
# that they have been altered from the originals.
|
||||
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
|
||||
"""Tests for visualization tools."""
|
||||
|
||||
import os
|
||||
|
@ -382,6 +384,39 @@ c1_0: 0 ════════════════════════
|
|||
self.assertEqual(r_exp,
|
||||
[[(op.name, op.qargs, op.cargs) for op in ops] for ops in layered_ops])
|
||||
|
||||
def test_generate_latex_label_nomathmode(self):
|
||||
"""Test generate latex label default."""
|
||||
self.assertEqual('abc', utils.generate_latex_label('abc'))
|
||||
|
||||
def test_generate_latex_label_nomathmode_utf8char(self):
|
||||
"""Test generate latex label utf8 characters."""
|
||||
self.assertEqual('{\\ensuremath{\\iiint}}X{\\ensuremath{\\forall}}Y',
|
||||
utils.generate_latex_label('∭X∀Y'))
|
||||
|
||||
def test_generate_latex_label_mathmode_utf8char(self):
|
||||
"""Test generate latex label mathtext with utf8."""
|
||||
self.assertEqual(
|
||||
'abc_{\\ensuremath{\\iiint}}X{\\ensuremath{\\forall}}Y',
|
||||
utils.generate_latex_label('$abc_$∭X∀Y'))
|
||||
|
||||
def test_generate_latex_label_mathmode_underscore_outside(self):
|
||||
"""Test generate latex label with underscore outside mathmode."""
|
||||
self.assertEqual(
|
||||
'abc{\\_}{\\ensuremath{\\iiint}}X{\\ensuremath{\\forall}}Y',
|
||||
utils.generate_latex_label('$abc$_∭X∀Y'))
|
||||
|
||||
def test_generate_latex_label_escaped_dollar_signs(self):
|
||||
"""Test generate latex label with escaped dollarsign."""
|
||||
self.assertEqual(
|
||||
'{\\$}{\\ensuremath{\\forall}}{\\$}',
|
||||
utils.generate_latex_label('\$∀\$')) # noqa
|
||||
|
||||
def test_generate_latex_label_escaped_dollar_sign_in_mathmode(self):
|
||||
"""Test generate latex label with escaped dollar sign in mathmode."""
|
||||
self.assertEqual(
|
||||
'a$bc{\\_}{\\ensuremath{\\iiint}}X{\\ensuremath{\\forall}}Y',
|
||||
utils.generate_latex_label('$a$bc$_∭X∀Y')) # noqa
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
Loading…
Reference in New Issue