Move circuit drawer files to `qiskit.visualization.circuit` (#8306)

* Add graph and circuit dirs

* Move files to new folders

* Finishing transition to circuit and graph dirs

* Finish import changes

* Positioning files and setting init entries

* Final tweaks to compatibility and lint

* Reduce to circuit dir only

* Cleanup

* Add qcstyle stub for docs

* Merge main conflicts fix

* Lint

* Change test message

* Fix _directive change

* Fix op.condition reference

* Change to _utils and cleanup

* Lint

* Fix _trim and dag_drawer test

* Allow direct import of text, etc.

* Add comment explaining backwards-compatibility imports

* Add release note

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
This commit is contained in:
Edwin Navarro 2022-09-06 18:12:00 -07:00 committed by GitHub
parent 6927a8d26b
commit bb5201184b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1844 additions and 1947 deletions

View File

@ -116,29 +116,37 @@ import os
import sys
import warnings
from qiskit.visualization.counts_visualization import plot_histogram
from qiskit.visualization.state_visualization import (
from .array import array_to_latex
from .circuit import circuit_drawer
from .counts_visualization import plot_histogram
from .bloch import Bloch, Arrow3D
from .state_visualization import (
plot_state_hinton,
plot_bloch_vector,
plot_bloch_multivector,
plot_state_city,
plot_state_paulivec,
plot_state_qsphere,
state_drawer,
)
from qiskit.visualization.transition_visualization import visualize_transition
from qiskit.visualization.array import array_to_latex
from .circuit_visualization import circuit_drawer
from .transition_visualization import visualize_transition
from .dag_visualization import dag_drawer
from .exceptions import VisualizationError
from .gate_map import plot_gate_map, plot_circuit_layout, plot_error_map, plot_coupling_map
from .pass_manager_visualization import pass_manager_drawer
from .pulse.interpolation import step_wise, linear, cubic_spline
from .pulse.qcstyle import PulseStyle, SchedStyle
from .pulse_visualization import pulse_drawer
from .pulse_v2 import draw as pulse_drawer_v2
from .timeline import draw as timeline_drawer
from .exceptions import VisualizationError
# These modules aren't part of the public interface, and were moved in Terra 0.22. They're
# re-imported here to allow a backwards compatible path, and should be deprecated in Terra 0.23.
from .circuit import text, matplotlib, latex
_DEPRECATED_NAMES = {
"HAS_MATPLOTLIB",
"HAS_PYLATEX",

View File

@ -0,0 +1,15 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
""" Init for circuit visualizations """
from .circuit_visualization import circuit_drawer

View File

@ -0,0 +1,637 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Common circuit visualization utilities."""
import re
from collections import OrderedDict
from warnings import warn
import numpy as np
from qiskit.circuit import (
BooleanExpression,
Clbit,
ControlledGate,
Delay,
Gate,
Instruction,
Measure,
ControlFlowOp,
)
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.circuit import ClassicalRegister
from qiskit.circuit.tools import pi_check
from qiskit.converters import circuit_to_dag
from qiskit.utils import optionals as _optionals
from ..exceptions import VisualizationError
def get_gate_ctrl_text(op, drawer, style=None, calibrations=None):
"""Load the gate_text and ctrl_text strings based on names and labels"""
op_label = getattr(op, "label", None)
op_type = type(op)
base_name = base_label = base_type = None
if hasattr(op, "base_gate"):
base_name = op.base_gate.name
base_label = op.base_gate.label
base_type = type(op.base_gate)
ctrl_text = None
if base_label:
gate_text = base_label
ctrl_text = op_label
elif op_label and isinstance(op, ControlledGate):
gate_text = base_name
ctrl_text = op_label
elif op_label:
gate_text = op_label
elif base_name:
gate_text = base_name
else:
gate_text = op.name
# raw_gate_text is used in color selection in mpl instead of op.name, since
# if it's a controlled gate, the color will likely not be the base_name color
raw_gate_text = op.name if gate_text == base_name else gate_text
# For mpl and latex drawers, check style['disptex'] in qcstyle.py
if drawer != "text" and gate_text in style["disptex"]:
# First check if this entry is in the old style disptex that
# included "$\\mathrm{ }$". If so, take it as is.
if style["disptex"][gate_text][0] == "$" and style["disptex"][gate_text][-1] == "$":
gate_text = style["disptex"][gate_text]
else:
gate_text = f"$\\mathrm{{{style['disptex'][gate_text]}}}$"
elif drawer == "latex":
# Special formatting for Booleans in latex (due to '~' causing crash)
if (gate_text == op.name and op_type is BooleanExpression) or (
gate_text == base_name and base_type is BooleanExpression
):
gate_text = gate_text.replace("~", "$\\neg$").replace("&", "\\&")
gate_text = f"$\\texttt{{{gate_text}}}$"
# Capitalize if not a user-created gate or instruction
elif (
(gate_text == op.name and op_type not in (Gate, Instruction))
or (gate_text == base_name and base_type not in (Gate, Instruction))
) and (op_type is not PauliEvolutionGate):
gate_text = f"$\\mathrm{{{gate_text.capitalize()}}}$"
else:
gate_text = f"$\\mathrm{{{gate_text}}}$"
# Remove mathmode _, ^, and - formatting from user names and labels
gate_text = gate_text.replace("_", "\\_")
gate_text = gate_text.replace("^", "\\string^")
gate_text = gate_text.replace("-", "\\mbox{-}")
ctrl_text = f"$\\mathrm{{{ctrl_text}}}$"
# Only captitalize internally-created gate or instruction names
elif (
(gate_text == op.name and op_type not in (Gate, Instruction))
or (gate_text == base_name and base_type not in (Gate, Instruction))
) and (op_type is not PauliEvolutionGate):
gate_text = gate_text.capitalize()
if drawer == "mpl" and op.name in calibrations:
if isinstance(op, ControlledGate):
ctrl_text = "" if ctrl_text is None else ctrl_text
ctrl_text = "(cal)\n" + ctrl_text
else:
gate_text = gate_text + "\n(cal)"
return gate_text, ctrl_text, raw_gate_text
def get_param_str(op, drawer, ndigits=3):
"""Get the params as a string to add to the gate text display"""
if not hasattr(op, "params") or any(isinstance(param, np.ndarray) for param in op.params):
return ""
if isinstance(op, ControlFlowOp):
return ""
if isinstance(op, Delay):
param_list = [f"{op.params[0]}[{op.unit}]"]
else:
param_list = []
for count, param in enumerate(op.params):
# Latex drawer will cause an xy-pic error and mpl drawer will overwrite
# the right edge if param string too long, so limit params.
if (drawer == "latex" and count > 3) or (drawer == "mpl" and count > 15):
param_list.append("...")
break
try:
param_list.append(pi_check(param, output=drawer, ndigits=ndigits))
except TypeError:
param_list.append(str(param))
param_str = ""
if param_list:
if drawer == "latex":
param_str = f"\\,(\\mathrm{{{','.join(param_list)}}})"
elif drawer == "mpl":
param_str = f"{', '.join(param_list)}".replace("-", "$-$")
else:
param_str = f"({','.join(param_list)})"
return param_str
def get_wire_map(circuit, bits, cregbundle):
"""Map the bits and registers to the index from the top of the drawing.
The key to the dict is either the (Qubit, Clbit) or if cregbundle True,
the register that is being bundled.
Args:
circuit (QuantumCircuit): the circuit being drawn
bits (list(Qubit, Clbit)): the Qubit's and Clbit's in the circuit
cregbundle (bool): if True bundle classical registers. Default: ``True``.
Returns:
dict((Qubit, Clbit, ClassicalRegister): index): map of bits/registers
to index
"""
prev_reg = None
wire_index = 0
wire_map = {}
for bit in bits:
register = get_bit_register(circuit, bit)
if register is None or not isinstance(bit, Clbit) or not cregbundle:
wire_map[bit] = wire_index
wire_index += 1
elif register is not None and cregbundle and register != prev_reg:
prev_reg = register
wire_map[register] = wire_index
wire_index += 1
return wire_map
def get_bit_register(circuit, bit):
"""Get the register for a bit if there is one
Args:
circuit (QuantumCircuit): the circuit being drawn
bit (Qubit, Clbit): the bit to use to find the register and indexes
Returns:
ClassicalRegister: register associated with the bit
"""
bit_loc = circuit.find_bit(bit)
return bit_loc.registers[0][0] if bit_loc.registers else None
def get_bit_reg_index(circuit, bit, reverse_bits=None):
"""Get the register for a bit if there is one, and the index of the bit
from the top of the circuit, or the index of the bit within a register.
Args:
circuit (QuantumCircuit): the circuit being drawn
bit (Qubit, Clbit): the bit to use to find the register and indexes
reverse_bits (bool): deprecated option to reverse order of the bits
Returns:
(ClassicalRegister, None): register associated with the bit
int: index of the bit from the top of the circuit
int: index of the bit within the register, if there is a register
"""
if reverse_bits is not None:
warn(
"The 'reverse_bits' kwarg to the function "
"~qiskit.visualization.utils.get_bit_reg_index "
"is deprecated as of 0.22.0 and will be removed no earlier than 3 months "
"after the release date.",
DeprecationWarning,
2,
)
bit_loc = circuit.find_bit(bit)
bit_index = bit_loc.index
register, reg_index = bit_loc.registers[0] if bit_loc.registers else (None, None)
return register, bit_index, reg_index
def get_wire_label(drawer, register, index, layout=None, cregbundle=True):
"""Get the bit labels to display to the left of the wires.
Args:
drawer (str): which drawer is calling ("text", "mpl", or "latex")
register (QuantumRegister or ClassicalRegister): get wire_label for this register
index (int): index of bit in register
layout (Layout): Optional. mapping of virtual to physical bits
cregbundle (bool): Optional. if set True bundle classical registers.
Default: ``True``.
Returns:
str: label to display for the register/index
"""
index_str = f"{index}" if drawer == "text" else f"{{{index}}}"
if register is None:
wire_label = index_str
return wire_label
if drawer == "text":
reg_name = f"{register.name}"
reg_name_index = f"{register.name}_{index}"
else:
reg_name = f"{{{fix_special_characters(register.name)}}}"
reg_name_index = f"{reg_name}_{{{index}}}"
# Clbits
if isinstance(register, ClassicalRegister):
if cregbundle and drawer != "latex":
wire_label = f"{register.name}"
return wire_label
if register.size == 1 or cregbundle:
wire_label = reg_name
else:
wire_label = reg_name_index
return wire_label
# Qubits
if register.size == 1:
wire_label = reg_name
elif layout is None:
wire_label = reg_name_index
elif layout[index]:
virt_bit = layout[index]
try:
virt_reg = next(reg for reg in layout.get_registers() if virt_bit in reg)
if drawer == "text":
wire_label = f"{virt_reg.name}_{virt_reg[:].index(virt_bit)} -> {index}"
else:
wire_label = (
f"{{{virt_reg.name}}}_{{{virt_reg[:].index(virt_bit)}}} \\mapsto {{{index}}}"
)
except StopIteration:
if drawer == "text":
wire_label = f"{virt_bit} -> {index}"
else:
wire_label = f"{{{virt_bit}}} \\mapsto {{{index}}}"
if drawer != "text":
wire_label = wire_label.replace(" ", "\\;") # use wider spaces
else:
wire_label = index_str
return wire_label
def get_condition_label_val(condition, circuit, cregbundle, reverse_bits=None):
"""Get the label and value list to display a condition
Args:
condition (Union[Clbit, ClassicalRegister], int): classical condition
circuit (QuantumCircuit): the circuit that is being drawn
cregbundle (bool): if set True bundle classical registers
reverse_bits (bool): deprecated option to reverse order of the bits
Returns:
str: label to display for the condition
list(str): list of 1's and 0's indicating values of condition
"""
if reverse_bits is not None:
warn(
"The 'reverse_bits' kwarg to the function "
"~qiskit.visualization.utils.get_condition_label_val "
"is deprecated as of 0.22.0 and will be removed no earlier than 3 months "
"after the release date.",
DeprecationWarning,
2,
)
cond_is_bit = bool(isinstance(condition[0], Clbit))
cond_val = int(condition[1])
# if condition on a register, return list of 1's and 0's indicating
# closed or open, else only one element is returned
if isinstance(condition[0], ClassicalRegister) and not cregbundle:
val_bits = list(f"{cond_val:0{condition[0].size}b}")[::-1]
else:
val_bits = list(str(cond_val))
label = ""
if cond_is_bit and cregbundle:
register, _, reg_index = get_bit_reg_index(circuit, condition[0])
if register is not None:
label = f"{register.name}_{reg_index}={hex(cond_val)}"
elif not cond_is_bit:
label = hex(cond_val)
return label, val_bits
def fix_special_characters(label):
"""
Convert any special characters for mpl and latex drawers.
Currently only checks for multiple underscores in register names
and uses wider space for mpl and latex drawers.
Args:
label (str): the label to fix
Returns:
str: label to display
"""
label = label.replace("_", r"\_").replace(" ", "\\;")
return label
@_optionals.HAS_PYLATEX.require_in_call("the latex and latex_source circuit drawers")
def generate_latex_label(label):
"""Convert a label to a valid latex string."""
from pylatexenc.latexencode import utf8tolatex
regex = re.compile(r"(?<!\\)\$(.*)(?<!\\)\$")
match = regex.search(label)
if not match:
label = label.replace(r"\$", "$")
final_str = utf8tolatex(label, non_ascii_only=True)
else:
mathmode_string = match.group(1).replace(r"\$", "$")
before_match = label[: match.start()]
before_match = before_match.replace(r"\$", "$")
after_match = label[match.end() :]
after_match = after_match.replace(r"\$", "$")
final_str = (
utf8tolatex(before_match, non_ascii_only=True)
+ mathmode_string
+ utf8tolatex(after_match, non_ascii_only=True)
)
return final_str.replace(" ", "\\,") # Put in proper spaces
def _get_layered_instructions(
circuit, reverse_bits=False, justify=None, idle_wires=True, wire_order=None
):
"""
Given a circuit, return a tuple (qubits, clbits, nodes) where
qubits and clbits are the quantum and classical registers
in order (based on reverse_bits or wire_order) and nodes
is a list of DAGOpNodes.
Args:
circuit (QuantumCircuit): From where the information is extracted.
reverse_bits (bool): If true the order of the bits in the registers is
reversed.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
wire_order (list): A list of ints that modifies the order of the bits
Returns:
Tuple(list,list,list): To be consumed by the visualizer directly.
Raises:
VisualizationError: if both reverse_bits and wire_order are entered.
"""
if justify:
justify = justify.lower()
# default to left
justify = justify if justify in ("right", "none") else "left"
qubits = circuit.qubits
clbits = circuit.clbits
nodes = []
# Create a mapping of each register to the max layer number for all measure ops
# with that register as the target. Then when an op with condition is seen,
# it will be placed to the right of the measure op if the register matches.
measure_map = OrderedDict([(c, -1) for c in clbits])
if reverse_bits and wire_order is not None:
raise VisualizationError("Cannot set both reverse_bits and wire_order in the same drawing.")
if reverse_bits:
qubits.reverse()
clbits.reverse()
elif wire_order is not None:
new_qubits = []
new_clbits = []
for bit in wire_order:
if bit < len(qubits):
new_qubits.append(qubits[bit])
else:
new_clbits.append(clbits[bit - len(qubits)])
qubits = new_qubits
clbits = new_clbits
dag = circuit_to_dag(circuit)
dag.qubits = qubits
dag.clbits = clbits
if justify == "none":
for node in dag.topological_op_nodes():
nodes.append([node])
else:
nodes = _LayerSpooler(dag, justify, measure_map)
# Optionally remove all idle wires and instructions that are on them and
# on them only.
if not idle_wires:
for wire in dag.idle_wires(ignore=["barrier", "delay"]):
if wire in qubits:
qubits.remove(wire)
if wire in clbits:
clbits.remove(wire)
nodes = [[node for node in layer if any(q in qubits for q in node.qargs)] for layer in nodes]
return qubits, clbits, nodes
def _sorted_nodes(dag_layer):
"""Convert DAG layer into list of nodes sorted by node_id
qiskit-terra #2802
"""
nodes = dag_layer["graph"].op_nodes()
# sort into the order they were input
nodes.sort(key=lambda nd: nd._node_id)
return nodes
def _get_gate_span(qubits, node):
"""Get the list of qubits drawing this gate would cover
qiskit-terra #2802
"""
min_index = len(qubits)
max_index = 0
for qreg in node.qargs:
index = qubits.index(qreg)
if index < min_index:
min_index = index
if index > max_index:
max_index = index
if node.cargs or getattr(node.op, "condition", None):
return qubits[min_index : len(qubits)]
return qubits[min_index : max_index + 1]
def _any_crossover(qubits, node, nodes):
"""Return True .IFF. 'node' crosses over any 'nodes'."""
gate_span = _get_gate_span(qubits, node)
all_indices = []
for check_node in nodes:
if check_node != node:
all_indices += _get_gate_span(qubits, check_node)
return any(i in gate_span for i in all_indices)
class _LayerSpooler(list):
"""Manipulate list of layer dicts for _get_layered_instructions."""
def __init__(self, dag, justification, measure_map):
"""Create spool"""
super().__init__()
self.dag = dag
self.qubits = dag.qubits
self.clbits = dag.clbits
self.justification = justification
self.measure_map = measure_map
self.cregs = [self.dag.cregs[reg] for reg in self.dag.cregs]
if self.justification == "left":
for dag_layer in dag.layers():
current_index = len(self) - 1
dag_nodes = _sorted_nodes(dag_layer)
for node in dag_nodes:
self.add(node, current_index)
else:
dag_layers = []
for dag_layer in dag.layers():
dag_layers.append(dag_layer)
# going right to left!
dag_layers.reverse()
for dag_layer in dag_layers:
current_index = 0
dag_nodes = _sorted_nodes(dag_layer)
for node in dag_nodes:
self.add(node, current_index)
def is_found_in(self, node, nodes):
"""Is any qreq in node found in any of nodes?"""
all_qargs = []
for a_node in nodes:
for qarg in a_node.qargs:
all_qargs.append(qarg)
return any(i in node.qargs for i in all_qargs)
def insertable(self, node, nodes):
"""True .IFF. we can add 'node' to layer 'nodes'"""
return not _any_crossover(self.qubits, node, nodes)
def slide_from_left(self, node, index):
"""Insert node into first layer where there is no conflict going l > r"""
measure_layer = None
if isinstance(node.op, Measure):
measure_bit = next(bit for bit in self.measure_map if node.cargs[0] == bit)
if not self:
inserted = True
self.append([node])
else:
inserted = False
curr_index = index
last_insertable_index = -1
index_stop = -1
if node.op.condition:
if isinstance(node.op.condition[0], Clbit):
cond_bit = [clbit for clbit in self.clbits if node.op.condition[0] == clbit]
index_stop = self.measure_map[cond_bit[0]]
else:
for bit in node.op.condition[0]:
max_index = -1
if bit in self.measure_map:
if self.measure_map[bit] > max_index:
index_stop = max_index = self.measure_map[bit]
if node.cargs:
for carg in node.cargs:
try:
carg_bit = next(bit for bit in self.measure_map if carg == bit)
if self.measure_map[carg_bit] > index_stop:
index_stop = self.measure_map[carg_bit]
except StopIteration:
pass
while curr_index > index_stop:
if self.is_found_in(node, self[curr_index]):
break
if self.insertable(node, self[curr_index]):
last_insertable_index = curr_index
curr_index = curr_index - 1
if last_insertable_index >= 0:
inserted = True
self[last_insertable_index].append(node)
measure_layer = last_insertable_index
else:
inserted = False
curr_index = index
while curr_index < len(self):
if self.insertable(node, self[curr_index]):
self[curr_index].append(node)
measure_layer = curr_index
inserted = True
break
curr_index = curr_index + 1
if not inserted:
self.append([node])
if isinstance(node.op, Measure):
if not measure_layer:
measure_layer = len(self) - 1
if measure_layer > self.measure_map[measure_bit]:
self.measure_map[measure_bit] = measure_layer
def slide_from_right(self, node, index):
"""Insert node into rightmost layer as long there is no conflict."""
if not self:
self.insert(0, [node])
inserted = True
else:
inserted = False
curr_index = index
last_insertable_index = None
while curr_index < len(self):
if self.is_found_in(node, self[curr_index]):
break
if self.insertable(node, self[curr_index]):
last_insertable_index = curr_index
curr_index = curr_index + 1
if last_insertable_index:
self[last_insertable_index].append(node)
inserted = True
else:
curr_index = index
while curr_index > -1:
if self.insertable(node, self[curr_index]):
self[curr_index].append(node)
inserted = True
break
curr_index = curr_index - 1
if not inserted:
self.insert(0, [node])
def add(self, node, index):
"""Add 'node' where it belongs, starting the try at 'index'."""
if self.justification == "left":
self.slide_from_left(node, index)
else:
self.slide_from_right(node, index)

View File

@ -0,0 +1,663 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Module for the primary interface to the circuit drawers.
This module contains the end user facing API for drawing quantum circuits.
There are 3 available drawer backends:
0. ASCII art
1. LaTeX
2. Matplotlib
This provides a single function entry point to drawing a circuit object with
any of the backends.
"""
import logging
import os
import subprocess
import tempfile
from warnings import warn
from qiskit import user_config
from qiskit.utils import optionals as _optionals
from . import latex as _latex
from . import text as _text
from . import matplotlib as _matplotlib
from . import _utils
from ..utils import _trim as trim_image
from ..exceptions import VisualizationError
logger = logging.getLogger(__name__)
def circuit_drawer(
circuit,
scale=None,
filename=None,
style=None,
output=None,
interactive=False,
plot_barriers=True,
reverse_bits=False,
justify=None,
vertical_compression="medium",
idle_wires=True,
with_layout=True,
fold=None,
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:
**text**: ASCII art TextDrawing that can be printed in the console.
**matplotlib**: images with color rendered purely in Python.
**latex**: high-quality images compiled via latex.
**latex_source**: raw uncompiled latex output.
Args:
circuit (QuantumCircuit): the quantum circuit to draw
scale (float): scale of image to draw (shrink if < 1.0). Only used by
the `mpl`, `latex` and `latex_source` outputs. Defaults to 1.0.
filename (str): file path to save image to. Defaults to None.
style (dict or str): dictionary of style or file name of style json file.
This option is only used by the `mpl` or `latex` output type.
If `style` is a str, it is used as the path to a json file
which contains a style dict. The file will be opened, parsed, and
then any style elements in the dict will replace the default values
in the input dict. A file to be loaded must end in ``.json``, but
the name entered here can omit ``.json``. For example,
``style='iqx.json'`` or ``style='iqx'``.
If `style` is a dict and the ``'name'`` key is set, that name
will be used to load a json file, followed by loading the other
items in the style dict. For example, ``style={'name': 'iqx'}``.
If `style` is not a str and `name` is not a key in the style dict,
then the default value from the user config file (usually
``~/.qiskit/settings.conf``) will be used, for example,
``circuit_mpl_style = iqx``.
If none of these are set, the `default` style will be used.
The search path for style json files can be specified in the user
config, for example,
``circuit_mpl_style_path = /home/user/styles:/home/user``.
See: :class:`~qiskit.visualization.qcstyle.DefaultStyle` for more
information on the contents.
output (str): select the output method to use for drawing the circuit.
Valid choices are ``text``, ``mpl``, ``latex``, ``latex_source``.
By default the `text` drawer is used unless the user config file
(usually ``~/.qiskit/settings.conf``) has an alternative backend set
as the default. For example, ``circuit_drawer = latex``. If the output
kwarg is set, that backend will always be used over the default in
the user config file.
interactive (bool): when set to true, show the circuit in a new window
(for `mpl` this depends on the matplotlib backend being used
supporting this). Note when used with either the `text` or the
`latex_source` output type this has no effect and will be silently
ignored. Defaults to False.
reverse_bits (bool): when set to True, reverse the bit order inside
registers for the output visualization. Defaults to False.
plot_barriers (bool): enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (string): options are ``left``, ``right`` or ``none``. If
anything else is supplied, it defaults to left justified. It refers
to where gates should be placed in the output circuit if there is
an option. ``none`` results in each gate being placed in its own
column.
vertical_compression (string): ``high``, ``medium`` or ``low``. It
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
the `text` output, will be silently ignored otherwise.
idle_wires (bool): include idle wires (wires with no circuit elements)
in output visualization. Default is True.
with_layout (bool): include layout information, with labels on the
physical layout. Default is True.
fold (int): sets pagination. It can be disabled using -1. In `text`,
sets the length of the lines. This is useful when the drawing does
not fit in the console. If None (default), it will try to guess the
console width using ``shutil.get_terminal_size()``. However, if
running in jupyter, the default line length is set to 80 characters.
In `mpl`, it is the number of (visual) layers before folding.
Default is 25.
ax (matplotlib.axes.Axes): Only used by the `mpl` backend. An optional
Axes object to be used for the visualization output. If none is
specified, a new matplotlib Figure will be created and used.
Additionally, if specified there will be no returned Figure since
it is redundant.
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Default is False.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default is True.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
:class:`TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
:class:`str`:
* `TextDrawing` (output='text')
A drawing that can be printed as ascii art.
* `matplotlib.figure.Figure` (output='mpl')
A matplotlib figure object for the circuit diagram.
* `PIL.Image` (output='latex')
An in-memory representation of the image of the circuit diagram.
* `str` (output='latex_source')
The LaTeX source code for visualizing the circuit diagram.
Raises:
VisualizationError: when an invalid output method is selected
MissingOptionalLibraryError: when the output methods requires non-installed libraries.
Example:
.. jupyter-execute::
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.visualization import circuit_drawer
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
circuit_drawer(qc, output='mpl', style={'backgroundcolor': '#EEEEEE'})
"""
image = None
config = user_config.get_config()
# Get default from config file else use text
default_output = "text"
if config:
default_output = config.get("circuit_drawer", "text")
if default_output == "auto":
if _optionals.HAS_MATPLOTLIB:
default_output = "mpl"
else:
default_output = "text"
if output is None:
output = default_output
if wire_order is not None and reverse_bits:
raise VisualizationError(
"The wire_order option cannot be set when the reverse_bits option is True."
)
if wire_order is not None and len(wire_order) != circuit.num_qubits + circuit.num_clbits:
raise VisualizationError(
"The wire_order list must be the same "
"length as the sum of the number of qubits and clbits in the circuit."
)
if wire_order is not None and set(wire_order) != set(
range(circuit.num_qubits + circuit.num_clbits)
):
raise VisualizationError(
"There must be one and only one entry in the "
"wire_order list for the index of each qubit and each clbit in the circuit."
)
if cregbundle and (reverse_bits or wire_order is not None):
cregbundle = False
warn(
"Cregbundle set to False since either reverse_bits or wire_order has been set.",
RuntimeWarning,
2,
)
if output == "text":
return _text_circuit_drawer(
circuit,
filename=filename,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
justify=justify,
vertical_compression=vertical_compression,
idle_wires=idle_wires,
with_layout=with_layout,
fold=fold,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex":
image = _latex_circuit_drawer(
circuit,
filename=filename,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex_source":
return _generate_latex_source(
circuit,
filename=filename,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "mpl":
image = _matplotlib_circuit_drawer(
circuit,
scale=scale,
filename=filename,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
fold=fold,
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
else:
raise VisualizationError(
"Invalid output type %s selected. The only valid choices "
"are text, latex, latex_source, and mpl" % output
)
if image and interactive:
image.show()
return image
# -----------------------------------------------------------------------------
# _text_circuit_drawer
# -----------------------------------------------------------------------------
def _text_circuit_drawer(
circuit,
filename=None,
reverse_bits=False,
plot_barriers=True,
justify=None,
vertical_compression="high",
idle_wires=True,
with_layout=True,
fold=None,
initial_state=True,
cregbundle=False,
encoding=None,
wire_order=None,
):
"""Draws a circuit using ascii art.
Args:
circuit (QuantumCircuit): Input circuit
filename (str): Optional filename to write the result
reverse_bits (bool): Rearrange the bits in reverse order.
plot_barriers (bool): Draws the barriers when they are there.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
vertical_compression (string): `high`, `medium`, or `low`. It merges the
lines so the drawing will take less vertical room. Default is `high`.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information with labels on the physical
layout. Default: True
fold (int): Optional. Breaks the circuit drawing to this length. This
is useful when the drawing does not fit in the console. If
None (default), it will try to guess the console width using
`shutil.get_terminal_size()`. If you don't want pagination
at all, set `fold=-1`.
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``True``.
encoding (str): Optional. Sets the encoding preference of the output.
Default: ``sys.stdout.encoding``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
TextDrawing: An instance that, when printed, draws the circuit in ascii art.
Raises:
VisualizationError: When the filename extenstion is not .txt.
"""
qubits, clbits, nodes = _utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
text_drawing = _text.TextDrawing(
qubits,
clbits,
nodes,
reverse_bits=reverse_bits,
layout=None,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
encoding=encoding,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
text_drawing.plotbarriers = plot_barriers
text_drawing.line_length = fold
text_drawing.vertical_compression = vertical_compression
if filename:
text_drawing.dump(filename, encoding=encoding)
return text_drawing
# -----------------------------------------------------------------------------
# latex_circuit_drawer
# -----------------------------------------------------------------------------
@_optionals.HAS_PDFLATEX.require_in_call("LaTeX circuit drawing")
@_optionals.HAS_PDFTOCAIRO.require_in_call("LaTeX circuit drawing")
@_optionals.HAS_PIL.require_in_call("LaTeX circuit drawing")
def _latex_circuit_drawer(
circuit,
scale=0.7,
style=None,
filename=None,
plot_barriers=True,
reverse_bits=False,
justify=None,
idle_wires=True,
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Draw a quantum circuit based on latex (Qcircuit package)
Requires version >=2.6.0 of the qcircuit LaTeX package.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
style (dict or str): dictionary of style or file name of style file
filename (str): file path to save image to
reverse_bits (bool): When set to True reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
PIL.Image: an in-memory representation of the circuit diagram
Raises:
MissingOptionalLibraryError: if pillow, pdflatex, or poppler are not installed
VisualizationError: if one of the conversion utilities failed for some internal or
file-access reason.
"""
from PIL import Image
tmpfilename = "circuit"
with tempfile.TemporaryDirectory() as tmpdirname:
tmppath = os.path.join(tmpdirname, tmpfilename + ".tex")
_generate_latex_source(
circuit,
filename=tmppath,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
try:
subprocess.run(
[
"pdflatex",
"-halt-on-error",
f"-output-directory={tmpdirname}",
f"{tmpfilename + '.tex'}",
],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
check=True,
)
except OSError as exc:
# OSError should generally not occur, because it's usually only triggered if `pdflatex`
# doesn't exist as a command, but we've already checked that.
raise VisualizationError("`pdflatex` command could not be run.") from exc
except subprocess.CalledProcessError as exc:
with open("latex_error.log", "wb") as error_file:
error_file.write(exc.stdout)
logger.warning(
"Unable to compile LaTeX. Perhaps you are missing the `qcircuit` package."
" The output from the `pdflatex` command is in `latex_error.log`."
)
raise VisualizationError(
"`pdflatex` call did not succeed: see `latex_error.log`."
) from exc
base = os.path.join(tmpdirname, tmpfilename)
try:
subprocess.run(
["pdftocairo", "-singlefile", "-png", "-q", base + ".pdf", base],
check=True,
)
except (OSError, subprocess.CalledProcessError) as exc:
message = "`pdftocairo` failed to produce an image."
logger.warning(message)
raise VisualizationError(message) from exc
image = Image.open(base + ".png")
image = trim_image(image)
if filename:
if filename.endswith(".pdf"):
os.rename(base + ".pdf", filename)
else:
try:
image.save(filename)
except (ValueError, OSError) as exc:
raise VisualizationError(
f"Pillow could not write the image file '{filename}'."
) from exc
return image
def _generate_latex_source(
circuit,
filename=None,
scale=0.7,
style=None,
reverse_bits=False,
plot_barriers=True,
justify=None,
idle_wires=True,
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Convert QuantumCircuit to LaTeX string.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
style (dict or str): dictionary of style or file name of style file
filename (str): optional filename to write latex
reverse_bits (bool): When set to True reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
str: Latex string appropriate for writing to file.
"""
qubits, clbits, nodes = _utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
qcimg = _latex.QCircuitImage(
qubits,
clbits,
nodes,
scale,
style=style,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
layout=None,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
latex = qcimg.latex()
if filename:
with open(filename, "w") as latex_file:
latex_file.write(latex)
return latex
# -----------------------------------------------------------------------------
# matplotlib_circuit_drawer
# -----------------------------------------------------------------------------
def _matplotlib_circuit_drawer(
circuit,
scale=None,
filename=None,
style=None,
plot_barriers=True,
reverse_bits=False,
justify=None,
idle_wires=True,
with_layout=True,
fold=None,
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):
"""Draw a quantum circuit based on matplotlib.
If `%matplotlib inline` is invoked in a Jupyter notebook, it visualizes a circuit inline.
We recommend `%config InlineBackend.figure_format = 'svg'` for the inline visualization.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
filename (str): file path to save image to
style (dict or str): dictionary of style or file name of style file
reverse_bits (bool): When set to True, reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str): `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True.
fold (int): Number of vertical layers allowed before folding. Default is 25.
ax (matplotlib.axes.Axes): An optional Axes object to be used for
the visualization output. If none is specified, a new matplotlib
Figure will be created and used. Additionally, if specified there
will be no returned Figure since it is redundant.
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True bundle classical registers.
Default: ``True``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
matplotlib.figure: a matplotlib figure object for the circuit diagram
if the ``ax`` kwarg is not set.
"""
qubits, clbits, nodes = _utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
if fold is None:
fold = 25
qcd = _matplotlib.MatplotlibDrawer(
qubits,
clbits,
nodes,
scale=scale,
style=style,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
layout=None,
fold=fold,
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
calibrations=None,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
return qcd.draw(filename)

View File

@ -10,7 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""latex visualization backends."""
"""latex visualization backend."""
import io
import math
@ -22,9 +22,10 @@ from qiskit.circuit import Clbit, Qubit, ClassicalRegister, QuantumRegister, Qua
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.library.standard_gates import SwapGate, XGate, ZGate, RZZGate, U1Gate, PhaseGate
from qiskit.circuit.measure import Measure
from qiskit.visualization.qcstyle import load_style
from qiskit.circuit.tools.pi_check import pi_check
from .utils import (
from .qcstyle import load_style
from ._utils import (
get_gate_ctrl_text,
get_param_str,
get_wire_map,

View File

@ -30,8 +30,11 @@ from qiskit.circuit.library.standard_gates import (
ZGate,
)
from qiskit.extensions import Initialize
from qiskit.visualization.qcstyle import load_style
from qiskit.visualization.utils import (
from qiskit.circuit.tools.pi_check import pi_check
from qiskit.utils import optionals as _optionals
from .qcstyle import load_style
from ._utils import (
get_gate_ctrl_text,
get_param_str,
get_wire_map,
@ -39,10 +42,8 @@ from qiskit.visualization.utils import (
get_bit_reg_index,
get_wire_label,
get_condition_label_val,
matplotlib_close_if_inline,
)
from qiskit.circuit.tools.pi_check import pi_check
from qiskit.utils import optionals as _optionals
from ..utils import matplotlib_close_if_inline
# Default gate width and height
WID = 0.65

View File

@ -0,0 +1,396 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""mpl circuit visualization style."""
import json
import os
from warnings import warn
from qiskit import user_config
class DefaultStyle:
"""Creates a Default Style dictionary
**Style Dict Details**
The style dict contains numerous options that define the style of the
output circuit visualization. The style dict is used by the `mpl` or
`latex` output. The options available in the style dict are defined below:
name (str): the name of the style. The name can be set to ``iqx``,
``iqx-dark``, ``textbook``, ``bw``, ``default``, or the name of a
user-created json file. This overrides the setting in the user config
file (usually ``~/.qiskit/settings.conf``).
textcolor (str): the color code to use for all text not inside a gate.
Defaults to ``#000000``
subtextcolor (str): the color code to use for subtext. Defaults to
``#000000``
linecolor (str): the color code to use for lines. Defaults to
``#000000``
creglinecolor (str): the color code to use for classical register
lines. Defaults to ``#778899``
gatetextcolor (str): the color code to use for gate text. Defaults to
``#000000``
gatefacecolor (str): the color code to use for a gate if no color
specified in the 'displaycolor' dict. Defaults to ``#BB8BFF``
barrierfacecolor (str): the color code to use for barriers. Defaults to
``#BDBDBD``
backgroundcolor (str): the color code to use for the background.
Defaults to ``#FFFFFF``
edgecolor (str): the color code to use for gate edges when using the
`bw` style. Defaults to ``#000000``.
fontsize (int): the font size to use for text. Defaults to 13.
subfontsize (int): the font size to use for subtext. Defaults to 8.
showindex (bool): if set to True, show the index numbers at the top.
Defaults to False.
figwidth (int): the maximum width (in inches) for the output figure.
If set to -1, the maximum displayable width will be used.
Defaults to -1.
dpi (int): the DPI to use for the output image. Defaults to 150.
margin (list): a list of margin values to adjust spacing around output
image. Takes a list of 4 ints: [x left, x right, y bottom, y top].
Defaults to [2.0, 0.1, 0.1, 0.3].
creglinestyle (str): The style of line to use for classical registers.
Choices are ``solid``, ``doublet``, or any valid matplotlib
`linestyle` kwarg value. Defaults to ``doublet``.
displaytext (dict): a dictionary of the text to use for certain element
types in the output visualization. These items allow the use of
LaTeX formatting for gate names. The 'displaytext' dict can contain
any number of elements. User created names and labels may be used as
keys, which allow these to have Latex formatting. The default
values are (`default.json`)::
{
'u1': 'U_1',
'u2': 'U_2',
'u3': 'U_3',
'sdg': 'S^\\dagger',
'sx': '\\sqrt{X}',
'sxdg': '\\sqrt{X}^\\dagger',
't': 'T',
'tdg': 'T^\\dagger',
'dcx': 'Dcx',
'iswap': 'Iswap',
'ms': 'MS',
'rx': 'R_X',
'ry': 'R_Y',
'rz': 'R_Z',
'rxx': 'R_{XX}',
'ryy': 'R_{YY}',
'rzx': 'R_{ZX}',
'rzz': 'ZZ',
'reset': '\\left|0\\right\\rangle',
'initialize': '|\\psi\\rangle'
}
displaycolor (dict): the color codes to use for each circuit element in
the form (gate_color, text_color). Colors can also be entered without
the text color, such as 'u1': '#FA74A6', in which case the text color
will always be `gatetextcolor`. The `displaycolor` dict can contain
any number of elements. User names and labels may be used as keys,
which allows for custom colors for user-created gates. The default
values are (`default.json`)::
{
'u1': ('#FA74A6', '#000000'),
'u2': ('#FA74A6', '#000000'),
'u3': ('#FA74A6', '#000000'),
'id': ('#05BAB6', '#000000'),
'u': ('#BB8BFF', '#000000'),
'p': ('#BB8BFF', '#000000'),
'x': ('#05BAB6', '#000000'),
'y': ('#05BAB6', '#000000'),
'z': ('#05BAB6', '#000000'),
'h': ('#6FA4FF', '#000000'),
'cx': ('#6FA4FF', '#000000'),
'ccx': ('#BB8BFF', '#000000'),
'mcx': ('#BB8BFF', '#000000'),
'mcx_gray': ('#BB8BFF', '#000000'),
'cy': ('#6FA4FF', '#000000'),
'cz': ('#6FA4FF', '#000000'),
'swap': ('#6FA4FF', '#000000'),
'cswap': ('#BB8BFF', '#000000'),
'ccswap': ('#BB8BFF', '#000000'),
'dcx': ('#6FA4FF', '#000000'),
'cdcx': ('#BB8BFF', '#000000'),
'ccdcx': ('#BB8BFF', '#000000'),
'iswap': ('#6FA4FF', '#000000'),
's': ('#6FA4FF', '#000000'),
'sdg': ('#6FA4FF', '#000000'),
't': ('#BB8BFF', '#000000'),
'tdg': ('#BB8BFF', '#000000'),
'sx': ('#6FA4FF', '#000000'),
'sxdg': ('#6FA4FF', '#000000')
'r': ('#BB8BFF', '#000000'),
'rx': ('#BB8BFF', '#000000'),
'ry': ('#BB8BFF', '#000000'),
'rz': ('#BB8BFF', '#000000'),
'rxx': ('#BB8BFF', '#000000'),
'ryy': ('#BB8BFF', '#000000'),
'rzx': ('#BB8BFF', '#000000'),
'reset': ('#000000', '#FFFFFF'),
'target': ('#FFFFFF', '#FFFFFF'),
'measure': ('#000000', '#FFFFFF'),
}
"""
def __init__(self):
colors = {
"### Default Colors": "Default Colors",
"basis": "#FA74A6", # Red
"clifford": "#6FA4FF", # Light Blue
"pauli": "#05BAB6", # Green
"def_other": "#BB8BFF", # Purple
"### IQX Colors": "IQX Colors",
"classical": "#002D9C", # Dark Blue
"phase": "#33B1FF", # Cyan
"hadamard": "#FA4D56", # Light Red
"non_unitary": "#A8A8A8", # Medium Gray
"iqx_other": "#9F1853", # Dark Red
"### B/W": "B/W",
"black": "#000000",
"white": "#FFFFFF",
"dark_gray": "#778899",
"light_gray": "#BDBDBD",
}
self.style = {
"name": "default",
"tc": colors["black"], # Non-gate Text Color
"gt": colors["black"], # Gate Text Color
"sc": colors["black"], # Gate Subtext Color
"lc": colors["black"], # Line Color
"cc": colors["dark_gray"], # creg Line Color
"gc": colors["def_other"], # Default Gate Color
"bc": colors["light_gray"], # Barrier Color
"bg": colors["white"], # Background Color
"ec": None, # Edge Color (B/W only)
"fs": 13, # Gate Font Size
"sfs": 8, # Subtext Font Size
"index": False,
"figwidth": -1,
"dpi": 150,
"margin": [2.0, 0.1, 0.1, 0.3],
"cline": "doublet",
"disptex": {
"u1": "U_1",
"u2": "U_2",
"u3": "U_3",
"id": "I",
"sdg": "S^\\dagger",
"sx": "\\sqrt{X}",
"sxdg": "\\sqrt{X}^\\dagger",
"tdg": "T^\\dagger",
"ms": "MS",
"rx": "R_X",
"ry": "R_Y",
"rz": "R_Z",
"rxx": "R_{XX}",
"ryy": "R_{YY}",
"rzx": "R_{ZX}",
"rzz": "ZZ",
"reset": "\\left|0\\right\\rangle",
"initialize": "$|\\psi\\rangle$",
},
"dispcol": {
"u1": (colors["basis"], colors["black"]),
"u2": (colors["basis"], colors["black"]),
"u3": (colors["basis"], colors["black"]),
"u": (colors["def_other"], colors["black"]),
"p": (colors["def_other"], colors["black"]),
"id": (colors["pauli"], colors["black"]),
"x": (colors["pauli"], colors["black"]),
"y": (colors["pauli"], colors["black"]),
"z": (colors["pauli"], colors["black"]),
"h": (colors["clifford"], colors["black"]),
"cx": (colors["clifford"], colors["black"]),
"ccx": (colors["def_other"], colors["black"]),
"mcx": (colors["def_other"], colors["black"]),
"mcx_gray": (colors["def_other"], colors["black"]),
"cy": (colors["clifford"], colors["black"]),
"cz": (colors["clifford"], colors["black"]),
"swap": (colors["clifford"], colors["black"]),
"cswap": (colors["def_other"], colors["black"]),
"ccswap": (colors["def_other"], colors["black"]),
"dcx": (colors["clifford"], colors["black"]),
"cdcx": (colors["def_other"], colors["black"]),
"ccdcx": (colors["def_other"], colors["black"]),
"iswap": (colors["clifford"], colors["black"]),
"s": (colors["clifford"], colors["black"]),
"sdg": (colors["clifford"], colors["black"]),
"t": (colors["def_other"], colors["black"]),
"tdg": (colors["def_other"], colors["black"]),
"sx": (colors["clifford"], colors["black"]),
"sxdg": (colors["clifford"], colors["black"]),
"r": (colors["def_other"], colors["black"]),
"rx": (colors["def_other"], colors["black"]),
"ry": (colors["def_other"], colors["black"]),
"rz": (colors["def_other"], colors["black"]),
"rxx": (colors["def_other"], colors["black"]),
"ryy": (colors["def_other"], colors["black"]),
"rzx": (colors["def_other"], colors["black"]),
"reset": (colors["black"], colors["white"]),
"target": (colors["white"], colors["white"]),
"measure": (colors["black"], colors["white"]),
},
}
def load_style(style):
"""Utility function to load style from json files and call set_style."""
current_style = DefaultStyle().style
style_name = "default"
def_font_ratio = current_style["fs"] / current_style["sfs"]
config = user_config.get_config()
if style is None:
if config:
style = config.get("circuit_mpl_style", "default")
else:
style = "default"
if style is False:
style_name = "bw"
elif isinstance(style, dict) and "name" in style:
style_name = style["name"]
elif isinstance(style, str):
style_name = style
elif not isinstance(style, (str, dict)):
warn(
f"style parameter '{style}' must be a str or a dictionary. Will use default style.",
UserWarning,
2,
)
if style_name.endswith(".json"):
style_name = style_name[:-5]
# Search for file in 'styles' dir, then config_path, and finally 'cwd'
style_path = []
if style_name != "default":
style_name = style_name + ".json"
spath = os.path.dirname(os.path.abspath(__file__))
style_path.append(os.path.join(spath, "styles", style_name))
if config:
config_path = config.get("circuit_mpl_style_path", "")
if config_path:
for path in config_path:
style_path.append(os.path.normpath(os.path.join(path, style_name)))
style_path.append(os.path.normpath(os.path.join("", style_name)))
for path in style_path:
exp_user = os.path.expanduser(path)
if os.path.isfile(exp_user):
try:
with open(exp_user) as infile:
json_style = json.load(infile)
set_style(current_style, json_style)
break
except json.JSONDecodeError as err:
warn(
f"Could not decode JSON in file '{path}': {str(err)}. "
"Will use default style.",
UserWarning,
2,
)
break
except (OSError, FileNotFoundError):
warn(
f"Error loading JSON file '{path}'. Will use default style.",
UserWarning,
2,
)
break
else:
warn(
f"Style JSON file '{style_name}' not found in any of these locations: "
f"{', '.join(style_path)}. "
"Will use default style.",
UserWarning,
2,
)
if isinstance(style, dict):
set_style(current_style, style)
return current_style, def_font_ratio
def set_style(current_style, new_style):
"""Utility function to take elements in new_style and
write them into current_style.
"""
valid_fields = {
"name",
"textcolor",
"gatetextcolor",
"subtextcolor",
"linecolor",
"creglinecolor",
"gatefacecolor",
"barrierfacecolor",
"backgroundcolor",
"edgecolor",
"fontsize",
"subfontsize",
"showindex",
"figwidth",
"dpi",
"margin",
"creglinestyle",
"displaytext",
"displaycolor",
}
current_style.update(new_style)
current_style["tc"] = current_style.get("textcolor", current_style["tc"])
current_style["gt"] = current_style.get("gatetextcolor", current_style["gt"])
current_style["sc"] = current_style.get("subtextcolor", current_style["sc"])
current_style["lc"] = current_style.get("linecolor", current_style["lc"])
current_style["cc"] = current_style.get("creglinecolor", current_style["cc"])
current_style["gc"] = current_style.get("gatefacecolor", current_style["gc"])
current_style["bc"] = current_style.get("barrierfacecolor", current_style["bc"])
current_style["bg"] = current_style.get("backgroundcolor", current_style["bg"])
current_style["ec"] = current_style.get("edgecolor", current_style["ec"])
current_style["fs"] = current_style.get("fontsize", current_style["fs"])
current_style["sfs"] = current_style.get("subfontsize", current_style["sfs"])
current_style["index"] = current_style.get("showindex", current_style["index"])
current_style["cline"] = current_style.get("creglinestyle", current_style["cline"])
current_style["disptex"] = {**current_style["disptex"], **new_style.get("displaytext", {})}
current_style["dispcol"] = {**current_style["dispcol"], **new_style.get("displaycolor", {})}
unsupported_keys = set(new_style) - valid_fields
if unsupported_keys:
warn(
f"style option/s ({', '.join(unsupported_keys)}) is/are not supported",
UserWarning,
2,
)

View File

@ -24,7 +24,8 @@ from qiskit.circuit import Reset
from qiskit.circuit import Measure
from qiskit.circuit.library.standard_gates import IGate, RZZGate, SwapGate, SXGate, SXdgGate
from qiskit.circuit.tools.pi_check import pi_check
from qiskit.visualization.utils import (
from ._utils import (
get_gate_ctrl_text,
get_param_str,
get_wire_map,
@ -33,7 +34,7 @@ from qiskit.visualization.utils import (
get_wire_label,
get_condition_label_val,
)
from .exceptions import VisualizationError
from ..exceptions import VisualizationError
class TextDrawerCregBundle(VisualizationError):

View File

@ -10,655 +10,10 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Module for the primary interface to the circuit drawers.
This module contains the end user facing API for drawing quantum circuits.
There are 3 available drawer backends available:
0. ASCII art
1. LaTeX
2. Matplotlib
This provides a single function entry point to drawing a circuit object with
any of the backends.
"""
import logging
import os
import shutil
import subprocess
import tempfile
from warnings import warn
from qiskit import user_config
from qiskit.utils import optionals as _optionals
from qiskit.visualization.exceptions import VisualizationError
from qiskit.visualization import latex as _latex
from qiskit.visualization import text as _text
from qiskit.visualization import utils
from qiskit.visualization import matplotlib as _matplotlib
logger = logging.getLogger(__name__)
def circuit_drawer(
circuit,
scale=None,
filename=None,
style=None,
output=None,
interactive=False,
plot_barriers=True,
reverse_bits=False,
justify=None,
vertical_compression="medium",
idle_wires=True,
with_layout=True,
fold=None,
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:
**text**: ASCII art TextDrawing that can be printed in the console.
**matplotlib**: images with color rendered purely in Python.
**latex**: high-quality images compiled via latex.
**latex_source**: raw uncompiled latex output.
Args:
circuit (QuantumCircuit): the quantum circuit to draw
scale (float): scale of image to draw (shrink if < 1.0). Only used by
the `mpl`, `latex` and `latex_source` outputs. Defaults to 1.0.
filename (str): file path to save image to. Defaults to None.
style (dict or str): dictionary of style or file name of style json file.
This option is only used by the `mpl` or `latex` output type.
If `style` is a str, it is used as the path to a json file
which contains a style dict. The file will be opened, parsed, and
then any style elements in the dict will replace the default values
in the input dict. A file to be loaded must end in ``.json``, but
the name entered here can omit ``.json``. For example,
``style='iqx.json'`` or ``style='iqx'``.
If `style` is a dict and the ``'name'`` key is set, that name
will be used to load a json file, followed by loading the other
items in the style dict. For example, ``style={'name': 'iqx'}``.
If `style` is not a str and `name` is not a key in the style dict,
then the default value from the user config file (usually
``~/.qiskit/settings.conf``) will be used, for example,
``circuit_mpl_style = iqx``.
If none of these are set, the `default` style will be used.
The search path for style json files can be specified in the user
config, for example,
``circuit_mpl_style_path = /home/user/styles:/home/user``.
See: :class:`~qiskit.visualization.qcstyle.DefaultStyle` for more
information on the contents.
output (str): select the output method to use for drawing the circuit.
Valid choices are ``text``, ``mpl``, ``latex``, ``latex_source``.
By default the `text` drawer is used unless the user config file
(usually ``~/.qiskit/settings.conf``) has an alternative backend set
as the default. For example, ``circuit_drawer = latex``. If the output
kwarg is set, that backend will always be used over the default in
the user config file.
interactive (bool): when set to true, show the circuit in a new window
(for `mpl` this depends on the matplotlib backend being used
supporting this). Note when used with either the `text` or the
`latex_source` output type this has no effect and will be silently
ignored. Defaults to False.
reverse_bits (bool): when set to True, reverse the bit order inside
registers for the output visualization. Defaults to False.
plot_barriers (bool): enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (string): options are ``left``, ``right`` or ``none``. If
anything else is supplied, it defaults to left justified. It refers
to where gates should be placed in the output circuit if there is
an option. ``none`` results in each gate being placed in its own
column.
vertical_compression (string): ``high``, ``medium`` or ``low``. It
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
the `text` output, will be silently ignored otherwise.
idle_wires (bool): include idle wires (wires with no circuit elements)
in output visualization. Default is True.
with_layout (bool): include layout information, with labels on the
physical layout. Default is True.
fold (int): sets pagination. It can be disabled using -1. In `text`,
sets the length of the lines. This is useful when the drawing does
not fit in the console. If None (default), it will try to guess the
console width using ``shutil.get_terminal_size()``. However, if
running in jupyter, the default line length is set to 80 characters.
In `mpl`, it is the number of (visual) layers before folding.
Default is 25.
ax (matplotlib.axes.Axes): Only used by the `mpl` backend. An optional
Axes object to be used for the visualization output. If none is
specified, a new matplotlib Figure will be created and used.
Additionally, if specified there will be no returned Figure since
it is redundant.
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Default is False.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default is True.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
:class:`TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
:class:`str`:
* `TextDrawing` (output='text')
A drawing that can be printed as ascii art.
* `matplotlib.figure.Figure` (output='mpl')
A matplotlib figure object for the circuit diagram.
* `PIL.Image` (output='latex')
An in-memory representation of the image of the circuit diagram.
* `str` (output='latex_source')
The LaTeX source code for visualizing the circuit diagram.
Raises:
VisualizationError: when an invalid output method is selected
MissingOptionalLibraryError: when the output methods requires non-installed libraries.
Example:
.. jupyter-execute::
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.visualization import circuit_drawer
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
circuit_drawer(qc, output='mpl', style={'backgroundcolor': '#EEEEEE'})
"""
image = None
config = user_config.get_config()
# Get default from config file else use text
default_output = "text"
if config:
default_output = config.get("circuit_drawer", "text")
if default_output == "auto":
if _optionals.HAS_MATPLOTLIB:
default_output = "mpl"
else:
default_output = "text"
if output is None:
output = default_output
if wire_order is not None and reverse_bits:
raise VisualizationError(
"The wire_order option cannot be set when the reverse_bits option is True."
)
if wire_order is not None and len(wire_order) != circuit.num_qubits + circuit.num_clbits:
raise VisualizationError(
"The wire_order list must be the same "
"length as the sum of the number of qubits and clbits in the circuit."
)
if wire_order is not None and set(wire_order) != set(
range(circuit.num_qubits + circuit.num_clbits)
):
raise VisualizationError(
"There must be one and only one entry in the "
"wire_order list for the index of each qubit and each clbit in the circuit."
)
if cregbundle and (reverse_bits or wire_order is not None):
cregbundle = False
warn(
"Cregbundle set to False since either reverse_bits or wire_order has been set.",
RuntimeWarning,
2,
)
if output == "text":
return _text_circuit_drawer(
circuit,
filename=filename,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
justify=justify,
vertical_compression=vertical_compression,
idle_wires=idle_wires,
with_layout=with_layout,
fold=fold,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex":
image = _latex_circuit_drawer(
circuit,
filename=filename,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex_source":
return _generate_latex_source(
circuit,
filename=filename,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "mpl":
image = _matplotlib_circuit_drawer(
circuit,
scale=scale,
filename=filename,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
fold=fold,
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
else:
raise VisualizationError(
"Invalid output type %s selected. The only valid choices "
"are text, latex, latex_source, and mpl" % output
)
if image and interactive:
image.show()
return image
# -----------------------------------------------------------------------------
# _text_circuit_drawer
# -----------------------------------------------------------------------------
def _text_circuit_drawer(
circuit,
filename=None,
reverse_bits=False,
plot_barriers=True,
justify=None,
vertical_compression="high",
idle_wires=True,
with_layout=True,
fold=None,
initial_state=True,
cregbundle=False,
encoding=None,
wire_order=None,
):
"""Draws a circuit using ascii art.
Args:
circuit (QuantumCircuit): Input circuit
filename (str): Optional filename to write the result
reverse_bits (bool): Rearrange the bits in reverse order.
plot_barriers (bool): Draws the barriers when they are there.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
vertical_compression (string): `high`, `medium`, or `low`. It merges the
lines so the drawing will take less vertical room. Default is `high`.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information with labels on the physical
layout. Default: True
fold (int): Optional. Breaks the circuit drawing to this length. This
is useful when the drawing does not fit in the console. If
None (default), it will try to guess the console width using
`shutil.get_terminal_size()`. If you don't want pagination
at all, set `fold=-1`.
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``True``.
encoding (str): Optional. Sets the encoding preference of the output.
Default: ``sys.stdout.encoding``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
TextDrawing: An instance that, when printed, draws the circuit in ascii art.
Raises:
VisualizationError: When the filename extenstion is not .txt.
"""
qubits, clbits, nodes = utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
text_drawing = _text.TextDrawing(
qubits,
clbits,
nodes,
reverse_bits=reverse_bits,
layout=None,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
encoding=encoding,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
text_drawing.plotbarriers = plot_barriers
text_drawing.line_length = fold
text_drawing.vertical_compression = vertical_compression
if filename:
text_drawing.dump(filename, encoding=encoding)
return text_drawing
# -----------------------------------------------------------------------------
# latex_circuit_drawer
# -----------------------------------------------------------------------------
@_optionals.HAS_PDFLATEX.require_in_call("LaTeX circuit drawing")
@_optionals.HAS_PDFTOCAIRO.require_in_call("LaTeX circuit drawing")
@_optionals.HAS_PIL.require_in_call("LaTeX circuit drawing")
def _latex_circuit_drawer(
circuit,
scale=0.7,
style=None,
filename=None,
plot_barriers=True,
reverse_bits=False,
justify=None,
idle_wires=True,
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Draw a quantum circuit based on latex (Qcircuit package)
Requires version >=2.6.0 of the qcircuit LaTeX package.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
style (dict or str): dictionary of style or file name of style file
filename (str): file path to save image to
reverse_bits (bool): When set to True reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
PIL.Image: an in-memory representation of the circuit diagram
Raises:
MissingOptionalLibraryError: if pillow, pdflatex, or poppler are not installed
VisualizationError: if one of the conversion utilities failed for some internal or
file-access reason.
"""
from PIL import Image
tmpfilename = "circuit"
with tempfile.TemporaryDirectory() as tmpdirname:
tmppath = os.path.join(tmpdirname, tmpfilename + ".tex")
_generate_latex_source(
circuit,
filename=tmppath,
scale=scale,
style=style,
plot_barriers=plot_barriers,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
try:
subprocess.run(
[
"pdflatex",
"-halt-on-error",
f"-output-directory={tmpdirname}",
f"{tmpfilename + '.tex'}",
],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
check=True,
)
except OSError as exc:
# OSError should generally not occur, because it's usually only triggered if `pdflatex`
# doesn't exist as a command, but we've already checked that.
raise VisualizationError("`pdflatex` command could not be run.") from exc
except subprocess.CalledProcessError as exc:
with open("latex_error.log", "wb") as error_file:
error_file.write(exc.stdout)
logger.warning(
"Unable to compile LaTeX. Perhaps you are missing the `qcircuit` package."
" The output from the `pdflatex` command is in `latex_error.log`."
)
raise VisualizationError(
"`pdflatex` call did not succeed: see `latex_error.log`."
) from exc
base = os.path.join(tmpdirname, tmpfilename)
try:
subprocess.run(
["pdftocairo", "-singlefile", "-png", "-q", base + ".pdf", base],
check=True,
)
except (OSError, subprocess.CalledProcessError) as exc:
message = "`pdftocairo` failed to produce an image."
logger.warning(message)
raise VisualizationError(message) from exc
image = Image.open(base + ".png")
image = utils._trim(image)
if filename:
if filename.endswith(".pdf"):
shutil.move(base + ".pdf", filename)
else:
try:
image.save(filename)
except (ValueError, OSError) as exc:
raise VisualizationError(
f"Pillow could not write the image file '{filename}'."
) from exc
return image
def _generate_latex_source(
circuit,
filename=None,
scale=0.7,
style=None,
reverse_bits=False,
plot_barriers=True,
justify=None,
idle_wires=True,
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Convert QuantumCircuit to LaTeX string.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
style (dict or str): dictionary of style or file name of style file
filename (str): optional filename to write latex
reverse_bits (bool): When set to True reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
str: Latex string appropriate for writing to file.
"""
qubits, clbits, nodes = utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
qcimg = _latex.QCircuitImage(
qubits,
clbits,
nodes,
scale,
style=style,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
layout=None,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
latex = qcimg.latex()
if filename:
with open(filename, "w") as latex_file:
latex_file.write(latex)
return latex
# -----------------------------------------------------------------------------
# matplotlib_circuit_drawer
# -----------------------------------------------------------------------------
def _matplotlib_circuit_drawer(
circuit,
scale=None,
filename=None,
style=None,
plot_barriers=True,
reverse_bits=False,
justify=None,
idle_wires=True,
with_layout=True,
fold=None,
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):
"""Draw a quantum circuit based on matplotlib.
If `%matplotlib inline` is invoked in a Jupyter notebook, it visualizes a circuit inline.
We recommend `%config InlineBackend.figure_format = 'svg'` for the inline visualization.
Args:
circuit (QuantumCircuit): a quantum circuit
scale (float): scaling factor
filename (str): file path to save image to
style (dict or str): dictionary of style or file name of style file
reverse_bits (bool): When set to True, reverse the bit order inside
registers for the output visualization.
plot_barriers (bool): Enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (str): `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
with_layout (bool): Include layout information, with labels on the physical
layout. Default: True.
fold (int): Number of vertical layers allowed before folding. Default is 25.
ax (matplotlib.axes.Axes): An optional Axes object to be used for
the visualization output. If none is specified, a new matplotlib
Figure will be created and used. Additionally, if specified there
will be no returned Figure since it is redundant.
initial_state (bool): Optional. Adds |0> in the beginning of the line.
Default: `False`.
cregbundle (bool): Optional. If set True bundle classical registers.
Default: ``True``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Returns:
matplotlib.figure: a matplotlib figure object for the circuit diagram
if the ``ax`` kwarg is not set.
"""
qubits, clbits, nodes = utils._get_layered_instructions(
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
if fold is None:
fold = 25
qcd = _matplotlib.MatplotlibDrawer(
qubits,
clbits,
nodes,
scale=scale,
style=style,
reverse_bits=reverse_bits,
plot_barriers=plot_barriers,
layout=None,
fold=fold,
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
global_phase=None,
calibrations=None,
qregs=None,
cregs=None,
with_layout=with_layout,
circuit=circuit,
)
return qcd.draw(filename)
# Temporary import from 0.22.0 to be deprecated in future
# pylint: disable=unused-import
from .circuit.circuit_visualization import circuit_drawer

View File

@ -19,9 +19,9 @@ import inspect
import tempfile
from qiskit.utils import optionals as _optionals
from qiskit.visualization import utils
from qiskit.visualization.exceptions import VisualizationError
from qiskit.transpiler.basepasses import AnalysisPass, TransformationPass
from . import utils
from .exceptions import VisualizationError
DEFAULT_STYLE = {AnalysisPass: "red", TransformationPass: "blue"}

View File

@ -1,186 +0,0 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
matplotlib pulse visualization.
"""
import warnings
from typing import Union, Callable, List, Dict, Tuple
from qiskit.pulse import Schedule, Instruction, Waveform
from qiskit.pulse.channels import Channel
from qiskit.utils import optionals as _optionals
from qiskit.visualization.pulse.qcstyle import PulseStyle, SchedStyle
from qiskit.visualization.exceptions import VisualizationError
from qiskit.visualization.pulse import matplotlib as _matplotlib
from qiskit.visualization.utils import matplotlib_close_if_inline
@_optionals.HAS_MATPLOTLIB.require_in_call
def pulse_drawer(
data: Union[Waveform, Union[Schedule, Instruction]],
dt: int = 1,
style: Union[PulseStyle, SchedStyle] = None,
filename: str = None,
interp_method: Callable = None,
scale: float = None,
channel_scales: Dict[Channel, float] = None,
plot_all: bool = False,
plot_range: Tuple[float, float] = None,
interactive: bool = False,
table: bool = False,
label: bool = False,
framechange: bool = True,
channels: List[Channel] = None,
show_framechange_channels: bool = True,
draw_title: bool = False,
):
"""Deprecated.
Plot the interpolated envelope of pulse and schedule.
Args:
data: Pulse or schedule object to plot.
dt: Time interval of samples. Pulses are visualized in the unit of
cycle time if not provided.
style: A style sheet to configure plot appearance.
See :mod:`~qiskit.visualization.pulse.qcstyle` for more information.
filename: Name required to save pulse image. The drawer just returns
`matplot.Figure` object if not provided.
interp_method: Interpolation function. Interpolation is disabled in default.
See :mod:`~qiskit.visualization.pulse.interpolation` for more information.
scale: Scaling of waveform amplitude. Pulses are automatically
scaled channel by channel if not provided.
channel_scales: Dictionary of scale factor for specific channels.
Scale of channels not specified here is overwritten by `scale`.
plot_all: When set `True` plot empty channels.
plot_range: A tuple of time range to plot.
interactive: When set `True` show the circuit in a new window.
This depends on the matplotlib backend being used supporting this.
table: When set `True` draw event table for supported commands.
label: When set `True` draw label for individual instructions.
framechange: When set `True` draw framechange indicators.
channels: A list of channel names to plot.
All non-empty channels are shown if not provided.
show_framechange_channels: When set `True` plot channels
with only framechange instructions.
draw_title: Add a title to the plot when set to ``True``.
Returns:
matplotlib.figure.Figure: A matplotlib figure object for the pulse envelope.
Example:
This example shows how to visualize your pulse schedule.
Pulse names are added to the plot, unimportant channels are removed
and the time window is truncated to draw out U3 pulse sequence of interest.
.. jupyter-execute::
import numpy as np
import qiskit
from qiskit import pulse
from qiskit.providers.fake_provider import FakeAlmaden
inst_map = FakeAlmaden().defaults().instruction_schedule_map
sched = pulse.Schedule()
sched += inst_map.get('u3', 0, np.pi, 0, np.pi)
sched += inst_map.get('measure', list(range(20))) << sched.duration
channels = [pulse.DriveChannel(0), pulse.MeasureChannel(0)]
scales = {pulse.DriveChannel(0): 10}
qiskit.visualization.pulse_drawer(sched,
channels=channels,
plot_range=(0, 1000),
label=True,
channel_scales=scales)
You are also able to call visualization module from the instance method::
sched.draw(channels=channels, plot_range=(0, 1000), label=True, channel_scales=scales)
To customize the format of the schedule plot, you can setup your style sheet.
.. jupyter-execute::
import numpy as np
import qiskit
from qiskit import pulse
from qiskit.providers.fake_provider import FakeAlmaden
inst_map = FakeAlmaden().defaults().instruction_schedule_map
sched = pulse.Schedule()
sched += inst_map.get('u3', 0, np.pi, 0, np.pi)
sched += inst_map.get('measure', list(range(20))) << sched.duration
# setup style sheet
my_style = qiskit.visualization.SchedStyle(
figsize = (10, 5),
bg_color='w',
d_ch_color = ['#32cd32', '#556b2f'])
channels = [pulse.DriveChannel(0), pulse.MeasureChannel(0)]
scales = {pulse.DriveChannel(0): 10}
qiskit.visualization.pulse_drawer(sched, style=my_style,
channels=channels,
plot_range=(0, 1000),
label=True,
channel_scales=scales)
Raises:
VisualizationError: when invalid data is given
MissingOptionalLibraryError: when matplotlib is not installed
"""
warnings.warn(
"This legacy pulse drawer is deprecated and will be removed no earlier than "
"3 months after the release date. Use `qiskit.visualization.pulse_drawer_v2` "
"instead. After the legacy drawer is removed, the import path of this module "
"will be dedicated to the v2 drawer. "
"New drawer will provide much more flexibility with richer stylesheets "
"and cleaner visualization.",
DeprecationWarning,
)
if isinstance(data, Waveform):
drawer = _matplotlib.WaveformDrawer(style=style)
image = drawer.draw(data, dt=dt, interp_method=interp_method, scale=scale)
elif isinstance(data, (Schedule, Instruction)):
drawer = _matplotlib.ScheduleDrawer(style=style)
image = drawer.draw(
data,
dt=dt,
interp_method=interp_method,
scale=scale,
channel_scales=channel_scales,
plot_range=plot_range,
plot_all=plot_all,
table=table,
label=label,
framechange=framechange,
channels=channels,
show_framechange_channels=show_framechange_channels,
draw_title=draw_title,
)
else:
raise VisualizationError("This data cannot be visualized.")
if filename:
image.savefig(filename, dpi=drawer.style.dpi, bbox_inches="tight")
matplotlib_close_if_inline(image)
if image and interactive:
image.show()
return image

View File

@ -12,385 +12,6 @@
"""mpl circuit visualization style."""
import json
import os
from warnings import warn
from qiskit import user_config
class DefaultStyle:
"""Creates a Default Style dictionary
**Style Dict Details**
The style dict contains numerous options that define the style of the
output circuit visualization. The style dict is used by the `mpl` or
`latex` output. The options available in the style dict are defined below:
name (str): the name of the style. The name can be set to ``iqx``,
``iqx-dark``, ``textbook``, ``bw``, ``default``, or the name of a
user-created json file. This overrides the setting in the user config
file (usually ``~/.qiskit/settings.conf``).
textcolor (str): the color code to use for all text not inside a gate.
Defaults to ``#000000``
subtextcolor (str): the color code to use for subtext. Defaults to
``#000000``
linecolor (str): the color code to use for lines. Defaults to
``#000000``
creglinecolor (str): the color code to use for classical register
lines. Defaults to ``#778899``
gatetextcolor (str): the color code to use for gate text. Defaults to
``#000000``
gatefacecolor (str): the color code to use for a gate if no color
specified in the 'displaycolor' dict. Defaults to ``#BB8BFF``
barrierfacecolor (str): the color code to use for barriers. Defaults to
``#BDBDBD``
backgroundcolor (str): the color code to use for the background.
Defaults to ``#FFFFFF``
edgecolor (str): the color code to use for gate edges when using the
`bw` style. Defaults to ``#000000``.
fontsize (int): the font size to use for text. Defaults to 13.
subfontsize (int): the font size to use for subtext. Defaults to 8.
showindex (bool): if set to True, show the index numbers at the top.
Defaults to False.
figwidth (int): the maximum width (in inches) for the output figure.
If set to -1, the maximum displayable width will be used.
Defaults to -1.
dpi (int): the DPI to use for the output image. Defaults to 150.
margin (list): a list of margin values to adjust spacing around output
image. Takes a list of 4 ints: [x left, x right, y bottom, y top].
Defaults to [2.0, 0.1, 0.1, 0.3].
creglinestyle (str): The style of line to use for classical registers.
Choices are ``solid``, ``doublet``, or any valid matplotlib
`linestyle` kwarg value. Defaults to ``doublet``.
displaytext (dict): a dictionary of the text to use for certain element
types in the output visualization. These items allow the use of
LaTeX formatting for gate names. The 'displaytext' dict can contain
any number of elements. User created names and labels may be used as
keys, which allow these to have Latex formatting. The default
values are (`default.json`)::
{
'u1': 'U_1',
'u2': 'U_2',
'u3': 'U_3',
'sdg': 'S^\\dagger',
'sx': '\\sqrt{X}',
'sxdg': '\\sqrt{X}^\\dagger',
't': 'T',
'tdg': 'T^\\dagger',
'dcx': 'Dcx',
'iswap': 'Iswap',
'ms': 'MS',
'rx': 'R_X',
'ry': 'R_Y',
'rz': 'R_Z',
'rxx': 'R_{XX}',
'ryy': 'R_{YY}',
'rzx': 'R_{ZX}',
'rzz': 'ZZ',
'reset': '\\left|0\\right\\rangle',
'initialize': '|\\psi\\rangle'
}
displaycolor (dict): the color codes to use for each circuit element in
the form (gate_color, text_color). Colors can also be entered without
the text color, such as 'u1': '#FA74A6', in which case the text color
will always be `gatetextcolor`. The `displaycolor` dict can contain
any number of elements. User names and labels may be used as keys,
which allows for custom colors for user-created gates. The default
values are (`default.json`)::
{
'u1': ('#FA74A6', '#000000'),
'u2': ('#FA74A6', '#000000'),
'u3': ('#FA74A6', '#000000'),
'id': ('#05BAB6', '#000000'),
'u': ('#BB8BFF', '#000000'),
'p': ('#BB8BFF', '#000000'),
'x': ('#05BAB6', '#000000'),
'y': ('#05BAB6', '#000000'),
'z': ('#05BAB6', '#000000'),
'h': ('#6FA4FF', '#000000'),
'cx': ('#6FA4FF', '#000000'),
'ccx': ('#BB8BFF', '#000000'),
'mcx': ('#BB8BFF', '#000000'),
'mcx_gray': ('#BB8BFF', '#000000'),
'cy': ('#6FA4FF', '#000000'),
'cz': ('#6FA4FF', '#000000'),
'swap': ('#6FA4FF', '#000000'),
'cswap': ('#BB8BFF', '#000000'),
'ccswap': ('#BB8BFF', '#000000'),
'dcx': ('#6FA4FF', '#000000'),
'cdcx': ('#BB8BFF', '#000000'),
'ccdcx': ('#BB8BFF', '#000000'),
'iswap': ('#6FA4FF', '#000000'),
's': ('#6FA4FF', '#000000'),
'sdg': ('#6FA4FF', '#000000'),
't': ('#BB8BFF', '#000000'),
'tdg': ('#BB8BFF', '#000000'),
'sx': ('#6FA4FF', '#000000'),
'sxdg': ('#6FA4FF', '#000000')
'r': ('#BB8BFF', '#000000'),
'rx': ('#BB8BFF', '#000000'),
'ry': ('#BB8BFF', '#000000'),
'rz': ('#BB8BFF', '#000000'),
'rxx': ('#BB8BFF', '#000000'),
'ryy': ('#BB8BFF', '#000000'),
'rzx': ('#BB8BFF', '#000000'),
'reset': ('#000000', '#FFFFFF'),
'target': ('#FFFFFF', '#FFFFFF'),
'measure': ('#000000', '#FFFFFF'),
}
"""
def __init__(self):
colors = {
"### Default Colors": "Default Colors",
"basis": "#FA74A6", # Red
"clifford": "#6FA4FF", # Light Blue
"pauli": "#05BAB6", # Green
"def_other": "#BB8BFF", # Purple
"### IQX Colors": "IQX Colors",
"classical": "#002D9C", # Dark Blue
"phase": "#33B1FF", # Cyan
"hadamard": "#FA4D56", # Light Red
"non_unitary": "#A8A8A8", # Medium Gray
"iqx_other": "#9F1853", # Dark Red
"### B/W": "B/W",
"black": "#000000",
"white": "#FFFFFF",
"dark_gray": "#778899",
"light_gray": "#BDBDBD",
}
self.style = {
"name": "default",
"tc": colors["black"], # Non-gate Text Color
"gt": colors["black"], # Gate Text Color
"sc": colors["black"], # Gate Subtext Color
"lc": colors["black"], # Line Color
"cc": colors["dark_gray"], # creg Line Color
"gc": colors["def_other"], # Default Gate Color
"bc": colors["light_gray"], # Barrier Color
"bg": colors["white"], # Background Color
"ec": None, # Edge Color (B/W only)
"fs": 13, # Gate Font Size
"sfs": 8, # Subtext Font Size
"index": False,
"figwidth": -1,
"dpi": 150,
"margin": [2.0, 0.1, 0.1, 0.3],
"cline": "doublet",
"disptex": {
"u1": "U_1",
"u2": "U_2",
"u3": "U_3",
"id": "I",
"sdg": "S^\\dagger",
"sx": "\\sqrt{X}",
"sxdg": "\\sqrt{X}^\\dagger",
"tdg": "T^\\dagger",
"ms": "MS",
"rx": "R_X",
"ry": "R_Y",
"rz": "R_Z",
"rxx": "R_{XX}",
"ryy": "R_{YY}",
"rzx": "R_{ZX}",
"rzz": "ZZ",
"reset": "\\left|0\\right\\rangle",
"initialize": "$|\\psi\\rangle$",
},
"dispcol": {
"u1": (colors["basis"], colors["black"]),
"u2": (colors["basis"], colors["black"]),
"u3": (colors["basis"], colors["black"]),
"u": (colors["def_other"], colors["black"]),
"p": (colors["def_other"], colors["black"]),
"id": (colors["pauli"], colors["black"]),
"x": (colors["pauli"], colors["black"]),
"y": (colors["pauli"], colors["black"]),
"z": (colors["pauli"], colors["black"]),
"h": (colors["clifford"], colors["black"]),
"cx": (colors["clifford"], colors["black"]),
"ccx": (colors["def_other"], colors["black"]),
"mcx": (colors["def_other"], colors["black"]),
"mcx_gray": (colors["def_other"], colors["black"]),
"cy": (colors["clifford"], colors["black"]),
"cz": (colors["clifford"], colors["black"]),
"swap": (colors["clifford"], colors["black"]),
"cswap": (colors["def_other"], colors["black"]),
"ccswap": (colors["def_other"], colors["black"]),
"dcx": (colors["clifford"], colors["black"]),
"cdcx": (colors["def_other"], colors["black"]),
"ccdcx": (colors["def_other"], colors["black"]),
"iswap": (colors["clifford"], colors["black"]),
"s": (colors["clifford"], colors["black"]),
"sdg": (colors["clifford"], colors["black"]),
"t": (colors["def_other"], colors["black"]),
"tdg": (colors["def_other"], colors["black"]),
"sx": (colors["clifford"], colors["black"]),
"sxdg": (colors["clifford"], colors["black"]),
"r": (colors["def_other"], colors["black"]),
"rx": (colors["def_other"], colors["black"]),
"ry": (colors["def_other"], colors["black"]),
"rz": (colors["def_other"], colors["black"]),
"rxx": (colors["def_other"], colors["black"]),
"ryy": (colors["def_other"], colors["black"]),
"rzx": (colors["def_other"], colors["black"]),
"reset": (colors["black"], colors["white"]),
"target": (colors["white"], colors["white"]),
"measure": (colors["black"], colors["white"]),
},
}
def load_style(style):
"""Utility function to load style from json files and call set_style."""
current_style = DefaultStyle().style
style_name = "default"
def_font_ratio = current_style["fs"] / current_style["sfs"]
config = user_config.get_config()
if style is None:
if config:
style = config.get("circuit_mpl_style", "default")
else:
style = "default"
if style is False:
style_name = "bw"
elif isinstance(style, dict) and "name" in style:
style_name = style["name"]
elif isinstance(style, str):
style_name = style
elif not isinstance(style, (str, dict)):
warn(
f"style parameter '{style}' must be a str or a dictionary. Will use default style.",
UserWarning,
2,
)
if style_name.endswith(".json"):
style_name = style_name[:-5]
# Search for file in 'styles' dir, then config_path, and finally 'cwd'
style_path = []
if style_name != "default":
style_name = style_name + ".json"
spath = os.path.dirname(os.path.abspath(__file__))
style_path.append(os.path.join(spath, "styles", style_name))
if config:
config_path = config.get("circuit_mpl_style_path", "")
if config_path:
for path in config_path:
style_path.append(os.path.normpath(os.path.join(path, style_name)))
style_path.append(os.path.normpath(os.path.join("", style_name)))
for path in style_path:
exp_user = os.path.expanduser(path)
if os.path.isfile(exp_user):
try:
with open(exp_user) as infile:
json_style = json.load(infile)
set_style(current_style, json_style)
break
except json.JSONDecodeError as err:
warn(
f"Could not decode JSON in file '{path}': {str(err)}. "
"Will use default style.",
UserWarning,
2,
)
break
except (OSError, FileNotFoundError):
warn(
f"Error loading JSON file '{path}'. Will use default style.",
UserWarning,
2,
)
break
else:
warn(
f"Style JSON file '{style_name}' not found in any of these locations: "
f"{', '.join(style_path)}. "
"Will use default style.",
UserWarning,
2,
)
if isinstance(style, dict):
set_style(current_style, style)
return current_style, def_font_ratio
def set_style(current_style, new_style):
"""Utility function to take elements in new_style and
write them into current_style.
"""
valid_fields = {
"name",
"textcolor",
"gatetextcolor",
"subtextcolor",
"linecolor",
"creglinecolor",
"gatefacecolor",
"barrierfacecolor",
"backgroundcolor",
"edgecolor",
"fontsize",
"subfontsize",
"showindex",
"figwidth",
"dpi",
"margin",
"creglinestyle",
"displaytext",
"displaycolor",
}
current_style.update(new_style)
current_style["tc"] = current_style.get("textcolor", current_style["tc"])
current_style["gt"] = current_style.get("gatetextcolor", current_style["gt"])
current_style["sc"] = current_style.get("subtextcolor", current_style["sc"])
current_style["lc"] = current_style.get("linecolor", current_style["lc"])
current_style["cc"] = current_style.get("creglinecolor", current_style["cc"])
current_style["gc"] = current_style.get("gatefacecolor", current_style["gc"])
current_style["bc"] = current_style.get("barrierfacecolor", current_style["bc"])
current_style["bg"] = current_style.get("backgroundcolor", current_style["bg"])
current_style["ec"] = current_style.get("edgecolor", current_style["ec"])
current_style["fs"] = current_style.get("fontsize", current_style["fs"])
current_style["sfs"] = current_style.get("subfontsize", current_style["sfs"])
current_style["index"] = current_style.get("showindex", current_style["index"])
current_style["cline"] = current_style.get("creglinestyle", current_style["cline"])
current_style["disptex"] = {**current_style["disptex"], **new_style.get("displaytext", {})}
current_style["dispcol"] = {**current_style["dispcol"], **new_style.get("displaycolor", {})}
unsupported_keys = set(new_style) - valid_fields
if unsupported_keys:
warn(
f"style option/s ({', '.join(unsupported_keys)}) is/are not supported",
UserWarning,
2,
)
# Temporary import from 0.22.0 to be deprecated in future
# pylint: disable=unused-wildcard-import,wildcard-import
from .circuit.qcstyle import *

View File

@ -23,18 +23,16 @@ import colorsys
import numpy as np
from qiskit import user_config
from qiskit.quantum_info.states.statevector import Statevector
from qiskit.quantum_info.operators.symplectic import PauliList, SparsePauliOp
from qiskit.quantum_info.states.densitymatrix import DensityMatrix
from qiskit.visualization.array import array_to_latex
from qiskit.utils.deprecation import deprecate_arguments
from qiskit.utils import optionals as _optionals
from qiskit.visualization.exceptions import VisualizationError
from qiskit.visualization.utils import (
_bloch_multivector_data,
_paulivec_data,
matplotlib_close_if_inline,
)
from qiskit.circuit.tools.pi_check import pi_check
from .array import array_to_latex
from .utils import matplotlib_close_if_inline
from .exceptions import VisualizationError
@deprecate_arguments({"rho": "state"})
@_optionals.HAS_MATPLOTLIB.require_in_call
@ -223,7 +221,7 @@ def plot_bloch_vector(bloch, title="", ax=None, figsize=None, coord_type="cartes
plot_bloch_vector([1, np.pi/2, np.pi/3], coord_type='spherical')
"""
from qiskit.visualization.bloch import Bloch
from .bloch import Bloch
if figsize is None:
figsize = (5, 5)
@ -807,7 +805,7 @@ def plot_state_qsphere(
from matplotlib.patches import Circle
import seaborn as sns
from scipy import linalg
from qiskit.visualization.bloch import Arrow3D
from .bloch import Arrow3D
rho = DensityMatrix(state)
num = rho.num_qubits
@ -1469,3 +1467,51 @@ def state_drawer(state, output=None, **drawer_args):
output, type(state).__name__
)
) from err
def _bloch_multivector_data(state):
"""Return list of Bloch vectors for each qubit
Args:
state (DensityMatrix or Statevector): an N-qubit state.
Returns:
list: list of Bloch vectors (x, y, z) for each qubit.
Raises:
VisualizationError: if input is not an N-qubit state.
"""
rho = DensityMatrix(state)
num = rho.num_qubits
if num is None:
raise VisualizationError("Input is not a multi-qubit quantum state.")
pauli_singles = PauliList(["X", "Y", "Z"])
bloch_data = []
for i in range(num):
if num > 1:
paulis = PauliList.from_symplectic(
np.zeros((3, (num - 1)), dtype=bool), np.zeros((3, (num - 1)), dtype=bool)
).insert(i, pauli_singles, qubit=True)
else:
paulis = pauli_singles
bloch_state = [np.real(np.trace(np.dot(mat, rho.data))) for mat in paulis.matrix_iter()]
bloch_data.append(bloch_state)
return bloch_data
def _paulivec_data(state):
"""Return paulivec data for plotting.
Args:
state (DensityMatrix or Statevector): an N-qubit state.
Returns:
tuple: (labels, values) for Pauli vector.
Raises:
VisualizationError: if input is not an N-qubit state.
"""
rho = SparsePauliOp.from_operator(DensityMatrix(state))
if rho.num_qubits is None:
raise VisualizationError("Input is not a multi-qubit quantum state.")
return rho.paulis.to_labels(), np.real(rho.coeffs)

View File

@ -162,8 +162,8 @@ def visualize_transition(circuit, trace=False, saveas=None, fpg=100, spg=2):
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
from qiskit.visualization.bloch import Bloch
from qiskit.visualization.exceptions import VisualizationError
from .bloch import Bloch
from .exceptions import VisualizationError
has_matplotlib = True
except ImportError:

View File

@ -12,362 +12,7 @@
"""Common visualization utilities."""
import re
from collections import OrderedDict
from warnings import warn
import numpy as np
from qiskit.circuit import (
BooleanExpression,
Clbit,
ControlledGate,
Delay,
Gate,
Instruction,
Measure,
ControlFlowOp,
)
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.circuit import ClassicalRegister
from qiskit.circuit.tools import pi_check
from qiskit.converters import circuit_to_dag
from qiskit.quantum_info.operators.symplectic import PauliList, SparsePauliOp
from qiskit.quantum_info.states import DensityMatrix
from qiskit.utils import optionals as _optionals
from qiskit.visualization.exceptions import VisualizationError
def get_gate_ctrl_text(op, drawer, style=None, calibrations=None):
"""Load the gate_text and ctrl_text strings based on names and labels"""
op_label = getattr(op, "label", None)
op_type = type(op)
base_name = base_label = base_type = None
if hasattr(op, "base_gate"):
base_name = op.base_gate.name
base_label = op.base_gate.label
base_type = type(op.base_gate)
ctrl_text = None
if base_label:
gate_text = base_label
ctrl_text = op_label
elif op_label and isinstance(op, ControlledGate):
gate_text = base_name
ctrl_text = op_label
elif op_label:
gate_text = op_label
elif base_name:
gate_text = base_name
else:
gate_text = op.name
# raw_gate_text is used in color selection in mpl instead of op.name, since
# if it's a controlled gate, the color will likely not be the base_name color
raw_gate_text = op.name if gate_text == base_name else gate_text
# For mpl and latex drawers, check style['disptex'] in qcstyle.py
if drawer != "text" and gate_text in style["disptex"]:
# First check if this entry is in the old style disptex that
# included "$\\mathrm{ }$". If so, take it as is.
if style["disptex"][gate_text][0] == "$" and style["disptex"][gate_text][-1] == "$":
gate_text = style["disptex"][gate_text]
else:
gate_text = f"$\\mathrm{{{style['disptex'][gate_text]}}}$"
elif drawer == "latex":
# Special formatting for Booleans in latex (due to '~' causing crash)
if (gate_text == op.name and op_type is BooleanExpression) or (
gate_text == base_name and base_type is BooleanExpression
):
gate_text = gate_text.replace("~", "$\\neg$").replace("&", "\\&")
gate_text = f"$\\texttt{{{gate_text}}}$"
# Capitalize if not a user-created gate or instruction
elif (
(gate_text == op.name and op_type not in (Gate, Instruction))
or (gate_text == base_name and base_type not in (Gate, Instruction))
) and (op_type is not PauliEvolutionGate):
gate_text = f"$\\mathrm{{{gate_text.capitalize()}}}$"
else:
gate_text = f"$\\mathrm{{{gate_text}}}$"
# Remove mathmode _, ^, and - formatting from user names and labels
gate_text = gate_text.replace("_", "\\_")
gate_text = gate_text.replace("^", "\\string^")
gate_text = gate_text.replace("-", "\\mbox{-}")
ctrl_text = f"$\\mathrm{{{ctrl_text}}}$"
# Only captitalize internally-created gate or instruction names
elif (
(gate_text == op.name and op_type not in (Gate, Instruction))
or (gate_text == base_name and base_type not in (Gate, Instruction))
) and (op_type is not PauliEvolutionGate):
gate_text = gate_text.capitalize()
if drawer == "mpl" and op.name in calibrations:
if isinstance(op, ControlledGate):
ctrl_text = "" if ctrl_text is None else ctrl_text
ctrl_text = "(cal)\n" + ctrl_text
else:
gate_text = gate_text + "\n(cal)"
return gate_text, ctrl_text, raw_gate_text
def get_param_str(op, drawer, ndigits=3):
"""Get the params as a string to add to the gate text display"""
if not hasattr(op, "params") or any(isinstance(param, np.ndarray) for param in op.params):
return ""
if isinstance(op, ControlFlowOp):
return ""
if isinstance(op, Delay):
param_list = [f"{op.params[0]}[{op.unit}]"]
else:
param_list = []
for count, param in enumerate(op.params):
# Latex drawer will cause an xy-pic error and mpl drawer will overwrite
# the right edge if param string too long, so limit params.
if (drawer == "latex" and count > 3) or (drawer == "mpl" and count > 15):
param_list.append("...")
break
try:
param_list.append(pi_check(param, output=drawer, ndigits=ndigits))
except TypeError:
param_list.append(str(param))
param_str = ""
if param_list:
if drawer == "latex":
param_str = f"\\,(\\mathrm{{{','.join(param_list)}}})"
elif drawer == "mpl":
param_str = f"{', '.join(param_list)}".replace("-", "$-$")
else:
param_str = f"({','.join(param_list)})"
return param_str
def get_wire_map(circuit, bits, cregbundle):
"""Map the bits and registers to the index from the top of the drawing.
The key to the dict is either the (Qubit, Clbit) or if cregbundle True,
the register that is being bundled.
Args:
circuit (QuantumCircuit): the circuit being drawn
bits (list(Qubit, Clbit)): the Qubit's and Clbit's in the circuit
cregbundle (bool): if True bundle classical registers. Default: ``True``.
Returns:
dict((Qubit, Clbit, ClassicalRegister): index): map of bits/registers
to index
"""
prev_reg = None
wire_index = 0
wire_map = {}
for bit in bits:
register = get_bit_register(circuit, bit)
if register is None or not isinstance(bit, Clbit) or not cregbundle:
wire_map[bit] = wire_index
wire_index += 1
elif register is not None and cregbundle and register != prev_reg:
prev_reg = register
wire_map[register] = wire_index
wire_index += 1
return wire_map
def get_bit_register(circuit, bit):
"""Get the register for a bit if there is one
Args:
circuit (QuantumCircuit): the circuit being drawn
bit (Qubit, Clbit): the bit to use to find the register and indexes
Returns:
ClassicalRegister: register associated with the bit
"""
bit_loc = circuit.find_bit(bit)
return bit_loc.registers[0][0] if bit_loc.registers else None
def get_bit_reg_index(circuit, bit, reverse_bits=None):
"""Get the register for a bit if there is one, and the index of the bit
from the top of the circuit, or the index of the bit within a register.
Args:
circuit (QuantumCircuit): the circuit being drawn
bit (Qubit, Clbit): the bit to use to find the register and indexes
reverse_bits (bool): deprecated option to reverse order of the bits
Returns:
(ClassicalRegister, None): register associated with the bit
int: index of the bit from the top of the circuit
int: index of the bit within the register, if there is a register
"""
if reverse_bits is not None:
warn(
"The 'reverse_bits' kwarg to the function "
"~qiskit.visualization.utils.get_bit_reg_index "
"is deprecated as of 0.22.0 and will be removed no earlier than 3 months "
"after the release date.",
DeprecationWarning,
2,
)
bit_loc = circuit.find_bit(bit)
bit_index = bit_loc.index
register, reg_index = bit_loc.registers[0] if bit_loc.registers else (None, None)
return register, bit_index, reg_index
def get_wire_label(drawer, register, index, layout=None, cregbundle=True):
"""Get the bit labels to display to the left of the wires.
Args:
drawer (str): which drawer is calling ("text", "mpl", or "latex")
register (QuantumRegister or ClassicalRegister): get wire_label for this register
index (int): index of bit in register
layout (Layout): Optional. mapping of virtual to physical bits
cregbundle (bool): Optional. if set True bundle classical registers.
Default: ``True``.
Returns:
str: label to display for the register/index
"""
index_str = f"{index}" if drawer == "text" else f"{{{index}}}"
if register is None:
wire_label = index_str
return wire_label
if drawer == "text":
reg_name = f"{register.name}"
reg_name_index = f"{register.name}_{index}"
else:
reg_name = f"{{{fix_special_characters(register.name)}}}"
reg_name_index = f"{reg_name}_{{{index}}}"
# Clbits
if isinstance(register, ClassicalRegister):
if cregbundle and drawer != "latex":
wire_label = f"{register.name}"
return wire_label
if register.size == 1 or cregbundle:
wire_label = reg_name
else:
wire_label = reg_name_index
return wire_label
# Qubits
if register.size == 1:
wire_label = reg_name
elif layout is None:
wire_label = reg_name_index
elif layout[index]:
virt_bit = layout[index]
try:
virt_reg = next(reg for reg in layout.get_registers() if virt_bit in reg)
if drawer == "text":
wire_label = f"{virt_reg.name}_{virt_reg[:].index(virt_bit)} -> {index}"
else:
wire_label = (
f"{{{virt_reg.name}}}_{{{virt_reg[:].index(virt_bit)}}} \\mapsto {{{index}}}"
)
except StopIteration:
if drawer == "text":
wire_label = f"{virt_bit} -> {index}"
else:
wire_label = f"{{{virt_bit}}} \\mapsto {{{index}}}"
if drawer != "text":
wire_label = wire_label.replace(" ", "\\;") # use wider spaces
else:
wire_label = index_str
return wire_label
def get_condition_label_val(condition, circuit, cregbundle, reverse_bits=None):
"""Get the label and value list to display a condition
Args:
condition (Union[Clbit, ClassicalRegister], int): classical condition
circuit (QuantumCircuit): the circuit that is being drawn
cregbundle (bool): if set True bundle classical registers
reverse_bits (bool): deprecated option to reverse order of the bits
Returns:
str: label to display for the condition
list(str): list of 1's and 0's indicating values of condition
"""
if reverse_bits is not None:
warn(
"The 'reverse_bits' kwarg to the function "
"~qiskit.visualization.utils.get_condition_label_val "
"is deprecated as of 0.22.0 and will be removed no earlier than 3 months "
"after the release date.",
DeprecationWarning,
2,
)
cond_is_bit = bool(isinstance(condition[0], Clbit))
cond_val = int(condition[1])
# if condition on a register, return list of 1's and 0's indicating
# closed or open, else only one element is returned
if isinstance(condition[0], ClassicalRegister) and not cregbundle:
val_bits = list(f"{cond_val:0{condition[0].size}b}")[::-1]
else:
val_bits = list(str(cond_val))
label = ""
if cond_is_bit and cregbundle:
register, _, reg_index = get_bit_reg_index(circuit, condition[0])
if register is not None:
label = f"{register.name}_{reg_index}={hex(cond_val)}"
elif not cond_is_bit:
label = hex(cond_val)
return label, val_bits
def fix_special_characters(label):
"""
Convert any special characters for mpl and latex drawers.
Currently only checks for multiple underscores in register names
and uses wider space for mpl and latex drawers.
Args:
label (str): the label to fix
Returns:
str: label to display
"""
label = label.replace("_", r"\_").replace(" ", "\\;")
return label
@_optionals.HAS_PYLATEX.require_in_call("the latex and latex_source circuit drawers")
def generate_latex_label(label):
"""Convert a label to a valid latex string."""
from pylatexenc.latexencode import utf8tolatex
regex = re.compile(r"(?<!\\)\$(.*)(?<!\\)\$")
match = regex.search(label)
if not match:
label = label.replace(r"\$", "$")
final_str = utf8tolatex(label, non_ascii_only=True)
else:
mathmode_string = match.group(1).replace(r"\$", "$")
before_match = label[: match.start()]
before_match = before_match.replace(r"\$", "$")
after_match = label[match.end() :]
after_match = after_match.replace(r"\$", "$")
final_str = (
utf8tolatex(before_match, non_ascii_only=True)
+ mathmode_string
+ utf8tolatex(after_match, non_ascii_only=True)
)
return final_str.replace(" ", "\\,") # Put in proper spaces
@_optionals.HAS_PIL.require_in_call("the latex circuit drawer")
@ -384,322 +29,6 @@ def _trim(image):
return image
def _get_layered_instructions(
circuit, reverse_bits=False, justify=None, idle_wires=True, wire_order=None
):
"""
Given a circuit, return a tuple (qubits, clbits, nodes) where
qubits and clbits are the quantum and classical registers
in order (based on reverse_bits or wire_order) and nodes
is a list of DAGOpNodes.
Args:
circuit (QuantumCircuit): From where the information is extracted.
reverse_bits (bool): If true the order of the bits in the registers is
reversed.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
idle_wires (bool): Include idle wires. Default is True.
wire_order (list): A list of ints that modifies the order of the bits
Returns:
Tuple(list,list,list): To be consumed by the visualizer directly.
Raises:
VisualizationError: if both reverse_bits and wire_order are entered.
"""
if justify:
justify = justify.lower()
# default to left
justify = justify if justify in ("right", "none") else "left"
qubits = circuit.qubits
clbits = circuit.clbits
nodes = []
# Create a mapping of each register to the max layer number for all measure ops
# with that register as the target. Then when an op with condition is seen,
# it will be placed to the right of the measure op if the register matches.
measure_map = OrderedDict([(c, -1) for c in clbits])
if reverse_bits and wire_order is not None:
raise VisualizationError("Cannot set both reverse_bits and wire_order in the same drawing.")
if reverse_bits:
qubits.reverse()
clbits.reverse()
elif wire_order is not None:
new_qubits = []
new_clbits = []
for bit in wire_order:
if bit < len(qubits):
new_qubits.append(qubits[bit])
else:
new_clbits.append(clbits[bit - len(qubits)])
qubits = new_qubits
clbits = new_clbits
dag = circuit_to_dag(circuit)
dag.qubits = qubits
dag.clbits = clbits
if justify == "none":
for node in dag.topological_op_nodes():
nodes.append([node])
else:
nodes = _LayerSpooler(dag, justify, measure_map)
# Optionally remove all idle wires and instructions that are on them and
# on them only.
if not idle_wires:
for wire in dag.idle_wires(ignore=["barrier", "delay"]):
if wire in qubits:
qubits.remove(wire)
if wire in clbits:
clbits.remove(wire)
nodes = [[node for node in layer if any(q in qubits for q in node.qargs)] for layer in nodes]
return qubits, clbits, nodes
def _sorted_nodes(dag_layer):
"""Convert DAG layer into list of nodes sorted by node_id
qiskit-terra #2802
"""
nodes = dag_layer["graph"].op_nodes()
# sort into the order they were input
nodes.sort(key=lambda nd: nd._node_id)
return nodes
def _get_gate_span(qubits, node):
"""Get the list of qubits drawing this gate would cover
qiskit-terra #2802
"""
min_index = len(qubits)
max_index = 0
for qreg in node.qargs:
index = qubits.index(qreg)
if index < min_index:
min_index = index
if index > max_index:
max_index = index
if node.cargs or getattr(node.op, "condition", None):
return qubits[min_index : len(qubits)]
return qubits[min_index : max_index + 1]
def _any_crossover(qubits, node, nodes):
"""Return True .IFF. 'node' crosses over any 'nodes'."""
gate_span = _get_gate_span(qubits, node)
all_indices = []
for check_node in nodes:
if check_node != node:
all_indices += _get_gate_span(qubits, check_node)
return any(i in gate_span for i in all_indices)
class _LayerSpooler(list):
"""Manipulate list of layer dicts for _get_layered_instructions."""
def __init__(self, dag, justification, measure_map):
"""Create spool"""
super().__init__()
self.dag = dag
self.qubits = dag.qubits
self.clbits = dag.clbits
self.justification = justification
self.measure_map = measure_map
self.cregs = [self.dag.cregs[reg] for reg in self.dag.cregs]
if self.justification == "left":
for dag_layer in dag.layers():
current_index = len(self) - 1
dag_nodes = _sorted_nodes(dag_layer)
for node in dag_nodes:
self.add(node, current_index)
else:
dag_layers = []
for dag_layer in dag.layers():
dag_layers.append(dag_layer)
# going right to left!
dag_layers.reverse()
for dag_layer in dag_layers:
current_index = 0
dag_nodes = _sorted_nodes(dag_layer)
for node in dag_nodes:
self.add(node, current_index)
def is_found_in(self, node, nodes):
"""Is any qreq in node found in any of nodes?"""
all_qargs = []
for a_node in nodes:
for qarg in a_node.qargs:
all_qargs.append(qarg)
return any(i in node.qargs for i in all_qargs)
def insertable(self, node, nodes):
"""True .IFF. we can add 'node' to layer 'nodes'"""
return not _any_crossover(self.qubits, node, nodes)
def slide_from_left(self, node, index):
"""Insert node into first layer where there is no conflict going l > r"""
measure_layer = None
if isinstance(node.op, Measure):
measure_bit = next(bit for bit in self.measure_map if node.cargs[0] == bit)
if not self:
inserted = True
self.append([node])
else:
inserted = False
curr_index = index
last_insertable_index = -1
index_stop = -1
if getattr(node.op, "condition", None):
if isinstance(node.op.condition[0], Clbit):
cond_bit = [clbit for clbit in self.clbits if node.op.condition[0] == clbit]
index_stop = self.measure_map[cond_bit[0]]
else:
for bit in node.op.condition[0]:
max_index = -1
if bit in self.measure_map:
if self.measure_map[bit] > max_index:
index_stop = max_index = self.measure_map[bit]
if node.cargs:
for carg in node.cargs:
try:
carg_bit = next(bit for bit in self.measure_map if carg == bit)
if self.measure_map[carg_bit] > index_stop:
index_stop = self.measure_map[carg_bit]
except StopIteration:
pass
while curr_index > index_stop:
if self.is_found_in(node, self[curr_index]):
break
if self.insertable(node, self[curr_index]):
last_insertable_index = curr_index
curr_index = curr_index - 1
if last_insertable_index >= 0:
inserted = True
self[last_insertable_index].append(node)
measure_layer = last_insertable_index
else:
inserted = False
curr_index = index
while curr_index < len(self):
if self.insertable(node, self[curr_index]):
self[curr_index].append(node)
measure_layer = curr_index
inserted = True
break
curr_index = curr_index + 1
if not inserted:
self.append([node])
if isinstance(node.op, Measure):
if not measure_layer:
measure_layer = len(self) - 1
if measure_layer > self.measure_map[measure_bit]:
self.measure_map[measure_bit] = measure_layer
def slide_from_right(self, node, index):
"""Insert node into rightmost layer as long there is no conflict."""
if not self:
self.insert(0, [node])
inserted = True
else:
inserted = False
curr_index = index
last_insertable_index = None
while curr_index < len(self):
if self.is_found_in(node, self[curr_index]):
break
if self.insertable(node, self[curr_index]):
last_insertable_index = curr_index
curr_index = curr_index + 1
if last_insertable_index:
self[last_insertable_index].append(node)
inserted = True
else:
curr_index = index
while curr_index > -1:
if self.insertable(node, self[curr_index]):
self[curr_index].append(node)
inserted = True
break
curr_index = curr_index - 1
if not inserted:
self.insert(0, [node])
def add(self, node, index):
"""Add 'node' where it belongs, starting the try at 'index'."""
if self.justification == "left":
self.slide_from_left(node, index)
else:
self.slide_from_right(node, index)
def _bloch_multivector_data(state):
"""Return list of Bloch vectors for each qubit
Args:
state (DensityMatrix or Statevector): an N-qubit state.
Returns:
list: list of Bloch vectors (x, y, z) for each qubit.
Raises:
VisualizationError: if input is not an N-qubit state.
"""
rho = DensityMatrix(state)
num = rho.num_qubits
if num is None:
raise VisualizationError("Input is not a multi-qubit quantum state.")
pauli_singles = PauliList(["X", "Y", "Z"])
bloch_data = []
for i in range(num):
if num > 1:
paulis = PauliList.from_symplectic(
np.zeros((3, (num - 1)), dtype=bool), np.zeros((3, (num - 1)), dtype=bool)
).insert(i, pauli_singles, qubit=True)
else:
paulis = pauli_singles
bloch_state = [np.real(np.trace(np.dot(mat, rho.data))) for mat in paulis.matrix_iter()]
bloch_data.append(bloch_state)
return bloch_data
def _paulivec_data(state):
"""Return paulivec data for plotting.
Args:
state (DensityMatrix or Statevector): an N-qubit state.
Returns:
tuple: (labels, values) for Pauli vector.
Raises:
VisualizationError: if input is not an N-qubit state.
"""
rho = SparsePauliOp.from_operator(DensityMatrix(state))
if rho.num_qubits is None:
raise VisualizationError("Input is not a multi-qubit quantum state.")
return rho.paulis.to_labels(), np.real(rho.coeffs)
MATPLOTLIB_INLINE_BACKENDS = {
"module://ipykernel.pylab.backend_inline",
"module://matplotlib_inline.backend_inline",

View File

@ -0,0 +1,9 @@
---
upgrade:
- |
The visualization module :mod:`qiskit.visualization` has seen some internal
reorganisation. This should not have affected the public interface, but if
you were accessing any internals of the circuit drawers, they may now be in
different places. The only parts of the visualization module that are
considered public are the components that are documented in this online
documentation.

View File

@ -24,7 +24,7 @@ from numpy import pi
from qiskit.test import QiskitTestCase
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile
from qiskit.providers.fake_provider import FakeTenerife
from qiskit.visualization.circuit_visualization import _matplotlib_circuit_drawer
from qiskit.visualization.circuit.circuit_visualization import _matplotlib_circuit_drawer
from qiskit.circuit.library import (
XGate,
MCXGate,

View File

@ -18,11 +18,11 @@ import json
import os
from contextlib import contextmanager
from qiskit.visualization.state_visualization import state_drawer
from qiskit import BasicAer, execute
from qiskit.test import QiskitTestCase
from qiskit import QuantumCircuit
from qiskit.utils import optionals
from qiskit.visualization.state_visualization import state_drawer
from qiskit.visualization.counts_visualization import plot_histogram
from qiskit.visualization.gate_map import plot_gate_map, plot_coupling_map
from qiskit.providers.fake_provider import (

View File

@ -20,7 +20,7 @@ from qiskit import QuantumCircuit
from qiskit.test import QiskitTestCase
from qiskit.utils import optionals
from qiskit import visualization
from qiskit.visualization import text
from qiskit.visualization.circuit import text
from qiskit.visualization.exceptions import VisualizationError
if optionals.HAS_MATPLOTLIB:

View File

@ -681,7 +681,7 @@ class TestLatexSourceGenerator(QiskitVisualizationTestCase):
self.assertEqualToReference(filename)
def test_wire_order(self):
"""Test the wire_order option"""
"""Test the wire_order option to latex drawer"""
filename = self._get_resource_path("test_latex_wire_order.tex")
qr = QuantumRegister(4, "q")
cr = ClassicalRegister(4, "c")

View File

@ -25,8 +25,8 @@ from qiskit.quantum_info.operators import SuperOp
from qiskit.quantum_info.random import random_unitary
from qiskit.test import QiskitTestCase
from qiskit.transpiler import Layout
from qiskit.visualization import text as elements
from qiskit.visualization.circuit_visualization import _text_circuit_drawer
from qiskit.visualization.circuit import text as elements
from qiskit.visualization.circuit.circuit_visualization import _text_circuit_drawer
from qiskit.extensions import UnitaryGate, HamiltonianGate
from qiskit.extensions.quantum_initializer import UCGate
from qiskit.circuit.library import (

View File

@ -19,9 +19,9 @@ import unittest
from PIL import Image
from qiskit.circuit import QuantumRegister, QuantumCircuit, Qubit, Clbit
from qiskit.tools.visualization import dag_drawer
from qiskit.visualization import dag_drawer
from qiskit.exceptions import InvalidFileError
from qiskit.visualization.exceptions import VisualizationError
from qiskit.visualization import VisualizationError
from qiskit.converters import circuit_to_dag
from qiskit.utils import optionals as _optionals
from .visualization import path_to_diagram_reference, QiskitVisualizationTestCase
@ -41,7 +41,8 @@ class TestDagDrawer(QiskitVisualizationTestCase):
@unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed")
def test_dag_drawer_invalid_style(self):
"""Test dag draw with invalid style."""
self.assertRaises(VisualizationError, dag_drawer, self.dag, style="multicolor")
with self.assertRaisesRegex(VisualizationError, "Invalid style multicolor"):
dag_drawer(self.dag, style="multicolor")
@unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed")
def test_dag_drawer_checks_filename_correct_format(self):

View File

@ -24,7 +24,7 @@ from qiskit.providers.fake_provider import (
FakeKolkataV2,
FakeWashingtonV2,
)
from qiskit.visualization.gate_map import (
from qiskit.visualization import (
plot_gate_map,
plot_coupling_map,
plot_circuit_layout,

View File

@ -19,7 +19,7 @@ from collections import Counter
import matplotlib as mpl
from PIL import Image
from qiskit.tools.visualization import plot_histogram
from qiskit.visualization import plot_histogram
from qiskit.utils import optionals
from .visualization import QiskitVisualizationTestCase

View File

@ -17,14 +17,13 @@ import numpy as np
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.circuit import Qubit, Clbit
from qiskit.visualization import utils, array_to_latex
from qiskit.visualization.circuit import _utils
from qiskit.visualization import array_to_latex
from qiskit.test import QiskitTestCase
class TestVisualizationUtils(QiskitTestCase):
"""Tests for visualizer utilities.
Since the utilities in qiskit/tools/visualization/_utils.py are used by several visualizers
the need to be check if the interface or their result changes."""
"""Tests for circuit drawer utilities."""
def setUp(self):
super().setUp()
@ -45,7 +44,7 @@ class TestVisualizationUtils(QiskitTestCase):
def test_get_layered_instructions(self):
"""_get_layered_instructions without reverse_bits"""
(qregs, cregs, layered_ops) = utils._get_layered_instructions(self.circuit)
(qregs, cregs, layered_ops) = _utils._get_layered_instructions(self.circuit)
exp = [
[("cx", (self.qr2[0], self.qr2[1]), ()), ("cx", (self.qr1[0], self.qr1[1]), ())],
@ -64,7 +63,7 @@ class TestVisualizationUtils(QiskitTestCase):
def test_get_layered_instructions_reverse_bits(self):
"""_get_layered_instructions with reverse_bits=True"""
(qregs, cregs, layered_ops) = utils._get_layered_instructions(
(qregs, cregs, layered_ops) = _utils._get_layered_instructions(
self.circuit, reverse_bits=True
)
@ -100,7 +99,7 @@ class TestVisualizationUtils(QiskitTestCase):
circuit.cx(qr1[1], qr1[0])
circuit.measure(qr1[1], cr1[1])
(qregs, cregs, layered_ops) = utils._get_layered_instructions(circuit, idle_wires=False)
(qregs, cregs, layered_ops) = _utils._get_layered_instructions(circuit, idle_wires=False)
exp = [
[("cx", (qr2[0], qr2[1]), ()), ("cx", (qr1[0], qr1[1]), ())],
@ -133,7 +132,7 @@ class TestVisualizationUtils(QiskitTestCase):
qc.h(2)
qc.cx(0, 3)
(_, _, layered_ops) = utils._get_layered_instructions(qc, justify="left")
(_, _, layered_ops) = _utils._get_layered_instructions(qc, justify="left")
l_exp = [
[
@ -163,7 +162,7 @@ class TestVisualizationUtils(QiskitTestCase):
qc.h(2)
qc.cx(0, 3)
(_, _, layered_ops) = utils._get_layered_instructions(qc, justify="right")
(_, _, layered_ops) = _utils._get_layered_instructions(qc, justify="right")
r_exp = [
[("cx", (Qubit(QuantumRegister(4, "q"), 0), Qubit(QuantumRegister(4, "q"), 3)), ())],
@ -212,7 +211,7 @@ class TestVisualizationUtils(QiskitTestCase):
"""
qc = QuantumCircuit.from_qasm_str(qasm)
(_, _, layered_ops) = utils._get_layered_instructions(qc, justify="left")
(_, _, layered_ops) = _utils._get_layered_instructions(qc, justify="left")
l_exp = [
[
@ -279,7 +278,7 @@ class TestVisualizationUtils(QiskitTestCase):
"""
qc = QuantumCircuit.from_qasm_str(qasm)
(_, _, layered_ops) = utils._get_layered_instructions(qc, justify="right")
(_, _, layered_ops) = _utils._get_layered_instructions(qc, justify="right")
r_exp = [
[
@ -332,7 +331,7 @@ class TestVisualizationUtils(QiskitTestCase):
qc_2.measure(0, 0)
qc.append(qc_2, [1], [0])
(_, _, layered_ops) = utils._get_layered_instructions(qc)
(_, _, layered_ops) = _utils._get_layered_instructions(qc)
expected = [
[("h", (Qubit(QuantumRegister(2, "q"), 0),), ())],
@ -358,37 +357,38 @@ class TestVisualizationUtils(QiskitTestCase):
def test_generate_latex_label_nomathmode(self):
"""Test generate latex label default."""
self.assertEqual("abc", utils.generate_latex_label("abc"))
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")
"{\\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"),
_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"),
_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(r"\$∀\$"))
self.assertEqual("${\\ensuremath{\\forall}}$", _utils.generate_latex_label(r"\$∀\$"))
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(r"$a$bc$_∭X∀Y"),
_utils.generate_latex_label(r"$a$bc$_∭X∀Y"),
)
def test_array_to_latex(self):