Skip to content

Commit

Permalink
Update and expand DAGDependency drawer (#11103)
Browse files Browse the repository at this point in the history
* Update and expand dag dependency drawer

* Increase tolerance

* Add conditions and truncate long lists

* Lint

* Cyclic import

* Lint

* Reno
  • Loading branch information
enavarro51 authored Nov 2, 2023
1 parent 34773f3 commit e7b1f5e
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 23 deletions.
87 changes: 67 additions & 20 deletions qiskit/visualization/dag_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
from rustworkx.visualization import graphviz_draw

from qiskit.dagcircuit.dagnode import DAGOpNode, DAGInNode, DAGOutNode
from qiskit.circuit import Qubit
from qiskit.circuit import Qubit, Clbit, ClassicalRegister
from qiskit.circuit.classical import expr
from qiskit.converters import dagdependency_to_circuit
from qiskit.utils import optionals as _optionals
from qiskit.exceptions import InvalidFileError
from .exceptions import VisualizationError
Expand Down Expand Up @@ -72,45 +74,90 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"):
# the two tradeoffs ere that it will not handle subclasses and it is
# slower (which doesn't matter for a visualization function)
type_str = str(type(dag))
register_bit_labels = {
bit: f"{reg.name}[{idx}]"
for reg in list(dag.qregs.values()) + list(dag.cregs.values())
for (idx, bit) in enumerate(reg)
}
if "DAGDependency" in type_str:
# pylint: disable=cyclic-import
from qiskit.visualization.circuit._utils import get_bit_reg_index

qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
clbit_indices = {bit: index for index, bit in enumerate(dag.clbits)}
graph_attrs = {"dpi": str(100 * scale)}
dag_dep_circ = dagdependency_to_circuit(dag)

def node_attr_func(node):
if style == "plain":
return {}
if style == "color":
n = {}
n["label"] = str(node.node_id) + ": " + str(node.name)
if node.name == "measure":
n["color"] = "blue"
n["style"] = "filled"
n["fillcolor"] = "lightblue"
args = []
for count, arg in enumerate(node.qargs + node.cargs):
if count > 4:
args.append("...")
break
if isinstance(arg, Qubit):
f_str = f"q_{qubit_indices[arg]}"
elif isinstance(arg, Clbit):
f_str = f"c_{clbit_indices[arg]}"
else:
f_str = f"{arg.index}"
arg_str = register_bit_labels.get(arg, f_str)
args.append(arg_str)

n["color"] = "black"
n["label"] = (
str(node.node_id)
+ ": "
+ str(node.name)
+ " ("
+ str(args)[1:-1].replace("'", "")
+ ")"
)
if node.name == "barrier":
n["color"] = "black"
n["style"] = "filled"
n["fillcolor"] = "green"
if getattr(node.op, "_directive", False):
n["color"] = "black"
n["fillcolor"] = "grey"
elif getattr(node.op, "_directive", False):
n["style"] = "filled"
n["fillcolor"] = "red"
if getattr(node.op, "condition", None):
n["label"] = str(node.node_id) + ": " + str(node.name) + " (conditional)"
n["color"] = "black"
elif getattr(node.op, "condition", None):
condition = node.op.condition
if isinstance(condition, expr.Expr):
cond_txt = " (cond: [Expr]) ("
elif isinstance(condition[0], ClassicalRegister):
cond_txt = f" (cond: {condition[0].name}, {int(condition[1])}) ("
else:
register, bit_index, reg_index = get_bit_reg_index(
dag_dep_circ, condition[0]
)
if register is not None:
cond_txt = (
f" (cond: {register.name}[{reg_index}], {int(condition[1])}) ("
)
else:
cond_txt = f" (cond: {bit_index}, {int(condition[1])}) ("
n["style"] = "filled"
n["fillcolor"] = "lightgreen"
n["fillcolor"] = "green"
n["label"] = (
str(node.node_id)
+ ": "
+ str(node.name)
+ cond_txt
+ str(args)[1:-1].replace("'", "")
+ ")"
)
elif node.name != "measure": # measure is unfilled
n["style"] = "filled"
n["fillcolor"] = "lightblue"
return n
else:
raise VisualizationError("Unrecognized style %s for the dag_drawer." % style)

edge_attr_func = None

else:
register_bit_labels = {
bit: f"{reg.name}[{idx}]"
for reg in list(dag.qregs.values()) + list(dag.cregs.values())
for (idx, bit) in enumerate(reg)
}

graph_attrs = {"dpi": str(100 * scale)}

def node_attr_func(node):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
features:
- |
The :func:`.dag_drawer` has been updated for the :class:`.DAGDependency`. These
drawings have a new color scheme, and the nodes now indicate the ``Qubits`` and
``Clbits`` that are used by the node. If the node has a ``condition`` the drawings
will indicate that as well.
.. code-block:: python
from qiskit.circuit import QuantumCircuit
from qiskit.converters import circuit_to_dagdependency
qc = QuantumCircuit(3, 2)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.x(1)
qc.barrier()
qc.measure(0, 0)
dagdep = circuit_to_dagdependency(qc)
dagdep.draw()
Binary file added test/python/visualization/references/dag_dep.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/python/visualization/references/dag_no_reg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 30 additions & 3 deletions test/python/visualization/test_dag_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
import tempfile
import unittest

from qiskit.circuit import QuantumRegister, QuantumCircuit, Qubit, Clbit
from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit
from qiskit.visualization import dag_drawer
from qiskit.exceptions import InvalidFileError
from qiskit.visualization import VisualizationError
from qiskit.converters import circuit_to_dag
from qiskit.converters import circuit_to_dag, circuit_to_dagdependency
from qiskit.utils import optionals as _optionals
from .visualization import path_to_diagram_reference, QiskitVisualizationTestCase

Expand Down Expand Up @@ -79,7 +79,34 @@ def test_dag_drawer_no_register(self):
dag_drawer(dag, filename=tmp_path)
image_ref = path_to_diagram_reference("dag_no_reg.png")
image = Image.open(tmp_path)
self.assertImagesAreEqual(image, image_ref, 0.2)
self.assertImagesAreEqual(image, image_ref, 0.1)

@unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(_optionals.HAS_PIL, "PIL not installed")
def test_dag_drawer_with_dag_dep(self):
"""Test dag dependency visualization."""
from PIL import Image # pylint: disable=import-error

bits = [Qubit(), Clbit()]
qr = QuantumRegister(4, "qr")
cr = ClassicalRegister(4, "cr")
qc = QuantumCircuit(qr, bits, cr)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.x(3).c_if(cr[1], 1)
qc.h(3)
qc.x(4)
qc.barrier(0, 1)
qc.measure(0, 0)
dag = circuit_to_dagdependency(qc)
with tempfile.TemporaryDirectory() as tmpdirname:
tmp_path = os.path.join(tmpdirname, "dag_d.png")
dag_drawer(dag, filename=tmp_path)
image_ref = path_to_diagram_reference("dag_dep.png")
image = Image.open(tmp_path)
self.assertImagesAreEqual(image, image_ref, 0.1)


if __name__ == "__main__":
Expand Down

0 comments on commit e7b1f5e

Please sign in to comment.