Skip to content

Commit

Permalink
Add display of expressions to circuit drawers (#10869)
Browse files Browse the repository at this point in the history
* Fix override bug and testing

* Preliminary changes

* First attempts

* Work on x_index

* Early testing

* Cleanup getattr

* Complete adding exprs

* Add exprs to mpl

* Work on spacing

* Spacing changes

* Finish initial mpl expressions

* Exprs fully implemented mpl, pre-test

* Add exprs to text drawer

* Minor text fixes and update tests for cregbundle False

* Add tests

* Lint

* Fix test

* Adjust switch test

* Lint again

* Add expr_len kwarg and minor fixes

* Update for 10842 changes

* Lint

* Update refs for minor switch spacing

* Minor cleanup

* Only call QASM3Builder once

* Remove spurious print

* Minor documentation tweaks

* For mpl, adjust switch/expr spacing, fix 1 qarg flow ops, fix top level flow op layers

* Add tests for 1 qarg, nested expr, and for range

* Lint

* Fold on text test

* Remove wire_map from get_layered

---------

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
  • Loading branch information
enavarro51 and jakelishman authored Oct 19, 2023
1 parent a881215 commit 19862cc
Show file tree
Hide file tree
Showing 18 changed files with 447 additions and 47 deletions.
5 changes: 5 additions & 0 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,7 @@ def draw(
initial_state: bool = False,
cregbundle: bool = None,
wire_order: list = None,
expr_len: int = 30,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:
Expand Down Expand Up @@ -1766,6 +1767,9 @@ def draw(
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``).
expr_len (int): Optional. The number of characters to display if an :class:`~.expr.Expr`
is used for the condition in a :class:`.ControlFlowOp`. If this number is exceeded,
the string will be truncated at that number and '...' added to the end.
Returns:
:class:`.TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
Expand Down Expand Up @@ -1818,6 +1822,7 @@ def draw(
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
expr_len=expr_len,
)

def size(
Expand Down
17 changes: 8 additions & 9 deletions qiskit/visualization/circuit/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def _get_layered_instructions(
for node in dag.topological_op_nodes():
nodes.append([node])
else:
nodes = _LayerSpooler(dag, justify, measure_map, wire_map)
nodes = _LayerSpooler(dag, justify, measure_map)

# Optionally remove all idle wires and instructions that are on them and
# on them only.
Expand All @@ -454,7 +454,7 @@ def _sorted_nodes(dag_layer):
return nodes


def _get_gate_span(qubits, node, wire_map):
def _get_gate_span(qubits, node):
"""Get the list of qubits drawing this gate would cover
qiskit-terra #2802
"""
Expand All @@ -470,7 +470,7 @@ def _get_gate_span(qubits, node, wire_map):

# Because of wrapping boxes for mpl control flow ops, this
# type of op must be the only op in the layer
if wire_map is not None and isinstance(node.op, ControlFlowOp):
if isinstance(node.op, ControlFlowOp):
span = qubits
elif node.cargs or getattr(node.op, "condition", None):
span = qubits[min_index : len(qubits)]
Expand All @@ -480,28 +480,27 @@ def _get_gate_span(qubits, node, wire_map):
return span


def _any_crossover(qubits, node, nodes, wire_map):
def _any_crossover(qubits, node, nodes):
"""Return True .IFF. 'node' crosses over any 'nodes'."""
gate_span = _get_gate_span(qubits, node, wire_map)
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, wire_map)
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, wire_map):
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.wire_map = wire_map
self.cregs = [self.dag.cregs[reg] for reg in self.dag.cregs]

if self.justification == "left":
Expand Down Expand Up @@ -534,7 +533,7 @@ def is_found_in(self, node, nodes):

def insertable(self, node, nodes):
"""True .IFF. we can add 'node' to layer 'nodes'"""
return not _any_crossover(self.qubits, node, nodes, self.wire_map)
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"""
Expand Down
17 changes: 17 additions & 0 deletions qiskit/visualization/circuit/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def circuit_drawer(
initial_state=False,
cregbundle=None,
wire_order=None,
expr_len=30,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:
Expand Down Expand Up @@ -156,6 +157,9 @@ def circuit_drawer(
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).
expr_len (int): Optional. The number of characters to display if an :class:`~.expr.Expr`
is used for the condition in a :class:`.ControlFlowOp`. If this number is exceeded,
the string will be truncated at that number and '...' added to the end.
Returns:
:class:`TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
Expand Down Expand Up @@ -188,6 +192,7 @@ def circuit_drawer(
circuit_drawer(qc, output='mpl', style={'backgroundcolor': '#EEEEEE'})
"""
image = None
expr_len = max(expr_len, 0)
config = user_config.get_config()
# Get default from config file else use text
default_output = "text"
Expand Down Expand Up @@ -257,6 +262,7 @@ def circuit_drawer(
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=complete_wire_order,
expr_len=expr_len,
)
elif output == "latex":
image = _latex_circuit_drawer(
Expand Down Expand Up @@ -304,6 +310,7 @@ def circuit_drawer(
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=complete_wire_order,
expr_len=expr_len,
)
else:
raise VisualizationError(
Expand Down Expand Up @@ -334,6 +341,7 @@ def _text_circuit_drawer(
cregbundle=None,
encoding=None,
wire_order=None,
expr_len=30,
):
"""Draws a circuit using ascii art.
Expand Down Expand Up @@ -363,6 +371,9 @@ def _text_circuit_drawer(
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).
expr_len (int): Optional. The number of characters to display if an :class:`~.expr.Expr`
is used for the condition in a :class:`.ControlFlowOp`. If this number is exceeded,
the string will be truncated at that number and '...' added to the end.
Returns:
TextDrawing: An instance that, when printed, draws the circuit in ascii art.
Expand All @@ -387,6 +398,7 @@ def _text_circuit_drawer(
cregbundle=cregbundle,
encoding=encoding,
with_layout=with_layout,
expr_len=expr_len,
)
text_drawing.plotbarriers = plot_barriers
text_drawing.line_length = fold
Expand Down Expand Up @@ -612,6 +624,7 @@ def _matplotlib_circuit_drawer(
initial_state=False,
cregbundle=None,
wire_order=None,
expr_len=30,
):
"""Draw a quantum circuit based on matplotlib.
If `%matplotlib inline` is invoked in a Jupyter notebook, it visualizes a circuit inline.
Expand Down Expand Up @@ -643,6 +656,9 @@ def _matplotlib_circuit_drawer(
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).
expr_len (int): Optional. The number of characters to display if an :class:`~.expr.Expr`
is used for the condition in a :class:`.ControlFlowOp`. If this number is exceeded,
the string will be truncated at that number and '...' added to the end.
Returns:
matplotlib.figure: a matplotlib figure object for the circuit diagram
Expand Down Expand Up @@ -673,5 +689,6 @@ def _matplotlib_circuit_drawer(
initial_state=initial_state,
cregbundle=cregbundle,
with_layout=with_layout,
expr_len=expr_len,
)
return qcd.draw(filename)
Loading

0 comments on commit 19862cc

Please sign in to comment.