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

Qiskit / qiskit / 22309958047

23 Feb 2026 02:17PM UTC coverage: 87.87% (+0.03%) from 87.843%
22309958047

push

github

web-flow
Pivot to using ruff for all linting (#15603)

* Pivot to using ruff for all linting

This commit switches us to fully using ruff for all the linting in CI
and locally. The primary motivation for this change is to improve
productivity because ruff is signficantly faster. Pylint is incredibly
slow in general, but compared to ruff especially so. For example, on my
laptop ruff takes 0.04 seconds to run on the qiskit/ subdirectory (after
clearing the cache, with the cache populated it takes 0.025 sec) of the
source tree while running pylint on the same path took 70 sec. This leads
to people skipping lint locally and causes churn in CI becaus.

We had started to experimenting with ruff in the past and
used it for a some small set of rules but were still using pylint for
the bulk of the linting in the repo. The concern at the time was a loss
of lint coverage or a lot of code churn caused by migrating to a new tool.
Specifically pylint does more type inference and checking that ruff
doesn't. However since we started the experiment one major change in
qiskit is how much work is happening in rust now vs Python. At this
point any loss in lint coverage is unlikely to cause a significant
problem in practice and we'll make real productivity gains by making
this change.

* Remove out of date comment

* Update makefile

* Enable more rules

* Revert lambda autofixes

* Fix new rules

* Add bandit rules

* Enable Ruff native rules

* Remove pylint disable comments

* Enable docstring rules

This commit adds the docstring rules on the repo. This involves a few
more changes than previous commits because there are a lot of formatting
consistency rules that needed to be auto-applied. The checking also
found several instances where there was missing documentation that
should have been included.

* Add categories from old ruff config

* Fix from rebase

* Add flake8-raise rule

* Add flake8-pie rules

* Add implicit namespace rules

* Fix deprecation decorator error on impo... (continued)

624 of 677 new or added lines in 198 files covered. (92.17%)

16 existing lines in 9 files now uncovered.

100211 of 114044 relevant lines covered (87.87%)

1151735.18 hits per line

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

6.54
/qiskit/visualization/gate_map.py
1
# This code is part of Qiskit.
2
#
3
# (C) Copyright IBM 2017, 2024.
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 https://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
"""A module for visualizing device coupling maps"""
14

15
import math
1✔
16

17
import numpy as np
1✔
18
import rustworkx as rx
1✔
19
from rustworkx.visualization import graphviz_draw
1✔
20

21
from qiskit.exceptions import QiskitError
1✔
22
from qiskit.utils import optionals as _optionals
1✔
23
from qiskit.transpiler.coupling import CouplingMap
1✔
24
from .exceptions import VisualizationError
1✔
25

26

27
@_optionals.HAS_MATPLOTLIB.require_in_call
1✔
28
def plot_gate_map(
1✔
29
    backend,
30
    figsize=None,
31
    plot_directed=False,
32
    label_qubits=True,
33
    qubit_size=None,
34
    line_width=4,
35
    font_size=None,
36
    qubit_color=None,
37
    qubit_labels=None,
38
    line_color=None,
39
    font_color="white",
40
    ax=None,
41
    filename=None,
42
    qubit_coordinates=None,
43
):
44
    """Plots the gate map of a device.
45

46
    Args:
47
        backend (Backend): The backend instance that will be used to plot the device
48
            gate map.
49
        figsize (tuple): Output figure size (wxh) in inches.
50
        plot_directed (bool): Plot directed coupling map.
51
        label_qubits (bool): Label the qubits.
52
        qubit_size (float): Size of qubit marker.
53
        line_width (float): Width of lines.
54
        font_size (int): Font size of qubit labels.
55
        qubit_color (list): A list of colors for the qubits
56
        qubit_labels (list): A list of qubit labels
57
        line_color (list): A list of colors for each line from coupling_map.
58
        font_color (str): The font color for the qubit labels.
59
        ax (Axes): A Matplotlib axes instance.
60
        filename (str): file path to save image to.
61
        qubit_coordinates (Sequence): An optional sequence input (list or array being the
62
            most common) of 2d coordinates for each qubit. The length of the
63
            sequence much match the number of qubits on the backend. The sequence
64
            should be the planar coordinates in a 0-based square grid where each
65
            qubit is located.
66

67
    Returns:
68
        Figure: A Matplotlib figure instance.
69

70
    Raises:
71
        QiskitError: If you tried to pass a simulator or the backend is None,
72
            but one of num_qubits, mpl_data, or cmap is None.
73
        MissingOptionalLibraryError: If matplotlib not installed.
74

75
    Example:
76

77
        .. plot::
78
           :alt: Output from the previous code.
79
           :include-source:
80

81
           from qiskit.providers.fake_provider import GenericBackendV2
82
           from qiskit.visualization import plot_gate_map
83

84
           backend = GenericBackendV2(num_qubits=5)
85

86
           plot_gate_map(backend)
87
    """
88
    qubit_coordinates_map = {}
×
89

90
    qubit_coordinates_map[5] = [[1, 0], [0, 1], [1, 1], [1, 2], [2, 1]]
×
91

92
    qubit_coordinates_map[7] = [[0, 0], [0, 1], [0, 2], [1, 1], [2, 0], [2, 1], [2, 2]]
×
93

94
    qubit_coordinates_map[20] = [
×
95
        [0, 0],
96
        [0, 1],
97
        [0, 2],
98
        [0, 3],
99
        [0, 4],
100
        [1, 0],
101
        [1, 1],
102
        [1, 2],
103
        [1, 3],
104
        [1, 4],
105
        [2, 0],
106
        [2, 1],
107
        [2, 2],
108
        [2, 3],
109
        [2, 4],
110
        [3, 0],
111
        [3, 1],
112
        [3, 2],
113
        [3, 3],
114
        [3, 4],
115
    ]
116

117
    qubit_coordinates_map[15] = [
×
118
        [0, 0],
119
        [0, 1],
120
        [0, 2],
121
        [0, 3],
122
        [0, 4],
123
        [0, 5],
124
        [0, 6],
125
        [1, 7],
126
        [1, 6],
127
        [1, 5],
128
        [1, 4],
129
        [1, 3],
130
        [1, 2],
131
        [1, 1],
132
        [1, 0],
133
    ]
134

135
    qubit_coordinates_map[16] = [
×
136
        [1, 0],
137
        [1, 1],
138
        [2, 1],
139
        [3, 1],
140
        [1, 2],
141
        [3, 2],
142
        [0, 3],
143
        [1, 3],
144
        [3, 3],
145
        [4, 3],
146
        [1, 4],
147
        [3, 4],
148
        [1, 5],
149
        [2, 5],
150
        [3, 5],
151
        [1, 6],
152
    ]
153

154
    qubit_coordinates_map[27] = [
×
155
        [1, 0],
156
        [1, 1],
157
        [2, 1],
158
        [3, 1],
159
        [1, 2],
160
        [3, 2],
161
        [0, 3],
162
        [1, 3],
163
        [3, 3],
164
        [4, 3],
165
        [1, 4],
166
        [3, 4],
167
        [1, 5],
168
        [2, 5],
169
        [3, 5],
170
        [1, 6],
171
        [3, 6],
172
        [0, 7],
173
        [1, 7],
174
        [3, 7],
175
        [4, 7],
176
        [1, 8],
177
        [3, 8],
178
        [1, 9],
179
        [2, 9],
180
        [3, 9],
181
        [3, 10],
182
    ]
183

184
    qubit_coordinates_map[28] = [
×
185
        [0, 2],
186
        [0, 3],
187
        [0, 4],
188
        [0, 5],
189
        [0, 6],
190
        [1, 2],
191
        [1, 6],
192
        [2, 0],
193
        [2, 1],
194
        [2, 2],
195
        [2, 3],
196
        [2, 4],
197
        [2, 5],
198
        [2, 6],
199
        [2, 7],
200
        [2, 8],
201
        [3, 0],
202
        [3, 4],
203
        [3, 8],
204
        [4, 0],
205
        [4, 1],
206
        [4, 2],
207
        [4, 3],
208
        [4, 4],
209
        [4, 5],
210
        [4, 6],
211
        [4, 7],
212
        [4, 8],
213
    ]
214

215
    qubit_coordinates_map[53] = [
×
216
        [0, 2],
217
        [0, 3],
218
        [0, 4],
219
        [0, 5],
220
        [0, 6],
221
        [1, 2],
222
        [1, 6],
223
        [2, 0],
224
        [2, 1],
225
        [2, 2],
226
        [2, 3],
227
        [2, 4],
228
        [2, 5],
229
        [2, 6],
230
        [2, 7],
231
        [2, 8],
232
        [3, 0],
233
        [3, 4],
234
        [3, 8],
235
        [4, 0],
236
        [4, 1],
237
        [4, 2],
238
        [4, 3],
239
        [4, 4],
240
        [4, 5],
241
        [4, 6],
242
        [4, 7],
243
        [4, 8],
244
        [5, 2],
245
        [5, 6],
246
        [6, 0],
247
        [6, 1],
248
        [6, 2],
249
        [6, 3],
250
        [6, 4],
251
        [6, 5],
252
        [6, 6],
253
        [6, 7],
254
        [6, 8],
255
        [7, 0],
256
        [7, 4],
257
        [7, 8],
258
        [8, 0],
259
        [8, 1],
260
        [8, 2],
261
        [8, 3],
262
        [8, 4],
263
        [8, 5],
264
        [8, 6],
265
        [8, 7],
266
        [8, 8],
267
        [9, 2],
268
        [9, 6],
269
    ]
270

271
    qubit_coordinates_map[65] = [
×
272
        [0, 0],
273
        [0, 1],
274
        [0, 2],
275
        [0, 3],
276
        [0, 4],
277
        [0, 5],
278
        [0, 6],
279
        [0, 7],
280
        [0, 8],
281
        [0, 9],
282
        [1, 0],
283
        [1, 4],
284
        [1, 8],
285
        [2, 0],
286
        [2, 1],
287
        [2, 2],
288
        [2, 3],
289
        [2, 4],
290
        [2, 5],
291
        [2, 6],
292
        [2, 7],
293
        [2, 8],
294
        [2, 9],
295
        [2, 10],
296
        [3, 2],
297
        [3, 6],
298
        [3, 10],
299
        [4, 0],
300
        [4, 1],
301
        [4, 2],
302
        [4, 3],
303
        [4, 4],
304
        [4, 5],
305
        [4, 6],
306
        [4, 7],
307
        [4, 8],
308
        [4, 9],
309
        [4, 10],
310
        [5, 0],
311
        [5, 4],
312
        [5, 8],
313
        [6, 0],
314
        [6, 1],
315
        [6, 2],
316
        [6, 3],
317
        [6, 4],
318
        [6, 5],
319
        [6, 6],
320
        [6, 7],
321
        [6, 8],
322
        [6, 9],
323
        [6, 10],
324
        [7, 2],
325
        [7, 6],
326
        [7, 10],
327
        [8, 1],
328
        [8, 2],
329
        [8, 3],
330
        [8, 4],
331
        [8, 5],
332
        [8, 6],
333
        [8, 7],
334
        [8, 8],
335
        [8, 9],
336
        [8, 10],
337
    ]
338

339
    qubit_coordinates_map[127] = [
×
340
        [0, 0],
341
        [0, 1],
342
        [0, 2],
343
        [0, 3],
344
        [0, 4],
345
        [0, 5],
346
        [0, 6],
347
        [0, 7],
348
        [0, 8],
349
        [0, 9],
350
        [0, 10],
351
        [0, 11],
352
        [0, 12],
353
        [0, 13],
354
        [1, 0],
355
        [1, 4],
356
        [1, 8],
357
        [1, 12],
358
        [2, 0],
359
        [2, 1],
360
        [2, 2],
361
        [2, 3],
362
        [2, 4],
363
        [2, 5],
364
        [2, 6],
365
        [2, 7],
366
        [2, 8],
367
        [2, 9],
368
        [2, 10],
369
        [2, 11],
370
        [2, 12],
371
        [2, 13],
372
        [2, 14],
373
        [3, 2],
374
        [3, 6],
375
        [3, 10],
376
        [3, 14],
377
        [4, 0],
378
        [4, 1],
379
        [4, 2],
380
        [4, 3],
381
        [4, 4],
382
        [4, 5],
383
        [4, 6],
384
        [4, 7],
385
        [4, 8],
386
        [4, 9],
387
        [4, 10],
388
        [4, 11],
389
        [4, 12],
390
        [4, 13],
391
        [4, 14],
392
        [5, 0],
393
        [5, 4],
394
        [5, 8],
395
        [5, 12],
396
        [6, 0],
397
        [6, 1],
398
        [6, 2],
399
        [6, 3],
400
        [6, 4],
401
        [6, 5],
402
        [6, 6],
403
        [6, 7],
404
        [6, 8],
405
        [6, 9],
406
        [6, 10],
407
        [6, 11],
408
        [6, 12],
409
        [6, 13],
410
        [6, 14],
411
        [7, 2],
412
        [7, 6],
413
        [7, 10],
414
        [7, 14],
415
        [8, 0],
416
        [8, 1],
417
        [8, 2],
418
        [8, 3],
419
        [8, 4],
420
        [8, 5],
421
        [8, 6],
422
        [8, 7],
423
        [8, 8],
424
        [8, 9],
425
        [8, 10],
426
        [8, 11],
427
        [8, 12],
428
        [8, 13],
429
        [8, 14],
430
        [9, 0],
431
        [9, 4],
432
        [9, 8],
433
        [9, 12],
434
        [10, 0],
435
        [10, 1],
436
        [10, 2],
437
        [10, 3],
438
        [10, 4],
439
        [10, 5],
440
        [10, 6],
441
        [10, 7],
442
        [10, 8],
443
        [10, 9],
444
        [10, 10],
445
        [10, 11],
446
        [10, 12],
447
        [10, 13],
448
        [10, 14],
449
        [11, 2],
450
        [11, 6],
451
        [11, 10],
452
        [11, 14],
453
        [12, 1],
454
        [12, 2],
455
        [12, 3],
456
        [12, 4],
457
        [12, 5],
458
        [12, 6],
459
        [12, 7],
460
        [12, 8],
461
        [12, 9],
462
        [12, 10],
463
        [12, 11],
464
        [12, 12],
465
        [12, 13],
466
        [12, 14],
467
    ]
468

469
    qubit_coordinates_map[433] = [
×
470
        [0, 0],
471
        [0, 1],
472
        [0, 2],
473
        [0, 3],
474
        [0, 4],
475
        [0, 5],
476
        [0, 6],
477
        [0, 7],
478
        [0, 8],
479
        [0, 9],
480
        [0, 10],
481
        [0, 11],
482
        [0, 12],
483
        [0, 13],
484
        [0, 14],
485
        [0, 15],
486
        [0, 16],
487
        [0, 17],
488
        [0, 18],
489
        [0, 19],
490
        [0, 20],
491
        [0, 21],
492
        [0, 22],
493
        [0, 23],
494
        [0, 24],
495
        [0, 25],
496
        [1, 0],
497
        [1, 4],
498
        [1, 8],
499
        [1, 12],
500
        [1, 16],
501
        [1, 20],
502
        [1, 24],
503
        [2, 0],
504
        [2, 1],
505
        [2, 2],
506
        [2, 3],
507
        [2, 4],
508
        [2, 5],
509
        [2, 6],
510
        [2, 7],
511
        [2, 8],
512
        [2, 9],
513
        [2, 10],
514
        [2, 11],
515
        [2, 12],
516
        [2, 13],
517
        [2, 14],
518
        [2, 15],
519
        [2, 16],
520
        [2, 17],
521
        [2, 18],
522
        [2, 19],
523
        [2, 20],
524
        [2, 21],
525
        [2, 22],
526
        [2, 23],
527
        [2, 24],
528
        [2, 25],
529
        [2, 26],
530
        [3, 2],
531
        [3, 6],
532
        [3, 10],
533
        [3, 14],
534
        [3, 18],
535
        [3, 22],
536
        [3, 26],
537
        [4, 0],
538
        [4, 1],
539
        [4, 2],
540
        [4, 3],
541
        [4, 4],
542
        [4, 5],
543
        [4, 6],
544
        [4, 7],
545
        [4, 8],
546
        [4, 9],
547
        [4, 10],
548
        [4, 11],
549
        [4, 12],
550
        [4, 13],
551
        [4, 14],
552
        [4, 15],
553
        [4, 16],
554
        [4, 17],
555
        [4, 18],
556
        [4, 19],
557
        [4, 20],
558
        [4, 21],
559
        [4, 22],
560
        [4, 23],
561
        [4, 24],
562
        [4, 25],
563
        [4, 26],
564
        [5, 0],
565
        [5, 4],
566
        [5, 8],
567
        [5, 12],
568
        [5, 16],
569
        [5, 20],
570
        [5, 24],
571
        [6, 0],
572
        [6, 1],
573
        [6, 2],
574
        [6, 3],
575
        [6, 4],
576
        [6, 5],
577
        [6, 6],
578
        [6, 7],
579
        [6, 8],
580
        [6, 9],
581
        [6, 10],
582
        [6, 11],
583
        [6, 12],
584
        [6, 13],
585
        [6, 14],
586
        [6, 15],
587
        [6, 16],
588
        [6, 17],
589
        [6, 18],
590
        [6, 19],
591
        [6, 20],
592
        [6, 21],
593
        [6, 22],
594
        [6, 23],
595
        [6, 24],
596
        [6, 25],
597
        [6, 26],
598
        [7, 2],
599
        [7, 6],
600
        [7, 10],
601
        [7, 14],
602
        [7, 18],
603
        [7, 22],
604
        [7, 26],
605
        [8, 0],
606
        [8, 1],
607
        [8, 2],
608
        [8, 3],
609
        [8, 4],
610
        [8, 5],
611
        [8, 6],
612
        [8, 7],
613
        [8, 8],
614
        [8, 9],
615
        [8, 10],
616
        [8, 11],
617
        [8, 12],
618
        [8, 13],
619
        [8, 14],
620
        [8, 15],
621
        [8, 16],
622
        [8, 17],
623
        [8, 18],
624
        [8, 19],
625
        [8, 20],
626
        [8, 21],
627
        [8, 22],
628
        [8, 23],
629
        [8, 24],
630
        [8, 25],
631
        [8, 26],
632
        [9, 0],
633
        [9, 4],
634
        [9, 8],
635
        [9, 12],
636
        [9, 16],
637
        [9, 20],
638
        [9, 24],
639
        [10, 0],
640
        [10, 1],
641
        [10, 2],
642
        [10, 3],
643
        [10, 4],
644
        [10, 5],
645
        [10, 6],
646
        [10, 7],
647
        [10, 8],
648
        [10, 9],
649
        [10, 10],
650
        [10, 11],
651
        [10, 12],
652
        [10, 13],
653
        [10, 14],
654
        [10, 15],
655
        [10, 16],
656
        [10, 17],
657
        [10, 18],
658
        [10, 19],
659
        [10, 20],
660
        [10, 21],
661
        [10, 22],
662
        [10, 23],
663
        [10, 24],
664
        [10, 25],
665
        [10, 26],
666
        [11, 2],
667
        [11, 6],
668
        [11, 10],
669
        [11, 14],
670
        [11, 18],
671
        [11, 22],
672
        [11, 26],
673
        [12, 0],
674
        [12, 1],
675
        [12, 2],
676
        [12, 3],
677
        [12, 4],
678
        [12, 5],
679
        [12, 6],
680
        [12, 7],
681
        [12, 8],
682
        [12, 9],
683
        [12, 10],
684
        [12, 11],
685
        [12, 12],
686
        [12, 13],
687
        [12, 14],
688
        [12, 15],
689
        [12, 16],
690
        [12, 17],
691
        [12, 18],
692
        [12, 19],
693
        [12, 20],
694
        [12, 21],
695
        [12, 22],
696
        [12, 23],
697
        [12, 24],
698
        [12, 25],
699
        [12, 26],
700
        [13, 0],
701
        [13, 4],
702
        [13, 8],
703
        [13, 12],
704
        [13, 16],
705
        [13, 20],
706
        [13, 24],
707
        [14, 0],
708
        [14, 1],
709
        [14, 2],
710
        [14, 3],
711
        [14, 4],
712
        [14, 5],
713
        [14, 6],
714
        [14, 7],
715
        [14, 8],
716
        [14, 9],
717
        [14, 10],
718
        [14, 11],
719
        [14, 12],
720
        [14, 13],
721
        [14, 14],
722
        [14, 15],
723
        [14, 16],
724
        [14, 17],
725
        [14, 18],
726
        [14, 19],
727
        [14, 20],
728
        [14, 21],
729
        [14, 22],
730
        [14, 23],
731
        [14, 24],
732
        [14, 25],
733
        [14, 26],
734
        [15, 2],
735
        [15, 6],
736
        [15, 10],
737
        [15, 14],
738
        [15, 18],
739
        [15, 22],
740
        [15, 26],
741
        [16, 0],
742
        [16, 1],
743
        [16, 2],
744
        [16, 3],
745
        [16, 4],
746
        [16, 5],
747
        [16, 6],
748
        [16, 7],
749
        [16, 8],
750
        [16, 9],
751
        [16, 10],
752
        [16, 11],
753
        [16, 12],
754
        [16, 13],
755
        [16, 14],
756
        [16, 15],
757
        [16, 16],
758
        [16, 17],
759
        [16, 18],
760
        [16, 19],
761
        [16, 20],
762
        [16, 21],
763
        [16, 22],
764
        [16, 23],
765
        [16, 24],
766
        [16, 25],
767
        [16, 26],
768
        [17, 0],
769
        [17, 4],
770
        [17, 8],
771
        [17, 12],
772
        [17, 16],
773
        [17, 20],
774
        [17, 24],
775
        [18, 0],
776
        [18, 1],
777
        [18, 2],
778
        [18, 3],
779
        [18, 4],
780
        [18, 5],
781
        [18, 6],
782
        [18, 7],
783
        [18, 8],
784
        [18, 9],
785
        [18, 10],
786
        [18, 11],
787
        [18, 12],
788
        [18, 13],
789
        [18, 14],
790
        [18, 15],
791
        [18, 16],
792
        [18, 17],
793
        [18, 18],
794
        [18, 19],
795
        [18, 20],
796
        [18, 21],
797
        [18, 22],
798
        [18, 23],
799
        [18, 24],
800
        [18, 25],
801
        [18, 26],
802
        [19, 2],
803
        [19, 6],
804
        [19, 10],
805
        [19, 14],
806
        [19, 18],
807
        [19, 22],
808
        [19, 26],
809
        [20, 0],
810
        [20, 1],
811
        [20, 2],
812
        [20, 3],
813
        [20, 4],
814
        [20, 5],
815
        [20, 6],
816
        [20, 7],
817
        [20, 8],
818
        [20, 9],
819
        [20, 10],
820
        [20, 11],
821
        [20, 12],
822
        [20, 13],
823
        [20, 14],
824
        [20, 15],
825
        [20, 16],
826
        [20, 17],
827
        [20, 18],
828
        [20, 19],
829
        [20, 20],
830
        [20, 21],
831
        [20, 22],
832
        [20, 23],
833
        [20, 24],
834
        [20, 25],
835
        [20, 26],
836
        [21, 0],
837
        [21, 4],
838
        [21, 8],
839
        [21, 12],
840
        [21, 16],
841
        [21, 20],
842
        [21, 24],
843
        [22, 0],
844
        [22, 1],
845
        [22, 2],
846
        [22, 3],
847
        [22, 4],
848
        [22, 5],
849
        [22, 6],
850
        [22, 7],
851
        [22, 8],
852
        [22, 9],
853
        [22, 10],
854
        [22, 11],
855
        [22, 12],
856
        [22, 13],
857
        [22, 14],
858
        [22, 15],
859
        [22, 16],
860
        [22, 17],
861
        [22, 18],
862
        [22, 19],
863
        [22, 20],
864
        [22, 21],
865
        [22, 22],
866
        [22, 23],
867
        [22, 24],
868
        [22, 25],
869
        [22, 26],
870
        [23, 2],
871
        [23, 6],
872
        [23, 10],
873
        [23, 14],
874
        [23, 18],
875
        [23, 22],
876
        [23, 26],
877
        [24, 1],
878
        [24, 2],
879
        [24, 3],
880
        [24, 4],
881
        [24, 5],
882
        [24, 6],
883
        [24, 7],
884
        [24, 8],
885
        [24, 9],
886
        [24, 10],
887
        [24, 11],
888
        [24, 12],
889
        [24, 13],
890
        [24, 14],
891
        [24, 15],
892
        [24, 16],
893
        [24, 17],
894
        [24, 18],
895
        [24, 19],
896
        [24, 20],
897
        [24, 21],
898
        [24, 22],
899
        [24, 23],
900
        [24, 24],
901
        [24, 25],
902
        [24, 26],
903
    ]
904

905
    num_qubits = backend.num_qubits
×
906
    coupling_map = backend.coupling_map
×
907
    name = backend.name
×
908
    if qubit_coordinates is None and ("ibm" in name or "fake" in name):
×
909
        qubit_coordinates = qubit_coordinates_map.get(num_qubits, None)
×
910

911
    if qubit_coordinates:
×
912
        if len(qubit_coordinates) != num_qubits:
×
913
            raise QiskitError(
×
914
                f"The number of specified qubit coordinates {len(qubit_coordinates)} "
915
                f"does not match the device number of qubits: {num_qubits}"
916
            )
917
    return plot_coupling_map(
×
918
        num_qubits,
919
        qubit_coordinates,
920
        coupling_map.get_edges(),
921
        figsize,
922
        plot_directed,
923
        label_qubits,
924
        qubit_size,
925
        line_width,
926
        font_size,
927
        qubit_color,
928
        qubit_labels,
929
        line_color,
930
        font_color,
931
        ax,
932
        filename,
933
        planar=rx.is_planar(coupling_map.graph.to_undirected(multigraph=False)),
934
    )
935

936

937
@_optionals.HAS_MATPLOTLIB.require_in_call
1✔
938
@_optionals.HAS_GRAPHVIZ.require_in_call
1✔
939
def plot_coupling_map(
1✔
940
    num_qubits: int,
941
    qubit_coordinates: list[list[int]],
942
    coupling_map: list[list[int]],
943
    figsize=None,
944
    plot_directed=False,
945
    label_qubits=True,
946
    qubit_size=None,
947
    line_width=4,
948
    font_size=None,
949
    qubit_color=None,
950
    qubit_labels=None,
951
    line_color=None,
952
    font_color="white",
953
    ax=None,
954
    filename=None,
955
    *,
956
    planar=True,
957
):
958
    """Plots an arbitrary coupling map of qubits (embedded in a plane).
959

960
    Args:
961
        num_qubits (int): The number of qubits defined and plotted.
962
        qubit_coordinates (List[List[int]]): A list of two-element lists, with entries of each nested
963
            list being the planar coordinates in a 0-based square grid where each qubit is located.
964
        coupling_map (List[List[int]]): A list of two-element lists, with entries of each nested
965
            list being the qubit numbers of the bonds to be plotted.
966
        figsize (tuple): Output figure size (wxh) in inches.
967
        plot_directed (bool): Plot directed coupling map.
968
        label_qubits (bool): Label the qubits.
969
        qubit_size (float): Size of qubit marker.
970
        line_width (float): Width of lines.
971
        font_size (int): Font size of qubit labels.
972
        qubit_color (list): A list of colors for the qubits
973
        qubit_labels (list): A list of qubit labels
974
        line_color (list): A list of colors for each line from coupling_map.
975
        font_color (str): The font color for the qubit labels.
976
        ax (Axes): A Matplotlib axes instance.
977
        filename (str): file path to save image to.
978
        planar (bool): If the coupling map is planar or not. Default: ``True`` (i.e. it is planar)
979

980
    Returns:
981
        Figure: A Matplotlib figure instance.
982

983
    Raises:
984
        MissingOptionalLibraryError: If matplotlib or graphviz is not installed.
985
        QiskitError: If the length of qubit labels does not match the number of qubits.
986

987
    Example:
988

989
        .. plot::
990
           :alt: Output from the previous code.
991
           :include-source:
992

993
            from qiskit.visualization import plot_coupling_map
994

995
            num_qubits = 8
996
            qubit_coordinates = [[0, 1], [1, 1], [1, 0], [1, 2], [2, 0], [2, 2], [2, 1], [3, 1]]
997
            coupling_map = [[0, 1], [1, 2], [2, 3], [3, 5], [4, 5], [5, 6], [2, 4], [6, 7]]
998
            plot_coupling_map(num_qubits, qubit_coordinates, coupling_map)
999
    """
1000
    import matplotlib.pyplot as plt
×
1001
    from .utils import matplotlib_close_if_inline
×
1002

1003
    input_axes = False
×
1004
    if ax:
×
1005
        input_axes = True
×
1006

1007
    if qubit_size is None:
×
1008
        qubit_size = 30
×
1009

1010
    if qubit_labels is None:
×
1011
        qubit_labels = list(range(num_qubits))
×
NEW
1012
    elif len(qubit_labels) != num_qubits:
×
NEW
1013
        raise QiskitError("Length of qubit labels does not equal number of qubits.")
×
1014

1015
    if not label_qubits:
×
1016
        qubit_labels = [""] * num_qubits
×
1017

1018
    # set coloring
1019
    if qubit_color is None:
×
1020
        qubit_color = ["#648fff"] * num_qubits
×
1021
    if line_color is None:
×
1022
        line_color = ["#648fff"] * len(coupling_map)
×
1023

1024
    if num_qubits == 1:
×
1025
        graph = rx.PyDiGraph()
×
1026
        graph.add_node(0)
×
1027
    else:
1028
        graph = CouplingMap(coupling_map).graph
×
1029

1030
    if not plot_directed:
×
1031
        line_color_map = dict(zip(graph.edge_list(), line_color))
×
1032
        graph = graph.to_undirected(multigraph=False)
×
1033
        line_color = [line_color_map[edge] for edge in graph.edge_list()]
×
1034

1035
    for node in graph.node_indices():
×
1036
        graph[node] = node
×
1037

1038
    for edge_index in graph.edge_indices():
×
1039
        graph.update_edge_by_index(edge_index, edge_index)
×
1040

1041
    # pixel-to-inch conversion
1042
    px = 1.15 / plt.rcParams["figure.dpi"]
×
1043

1044
    if qubit_coordinates:
×
1045
        qubit_coordinates = [coordinates[::-1] for coordinates in qubit_coordinates]
×
1046

1047
    if font_size is None:
×
1048
        max_characters = max(1, max(len(str(x)) for x in qubit_labels))
×
1049
        if max_characters == 1:
×
1050
            font_size = 20
×
1051
        elif max_characters == 2:
×
1052
            font_size = 14
×
1053
        elif max_characters == 3:
×
1054
            font_size = 12
×
1055
        else:
1056
            font_size = 1
×
1057

1058
    def color_node(node):
×
1059
        if qubit_coordinates:
×
1060
            out_dict = {
×
1061
                "label": str(qubit_labels[node]),
1062
                "color": f'"{qubit_color[node]}"',
1063
                "fillcolor": f'"{qubit_color[node]}"',
1064
                "pos": f'"{qubit_coordinates[node][0]},{qubit_coordinates[node][1]}"',
1065
                "pin": "True",
1066
            }
1067
        else:
1068
            out_dict = {
×
1069
                "label": str(qubit_labels[node]),
1070
                "color": f'"{qubit_color[node]}"',
1071
                "fillcolor": f'"{qubit_color[node]}"',
1072
            }
1073
        out_dict["style"] = "filled"
×
1074
        out_dict["shape"] = "circle"
×
1075
        out_dict["fontcolor"] = f'"{font_color}"'
×
NEW
1076
        out_dict["fontsize"] = f'"{font_size!s}!"'
×
1077
        out_dict["height"] = str(qubit_size * px)
×
1078
        out_dict["fixedsize"] = "True"
×
1079
        out_dict["fontname"] = '"DejaVu Sans"'
×
1080
        return out_dict
×
1081

1082
    def color_edge(edge):
×
1083
        out_dict = {
×
1084
            "color": f'"{line_color[edge]}"',
1085
            "fillcolor": f'"{line_color[edge]}"',
1086
            "penwidth": str(line_width),
1087
        }
1088
        return out_dict
×
1089

1090
    graph_attributes = None
×
1091
    if not qubit_coordinates:
×
1092
        if planar:
×
1093
            graph_attributes = {
×
1094
                "overlap_scaling": "-7",
1095
                "overlap": "prism",
1096
                "model": "subset",
1097
            }
1098
        else:
1099
            graph_attributes = {
×
1100
                "overlap": "true",
1101
            }
1102
    plot = graphviz_draw(
×
1103
        graph,
1104
        method="neato",
1105
        graph_attr=graph_attributes,
1106
        node_attr_fn=color_node,
1107
        edge_attr_fn=color_edge,
1108
        filename=filename,
1109
    )
1110

1111
    if filename:
×
1112
        return None
×
1113

1114
    if not input_axes:
×
1115
        if figsize is None:
×
1116
            width, height = plot.size
×
1117
            figsize = (width * px, height * px)
×
1118
        fig, ax = plt.subplots(figsize=figsize)
×
1119
    ax.axis("off")
×
1120
    ax.imshow(plot)
×
1121

1122
    if not input_axes:
×
1123
        matplotlib_close_if_inline(fig)
×
1124
        return fig
×
1125

1126

1127
def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None):
1✔
1128
    """Plot the layout of a circuit transpiled for a given
1129
    target backend.
1130

1131
    Args:
1132
        circuit (QuantumCircuit): Input quantum circuit.
1133
        backend (Backend): Target backend.
1134
        view (str): How to label qubits in the layout. Options:
1135

1136
          - ``"virtual"``: Label each qubit with the index of the virtual qubit that
1137
            mapped to it.
1138
          - ``"physical"``: Label each qubit with the index of the physical qubit that it
1139
            corresponds to on the device.
1140

1141
        qubit_coordinates (Sequence): An optional sequence input (list or array being the
1142
            most common) of 2d coordinates for each qubit. The length of the
1143
            sequence must match the number of qubits on the backend. The sequence
1144
            should be the planar coordinates in a 0-based square grid where each
1145
            qubit is located.
1146

1147
    Returns:
1148
        Figure: A matplotlib figure showing layout.
1149

1150
    Raises:
1151
        QiskitError: Invalid view type given.
1152
        VisualizationError: Circuit has no layout attribute.
1153

1154
    Example:
1155
        .. plot::
1156
           :alt: Output from the previous code.
1157
           :include-source:
1158

1159
            from qiskit import QuantumCircuit, transpile
1160
            from qiskit.providers.fake_provider import GenericBackendV2
1161
            from qiskit.visualization import plot_circuit_layout
1162

1163
            ghz = QuantumCircuit(3, 3)
1164
            ghz.h(0)
1165
            for idx in range(1,3):
1166
                ghz.cx(0,idx)
1167
            ghz.measure(range(3), range(3))
1168

1169
            backend = GenericBackendV2(num_qubits=5)
1170
            new_circ_lv3 = transpile(ghz, backend=backend, optimization_level=3)
1171
            plot_circuit_layout(new_circ_lv3, backend)
1172
    """
