• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 13628810750

03 Mar 2025 10:34AM UTC coverage: 87.238% (-1.4%) from 88.599%
13628810750

Pull #12814

github

web-flow
Merge 67d3e702d into 5184ca43d
Pull Request #12814: Light Cone Transpiler Pass

78 of 80 new or added lines in 2 files covered. (97.5%)

4037 existing lines in 175 files now uncovered.

75619 of 86681 relevant lines covered (87.24%)

332801.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.45
/qiskit/visualization/dag_visualization.py
1
# This code is part of Qiskit.
2
#
3
# (C) Copyright IBM 2017, 2018, 2020.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
# pylint: disable=invalid-name
14

15
"""
16
Visualization function for DAG circuit representation.
17
"""
18

19
import io
1✔
20
import subprocess
1✔
21

22
from rustworkx.visualization import graphviz_draw
1✔
23

24
from qiskit.dagcircuit.dagnode import DAGOpNode, DAGInNode, DAGOutNode
1✔
25
from qiskit.dagcircuit.dagcircuit import DAGCircuit
1✔
26
from qiskit.circuit import Qubit, Clbit, ClassicalRegister
1✔
27
from qiskit.circuit.classical import expr
1✔
28
from qiskit.converters import dagdependency_to_circuit
1✔
29
from qiskit.utils import optionals as _optionals
1✔
30
from qiskit.exceptions import InvalidFileError
1✔
31
from .exceptions import VisualizationError
1✔
32

33

34
IMAGE_TYPES = {
1✔
35
    "canon",
36
    "cmap",
37
    "cmapx",
38
    "cmapx_np",
39
    "dia",
40
    "dot",
41
    "fig",
42
    "gd",
43
    "gd2",
44
    "gif",
45
    "hpgl",
46
    "imap",
47
    "imap_np",
48
    "ismap",
49
    "jpe",
50
    "jpeg",
51
    "jpg",
52
    "mif",
53
    "mp",
54
    "pcl",
55
    "pdf",
56
    "pic",
57
    "plain",
58
    "plain-ext",
59
    "png",
60
    "ps",
61
    "ps2",
62
    "svg",
63
    "svgz",
64
    "vml",
65
    "vmlz",
66
    "vrml",
67
    "vtx",
68
    "wbmp",
69
    "xdor",
70
    "xlib",
71
}
72

73

74
@_optionals.HAS_GRAPHVIZ.require_in_call
1✔
75
@_optionals.HAS_PIL.require_in_call
1✔
76
def dag_drawer(dag, scale=0.7, filename=None, style="color"):
1✔
77
    """Plot the directed acyclic graph (dag) to represent operation dependencies
78
    in a quantum circuit.
79

80
    This function calls the :func:`~rustworkx.visualization.graphviz_draw` function from the
81
    ``rustworkx`` package to draw the DAG.
82

83
    Args:
84
        dag (DAGCircuit or DAGDependency): The dag to draw.
85
        scale (float): scaling factor
86
        filename (str): file path to save image to (format inferred from name)
87
        style (str): 'plain': B&W graph
88
                     'color' (default): color input/output/op nodes
89

90
    Returns:
91
        PIL.Image: if in Jupyter notebook and not saving to file,
92
            otherwise None.
93

94
    Raises:
95
        VisualizationError: when style is not recognized.
96
        InvalidFileError: when filename provided is not valid
97
        ValueError: If the file extension for ``filename`` is not an image
98
            type supported by Graphviz.
99

100
    Example:
101
        .. plot::
102
           :include-source:
103
           :nofigs:
104

105
            from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
106
            from qiskit.dagcircuit import DAGCircuit
107
            from qiskit.converters import circuit_to_dag
108
            from qiskit.visualization import dag_drawer
109

110
            q = QuantumRegister(3, 'q')
111
            c = ClassicalRegister(3, 'c')
112
            circ = QuantumCircuit(q, c)
113
            circ.h(q[0])
114
            circ.cx(q[0], q[1])
115
            circ.measure(q[0], c[0])
116
            with circ.if_test((c, 2)):
117
                circ.rz(0.5, q[1])
118

119
            dag = circuit_to_dag(circ)
120
            dag_drawer(dag)
121
    """
