Skip to content

Calibration API

The qubitos.calibrator module provides functionality for loading, validating, and managing calibration data for quantum backends.

Overview

Calibration files contain hardware-specific parameters:

  • Qubit frequencies and anharmonicities
  • Coherence times (T1, T2)
  • Gate and readout fidelities
  • Coupling strengths between qubits

Quick Start

from qubitos.calibrator import CalibrationLoader

loader = CalibrationLoader()
calibration = loader.load("calibration/qutip_simulator.yaml")

print(f"Backend: {calibration.name}")
print(f"Qubits: {calibration.num_qubits}")

for qubit in calibration.qubits:
    print(f"  Q{qubit.index}: {qubit.frequency_ghz} GHz, T1={qubit.t1_us} µs")

Calibration File Format

Calibration files use YAML format:

name: qutip_simulator
version: "1.0"
timestamp: "2026-02-03T10:30:00Z"
num_qubits: 2

qubits:
  - index: 0
    frequency_ghz: 5.0
    anharmonicity_mhz: -300
    t1_us: 100
    t2_us: 80
    readout_fidelity: 0.99
    gate_fidelity: 0.999
    drive_amplitude: 1.0

  - index: 1
    frequency_ghz: 5.2
    anharmonicity_mhz: -320
    t1_us: 90
    t2_us: 70
    readout_fidelity: 0.98
    gate_fidelity: 0.998
    drive_amplitude: 1.0

couplers:
  - qubit_a: 0
    qubit_b: 1
    coupling_mhz: 5.0
    cz_fidelity: 0.99
    cz_duration_ns: 40

metadata:
  calibrated_by: "auto-tune"
  lab: "QubitOS Lab"

Loading Calibrations

From File Path

from qubitos.calibrator import CalibrationLoader

loader = CalibrationLoader()
calibration = loader.load("path/to/calibration.yaml")

From Calibration Directory

loader = CalibrationLoader(calibration_dir="calibrations/")

# Load specific file
cal1 = loader.load("backend_a.yaml")

# Load by backend name (searches common patterns)
cal2 = loader.load_for_backend("qutip_simulator")

Convenience Function

from qubitos.calibrator import load_calibration

calibration = load_calibration("calibration.yaml")

Validation

Calibration data is validated automatically on load:

  • T1/T2 physics constraints (T2 ≤ 2·T1)
  • Fidelity ranges (0 to 1)
  • Required fields

Disable Validation

loader = CalibrationLoader(validate=False)
calibration = loader.load("calibration.yaml")

Custom Validation

from qubitos.calibrator import CalibrationLoader, CalibrationError

loader = CalibrationLoader(validate=True)

try:
    calibration = loader.load("calibration.yaml")
except CalibrationError as e:
    print(f"Validation failed: {e}")

Caching

Loaded calibrations are cached by default:

# First load reads from disk
cal1 = loader.load("calibration.yaml")

# Second load uses cache
cal2 = loader.load("calibration.yaml")

# Force reload
cal3 = loader.load("calibration.yaml", use_cache=False)

# Clear all cached data
loader.clear_cache()

Saving Calibrations

Write calibration data back to disk:

from qubitos.calibrator import (
    BackendCalibration,
    QubitCalibration,
    CalibrationLoader,
)

# Create calibration programmatically
calibration = BackendCalibration(
    name="my_backend",
    version="1.0",
    num_qubits=1,
    qubits=[
        QubitCalibration(
            index=0,
            frequency_ghz=5.0,
            t1_us=100,
            t2_us=80,
        )
    ],
)

# Save to file
loader = CalibrationLoader()
loader.save(calibration, "output/calibration.yaml")

Using Calibration Data

With GRAPE Optimization

from qubitos.calibrator import load_calibration
from qubitos.pulsegen import GrapeOptimizer, GrapeConfig
from qubitos.pulsegen.hamiltonians import parse_pauli_string

# Load calibration
cal = load_calibration("calibration.yaml")
qubit = cal.qubits[0]

# Build Hamiltonian from calibration
H0 = parse_pauli_string(
    f"{qubit.frequency_ghz / 2} * Z0",
    num_qubits=1
)

config = GrapeConfig(
    num_time_steps=100,
    duration_ns=50,
)

optimizer = GrapeOptimizer(config)
# ... use with optimizer

Extracting Qubit Parameters

calibration = loader.load("calibration.yaml")

for qubit in calibration.qubits:
    print(f"Qubit {qubit.index}:")
    print(f"  Frequency: {qubit.frequency_ghz} GHz")
    print(f"  Anharmonicity: {qubit.anharmonicity_mhz} MHz")
    print(f"  T1: {qubit.t1_us} µs")
    print(f"  T2: {qubit.t2_us} µs")
    print(f"  Gate fidelity: {qubit.gate_fidelity}")

Extracting Coupler Parameters

for coupler in calibration.couplers:
    print(f"Coupler Q{coupler.qubit_a}-Q{coupler.qubit_b}:")
    print(f"  Coupling: {coupler.coupling_mhz} MHz")
    print(f"  CZ fidelity: {coupler.cz_fidelity}")
    print(f"  CZ duration: {coupler.cz_duration_ns} ns")

