diff --git a/cirq-google/cirq_google/calibration/phased_fsim.py b/cirq-google/cirq_google/calibration/phased_fsim.py index 0dfd36a72d3..714033a8f83 100644 --- a/cirq-google/cirq_google/calibration/phased_fsim.py +++ b/cirq-google/cirq_google/calibration/phased_fsim.py @@ -26,6 +26,7 @@ MutableMapping, Optional, Tuple, + Type, TypeVar, TYPE_CHECKING, Generic, @@ -40,8 +41,15 @@ import cirq from cirq.experiments.xeb_fitting import XEBPhasedFSimCharacterizationOptions from cirq_google.api import v2 -from cirq_google.engine import Calibration, CalibrationLayer, CalibrationResult, Engine, EngineJob -from cirq_google.ops import FSimGateFamily +from cirq_google.engine import ( + Calibration, + CalibrationLayer, + CalibrationResult, + Engine, + EngineJob, + util, +) +from cirq_google.ops import FSimGateFamily, SycamoreGate if TYPE_CHECKING: import cirq_google @@ -50,6 +58,11 @@ _FLOQUET_PHASED_FSIM_HANDLER_NAME = 'floquet_phased_fsim_characterization' _XEB_PHASED_FSIM_HANDLER_NAME = 'xeb_phased_fsim_characterization' _DEFAULT_XEB_CYCLE_DEPTHS = (5, 25, 50, 100, 200, 300) +# Copied from cirq-google/cirq_google/engine/calibration_to_noise_properties.py +GATE_ZPHASE_CODE_PAIRS: Dict[Type['cirq.Gate'], str] = { + SycamoreGate: 'syc', + cirq.ISwapPowGate: 'sqrt_iswap', +} T = TypeVar('T') @@ -331,6 +344,38 @@ def _json_dict_(self) -> Dict[str, Any]: } +def to_zphase_data(results: Iterable[PhasedFSimCalibrationResult]) -> util.ZPhaseDataType: + """Packages a collection of results into ZPhaseDataType. + + Args: + results: List of results to pack into ZPhaseDataType. If multiple results provide a value + for a given (gate, angle, qubits) tuple, only the last one will be kept. + + Returns: + A ZPhaseDataType-formatted result representation. This can be used with the + calibration-to-noise pipeline for generating noise models. + + Raises: + ValueError: if results for a gate other than Sycamore or ISwapPowGate are given. + """ + zphase_data: util.ZPhaseDataType = {} + for result in results: + gate_type = GATE_ZPHASE_CODE_PAIRS.get(type(result.gate)) + if gate_type is None: + raise ValueError( + f"Only 'SycamoreGate' and 'ISwapPowGate' are supported, got {result.gate}" + ) + gate_dict = zphase_data.setdefault(gate_type, {}) + for qubits, data in result.parameters.items(): + for angle, value in data.asdict().items(): + if value is None: + continue + angle_dict = gate_dict.setdefault(angle, {}) + angle_dict[qubits] = value + + return zphase_data + + def merge_matching_results( results: Iterable[PhasedFSimCalibrationResult], ) -> Optional[PhasedFSimCalibrationResult]: diff --git a/cirq-google/cirq_google/calibration/phased_fsim_test.py b/cirq-google/cirq_google/calibration/phased_fsim_test.py index 157d0de58fc..4329965e352 100644 --- a/cirq-google/cirq_google/calibration/phased_fsim_test.py +++ b/cirq-google/cirq_google/calibration/phased_fsim_test.py @@ -36,6 +36,7 @@ PhasedFSimCalibrationResult, WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, merge_matching_results, + to_zphase_data, try_convert_gate_to_fsim, try_convert_syc_or_sqrt_iswap_to_fsim, try_convert_sqrt_iswap_to_fsim, @@ -625,6 +626,64 @@ def test_get_parameters(): assert result.get_parameters(q_00, q_03) is None +def test_to_zphase_data(): + q0, q1, q2 = cirq.GridQubit.rect(1, 3) + result_1 = PhasedFSimCalibrationResult( + { + (q0, q1): PhasedFSimCharacterization(zeta=0.1, gamma=0.2), + (q1, q2): PhasedFSimCharacterization(zeta=0.3, gamma=0.4), + }, + gate=cirq_google.SycamoreGate(), + options=WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + result_2 = PhasedFSimCalibrationResult( + { + (q0, q1): PhasedFSimCharacterization(zeta=0.5, gamma=0.6), + (q1, q2): PhasedFSimCharacterization(zeta=0.7, gamma=0.8), + }, + gate=cirq.ISwapPowGate(), + options=WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + assert to_zphase_data([result_1, result_2]) == { + 'syc': {'zeta': {(q0, q1): 0.1, (q1, q2): 0.3}, 'gamma': {(q0, q1): 0.2, (q1, q2): 0.4}}, + 'sqrt_iswap': { + 'zeta': {(q0, q1): 0.5, (q1, q2): 0.7}, + 'gamma': {(q0, q1): 0.6, (q1, q2): 0.8}, + }, + } + # Test update and override + result_3 = PhasedFSimCalibrationResult( + { + (q0, q1): PhasedFSimCharacterization(theta=0.01), + (q1, q2): PhasedFSimCharacterization(zeta=0.02), + (q2, q0): PhasedFSimCharacterization(zeta=0.03, gamma=0.04, theta=0.05), + }, + gate=cirq_google.SycamoreGate(), + options=WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + assert to_zphase_data([result_1, result_3]) == { + 'syc': { + 'zeta': {(q0, q1): 0.1, (q1, q2): 0.02, (q2, q0): 0.03}, + 'gamma': {(q0, q1): 0.2, (q1, q2): 0.4, (q2, q0): 0.04}, + 'theta': {(q0, q1): 0.01, (q2, q0): 0.05}, + } + } + + +def test_to_zphase_unknown_gate_raises_error(): + q0, q1, q2 = cirq.GridQubit.rect(1, 3) + result_1 = PhasedFSimCalibrationResult( + { + (q0, q1): PhasedFSimCharacterization(zeta=0.1, gamma=0.2), + (q1, q2): PhasedFSimCharacterization(zeta=0.3, gamma=0.4), + }, + gate=cirq.CZPowGate(), + options=WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + with pytest.raises(ValueError, match="Only 'SycamoreGate' and 'ISwapPowGate' are supported"): + _ = to_zphase_data([result_1]) + + def test_merge_matching_results(): q_00, q_01, q_02, q_03 = [cirq.GridQubit(0, index) for index in range(4)] gate = cirq.FSimGate(theta=np.pi / 4, phi=0.0)