UNCOV
122
    from PIL import Image
×
123

124
    # NOTE: use type str checking to avoid potential cyclical import
125
    # the two tradeoffs ere that it will not handle subclasses and it is
126
    # slower (which doesn't matter for a visualization function)
127
    type_str = str(type(dag))
×
UNCOV
128
    register_bit_labels = {
×
129
        bit: f"{reg.name}[{idx}]"
130
        for reg in list(dag.qregs.values()) + list(dag.cregs.values())
131
        for (idx, bit) in enumerate(reg)
132
    }
UNCOV
133
    if "DAGDependency" in type_str:
×
134
        # pylint: disable=cyclic-import
UNCOV
135
        from qiskit.visualization.circuit._utils import get_bit_reg_index
×
136

137
        qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
×
138
        clbit_indices = {bit: index for index, bit in enumerate(dag.clbits)}
×
139
        graph_attrs = {"dpi": str(100 * scale)}
×
UNCOV
140
        dag_dep_circ = dagdependency_to_circuit(dag)
×
141

142
        def node_attr_func(node):
×
143
            if "DAGDependencyV2" in type_str:
×
UNCOV
144
                nid_str = str(node._node_id)
×
145
            else:
146
                nid_str = str(node.node_id)
×
147
            if style == "plain":
×
148
                return {}
×
149
            if style == "color":
×
150
                n = {}
×
151
                args = []
×
152
                for count, arg in enumerate(node.qargs + node.cargs):
×
153
                    if count > 4:
×
154
                        args.append("...")
×
155
                        break
×
156
                    if isinstance(arg, Qubit):
×
157
                        f_str = f"q_{qubit_indices[arg]}"
×
158
                    elif isinstance(arg, Clbit):
×
UNCOV
159
                        f_str = f"c_{clbit_indices[arg]}"
×
160
                    else:
161
                        f_str = f"{arg.index}"
×
162
                    arg_str = register_bit_labels.get(arg, f_str)
×
UNCOV
163
                    args.append(arg_str)
×
164

165
                n["color"] = "black"
×
UNCOV
166
                n["label"] = (
×
167
                    nid_str + ": " + str(node.name) + " (" + str(args)[1:-1].replace("'", "") + ")"
168
                )
169
                if node.name == "barrier":
×
170
                    n["style"] = "filled"
×
171
                    n["fillcolor"] = "grey"
×
172
                elif getattr(node.op, "_directive", False):
×
173
                    n["style"] = "filled"
×
174
                    n["fillcolor"] = "red"
×
175
                elif getattr(node.op, "condition", None):
×
176
                    condition = node.op.condition
×
177
                    if isinstance(condition, expr.Expr):
×
178
                        cond_txt = " (cond: [Expr]) ("
×
179
                    elif isinstance(condition[0], ClassicalRegister):
×
UNCOV
180
                        cond_txt = f" (cond: {condition[0].name}, {int(condition[1])}) ("
×
181
                    else:
UNCOV
182
                        register, bit_index, reg_index = get_bit_reg_index(
×
183
                            dag_dep_circ, condition[0]
184
                        )
185
                        if register is not None:
×
UNCOV
186
                            cond_txt = (
×
187
                                f" (cond: {register.name}[{reg_index}], {int(condition[1])}) ("
188
                            )
189
                        else:
190
                            cond_txt = f" (cond: {bit_index}, {int(condition[1])}) ("
×
191
                    n["style"] = "filled"
×
192
                    n["fillcolor"] = "green"
×
UNCOV
193
                    n["label"] = (
×
194
                        nid_str
195
                        + ": "
196
                        + str(node.name)
197
                        + cond_txt
198
                        + str(args)[1:-1].replace("'", "")
199
                        + ")"
200
                    )
201
                elif node.name != "measure":  # measure is unfilled
×
202
                    n["style"] = "filled"
×
203
                    n["fillcolor"] = "lightblue"
×
UNCOV
204
                return n
×
205
            else:
UNCOV
206
                raise VisualizationError(f"Unrecognized style {style} for the dag_drawer.")
×
207

UNCOV
208
        edge_attr_func = None
×
209

210
    else:
UNCOV
211
        graph_attrs = {"dpi": str(100 * scale)}
×
212

