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

Qiskit / qiskit / 9498296748

13 Jun 2024 10:48AM CUT coverage: 89.392% (-0.2%) from 89.585%
9498296748

push

github

web-flow
Add infrastructure for gates, instruction, and operations in Rust (#12459)

* Add infrastructure for gates, instruction, and operations in Rust

This commit adds a native representation of Gates, Instruction, and
Operations to rust's circuit module. At a high level this works by
either wrapping the Python object in a rust wrapper struct that tracks
metadata about the operations (name, num_qubits, etc) and then for other
details it calls back to Python to get dynamic details like the
definition, matrix, etc. For standard library gates like Swap, CX, H,
etc this replaces the on-circuit representation with a new rust enum
StandardGate. The enum representation is much more efficient and has a
minimal memory footprint (just the enum variant and then any parameters
or other mutable state stored in the circuit instruction). All the gate
properties such as the matrix, definiton, name, etc are statically
defined in rust code based on the enum variant (which represents the
gate).

The use of an enum to represent standard gates does mean a change in
what we store on a CircuitInstruction. To represent a standard gate
fully we need to store the mutable properties of the existing Gate class
on the circuit instruction as the gate by itself doesn't contain this
detail. That means, the parameters, label, unit, duration, and condition
are added to the rust side of circuit instrucion. However no Python side
access methods are added for these as they're internal only to the Rust
code. In Qiskit 2.0 to simplify this storage we'll be able to drop, unit,
duration, and condition from the api leaving only label and parameters.
But for right now we're tracking all of the fields.

To facilitate working with circuits and gates full from rust the
setting the `operation` attribute of a `CircuitInstruction` object now
transltates the python object to an internal rust representation.
For standard gates this translates it to the enum form described earlier,
and for other circuit oper... (continued)

1650 of 1956 new or added lines in 38 files covered. (84.36%)

25 existing lines in 3 files now uncovered.

63809 of 71381 relevant lines covered (89.39%)

299647.78 hits per line

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

0.0
/qiskit/visualization/pulse_v2/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 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
"""Matplotlib plotter API."""
14

15
from typing import Optional
×
16

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

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

27

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

31
    This plotter places canvas charts along y axis of 2D canvas with vertical offset.
32
    Each chart is map to X-Y axis of the canvas.
33
    """
34

35
    def __init__(self, canvas: core.DrawerCanvas, axis: Optional[plt.Axes] = None):
×
36
        """Create new plotter.
37

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

46
        # calculate height of all charts
47
        canvas_height = 0
×
48
        for chart in self.canvas.charts:
×
49
            if not chart.is_active and not self.canvas.formatter["control.show_empty_channel"]:
×
50
                continue
×
51
            canvas_height += chart.vmax - chart.vmin
×
52
        # set min canvas_height size
53
        canvas_height = max(canvas_height, 0.1)
×
54

55
        if axis is None:
×
56
            fig_h = canvas_height * self.canvas.formatter["general.fig_chart_height"]
×
57
            fig_w = self.canvas.formatter["general.fig_width"]
×
58

59
            self.figure = plt.figure(figsize=(fig_w, fig_h))
×
60
            self.ax = self.figure.add_subplot(1, 1, 1)
×
61
        else:
62
            self.figure = axis.figure
×
63
            self.ax = axis
×
64

65
        self.initialize_canvas()
×
66

67
    def initialize_canvas(self):
×
68
        """Format appearance of matplotlib canvas."""
69
        self.ax.set_facecolor(self.canvas.formatter["color.background"])
×
70

71
        # axis labels
72
        self.ax.set_yticklabels([])
×
73
        self.ax.yaxis.set_tick_params(left=False)
×
74

75
    def draw(self):
×
76
        """Output drawings stored in canvas object."""
77
        # axis configuration
78
        axis_config = self.canvas.layout["time_axis_map"](
×
79
            time_window=self.canvas.time_range,
80
            axis_breaks=self.canvas.time_breaks,
81
            dt=self.canvas.device.dt,
82
        )
83

84
        current_y = 0
×
85
        margin_y = self.canvas.formatter["margin.between_channel"]
×
86
        for chart in self.canvas.charts:
×
87
            if not chart.is_active and not self.canvas.formatter["control.show_empty_channel"]:
×
88
                continue
×
89
            current_y -= chart.vmax
×
90
            for _, data in chart.collections:
×
91
                # calculate scaling factor
92
                if not data.ignore_scaling:
×
93
                    # product of channel-wise scaling and chart level scaling
94
                    scale = max(self.canvas.chan_scales.get(chan, 1.0) for chan in data.channels)
×
95
                    scale *= chart.scale
×
96
                else:
97
                    scale = 1.0
×
98

99
                x = data.xvals
×
100
                y = scale * data.yvals + current_y
×
101

102
                if isinstance(data, drawings.LineData):
×
103
                    # line object
104
                    if data.fill:
×
105
                        self.ax.fill_between(x, y1=y, y2=current_y * np.ones_like(y), **data.styles)
×
106
                    else:
107
                        self.ax.plot(x, y, **data.styles)
×
108
                elif isinstance(data, drawings.TextData):
×
109
                    # text object
110
                    text = rf"${data.latex}$" if data.latex else data.text
×
111
                    # replace dynamic text
112
                    text = text.replace(types.DynamicString.SCALE, f"{chart.scale:.1f}")
×
113
                    self.ax.text(x=x[0], y=y[0], s=text, **data.styles)
×
114
                elif isinstance(data, drawings.BoxData):
×
115
                    xy = x[0], y[0]
×
116
                    box = Rectangle(
×
117
                        xy, width=x[1] - x[0], height=y[1] - y[0], fill=True, **data.styles
118
                    )
119
                    self.ax.add_patch(box)
×
120
                else:
121
                    raise VisualizationError(
×
122
                        "Data {name} is not supported "
123
                        "by {plotter}".format(name=data, plotter=self.__class__.__name__)
124
                    )
125
            # axis break
126
            for pos in axis_config.axis_break_pos:
×
127
                self.ax.text(
×
128
                    x=pos,
129
                    y=current_y,
130
                    s="//",
131
                    ha="center",
132
                    va="center",
133
                    zorder=self.canvas.formatter["layer.axis_label"],
134
                    fontsize=self.canvas.formatter["text_size.axis_break_symbol"],
135
                    rotation=180,
136
                )
137

138
            # shift chart position
139
            current_y += chart.vmin - margin_y
×
140

141
        # remove the last margin
142
        current_y += margin_y
×
143

144
        y_max = self.canvas.formatter["margin.top"]
×
145
        y_min = current_y - self.canvas.formatter["margin.bottom"]
×
146

147
        # plot axis break line
148
        for pos in axis_config.axis_break_pos:
×
149
            self.ax.plot(
×
150
                [pos, pos],
151
                [y_min, y_max],
152
                zorder=self.canvas.formatter["layer.fill_waveform"] + 1,
153
                linewidth=self.canvas.formatter["line_width.axis_break"],
154
                color=self.canvas.formatter["color.background"],
155
            )
156

157
        # label
158
        self.ax.set_xticks(list(axis_config.axis_map.keys()))
×
159
        self.ax.set_xticklabels(
×
160
            list(axis_config.axis_map.values()),
161
            fontsize=self.canvas.formatter["text_size.axis_label"],
162
        )
163
        self.ax.set_xlabel(
×
164
            axis_config.label, fontsize=self.canvas.formatter["text_size.axis_label"]
165
        )
166

167
        # boundary
168
        if axis_config.window == (0, 0):
×
169
            self.ax.set_xlim(0, 1)
×
170
        else:
171
            self.ax.set_xlim(*axis_config.window)
×
172
        self.ax.set_ylim(y_min, y_max)
×
173

174
        # title
175
        if self.canvas.fig_title:
×
176
            self.ax.text(
×
177
                x=axis_config.window[0],
178
                y=y_max,
179
                s=self.canvas.fig_title,
180
                ha="left",
181
                va="bottom",
182
                zorder=self.canvas.formatter["layer.fig_title"],
183
                color=self.canvas.formatter["color.fig_title"],
184
                size=self.canvas.formatter["text_size.fig_title"],
185
            )
186

187
    def get_image(self, interactive: bool = False) -> matplotlib.pyplot.Figure:
×
188
        """Get image data to return.
189

190
        Args:
191
            interactive: When set `True` show the circuit in a new window.
192
                This depends on the matplotlib backend being used supporting this.
193

194
        Returns:
195
            Matplotlib figure data.
196
        """
197
        matplotlib_close_if_inline(self.figure)
×
198

199
        if self.figure and interactive:
×
200
            self.figure.show()
×
201

202
        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

© 2025 Coveralls, Inc