qiskit-waveform-sim 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ """
2
+ qiskit-waveform-sim: Zero-bloat Classical Control Waveform Simulator & Interactive Pulse Sheet Viewer for Qiskit 2.5+
3
+
4
+ This package provides a native, offline, lazy-evaluated waveform simulator that works directly
5
+ with Qiskit's modern Target, op_start_times, BoxOp, fractional gates, and the new
6
+ qiskit.circuit.annotation.Annotation API.
7
+ """
8
+
9
+ from qiskit_waveform_sim.annotations import (
10
+ PulseAnnotationSerializer,
11
+ PulseEnvelopeAnnotation,
12
+ )
13
+ from qiskit_waveform_sim.core import (
14
+ AnalyticalEnvelopes,
15
+ ChannelEvent,
16
+ TargetWaveformSimulator,
17
+ WaveformSnippet,
18
+ )
19
+ from qiskit_waveform_sim.visualization import (
20
+ PulseSheetConfig,
21
+ create_combined_view,
22
+ create_iq_oscilloscope,
23
+ create_pulse_sheet,
24
+ show_iq_oscilloscope,
25
+ show_pulse_sheet,
26
+ )
27
+
28
+ __version__ = "0.1.0"
29
+ __all__ = [
30
+ "TargetWaveformSimulator",
31
+ "WaveformSnippet",
32
+ "ChannelEvent",
33
+ "AnalyticalEnvelopes",
34
+ "PulseEnvelopeAnnotation",
35
+ "PulseAnnotationSerializer",
36
+ "create_pulse_sheet",
37
+ "create_iq_oscilloscope",
38
+ "show_pulse_sheet",
39
+ "show_iq_oscilloscope",
40
+ "create_combined_view",
41
+ "PulseSheetConfig",
42
+ ]
@@ -0,0 +1,195 @@
1
+ """
2
+ Pulse Envelope Annotations for Qiskit 2.5+.
3
+
4
+ Provides custom Annotation subclasses for attaching parametric pulse specifications
5
+ to BoxOp blocks, with full OpenQASM 3 and QPY serialization support.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ from typing import Any
12
+
13
+ from qiskit.circuit.annotation import Annotation, OpenQASM3Serializer
14
+
15
+
16
+ class PulseEnvelopeAnnotation(Annotation):
17
+ """
18
+ Custom Qiskit 2.x Annotation attaching parametric pulse specs to a BoxOp.
19
+
20
+ This annotation can be attached to a `qc.box()` context to override the default
21
+ pulse envelope for gates within that box. It implements OpenQASM3Serializer
22
+ for round-trip serialization via `qiskit.qasm3.dumps()` and `qiskit.qpy.dump()`.
23
+
24
+ Examples
25
+ --------
26
+ >>> from qiskit import QuantumCircuit
27
+ >>> from qiskit_waveform_sim import PulseEnvelopeAnnotation
28
+ >>>
29
+ >>> qc = QuantumCircuit(1)
30
+ >>> with qc.box(duration=160, unit="dt",
31
+ ... annotations=[PulseEnvelopeAnnotation(shape="drag", amp=0.55, beta=0.25)]):
32
+ ... qc.sx(0)
33
+
34
+ **QPY Round-trip Example**
35
+ >>> from qiskit import qpy
36
+ >>> from qiskit_waveform_sim import PulseAnnotationSerializer
37
+ >>> import io
38
+ >>>
39
+ >>> qc = QuantumCircuit(1)
40
+ >>> with qc.box(duration=160, unit="dt",
41
+ ... annotations=[PulseEnvelopeAnnotation(shape="drag", amp=0.55, beta=0.25)]):
42
+ ... qc.sx(0)
43
+ >>>
44
+ >>> serializer = PulseAnnotationSerializer()
45
+ >>> buf = io.BytesIO()
46
+ >>> qpy.dump(qc, buf, annotation_factories={"pulse_sim.envelope": serializer.as_qpy()})
47
+ >>> buf.seek(0)
48
+ >>> loaded = qpy.load(buf, annotation_factories={"pulse_sim.envelope": serializer.as_qpy()})[0]
49
+ """
50
+
51
+ namespace = "pulse_sim.envelope"
52
+
53
+ def __init__(
54
+ self,
55
+ shape: str = "drag",
56
+ amp: float = 0.5,
57
+ beta: float = 0.08,
58
+ sigma_ratio: float = 0.25,
59
+ risefall_dt: int = 16,
60
+ ):
61
+ """
62
+ Initialize pulse envelope annotation.
63
+
64
+ Parameters
65
+ ----------
66
+ shape : str
67
+ Pulse shape: "drag" or "gaussian_square"
68
+ amp : float
69
+ Peak amplitude (normalized, 0.0-1.0)
70
+ beta : float
71
+ DRAG leakage suppression parameter (for drag shape)
72
+ sigma_ratio : float
73
+ Gaussian sigma as ratio of pulse duration (for drag shape)
74
+ risefall_dt : int
75
+ Rise/fall duration in dt (for gaussian_square shape)
76
+ """
77
+ if shape not in ("drag", "gaussian_square"):
78
+ raise ValueError(
79
+ f"Unsupported shape: {shape}. Use 'drag' or 'gaussian_square'"
80
+ )
81
+
82
+ self.shape = shape
83
+ self.amp = float(amp)
84
+ self.beta = float(beta)
85
+ self.sigma_ratio = float(sigma_ratio)
86
+ self.risefall_dt = int(risefall_dt)
87
+
88
+ def __repr__(self) -> str:
89
+ return (
90
+ f"PulseEnvelopeAnnotation(shape={self.shape!r}, amp={self.amp}, "
91
+ f"beta={self.beta}, sigma_ratio={self.sigma_ratio}, risefall_dt={self.risefall_dt})"
92
+ )
93
+
94
+ def __eq__(self, other: object) -> bool:
95
+ if not isinstance(other, PulseEnvelopeAnnotation):
96
+ return False
97
+ return (
98
+ self.shape == other.shape
99
+ and self.amp == other.amp
100
+ and self.beta == other.beta
101
+ and self.sigma_ratio == other.sigma_ratio
102
+ and self.risefall_dt == other.risefall_dt
103
+ )
104
+
105
+ def __hash__(self) -> int:
106
+ return hash(
107
+ (self.shape, self.amp, self.beta, self.sigma_ratio, self.risefall_dt)
108
+ )
109
+
110
+ def to_dict(self) -> dict[str, Any]:
111
+ """Serialize to dictionary for QPY."""
112
+ return {
113
+ "shape": self.shape,
114
+ "amp": self.amp,
115
+ "beta": self.beta,
116
+ "sigma_ratio": self.sigma_ratio,
117
+ "risefall_dt": self.risefall_dt,
118
+ }
119
+
120
+ @classmethod
121
+ def from_dict(cls, data: dict[str, Any]) -> PulseEnvelopeAnnotation:
122
+ """Deserialize from dictionary (QPY)."""
123
+ return cls(**data)
124
+
125
+
126
+ class PulseAnnotationSerializer(OpenQASM3Serializer):
127
+ """
128
+ Serializes PulseEnvelopeAnnotation to/from OpenQASM 3 and QPY.
129
+
130
+ OpenQASM 3 Export Format::
131
+
132
+ @pulse_sim.drag(amp=0.5, beta=0.08, sigma_ratio=0.25, risefall_dt=16)
133
+ box {
134
+ sx q[0];
135
+ }
136
+
137
+ QPY Serialization::
138
+
139
+ Uses the standard QPY annotation mechanism with namespace "pulse_sim.envelope"
140
+ via the `as_qpy()` method. Example:
141
+
142
+ >>> from qiskit import qpy
143
+ >>> from qiskit_waveform_sim import PulseAnnotationSerializer
144
+ >>> import io
145
+ >>>
146
+ >>> serializer = PulseAnnotationSerializer()
147
+ >>> buf = io.BytesIO()
148
+ >>> qpy.dump(qc, buf, annotation_factories={"pulse_sim.envelope": serializer.as_qpy()})
149
+ >>> buf.seek(0)
150
+ >>> loaded = qpy.load(buf, annotation_factories={"pulse_sim.envelope": serializer.as_qpy()})[0]
151
+ """
152
+
153
+ def dump(self, annotation: Annotation) -> str:
154
+ """
155
+ Serialize annotation to OpenQASM 3 pragma string.
156
+
157
+ Returns NotImplemented if namespace doesn't match.
158
+ """
159
+ if annotation.namespace != "pulse_sim.envelope":
160
+ return NotImplemented # type: ignore
161
+
162
+ if not isinstance(annotation, PulseEnvelopeAnnotation):
163
+ return NotImplemented # type: ignore
164
+
165
+ payload = {
166
+ "shape": annotation.shape,
167
+ "amp": annotation.amp,
168
+ "beta": annotation.beta,
169
+ "sigma_ratio": annotation.sigma_ratio,
170
+ "risefall_dt": annotation.risefall_dt,
171
+ }
172
+ # Use repr for valid Python literal that ast.literal_eval can parse
173
+ return repr(payload)
174
+
175
+ def load(self, namespace: str, payload: str) -> Annotation:
176
+ """
177
+ Deserialize annotation from OpenQASM 3 pragma string.
178
+
179
+ Returns NotImplemented if namespace doesn't match.
180
+ """
181
+ if namespace != "pulse_sim.envelope":
182
+ return NotImplemented # type: ignore
183
+
184
+ try:
185
+ data = ast.literal_eval(payload)
186
+ return PulseEnvelopeAnnotation(**data)
187
+ except (ValueError, SyntaxError, TypeError) as e:
188
+ raise ValueError(
189
+ f"Failed to parse PulseEnvelopeAnnotation payload: {payload}"
190
+ ) from e
191
+
192
+
193
+ # Register the serializer with Qiskit's annotation system
194
+ # This happens automatically when the module is imported and qiskit.qasm3 is used
195
+ # with annotation_handlers={"pulse_sim.envelope": PulseAnnotationSerializer()}