Security

The loader includes path traversal protection when a calibration directory is configured:

loader = CalibrationLoader(calibration_dir="calibrations/")

# This will raise CalibrationError
loader.load("../../../etc/passwd")  # Blocked!

API Reference

Main Classes

qubitos.calibrator.loader.CalibrationLoader

CalibrationLoader(
    calibration_dir: str | Path | None = None,
    validate: bool = True,
)

Loader for calibration data files.

Supports YAML calibration files with validation.

Initialize the calibration loader.

Parameters:

Name Type Description Default
calibration_dir str | Path | None

Default directory for calibration files

None
validate bool

Whether to validate loaded calibrations

True
Source code in src/qubitos/calibrator/loader.py
def __init__(
    self,
    calibration_dir: str | Path | None = None,
    validate: bool = True,
):
    """Initialize the calibration loader.

    Args:
        calibration_dir: Default directory for calibration files
        validate: Whether to validate loaded calibrations
    """
    self.calibration_dir = Path(calibration_dir) if calibration_dir else None
    self.validate = validate
    self._cache: dict[str, BackendCalibration] = {}

load

load(
    path: str | Path, use_cache: bool = True
) -> BackendCalibration

Load calibration data from a file.

Parameters:

Name Type Description Default
path str | Path

Path to calibration file

required
use_cache bool

Whether to use cached data

True

Returns:

Type Description
BackendCalibration

BackendCalibration with loaded data

Raises:

Type Description
CalibrationError

If file cannot be loaded or validation fails

Source code in src/qubitos/calibrator/loader.py
def load(
    self,
    path: str | Path,
    use_cache: bool = True,
) -> BackendCalibration:
    """Load calibration data from a file.

    Args:
        path: Path to calibration file
        use_cache: Whether to use cached data

    Returns:
        BackendCalibration with loaded data

    Raises:
        CalibrationError: If file cannot be loaded or validation fails
    """
    path = Path(path)

    # Try relative to calibration_dir if not absolute
    if not path.is_absolute() and self.calibration_dir:
        full_path = self.calibration_dir / path
        if full_path.exists():
            path = full_path

    # Resolve to absolute path for security validation and caching
    resolved_path = path.resolve()

    # Security check: prevent path traversal attacks
    # Ensure the resolved path doesn't escape the calibration directory
    if self.calibration_dir is not None:
        allowed_dir = self.calibration_dir.resolve()
        if not resolved_path.is_relative_to(allowed_dir):
            raise CalibrationError(
                f"Path traversal detected: {path} resolves outside "
                f"calibration directory {self.calibration_dir}"
            )

    cache_key = str(resolved_path)

    # Check cache
    if use_cache and cache_key in self._cache:
        logger.debug(f"Using cached calibration for {path}")
        return self._cache[cache_key]

    # Load file
    if not path.exists():
        raise CalibrationError(f"Calibration file not found: {path}")

    try:
        with open(path) as f:
            data = yaml.safe_load(f)
    except yaml.YAMLError as e:
        raise CalibrationError(f"Failed to parse calibration file: {e}") from e

    # Parse calibration
    calibration = self._parse_calibration(data)

    # Validate
    if self.validate:
        self._validate_calibration(calibration)

    # Cache
    self._cache[cache_key] = calibration

    logger.info(f"Loaded calibration from {path}")
    return calibration

load_for_backend

load_for_backend(backend_name: str) -> BackendCalibration

Load calibration for a named backend.

Searches for calibration files in the calibration directory.

Parameters:

Name Type Description Default
backend_name str

Backend name (e.g., "qutip_simulator")

required

Returns:

Type Description
BackendCalibration

BackendCalibration for the backend

Source code in src/qubitos/calibrator/loader.py
def load_for_backend(self, backend_name: str) -> BackendCalibration:
    """Load calibration for a named backend.

    Searches for calibration files in the calibration directory.

    Args:
        backend_name: Backend name (e.g., "qutip_simulator")

    Returns:
        BackendCalibration for the backend
    """
    if self.calibration_dir is None:
        raise CalibrationError("No calibration directory configured")

    # Try common file patterns
    patterns = [
        f"{backend_name}.yaml",
        f"{backend_name}.yml",
        f"defaults/{backend_name}.yaml",
        f"defaults/{backend_name}.yml",
    ]

    for pattern in patterns:
        path = self.calibration_dir / pattern
        if path.exists():
            return self.load(path)

    raise CalibrationError(f"No calibration found for backend: {backend_name}")

save

save(
    calibration: BackendCalibration, path: str | Path
) -> None

Save calibration data to a file.

Parameters:

Name Type Description Default
calibration BackendCalibration

Calibration data to save

required
path str | Path

Output file path

