Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop forcing mathtext mode for all strings #4669

Merged
merged 23 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
23cca0c
Stop forcing mathtext mode for all strings
mtreinish Jul 8, 2020
cbe796e
Also fix parameters
mtreinish Jul 8, 2020
bb92c1c
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 8, 2020
5eef233
Add explicit math mode to default gate names where necessary
mtreinish Jul 9, 2020
6e2c431
Remove conversions assuming hard coded mathtext mode
mtreinish Jul 9, 2020
83055e9
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 14, 2020
6955736
Don't slant letters for standard gates
mtreinish Jul 14, 2020
f2b305c
Stop hard coding latex characters and use pylatexenc
mtreinish Jul 14, 2020
3fc3b5f
Use mathtext for U_* standard gates too
mtreinish Jul 14, 2020
64db36a
Add pylatexenc to binder build
mtreinish Jul 14, 2020
0e3deca
Add release note
mtreinish Jul 14, 2020
ea4735e
Don't slant standard gate subscript and ALL CAPS
mtreinish Jul 15, 2020
3cc8153
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 15, 2020
58c5c08
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
Jul 16, 2020
5795216
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 20, 2020
0998cd7
Fix spacing with mathmode labels
mtreinish Jul 20, 2020
18ba617
Use \psi \rangle for initialize label
mtreinish Jul 20, 2020
1f854b6
Fix oversight in mathmode handling
mtreinish Jul 20, 2020
d948220
new references
Jul 21, 2020
671cdd6
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 21, 2020
fe7b826
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 26, 2020
f44960a
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
mtreinish Jul 28, 2020
d382010
Merge branch 'master' into fix-mathtext-mode-mpl-drawer
Cryoris Jul 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion postBuild
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

# Dependencies
# - matplotlib: for MPL drawer
# - pylatexenc: for MPL drawer
# - pillow: for image comparison
# - appmode: jupyter extension for executing the notebook
pip install matplotlib pillow appmode
pip install matplotlib pylatexenc pillow appmode

# Activation of appmode extension
jupyter nbextension enable --py --sys-prefix appmode
Expand Down
64 changes: 38 additions & 26 deletions qiskit/visualization/matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import itertools
import json
import logging
import re
from warnings import warn

import numpy as np

try:
Expand All @@ -32,6 +34,13 @@
except ImportError:
HAS_MATPLOTLIB = False

try:
from pylatexenc.latex2text import LatexNodes2Text

HAS_PYLATEX = True
except ImportError:
HAS_PYLATEX = False