213
        def node_attr_func(node):
×
214
            if style == "plain":
×
215
                return {}
×
216
            if style == "color":
×
217
                n = {}
×
218
                if isinstance(node, DAGOpNode):
×
219
                    n["label"] = node.name
×
220
                    n["color"] = "blue"
×
221
                    n["style"] = "filled"
×
222
                    n["fillcolor"] = "lightblue"
×
223
                if isinstance(node, DAGInNode):
×
224
                    if isinstance(node.wire, Qubit):
×
UNCOV
225
                        label = register_bit_labels.get(
×
226
                            node.wire, f"q_{dag.find_bit(node.wire).index}"
227
                        )
228
                    elif isinstance(node.wire, Clbit):
×
UNCOV
229
                        label = register_bit_labels.get(
×
230
                            node.wire, f"c_{dag.find_bit(node.wire).index}"
231
                        )
232
                    else:
UNCOV
233
                        label = str(node.wire.name)
×
234

235
                    n["label"] = label
×
236
                    n["color"] = "black"
×
237
                    n["style"] = "filled"
×
238
                    n["fillcolor"] = "green"
×
239
                if isinstance(node, DAGOutNode):
×
240
                    if isinstance(node.wire, Qubit):
×
UNCOV
241
                        label = register_bit_labels.get(
×
242
                            node.wire, f"q[{dag.find_bit(node.wire).index}]"
243
                        )
244
                    elif isinstance(node.wire, Clbit):
×
UNCOV
245
                        label = register_bit_labels.get(
×
246
                            node.wire, f"c[{dag.find_bit(node.wire).index}]"
247
                        )
248
                    else:
249
                        label = str(node.wire.name)
×
250
                    n["label"] = label
×
251
                    n["color"] = "black"
×
252
                    n["style"] = "filled"
×
253
                    n["fillcolor"] = "red"
×
UNCOV
254
                return n
×
255
            else:
UNCOV
256
                raise VisualizationError(f"Invalid style {style}")
×
257

258
        def edge_attr_func(edge):
×
259
            e = {}
×
260
            if isinstance(edge, Qubit):
×
261
                label = register_bit_labels.get(edge, f"q_{dag.find_bit(edge).index}")
×
262
            elif isinstance(edge, Clbit):
×
UNCOV
263
                label = register_bit_labels.get(edge, f"c_{dag.find_bit(edge).index}")
×
264
            else:
265
                label = str(edge.name)
×
266
            e["label"] = label
×
UNCOV
267
            return e
×
268

269
    image_type = "png"
×
270
    if filename:
×
271
        if "." not in filename:
×
272
            raise InvalidFileError("Parameter 'filename' must be in format 'name.extension'")
×
273
        image_type = filename.split(".")[-1]
×
274
        if image_type not in IMAGE_TYPES:
×
UNCOV
275
            raise ValueError(
×
276
                "The specified value for the image_type argument, "
277
                f"'{image_type}' is not a valid choice. It must be one of: "
278
                f"{IMAGE_TYPES}"
279
            )
280

281
    if isinstance(dag, DAGCircuit):
×
UNCOV
282
        dot_str = dag._to_dot(
×
283
            graph_attrs,
284
            node_attr_func,
285
            edge_attr_func,
286
        )
287

288
        prog = "dot"
×
289
        if not filename:
×
UNCOV
290
            dot_result = subprocess.run(
×
291
                [prog, "-T", image_type],
292
                input=dot_str.encode("utf-8"),
293
                capture_output=True,
294
                encoding=None,
295
                check=True,
296
                text=False,
297
            )
298
            dot_bytes_image = io.BytesIO(dot_result.stdout)
×
299
            image = Image.open(dot_bytes_image)
×
UNCOV
300
            return image
×
301
        else:
UNCOV
302
            subprocess.run(
×
303
                [prog, "-T", image_type, "-o", filename],
304
                input=dot_str,
305
                check=True,
306
                encoding="utf8",
307
                text=True,
308
            )
UNCOV
309
            return None
×
310
    else:
UNCOV
311
        return graphviz_draw(
×
312
            dag._multi_graph,
313
            node_attr_func,
314
            edge_attr_func,
315
            graph_attrs,
316
            filename,
317
            image_type,
318
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc