Skip to content

Commit

Permalink
Add display of internal circuits for ControlFlowOps to mpl circuit dr…
Browse files Browse the repository at this point in the history
…awer (#10207)

* Remove deprecated args from mpl drawer

* Reno mod

* Convert to wire_map, node_data, etc

* Lint

* Minor changes to match flow changes

* Cleanup

* Fix layer width per node and comments

* Lint

* Adjust layer num loading

* Revert n_lines

* Lint

* Add glob_data

* Lint and cleanup

* Initial PR for if-else

* Unused arg

* Lint

* Iniital box fold

* Fold flow boxes

* Add reno

* Fix initial mpl and window load

* Move figure, mpl, and style info to draw()

* Comments

* Change font vars and fix ax

* Make patches_mod global

* Minor fixes after pre merege

* Lint

* Add while

* Add for loop

* Finish SwitchCase and cleanup and utils gate_span

* Cleanup

* Fix gate_span bug and comments

* Fix layer bug by using all qubits in _get_layered

* Added final release note and deleted old one

* Minor spacing changes

* Add ipynb/mpl/circuit tests and references

* Lint

* Minor comments

* Adjust for_loop test

* Add test for IfElseOp with body

* Lint

* Update state_city image

* Comments, PORDER changes, cleanup

* Adjust MASK zorder

* Fix text color issue with other styles

* Final fixes
  • Loading branch information
enavarro51 authored Jul 20, 2023
1 parent c03e4c7 commit 9a78e11
Show file tree
Hide file tree
Showing 16 changed files with 790 additions and 67 deletions.
36 changes: 23 additions & 13 deletions qiskit/visualization/circuit/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
Measure,
)
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.circuit import ClassicalRegister, QuantumCircuit
from qiskit.circuit import ClassicalRegister, QuantumCircuit, Qubit, ControlFlowOp
from qiskit.circuit.tools import pi_check
from qiskit.converters import circuit_to_dag
from qiskit.utils import optionals as _optionals
Expand Down Expand Up @@ -361,7 +361,7 @@ def generate_latex_label(label):


def _get_layered_instructions(
circuit, reverse_bits=False, justify=None, idle_wires=True, wire_order=None
circuit, reverse_bits=False, justify=None, idle_wires=True, wire_order=None, wire_map=None
):
"""
Given a circuit, return a tuple (qubits, clbits, nodes) where
Expand Down Expand Up @@ -390,7 +390,10 @@ def _get_layered_instructions(
# default to left
justify = justify if justify in ("right", "none") else "left"

qubits = circuit.qubits.copy()
if wire_map is not None:
qubits = [bit for bit in wire_map if isinstance(bit, Qubit)]
else:
qubits = circuit.qubits.copy()
clbits = circuit.clbits.copy()
nodes = []

Expand Down Expand Up @@ -424,7 +427,7 @@ def _get_layered_instructions(
for node in dag.topological_op_nodes():
nodes.append([node])
else:
nodes = _LayerSpooler(dag, justify, measure_map)
nodes = _LayerSpooler(dag, justify, measure_map, wire_map)

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


def _get_gate_span(qubits, node):
def _get_gate_span(qubits, node, wire_map):
"""Get the list of qubits drawing this gate would cover
qiskit-terra #2802
"""
Expand All @@ -464,33 +467,40 @@ def _get_gate_span(qubits, node):
if index > max_index:
max_index = index

if node.cargs or getattr(node.op, "condition", None):
return qubits[min_index : len(qubits)]
# 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):
span = qubits
elif node.cargs or getattr(node.op, "condition", None):
span = qubits[min_index : len(qubits)]
else:
span = qubits[min_index : max_index + 1]

return qubits[min_index : max_index + 1]
return span


def _any_crossover(qubits, node, nodes):
def _any_crossover(qubits, node, nodes, wire_map):
"""Return True .IFF. 'node' crosses over any 'nodes'."""
gate_span = _get_gate_span(qubits, node)
gate_span = _get_gate_span(qubits, node, wire_map)
all_indices = []
for check_node in nodes:
if check_node != node:
all_indices += _get_gate_span(qubits, check_node)
all_indices += _get_gate_span(qubits, check_node, wire_map)
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):
def __init__(self, dag, justification, measure_map, wire_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 @@ -523,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)
return not _any_crossover(self.qubits, node, nodes, self.wire_map)

def slide_from_left(self, node, index):
"""Insert node into first layer where there is no conflict going l > r"""
Expand Down
Loading

0 comments on commit 9a78e11

Please sign in to comment.