from qiskit.circuit import ControlledGate
from qiskit.visualization.qcstyle import DefaultStyle, BWStyle
from qiskit import user_config
Expand Down Expand Up @@ -113,6 +122,10 @@ def __init__(self, qregs, cregs, ops,
raise ImportError('The class MatplotlibDrawer needs matplotlib. '
'To install, run "pip install matplotlib".')

if not HAS_PYLATEX:
raise ImportError('The class MatplotlibDrawer needs pylatexenc. '
'to install, run "pip install pylatexenc".')

self._ast = None
self._scale = 1.0 if scale is None else scale
self._creg = []
Expand Down Expand Up @@ -198,9 +211,6 @@ def __init__(self, qregs, cregs, ops,

# these char arrays are for finding text_width when not
# using get_renderer method for the matplotlib backend
self._latex_chars = ('$', '{', '}', '_', '\\left', '\\right',
'\\dagger', '\\rangle')
self._latex_chars1 = ('\\mapsto', '\\pi', '\\;')
self._char_list = {' ': (0.0958, 0.0583), '!': (0.1208, 0.0729), '"': (0.1396, 0.0875),
'#': (0.2521, 0.1562), '$': (0.1917, 0.1167), '%': (0.2854, 0.1771),
'&': (0.2333, 0.1458), "'": (0.0833, 0.0521), '(': (0.1167, 0.0729),
Expand Down Expand Up @@ -234,6 +244,8 @@ def __init__(self, qregs, cregs, ops,
'z': (0.1562, 0.0979), '{': (0.1917, 0.1188), '|': (0.1, 0.0604),
'}': (0.1896, 0.1188)}

self._mathmode_regex = re.compile(r"(?<!\\)\$(.*)(?<!\\)\$")

def _registers(self, creg, qreg):
self._creg = []
for r in creg:
Expand All @@ -255,12 +267,21 @@ def _get_text_width(self, text, fontsize):
t = plt.text(0.5, 0.5, text, fontsize=fontsize)
return t.get_window_extent(renderer=self.renderer).width / 60.0
else:
# if not using a get_renderer method, first remove
# any latex chars before getting width
for t in self._latex_chars1:
text = text.replace(t, 'r')
for t in self._latex_chars:
text = text.replace(t, '')
math_mode_match = self._mathmode_regex.search(text)
num_underscores = 0
num_carets = 0
if math_mode_match:
math_mode_text = math_mode_match.group(1)
num_underscores = math_mode_text.count('_')
num_carets = math_mode_text.count('^')
text = LatexNodes2Text().latex_to_text(text)
# If there are subscripts or superscripts in mathtext string
# we need to account for that spacing by manually removing
# from text string for text length
if num_underscores:
text = text.replace('_', '', num_underscores)
if num_carets:
text = text.replace('^', '', num_carets)

f = 0 if fontsize == self._style.fs else 1
sum_text = 0.0
Expand All @@ -285,9 +306,6 @@ def param_parse(self, v):
param_parts[i] = '$-$' + param_parts[i][1:]

param_parts = ', '.join(param_parts)
# remove $'s since "${}$".format will add them back on the outside
param_parts = param_parts.replace('$', '')
param_parts = param_parts.replace('-', u'\u02d7')
return param_parts

def _get_gate_ctrl_text(self, op):
Expand All @@ -309,18 +327,12 @@ def _get_gate_ctrl_text(self, op):
gate_text = op.name

if gate_text in self._style.disptex:
gate_text = "${}$".format(self._style.disptex[gate_text])
gate_text = "{}".format(self._style.disptex[gate_text])
else:
gate_text = "${}$".format(gate_text[0].upper() + gate_text[1:])
gate_text = "{}".format(gate_text[0].upper() + gate_text[1:])

# mathtext .format removes spaces so add them back and it changes
# hyphen to wide minus sign, so use unicode hyphen to prevent that
gate_text = gate_text.replace(' ', '\\;')
gate_text = gate_text.replace('-', u'\u02d7')
if ctrl_text:
ctrl_text = "${}$".format(ctrl_text[0].upper() + ctrl_text[1:])
ctrl_text = ctrl_text.replace(' ', '\\;')
ctrl_text = ctrl_text.replace('-', u'\u02d7')
ctrl_text = "{}".format(ctrl_text[0].upper() + ctrl_text[1:])
return gate_text, ctrl_text

def _get_colors(self, op):
Expand Down Expand Up @@ -613,7 +625,7 @@ def _fix_double_script(label):
label = _fix_double_script(label) + initial_qbit
text_width = self._get_text_width(label, self._style.fs)
else:
label = '${name}$'.format(name=reg.register.name)
label = '{name}'.format(name=reg.register.name)
label = _fix_double_script(label) + initial_qbit
text_width = self._get_text_width(label, self._style.fs)

Expand All @@ -635,7 +647,7 @@ def _fix_double_script(label):
for ii, (reg, nreg) in enumerate(itertools.zip_longest(self._creg, n_creg)):
pos = y_off - idx
if self.cregbundle:
label = '${}$'.format(reg.register.name)
label = '{}'.format(reg.register.name)
label = _fix_double_script(label) + initial_cbit
text_width = self._get_text_width(reg.register.name, self._style.fs) * 1.15
if text_width > longest_label_width:
Expand Down Expand Up @@ -757,7 +769,7 @@ def _draw_ops(self, verbose=False):

if op.name == 'cu1' or op.name == 'rzz' or base_name == 'rzz':
tname = 'U1' if op.name == 'cu1' else 'zz'
gate_width = (self._get_text_width(tname + ' ()$$',
gate_width = (self._get_text_width(tname + ' ()',
fontsize=self._style.sfs)
+ param_width) * 1.5
else:
Expand Down Expand Up @@ -824,7 +836,7 @@ def _draw_ops(self, verbose=False):
# load param
if (op.type == 'op' and hasattr(op.op, 'params') and len(op.op.params) > 0
and not any([isinstance(param, np.ndarray) for param in op.op.params])):
param = "${}$".format(self.param_parse(op.op.params))
param = "{}".format(self.param_parse(op.op.params))
else:
param = ''

Expand Down Expand Up @@ -920,7 +932,7 @@ def _draw_ops(self, verbose=False):
self._ctrl_qubit(q_xy[num_ctrl_qubits + 1], fc=ec, ec=ec, tc=tc)
stext = self._style.disptex['u1'] if op.name == 'cu1' else 'zz'
self._sidetext(qreg_b, tc=tc,
text='${}$'.format(stext) + ' ' + '({})'.format(param))
text='{}'.format(stext) + ' ' + '({})'.format(param))
self._line(qreg_b, qreg_t, lc=lc)

# swap gate
Expand Down
58 changes: 30 additions & 28 deletions qiskit/visualization/qcstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,32 +47,33 @@ def __init__(self):
self.sfs = 8
self.disptex = {
'id': 'I',
'u0': 'U_0',
'u1': 'U_1',
'u2': 'U_2',
'u3': 'U_3',
'u0': '$\\mathrm{U}_0$',
'u1': '$\\mathrm{U}_1$',
'u2': '$\\mathrm{U}_2$',
'u3': '$\\mathrm{U}_3$',
'x': 'X',
'y': 'Y',
'z': 'Z',
'h': 'H',
's': 'S',
'sdg': 'S^\\dagger',
'sdg': '$\\mathrm{S}^\\dagger$',
't': 'T',
'tdg': 'T^\\dagger',
'tdg': '$\\mathrm{T}^\\dagger$',
'iswap': 'Iswap',
'dcx': 'Dcx',
'ms': 'MS',
'diagonal': 'Diagonal',
'unitary': 'Unitary',
'r': 'R',
'rx': 'R_x',
'ry': 'R_y',
'rz': 'R_z',
'rxx': 'R_{xx}',
'ryy': 'R_{yy}',
'rzx': 'R_{zx}',
'reset': '\\left|0\\right\\rangle',
'initialize': '|psi>'
'rx': '$\\mathrm{R}_\\mathrm{X}$',
'ry': '$\\mathrm{R}_\\mathrm{Y}$',
'rz': '$\\mathrm{R}_\\mathrm{Z}$',
'rxx': '$\\mathrm{R}_{\\mathrm{XX}}$',
'ryy': '$\\mathrm{R}_{\\mathrm{YY}}$',
'rzx': '$\\mathrm{R}_{\\mathrm{ZX}}$',
'rzz': '$\\mathrm{R}_{\\mathrm{ZZ}}$',
'reset': '$\\left|0\\right\\rangle$',
'initialize': '$|\\psi\\rangle$'
}
self.dispcol = {
'u0': basis_color,
Expand Down Expand Up @@ -158,32 +159,33 @@ def __init__(self):
self.sfs = 8
self.disptex = {
'id': 'I',
'u0': 'U_0',
'u1': 'U_1',
'u2': 'U_2',
'u3': 'U_3',
'u0': '$\\mathrm{U}_0$',
'u1': '$\\mathrm{U}_1$',
'u2': '$\\mathrm{U}_2$',
'u3': '$\\mathrm{U}_3$',
'x': 'X',
'y': 'Y',
'z': 'Z',
'h': 'H',
's': 'S',
'sdg': 'S^\\dagger',
'sdg': '$\\mathrm{S}^\\dagger$',
't': 'T',
'tdg': 'T^\\dagger',
'tdg': '$\\mathrm{T}^\\dagger$',
'iswap': 'Iswap',
'dcx': 'Dcx',
'ms': 'MS',
'diagonal': 'Diagonal',
'unitary': 'Unitary',
'r': 'R',
'rx': 'R_x',
'ry': 'R_y',
'rz': 'R_z',
'rxx': 'R_{xx}',
'ryy': 'R_{yy}',
'rzx': 'R_{zx}',
'reset': '\\left|0\\right\\rangle',
'initialize': '|psi>'
'rx': '$\\mathrm{R}_\\mathrm{X}$',
'ry': '$\\mathrm{R}_\\mathrm{Y}$',
'rz': '$\\mathrm{R}_\\mathrm{Z}$',
'rxx': '$\\mathrm{R}_{\\mathrm{XX}}$',
'ryy': '$\\mathrm{R}_{\\mathrm{YY}}$',
'rzx': '$\\mathrm{R}_{\\mathrm{ZX}}$',
'rzz': '$\\mathrm{R}_{\\mathrm{ZZ}}$',
'reset': '$\\left|0\\right\\rangle$',
'initialize': '$|\\psi\\rangle$'
}
self.dispcol = {
'u0': '#ffffff',
Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/pylatexenc-matplotlib-428f285f4cfd2d7c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
upgrade:
- |
The ``'mpl'`` output mode for the
:meth:`~qiskit.circuit.QuantumCircuit.draw` method and
:func:`~qiskit.visualization.circuit_drawer` now requires the
`pylatexenc <https://pylatexenc.readthedocs.io/en/latest/latexencode/>`__
library to be installed. This was already an optional dependency for
visualization, but was only required for the ``'latex'`` output mode
before. It is now also required for the matplotlib drawer because it is
needed to handle correctly sizing gates with matplotlib's
`mathtext <https://matplotlib.org/3.2.2/tutorials/text/mathtext.html>`__
labels for gates.

Binary file modified test/ipynb/mpl/references/big_gates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/cnot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/conditional.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/creg_initial_false.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/creg_initial_true.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/cswap_rzz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/ctrl_labels.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/fold_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/fold_minus1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/ghz_to_gate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/long_name.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/no_barriers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/pauli_clifford.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/plot_barriers_false.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/plot_barriers_true.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/plot_partial_barrier.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/r_gates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/scale_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/scale_double.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/scale_half.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/ipynb/mpl/references/u_gates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.