1173
    if circuit._layout is None:
×
1174
        raise QiskitError("Circuit has no layout. Perhaps it has not been transpiled.")
×
1175

1176
    num_qubits = backend.num_qubits
×
1177
    cmap = backend.coupling_map
×
1178
    cmap_len = cmap.graph.num_edges()
×
1179

1180
    qubits = []
×
1181
    qubit_labels = [""] * num_qubits
×
1182

1183
    bit_locations = {
×
1184
        bit: {"register": register, "index": index}
1185
        for register in circuit._layout.initial_layout.get_registers()
1186
        for index, bit in enumerate(register)
1187
    }
1188
    for index, qubit in enumerate(circuit._layout.initial_layout.get_virtual_bits()):
×
1189
        if qubit not in bit_locations:
×
1190
            bit_locations[qubit] = {"register": None, "index": index}
×
1191

1192
    if view == "virtual":
×
1193
        for key, val in circuit._layout.initial_layout.get_virtual_bits().items():
×
1194
            bit_register = bit_locations[key]["register"]
×
1195
            if bit_register is None or bit_register.name != "ancilla":
×
1196
                qubits.append(val)
×
1197
                qubit_labels[val] = str(bit_locations[key]["index"])
×
1198

1199
    elif view == "physical":
×
1200
        for key, val in circuit._layout.initial_layout.get_physical_bits().items():
