mirror of https://github.com/Qiskit/qiskit.git
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:
parent
6927a8d26b
commit
bb5201184b
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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,
|
|
@ -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
|
|
@ -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,
|
||||
)
|
|
@ -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):
|
|
@ -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
|
||||
|
|
|
@ -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"}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 *
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
|
@ -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,
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue