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

Qiskit / qiskit / 22311005638

23 Feb 2026 02:17PM UTC coverage: 87.844% (+0.001%) from 87.843%
22311005638

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%)

15 existing lines in 7 files now uncovered.

100181 of 114044 relevant lines covered (87.84%)

1154495.57 hits per line

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

0.0
/qiskit/visualization/timeline/plotters/matplotlib.py
1
# This code is part of Qiskit.
2
#
3
# (C) Copyright IBM 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 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

14
"""Matplotlib plotter API."""
15

16
import matplotlib
×
UNCOV
17
import matplotlib.pyplot as plt
×
18
import numpy as np
×
19
from matplotlib.collections import PatchCollection
×
20
from matplotlib.patches import Rectangle
×
21

22
from qiskit.visualization.exceptions import VisualizationError
×
23
from qiskit.visualization.timeline import core, types, drawings
×
24
from qiskit.visualization.timeline.plotters.base_plotter import BasePlotter
×
25
from qiskit.visualization.utils import matplotlib_close_if_inline
×
26

27

28
class MplPlotter(BasePlotter):
×
29
    """Matplotlib API for pulse drawer.
30

31
    This plotter arranges bits along y axis of 2D canvas with vertical offset.
32
    """
33

NEW
34
    def __init__(self, canvas: core.DrawerCanvas, axis: plt.Axes | None = None):
×
35
        """Create new plotter.
36

37
        Args:
38
            canvas: Configured drawer canvas object. Canvas object should be updated
39
                with `.update` method before initializing the plotter.
40
            axis: Matplotlib axis object. When `axis` is provided, the plotter updates
41
                given axis instead of creating and returning new matplotlib figure.
42
        """
43
        super().__init__(canvas=canvas)
×
44

45
        if axis is None:
×
46
            fig_height = self.canvas.vmax - self.canvas.vmin
×
47
            fig_h = self.canvas.formatter["general.fig_unit_height"] * fig_height
×
48
            fig_w = self.canvas.formatter["general.fig_width"]
×
49

50
            self.figure = plt.figure(figsize=(fig_w, fig_h))
×
51
            self.ax = self.figure.add_subplot(1, 1, 1)
×
52
        else:
53
            self.figure = axis.figure
×
54
            self.ax = axis
×
55

56
        self.initialize_canvas()
×
57

58
    def initialize_canvas(self):
×
59
        """Format appearance of matplotlib canvas."""
60
        self.ax.set_facecolor(self.canvas.formatter["color.background"])
×
61

62
        # axis lines
63
        self.ax.spines["right"].set_color("none")
×
64
        self.ax.spines["left"].set_color("none")
×
65
        self.ax.spines["top"].set_color("none")
×
66

67
        # axis labels
68
        self.ax.set_yticks([])
×
69
        axis_config = self.canvas.layout["time_axis_map"](time_window=self.canvas.time_range)
×
70

71
        self.ax.set_xticks(list(axis_config.axis_map.keys()))
×
72
        self.ax.set_xticklabels(
×
73
            list(axis_config.axis_map.values()),
74
            fontsize=self.canvas.formatter["text_size.axis_label"],
75
        )
76
        self.ax.set_xlabel(
×
77
            axis_config.label, fontsize=self.canvas.formatter["text_size.axis_label"]
78
        )
79

80
        # boundary
81
        self.ax.set_xlim(*self.canvas.time_range)
×
82
        self.ax.set_ylim(self.canvas.vmin, self.canvas.vmax)
×
83

84
    def draw(self):
×
85
        """Output drawings stored in canvas object."""
86

87
        for _, data in self.canvas.collections:
×
88
            xvals = np.asarray(data.xvals, dtype=float)
×
89
            yvals = np.asarray(data.yvals, dtype=float)
×
90
            offsets = [self.canvas.assigned_coordinates[bit] for bit in data.bits]
×
91

92
            if isinstance(data, drawings.BoxData):
×
93
                # box data
