"""A module containing utilities for working with Quil-T calibrations."""
from collections.abc import Sequence
from dataclasses import dataclass
from typing import Any, Optional, Union
import quil.expression as quil_expr
import quil.instructions as quil_rs
from quil.program import CalibrationSet
from pyquil.quilatom import (
ExpressionDesignator,
MemoryReference,
QubitDesignator,
_convert_to_py_expression,
)
from pyquil.quilbase import (
AbstractInstruction,
DefCalibration,
DefMeasureCalibration,
_convert_to_py_qubit,
_convert_to_rs_instruction,
)
[docs]
class CalibrationError(Exception):
"""Base class for calibration errors."""
pass
[docs]
class CalibrationDoesntMatch(CalibrationError):
"""Raised when a calibration doesn't match an instruction."""
pass
[docs]
@dataclass
class CalibrationMatch:
"""A match between a calibration definition and an instruction."""
cal: Union[DefCalibration, DefMeasureCalibration]
settings: dict[Union[QubitDesignator, ExpressionDesignator], Any]
def _convert_to_calibration_match(
instruction: Union[quil_rs.Gate, quil_rs.Measurement],
calibration: Optional[Union[quil_rs.Calibration, quil_rs.MeasureCalibrationDefinition]],
) -> Optional[CalibrationMatch]:
if isinstance(instruction, quil_rs.Gate) and isinstance(calibration, quil_rs.Calibration):
target_qubits = instruction.qubits
target_values: Sequence[Union[quil_expr.Expression, MemoryReference]] = instruction.parameters
parameter_qubits = calibration.identifier.qubits
parameter_values: Sequence[Union[quil_expr.Expression, MemoryReference]] = calibration.identifier.parameters
py_calibration: Union[DefCalibration, DefMeasureCalibration] = DefCalibration._from_rs_calibration(calibration)
elif isinstance(instruction, quil_rs.Measurement) and isinstance(calibration, quil_rs.MeasureCalibrationDefinition):
target_qubits = [instruction.qubit]
target_values = (
[] if not instruction.target else [MemoryReference._from_rs_memory_reference(instruction.target)]
)
calibration_qubit = calibration.identifier.qubit
parameter_qubits = [] + [calibration_qubit] if calibration_qubit else []
parameter_values = [MemoryReference._from_parameter_str(calibration.identifier.parameter)]
py_calibration = DefMeasureCalibration._from_rs_measure_calibration_definition(calibration)
else:
return None
settings: dict[Union[QubitDesignator, ExpressionDesignator], Union[QubitDesignator, ExpressionDesignator]] = {
_convert_to_py_qubit(param): _convert_to_py_qubit(qubit)
for param, qubit in zip(parameter_qubits, target_qubits)
if isinstance(param, MemoryReference) or param.is_variable()
}
settings.update(
{
_convert_to_py_expression(param): _convert_to_py_expression(value)
for param, value in zip(parameter_values, target_values)
if isinstance(param, MemoryReference) or param.is_variable()
}
)
return CalibrationMatch(py_calibration, settings)
[docs]
def match_calibration(
instr: AbstractInstruction, cal: Union[DefCalibration, DefMeasureCalibration]
) -> Optional[CalibrationMatch]:
"""Match a calibration definition to an instruction.
On a successful match, return a (possibly empty) dictionary mapping calibration
arguments and parameters to their values.
On a failure, return None.
"""
calibration = _convert_to_rs_instruction(cal)
instruction = _convert_to_rs_instruction(instr)
if calibration.is_calibration_definition() and instruction.is_gate():
instruction = _convert_to_rs_instruction(instr)
gate = instruction.to_gate()
calibration_set = CalibrationSet([calibration.to_calibration_definition()], [])
matched_calibration: Optional[Union[quil_rs.Calibration, quil_rs.MeasureCalibrationDefinition]] = (
calibration_set.get_match_for_gate(gate)
)
return _convert_to_calibration_match(gate, matched_calibration)
if calibration.is_measure_calibration_definition() and instruction.is_measurement():
instruction = _convert_to_rs_instruction(instr)
measurement = instruction.to_measurement()
calibration_set = CalibrationSet([], [calibration.to_measure_calibration_definition()])
matched_calibration = calibration_set.get_match_for_measurement(measurement)
return _convert_to_calibration_match(measurement, matched_calibration)
return None