required
Source code in src/qubitos/calibrator/loader.py
def save(
    self,
    calibration: BackendCalibration,
    path: str | Path,
) -> None:
    """Save calibration data to a file.

    Args:
        calibration: Calibration data to save
        path: Output file path
    """
    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)

    data = {
        "name": calibration.name,
        "version": calibration.version,
        "timestamp": calibration.timestamp,
        "num_qubits": calibration.num_qubits,
        "qubits": [
            {
                "index": q.index,
                "frequency_ghz": q.frequency_ghz,
                "anharmonicity_mhz": q.anharmonicity_mhz,
                "t1_us": q.t1_us,
                "t2_us": q.t2_us,
                "readout_fidelity": q.readout_fidelity,
                "gate_fidelity": q.gate_fidelity,
                "drive_amplitude": q.drive_amplitude,
                **(
                    {
                        "awg": {
                            "sample_rate_ghz": q.awg_config.sample_rate_ghz,
                            "jitter_bound_ns": q.awg_config.jitter_bound_ns,
                            "min_samples": q.awg_config.min_samples,
                            "max_samples": q.awg_config.max_samples,
                        }
                    }
                    if q.awg_config is not None
                    else {}
                ),
            }
            for q in calibration.qubits
        ],
        "couplers": [
            {
                "qubit_a": c.qubit_a,
                "qubit_b": c.qubit_b,
                "coupling_mhz": c.coupling_mhz,
                "cz_fidelity": c.cz_fidelity,
                "cz_duration_ns": c.cz_duration_ns,
            }
            for c in calibration.couplers
        ],
        "metadata": calibration.metadata,
    }

    with open(path, "w") as f:
        yaml.dump(data, f, default_flow_style=False, sort_keys=False)

    logger.info(f"Saved calibration to {path}")

clear_cache

clear_cache() -> None

Clear the calibration cache.

Source code in src/qubitos/calibrator/loader.py
def clear_cache(self) -> None:
    """Clear the calibration cache."""
    self._cache.clear()

Data Classes

qubitos.calibrator.loader.BackendCalibration dataclass

BackendCalibration(
    name: str,
    version: str = "1.0",
    timestamp: str = "",
    num_qubits: int = 0,
    qubits: list[QubitCalibration] = list(),
    couplers: list[CouplerCalibration] = list(),
    metadata: dict[str, Any] = dict(),
)

Complete calibration data for a backend.

Attributes:

Name Type Description
name str

Backend name

version str

Calibration version string

timestamp str

Calibration timestamp (ISO format)

num_qubits int

Number of qubits

qubits list[QubitCalibration]

Per-qubit calibration data

couplers list[CouplerCalibration]

Per-coupler calibration data

metadata dict[str, Any]

Additional metadata

qubitos.calibrator.loader.QubitCalibration dataclass

QubitCalibration(
    index: int,
    frequency_ghz: float = 5.0,
    anharmonicity_mhz: float = -300.0,
    t1_us: float = 100.0,
    t2_us: float = 80.0,
    readout_fidelity: float = 0.99,
    gate_fidelity: float = 0.999,
    drive_amplitude: float = 1.0,
    awg_config: AWGClockConfig | None = None,
)

Calibration data for a single qubit.

Attributes:

Name Type Description
index int

Qubit index

frequency_ghz float

Qubit frequency in GHz

anharmonicity_mhz float

Anharmonicity in MHz (negative for transmons)

t1_us float

T1 relaxation time in microseconds

t2_us float

T2 dephasing time in microseconds

readout_fidelity float

Readout assignment fidelity

gate_fidelity float

Single-qubit gate fidelity

drive_amplitude float

Drive amplitude scaling factor

qubitos.calibrator.loader.CouplerCalibration dataclass

CouplerCalibration(
    qubit_a: int,
    qubit_b: int,
    coupling_mhz: float = 5.0,
    cz_fidelity: float = 0.99,
    cz_duration_ns: float = 40.0,
)

Calibration data for a qubit-qubit coupler.

Attributes:

Name Type Description
qubit_a int

First qubit index

qubit_b int

Second qubit index

coupling_mhz float

Coupling strength in MHz

cz_fidelity float

CZ gate fidelity

cz_duration_ns float

CZ gate duration in nanoseconds

Errors

qubitos.calibrator.loader.CalibrationError

Bases: Exception

Error loading or validating calibration data.

Convenience Functions

qubitos.calibrator.loader.load_calibration

load_calibration(path: str | Path) -> BackendCalibration

Load calibration data using the default loader.

Parameters:

Name Type Description Default
path str | Path

Path to calibration file

required

Returns:

Type Description
BackendCalibration

BackendCalibration with loaded data

Source code in src/qubitos/calibrator/loader.py
def load_calibration(path: str | Path) -> BackendCalibration:
    """Load calibration data using the default loader.

    Args:
        path: Path to calibration file

    Returns:
        BackendCalibration with loaded data
    """
    return get_default_loader().load(path)

qubitos.calibrator.loader.get_default_loader

get_default_loader() -> CalibrationLoader

Get the default calibration loader.

Source code in src/qubitos/calibrator/loader.py
def get_default_loader() -> CalibrationLoader:
    """Get the default calibration loader."""
    global _default_loader
    if _default_loader is None:
        _default_loader = CalibrationLoader()
    return _default_loader