Pulse Generation API¶
The qubitos.pulsegen module provides the GRAPE (Gradient Ascent Pulse Engineering) optimizer for synthesizing high-fidelity quantum gate pulses.
Overview¶
| Component | Description |
|---|---|
GrapeOptimizer |
Main optimization class |
GrapeConfig |
Configuration dataclass |
GrapeResult |
Optimization result |
generate_pulse |
Convenience function |
hamiltonians |
Hamiltonian construction utilities |
Quick Start¶
from qubitos.pulsegen import generate_pulse
# Generate an X-gate pulse
result = generate_pulse("X", duration_ns=20, target_fidelity=0.999)
print(f"Converged: {result.converged}")
print(f"Fidelity: {result.fidelity:.6f}")
print(f"I envelope: {result.i_envelope[:5]}...")
print(f"Q envelope: {result.q_envelope[:5]}...")
Using GrapeOptimizer¶
For more control, use the GrapeOptimizer class directly:
from qubitos.pulsegen import GrapeOptimizer, GrapeConfig
from qubitos.pulsegen.hamiltonians import get_target_unitary
# Configure optimization
config = GrapeConfig(
num_time_steps=100,
duration_ns=50,
max_iterations=200,
target_fidelity=0.999,
learning_rate=1.0,
)
# Create optimizer
optimizer = GrapeOptimizer(config)
# Get target unitary
target = get_target_unitary("H") # Hadamard gate
# Run optimization
result = optimizer.optimize(
target_unitary=target,
num_qubits=1,
)
With Custom Hamiltonians¶
from qubitos.pulsegen.hamiltonians import parse_pauli_string
# Define system Hamiltonian
H0 = parse_pauli_string("5.0 * Z0", num_qubits=1)
Hc = [
parse_pauli_string("X0", num_qubits=1),
parse_pauli_string("Y0", num_qubits=1),
]
result = optimizer.optimize(
target_unitary=target,
num_qubits=1,
drift_hamiltonian=H0,
control_hamiltonians=Hc,
)
Configuration Options¶
Essential Parameters¶
config = GrapeConfig(
num_time_steps=100, # Pulse discretization (more = finer control)
duration_ns=50.0, # Total pulse duration
target_fidelity=0.999, # Stop when reached
max_iterations=1000, # Maximum optimization steps
learning_rate=1.0, # Gradient ascent step size
)
Advanced Parameters¶
config = GrapeConfig(
# ... essential parameters ...
convergence_threshold=1e-8, # Stop if progress < this
max_amplitude=100.0, # Maximum pulse amplitude (MHz)
use_second_order=False, # Enable GRAPE-II (experimental)
regularization=0.0, # L2 penalty on pulses
random_seed=42, # For reproducibility
)
Supported Gates¶
Single-Qubit Gates¶
| Gate | Description |
|---|---|
X |
Pauli-X (NOT) |
Y |
Pauli-Y |
Z |
Pauli-Z |
H |
Hadamard |
SX |
√X gate |
RX |
Rotation around X (requires angle) |
RY |
Rotation around Y (requires angle) |
RZ |
Rotation around Z (requires angle) |
Two-Qubit Gates¶
| Gate | Description |
|---|---|
CZ |
Controlled-Z |
CNOT |
Controlled-NOT (CX) |
ISWAP |
iSWAP gate |
Custom Gates¶
import numpy as np
# Define custom unitary
custom_gate = np.array([
[1, 0],
[0, np.exp(1j * np.pi / 8)],
], dtype=np.complex128)
result = optimizer.optimize(
target_unitary=custom_gate,
num_qubits=1,
)
Optimization Callbacks¶
Monitor optimization progress:
def callback(iteration: int, fidelity: float) -> bool:
print(f"Iter {iteration}: F = {fidelity:.6f}")
# Return True to stop early
return fidelity > 0.9999
result = optimizer.optimize(
target_unitary=target,
num_qubits=1,
callback=callback,
)
Warm Starting¶
Use a previous result as starting point:
# First optimization
result1 = optimizer.optimize(target_unitary=X_gate, num_qubits=1)
# Warm start for similar gate
result2 = optimizer.optimize(
target_unitary=Y_gate,
num_qubits=1,
initial_pulses=(result1.i_envelope, result1.q_envelope),
)
Hamiltonians Module¶
The hamiltonians submodule provides utilities for constructing quantum Hamiltonians.
Pauli String Parsing¶
from qubitos.pulsegen.hamiltonians import parse_pauli_string
# Single term
H = parse_pauli_string("X0", num_qubits=1)
# Multiple terms with coefficients
H = parse_pauli_string("5.0 * Z0 + 0.1 * X0", num_qubits=1)
# Multi-qubit
H = parse_pauli_string("0.01 * Z0 Z1", num_qubits=2)
Standard Unitaries¶
from qubitos.pulsegen.hamiltonians import get_target_unitary
import numpy as np
# Standard gates
X = get_target_unitary("X")
H = get_target_unitary("H")
# Rotation gates
RX_90 = get_target_unitary("RX", angle=np.pi/2)
# Two-qubit gates
CZ = get_target_unitary("CZ", num_qubits=2)
# Embedded in larger space
X_on_q1 = get_target_unitary("X", num_qubits=3, qubit_indices=[1])
Building Hamiltonians¶
from qubitos.pulsegen.hamiltonians import build_hamiltonian
H0, Hc = build_hamiltonian(
drift="5.0 * Z0",
controls=["X0", "Y0"],
num_qubits=1,
)
API Reference¶
Main Classes¶
qubitos.pulsegen.grape.GrapeOptimizer ¶
GRAPE pulse optimizer.
Implements gradient ascent pulse engineering for quantum gate synthesis.
Initialize the optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
GrapeConfig | None
|
Optimization configuration. Uses defaults if None. |
None
|
Source code in src/qubitos/pulsegen/grape.py
optimize ¶
optimize(
target_unitary: NDArray[complex128],
num_qubits: int,
drift_hamiltonian: NDArray[complex128] | None = None,
control_hamiltonians: list[NDArray[complex128]]
| None = None,
initial_pulses: tuple[
NDArray[float64], NDArray[float64]
]
| None = None,
callback: Callable[[int, float], bool] | None = None,
) -> GrapeResult
Optimize pulses to implement a target unitary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_unitary
|
NDArray[complex128]
|
Target unitary matrix to implement |
required |
num_qubits
|
int
|
Number of qubits |
required |
drift_hamiltonian
|
NDArray[complex128] | None
|
Time-independent drift Hamiltonian (optional) |
None
|
control_hamiltonians
|
list[NDArray[complex128]] | None
|
List of control Hamiltonians for I and Q |
None
|
initial_pulses
|
tuple[NDArray[float64], NDArray[float64]] | None
|
Initial (I, Q) pulse envelopes (random if None) |
None
|
callback
|
Callable[[int, float], bool] | None
|
Called each iteration with (iteration, fidelity). Return True to stop optimization early. |
None
|
Returns:
| Type | Description |
|---|---|
GrapeResult
|
GrapeResult with optimized pulses and metrics |
Raises:
| Type | Description |
|---|---|
ValueError
|
If parameters are invalid |
Source code in src/qubitos/pulsegen/grape.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | |
qubitos.pulsegen.grape.GrapeConfig
dataclass
¶
GrapeConfig(
num_time_steps: int = 100,
duration_ns: int = 20,
target_fidelity: float = 0.999,
max_iterations: int = 1000,
learning_rate: float = 1.0,
convergence_threshold: float = 1e-08,
max_amplitude: float = 100.0,
use_second_order: bool = False,
regularization: float = 0.0,
random_seed: int | None = None,
duration: TimePoint | None = None,
awg_config: AWGClockConfig | None = None,
)
Configuration for GRAPE optimization.
Attributes:
| Name | Type | Description |
|---|---|---|
num_time_steps |
int
|
Number of time discretization steps (must be >= 1).
When |
duration_ns |
int
|
Total pulse duration in nanoseconds (must be > 0).
DEPRECATED in favor of |
target_fidelity |
float
|
Target gate fidelity (0 to 1) |
max_iterations |
int
|
Maximum optimization iterations |
learning_rate |
float
|
Initial learning rate for gradient ascent |
convergence_threshold |
float
|
Stop when fidelity improvement < threshold |
max_amplitude |
float
|
Maximum pulse amplitude (in MHz) |
use_second_order |
bool
|
Use second-order (GRAPE-II) optimization |
regularization |
float
|
L2 regularization strength for pulse smoothness |
random_seed |
int | None
|
Random seed for reproducibility |
duration |
TimePoint | None
|
TimePoint carrying nominal + quantized duration with
precision/jitter metadata. When set, |
awg_config |
AWGClockConfig | None
|
AWG clock configuration. Stored on the result for provenance tracking. |
effective_dt_seconds
property
¶
Time step in SI seconds for the GRAPE propagator.
effective_duration_ns
property
¶
Return the AWG-quantized duration, or fall back to duration_ns.
__post_init__ ¶
Validate configuration parameters.
Source code in src/qubitos/pulsegen/grape.py
qubitos.pulsegen.grape.GrapeResult
dataclass
¶
GrapeResult(
i_envelope: NDArray[float64],
q_envelope: NDArray[float64],
fidelity: float,
iterations: int,
converged: bool,
fidelity_history: list[float] = list(),
final_unitary: NDArray[complex128] | None = None,
duration: TimePoint | None = None,
awg_config: AWGClockConfig | None = None,
)
Result of GRAPE optimization.
Attributes:
| Name | Type | Description |
|---|---|---|
i_envelope |
NDArray[float64]
|
Optimized I (in-phase) pulse envelope |
q_envelope |
NDArray[float64]
|
Optimized Q (quadrature) pulse envelope |
fidelity |
float
|
Achieved gate fidelity (clamped to [0, 1]) |
iterations |
int
|
Number of iterations performed |
converged |
bool
|
Whether optimization converged |
fidelity_history |
list[float]
|
Fidelity at each iteration |
final_unitary |
NDArray[complex128] | None
|
The unitary implemented by the optimized pulse |
duration |
TimePoint | None
|
Quantized duration used for this optimization (provenance). |
awg_config |
AWGClockConfig | None
|
AWG clock config used for this optimization (provenance). |
qubitos.pulsegen.grape.GateType ¶
Bases: Enum
Supported quantum gate types.
Convenience Function¶
qubitos.pulsegen.grape.generate_pulse ¶
generate_pulse(
gate: str | GateType,
num_qubits: int = 1,
duration_ns: int = 20,
target_fidelity: float = 0.999,
qubit_indices: list[int] | None = None,
config: GrapeConfig | None = None,
) -> GrapeResult
Generate an optimized pulse for a quantum gate.
This is the main entry point for pulse generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gate
|
str | GateType
|
Target gate (e.g., "X", "H", "CZ") |
required |
num_qubits
|
int
|
Number of qubits in the system |
1
|
duration_ns
|
int
|
Pulse duration in nanoseconds (must be > 0) |
20
|
target_fidelity
|
float
|
Target gate fidelity |
0.999
|
qubit_indices
|
list[int] | None
|
Indices of target qubits (default: [0] or [0,1]) |
None
|
config
|
GrapeConfig | None
|
Advanced configuration options |
None
|
Returns:
| Type | Description |
|---|---|
GrapeResult
|
GrapeResult with optimized pulse envelopes |
Raises:
| Type | Description |
|---|---|
ValueError
|
If parameters are invalid |
Example
result = generate_pulse("X", duration_ns=20, target_fidelity=0.999) print(f"Fidelity: {result.fidelity:.4f}")
Source code in src/qubitos/pulsegen/grape.py
Hamiltonians¶
qubitos.pulsegen.hamiltonians.parse_pauli_string ¶
Parse a Pauli string expression into a Hamiltonian matrix.
Format: "coeff1 * P1 P2 + coeff2 * P3 - coeff3 * P4 P5"
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
str
|
Pauli string expression |
required |
num_qubits
|
int
|
Number of qubits |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
Hamiltonian matrix |
Example
H = parse_pauli_string("0.5 * X0 + 0.3 * Z0 Z1", num_qubits=2)
Source code in src/qubitos/pulsegen/hamiltonians.py
qubitos.pulsegen.hamiltonians.get_target_unitary ¶
get_target_unitary(
gate: str | GateType,
num_qubits: int = 1,
qubit_indices: list[int] | None = None,
angle: float | None = None,
) -> NDArray[np.complex128]
Get the target unitary for a quantum gate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gate
|
str | GateType
|
Gate name or GateType enum |
required |
num_qubits
|
int
|
Total number of qubits in the system |
1
|
qubit_indices
|
list[int] | None
|
Which qubits the gate acts on (default: first qubit(s)) |
None
|
angle
|
float | None
|
Rotation angle for parameterized gates (RX, RY, RZ) |
None
|
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
Unitary matrix for the gate |
Example
X = get_target_unitary("X", num_qubits=1) CZ = get_target_unitary("CZ", num_qubits=2, qubit_indices=[0, 1]) RX = get_target_unitary("RX", num_qubits=1, angle=np.pi/2)
Source code in src/qubitos/pulsegen/hamiltonians.py
qubitos.pulsegen.hamiltonians.build_hamiltonian ¶
build_hamiltonian(
drift: str | NDArray[complex128] | None = None,
controls: list[str]
| list[NDArray[complex128]]
| None = None,
num_qubits: int = 1,
) -> tuple[
NDArray[np.complex128], list[NDArray[np.complex128]]
]
Build drift and control Hamiltonians.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
drift
|
str | NDArray[complex128] | None
|
Drift Hamiltonian (Pauli string or matrix) |
None
|
controls
|
list[str] | list[NDArray[complex128]] | None
|
List of control Hamiltonians |
None
|
num_qubits
|
int
|
Number of qubits |
1
|
Returns:
| Type | Description |
|---|---|
tuple[NDArray[complex128], list[NDArray[complex128]]]
|
Tuple of (drift_hamiltonian, control_hamiltonians) |
Source code in src/qubitos/pulsegen/hamiltonians.py
qubitos.pulsegen.hamiltonians.rotation_gate ¶
Generate a rotation gate around a Pauli axis.
R_P(theta) = exp(-i * theta/2 * P) = cos(theta/2) * I - i * sin(theta/2) * P
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
axis
|
str
|
Rotation axis ("X", "Y", or "Z") |
required |
angle
|
float
|
Rotation angle in radians |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
2x2 rotation matrix |
Source code in src/qubitos/pulsegen/hamiltonians.py
qubitos.pulsegen.hamiltonians.tensor_product ¶
Compute tensor product of a list of operators.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operators
|
list[NDArray[complex128]]
|
List of 2x2 matrices |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
Tensor product matrix |
Source code in src/qubitos/pulsegen/hamiltonians.py
qubitos.pulsegen.hamiltonians.embed_gate ¶
embed_gate(
gate: NDArray[complex128],
num_qubits: int,
qubit_indices: list[int],
) -> NDArray[np.complex128]
Embed a gate in a larger Hilbert space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gate
|
NDArray[complex128]
|
Gate unitary matrix |
required |
num_qubits
|
int
|
Total number of qubits |
required |
qubit_indices
|
list[int]
|
Which qubits the gate acts on |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
Embedded gate matrix |