×
1201
            bit_register = bit_locations[val]["register"]
×
1202
            if bit_register is None or bit_register.name != "ancilla":
×
1203
                qubits.append(key)
×
1204
                qubit_labels[key] = str(key)
×
1205

1206
    else:
1207
        raise VisualizationError("Layout view must be 'virtual' or 'physical'.")
×
1208

1209
    qcolors = ["#648fff"] * num_qubits
×
1210
    for k in qubits:
×
1211
        qcolors[k] = "black"
×
1212

1213
    lcolors = ["#648fff"] * cmap_len
×
1214

1215
    for idx, edge in enumerate(cmap):
×
1216
        if edge[0] in qubits and edge[1] in qubits:
×
1217
            lcolors[idx] = "black"
×
1218

1219
    fig = plot_gate_map(
×
1220
        backend,
1221
        qubit_color=qcolors,
1222
        qubit_labels=qubit_labels,
1223
        line_color=lcolors,
1224
        qubit_coordinates=qubit_coordinates,
1225
    )
1226
    return fig
×
1227

1228

1229
@_optionals.HAS_MATPLOTLIB.require_in_call
1✔
1230
@_optionals.HAS_SEABORN.require_in_call
1✔
1231
def plot_error_map(backend, figsize=(15, 12), show_title=True, qubit_coordinates=None):
1✔
1232
    """Plots the error map of a given backend.
1233

1234
    Args:
1235
        backend (Backend): Given backend.
1236
        figsize (tuple): Figure size in inches.
1237
        show_title (bool): Show the title or not.
1238
        qubit_coordinates (Sequence): An optional sequence input (list or array being the
1239
            most common) of 2d coordinates for each qubit. The length of the
1240
            sequence much mast the number of qubits on the backend. The sequence
1241
            should be the planar coordinates in a 0-based square grid where each
1242
            qubit is located.
1243

1244
    Returns:
1245
        Figure: A matplotlib figure showing error map.
1246

1247
    Raises:
1248
        VisualizationError: The backend does not provide gate errors for the 'sx' gate.
1249
        MissingOptionalLibraryError: If matplotlib or seaborn is not installed.
1250

1251
    Example:
1252
        .. plot::
1253
           :alt: Output from the previous code.
1254
           :include-source:
1255

1256
            from qiskit.visualization import plot_error_map
1257
            from qiskit.providers.fake_provider import GenericBackendV2
1258

1259
            backend = GenericBackendV2(num_qubits=5)
1260
            plot_error_map(backend)
1261
    """