94
                if data.data_type in [
×
95
                    str(types.BoxType.SCHED_GATE.value),
96
                    str(types.BoxType.DELAY.value),
97
                ]:
98
                    # draw a smoothly rounded rectangle
99
                    xs, ys1, ys2 = self._time_bucket_outline(xvals, yvals)
×
100
                    self.ax.fill_between(
×
101
                        x=xs, y1=ys1 + offsets[0], y2=ys2 + offsets[0], **data.styles
102
                    )
103

104
                else:
105
                    # draw a rectangle
106
                    x0, x1 = xvals
×
107
                    y0, y1 = yvals + offsets[0]
×
108

109
                    rect = Rectangle(xy=(x0, y0), width=x1 - x0, height=y1 - y0)
×
110
                    pc = PatchCollection([rect], **data.styles)
×
111
                    self.ax.add_collection(pc)
×
112

113
            elif isinstance(data, drawings.LineData):
×
114
                # line data
115
                self.ax.plot(xvals, yvals + offsets[0], **data.styles)
×
116

117
            elif isinstance(data, drawings.TextData):
×
118
                # text data
119
                if data.latex is not None:
×
120
                    s = rf"${data.latex}$"
×
121
                else:
122
                    s = data.text
×
123

124
                self.ax.text(x=xvals[0], y=yvals[0] + offsets[0], s=s, **data.styles)
×
125

126
            elif isinstance(data, drawings.GateLinkData):
×
127
                # gate link data
128
                self.ax.plot(xvals.repeat(len(offsets)), offsets, **data.styles)
×
129

130
            else:
131
                raise VisualizationError(
×
132
                    f"Data {data} is not supported by {self.__class__.__name__}"
133
                )
134

135
    def _time_bucket_outline(
×
136
        self, xvals: np.ndarray, yvals: np.ndarray
137
    ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
138
        """Generate outline of time bucket. Edges are smoothly faded.
139

140
        Args:
141
            xvals: Left and right point coordinates.
142
            yvals: Bottom and top point coordinates.
143

144
        Returns:
145
            Coordinate vectors of time bucket fringe.
146
        """
147
        x0, x1 = xvals
×
148
        y0, y1 = yvals
×
149

150
        width = x1 - x0
×
151
        y_mid = 0.5 * (y0 + y1)
×
152

153
        risefall = int(min(self.canvas.formatter["time_bucket.edge_dt"], max(width / 2 - 2, 0)))
×
154
        edge = np.sin(np.pi / 2 * np.arange(0, risefall) / risefall)
×
155

156
        xs = np.concatenate(
×
157
            [
158
                np.arange(x0, x0 + risefall),
159
                [x0 + risefall, x1 - risefall],
160
                np.arange(x1 - risefall + 1, x1 + 1),
161
            ]
162
        )
163

164
        l1 = (y1 - y_mid) * np.concatenate([edge, [1, 1], edge[::-1]])
×
165
        l2 = (y0 - y_mid) * np.concatenate([edge, [1, 1], edge[::-1]])
×
166

167
        return xs, l1, l2
×
168

169
    def save_file(self, filename: str):
×
170
        """Save image to file.
171
        Args:
172
            filename: File path to output image data.
173
        """
174
        plt.savefig(filename, bbox_inches="tight", dpi=self.canvas.formatter["general.dpi"])
×
175

176
    def get_image(self, interactive: bool = False) -> matplotlib.pyplot.Figure:
×
177
        """Get image data to return.
178
        Args:
179
            interactive: When set `True` show the circuit in a new window.
180
                This depends on the matplotlib backend being used supporting this.
181
        Returns:
182
            Matplotlib figure data.
183
        """
184
        matplotlib_close_if_inline(self.figure)
×
185

186
        if self.figure and interactive:
×
187
            self.figure.show()
×
188
        try:
×
189
            self.figure.tight_layout()
×
190
        except AttributeError:
×
191
            pass
×
192
        return self.figure
×
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