1262
    import matplotlib
×
1263
    import matplotlib.pyplot as plt
×
1264
    from matplotlib import gridspec, ticker
×
1265
    import seaborn as sns
×
1266
    from .utils import matplotlib_close_if_inline
×
1267

1268
    color_map = sns.cubehelix_palette(reverse=True, as_cmap=True)
×
1269

1270
    backend_name = backend.name
×
1271
    num_qubits = backend.num_qubits
×
1272
    cmap = backend.coupling_map
×
1273
    two_q_error_map = {}
×
1274
    single_gate_errors = [0] * num_qubits
×
1275
    read_err = [0] * num_qubits
×
1276
    cx_errors = []
×
1277
    for gate, prop_dict in backend.target.items():
×
1278
        if prop_dict is None or None in prop_dict:
×
1279
            continue
×
1280
        for qargs, inst_props in prop_dict.items():
×
1281
            if inst_props is None:
×
1282
                continue
×
1283
            if gate == "measure":
×
1284
                if inst_props.error is not None:
×
1285
                    read_err[qargs[0]] = inst_props.error
×
1286
            elif len(qargs) == 1:
×
1287
                if inst_props.error is not None:
×
1288
                    single_gate_errors[qargs[0]] = max(
×
1289
                        single_gate_errors[qargs[0]], inst_props.error
1290
                    )
1291
            elif len(qargs) == 2:
×
1292
                if inst_props.error is not None:
×
1293
                    two_q_error_map[qargs] = max(two_q_error_map.get(qargs, 0), inst_props.error)
×
1294
    if cmap:
×
1295
        directed = False
×
1296
        if num_qubits < 20:
×
1297
            for edge in cmap:
×
NEW
1298
                if [edge[1], edge[0]] not in cmap:
×
1299
                    directed = True
×
1300
                    break
×
1301
        for line in cmap.get_edges():
×
1302
            err = two_q_error_map.get(tuple(line), 0)
×
1303
            cx_errors.append(err)
×
1304

1305
    # Convert to percent
1306
    single_gate_errors = 100 * np.asarray(single_gate_errors)
×
1307
    avg_1q_err = np.mean(single_gate_errors)
×
1308

1309
    single_norm = matplotlib.colors.Normalize(
×
1310
        vmin=min(single_gate_errors), vmax=max(single_gate_errors)
1311
    )
1312
    q_colors = [matplotlib.colors.to_hex(color_map(single_norm(err))) for err in single_gate_errors]
×
1313

1314
    directed = False
×
1315
    line_colors = []
×
1316
    if cmap:
×
1317

1318
        # Convert to percent
1319
        cx_errors = 100 * np.asarray(cx_errors)
×
1320
        avg_cx_err = np.mean(cx_errors)
×
1321

1322
        cx_norm = matplotlib.colors.Normalize(vmin=min(cx_errors), vmax=max(cx_errors))
×
1323
        line_colors = [matplotlib.colors.to_hex(color_map(cx_norm(err))) for err in cx_errors]
×
1324

1325
    read_err = 100 * np.asarray(read_err)
×
1326
    avg_read_err = np.mean(read_err)
×
1327
    max_read_err = np.max(read_err)
×
1328

1329
    fig = plt.figure(figsize=figsize)
×
1330
    gridspec.GridSpec(nrows=2, ncols=3)
×
1331

1332
    grid_spec = gridspec.GridSpec(
×
1333
        12, 12, height_ratios=[1] * 11 + [0.5], width_ratios=[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
1334
    )
1335

1336
    left_ax = plt.subplot(grid_spec[2:10, :1])
×
1337
    main_ax = plt.subplot(grid_spec[:11, 1:11])
×
1338
    right_ax = plt.subplot(grid_spec[2:10, 11:])
×
1339
    bleft_ax = plt.subplot(grid_spec[-1, :5])
×
1340
    if cmap:
×
1341
        bright_ax = plt.subplot(grid_spec[-1, 7:])
×
1342

1343
    qubit_size = 28
×
1344
    if num_qubits <= 5:
×
1345
        qubit_size = 20
×
1346
    plot_gate_map(
×
1347
        backend,
1348
        qubit_color=q_colors,
1349
        line_color=line_colors,
1350
        qubit_size=qubit_size,
1351
        line_width=5,
1352
        plot_directed=directed,
1353
        ax=main_ax,
1354
        qubit_coordinates=qubit_coordinates,
1355
    )
1356

1357
    main_ax.axis("off")
×
1358
    main_ax.set_aspect(1)
×
1359
    if cmap:
×
1360
        single_cb = matplotlib.colorbar.ColorbarBase(
×
1361
            bleft_ax, cmap=color_map, norm=single_norm, orientation="horizontal"
1362
        )
1363
        tick_locator = ticker.MaxNLocator(nbins=5)
×
1364
        single_cb.locator = tick_locator
×
1365
        single_cb.update_ticks()
×
1366
        single_cb.update_ticks()
×
1367
        bleft_ax.set_title(f"H error rate (%) [Avg. = {round(avg_1q_err, 3)}]")
×
1368

1369
    if cmap is None:
×
1370
        bleft_ax.axis("off")
×
1371
        bleft_ax.set_title(f"H error rate (%) = {round(avg_1q_err, 3)}")
×
1372

1373
    if cmap:
×
1374
        cx_cb = matplotlib.colorbar.ColorbarBase(
×
1375
            bright_ax, cmap=color_map, norm=cx_norm, orientation="horizontal"
1376
        )
1377
        tick_locator = ticker.MaxNLocator(nbins=5)
×
1378
        cx_cb.locator = tick_locator
×
1379
        cx_cb.update_ticks()
×
1380
        bright_ax.set_title(f"CNOT error rate (%) [Avg. = {round(avg_cx_err, 3)}]")
×
1381

1382
    if num_qubits < 10:
×
1383
        num_left = num_qubits
×
1384
        num_right = 0
×
1385
    else:
1386
        num_left = math.ceil(num_qubits / 2)
×
1387
        num_right = num_qubits - num_left
×
1388

1389
    left_ax.barh(range(num_left), read_err[:num_left], align="center", color="#DDBBBA")
×
1390
    left_ax.axvline(avg_read_err, linestyle="--", color="#212121")
×
1391
    left_ax.set_yticks(range(num_left))
×
1392
    left_ax.set_xticks([0, round(avg_read_err, 2), round(max_read_err, 2)])
×
1393
    left_ax.set_yticklabels([str(kk) for kk in range(num_left)], fontsize=12)
×
1394
    left_ax.invert_yaxis()
×
1395
    left_ax.set_title("Readout Error (%)", fontsize=12)
×
1396

1397
    for spine in left_ax.spines.values():
×
1398
        spine.set_visible(False)
×
1399

1400
    if num_right:
×
1401
        right_ax.barh(
×
1402
            range(num_left, num_qubits), read_err[num_left:], align="center", color="#DDBBBA"
1403
        )
1404
        right_ax.axvline(avg_read_err, linestyle="--", color="#212121")
×
1405
        right_ax.set_yticks(range(num_left, num_qubits))
×
1406
        right_ax.set_xticks([0, round(avg_read_err, 2), round(max_read_err, 2)])
×
1407
        right_ax.set_yticklabels([str(kk) for kk in range(num_left, num_qubits)], fontsize=12)
×
1408
        right_ax.invert_yaxis()
×
1409
        right_ax.invert_xaxis()
×
1410
        right_ax.yaxis.set_label_position("right")
×
1411
        right_ax.yaxis.tick_right()
×
1412
        right_ax.set_title("Readout Error (%)", fontsize=12)
×
1413
    else:
1414
        right_ax.axis("off")
×
1415

1416
    for spine in right_ax.spines.values():
×
1417
        spine.set_visible(False)
×
1418

1419
    if show_title:
×
1420
        fig.suptitle(f"{backend_name} Error Map", fontsize=24, y=0.9)
×
1421
    matplotlib_close_if_inline(fig)
×
1422
    return fig
×
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