Skip to content

Commit

Permalink
Support sqrt(iSWAP) and Sycamore gates in Floquet calibration (#4248)
Browse files Browse the repository at this point in the history
Goal of this PR is that circuits having any of these 2-qubit gates (sqrt(iSWAP), Sycamore) can be calibrated on real hardware. In #4164 I ensured that any FSim-compatible gate can be handled on compensation phase. But on characterization step we can support only these 2 gates, because only they are implemented in hardware (so far).

For that:
* Added method `try_convert_syc_or_sqrt_iswap_to_fsim` which is restriction of `try_convert_gate_to_fsim` on gates supported by hardware.
* Used that method in `workflow.py` as gate translator everywhere where `try_convert_sqrt_iswap_to_fsim` was used.
* Fixed a bug in `_merge_into_calibrations` so if there are calibrations for 2 different gates, it wouldn't fail with assertion error, but would append new calibration to the list of calibration.
* Modified a test `test_run_zeta_chi_gamma_calibration_for_moments` so the circuit under test contains gates of 2 types.
  • Loading branch information
fedimser committed Jun 24, 2021
1 parent aba9fa9 commit 09a2ea5
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 50 deletions.
1 change: 1 addition & 0 deletions cirq-google/cirq_google/calibration/__init__.py
Expand Up @@ -35,6 +35,7 @@
WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
merge_matching_results,
try_convert_sqrt_iswap_to_fsim,
try_convert_syc_or_sqrt_iswap_to_fsim,
)

from cirq_google.calibration.workflow import (
Expand Down
59 changes: 31 additions & 28 deletions cirq-google/cirq_google/calibration/phased_fsim.py
Expand Up @@ -15,6 +15,7 @@
import collections
import dataclasses
import functools
import math
import re
from typing import (
Any,
Expand Down Expand Up @@ -1016,35 +1017,12 @@ def try_convert_sqrt_iswap_to_fsim(gate: cirq.Gate) -> Optional[PhaseCalibratedF
either FSimGate, ISWapPowGate, PhasedFSimGate or PhasedISwapPowGate that is equivalent to
FSimGate(theta=±π/4, phi=0). None otherwise.
"""
if isinstance(gate, cirq.FSimGate):
if not np.isclose(gate.phi, 0.0):
return None
angle = gate.theta
elif isinstance(gate, cirq.ISwapPowGate):
angle = -gate.exponent * np.pi / 2
elif isinstance(gate, cirq.PhasedFSimGate):
if (
not np.isclose(gate.zeta, 0.0)
or not np.isclose(gate.chi, 0.0)
or not np.isclose(gate.gamma, 0.0)
or not np.isclose(gate.phi, 0.0)
):
return None
angle = gate.theta
elif isinstance(gate, cirq.PhasedISwapPowGate):
if not np.isclose(-gate.phase_exponent - 0.5, 0.0):
return None
angle = gate.exponent * np.pi / 2
else:
result = try_convert_gate_to_fsim(gate)
if result is None:
return None

angle_canonical = angle % (2 * np.pi)

if np.isclose(angle_canonical, np.pi / 4):
return PhaseCalibratedFSimGate(cirq.FSimGate(theta=np.pi / 4, phi=0.0), 0.0)
elif np.isclose(angle_canonical, 7 * np.pi / 4):
return PhaseCalibratedFSimGate(cirq.FSimGate(theta=np.pi / 4, phi=0.0), 0.5)

engine_gate = result.engine_gate
if math.isclose(engine_gate.theta, np.pi / 4) and math.isclose(engine_gate.phi, 0.0):
return result
return None


Expand Down Expand Up @@ -1097,3 +1075,28 @@ def try_convert_gate_to_fsim(gate: cirq.Gate) -> Optional[PhaseCalibratedFSimGat
phase_exponent = phase_exponent + 0.5
phase_exponent %= 1
return PhaseCalibratedFSimGate(cirq.FSimGate(theta=theta, phi=phi), phase_exponent)


def try_convert_syc_or_sqrt_iswap_to_fsim(
gate: cirq.Gate,
) -> Optional[PhaseCalibratedFSimGate]:
"""Converts a gate to equivalent PhaseCalibratedFSimGate if possible.
Args:
gate: Gate to convert.
Returns:
Equivalent PhaseCalibratedFSimGate if its `engine_gate` is Sycamore or inverse sqrt(iSWAP)
gate. Otherwise returns None.
"""
result = try_convert_gate_to_fsim(gate)
if result is None:
return None
engine_gate = result.engine_gate
if math.isclose(engine_gate.theta, np.pi / 2) and math.isclose(engine_gate.phi, np.pi / 6):
# Sycamore gate.
return result
if math.isclose(engine_gate.theta, np.pi / 4) and math.isclose(engine_gate.phi, 0.0):
# Inverse sqrt(iSWAP) gate.
return result
return None
29 changes: 26 additions & 3 deletions cirq-google/cirq_google/calibration/phased_fsim_test.py
Expand Up @@ -37,6 +37,7 @@
WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
merge_matching_results,
try_convert_gate_to_fsim,
try_convert_syc_or_sqrt_iswap_to_fsim,
try_convert_sqrt_iswap_to_fsim,
XEBPhasedFSimCalibrationRequest,
XEBPhasedFSimCalibrationOptions,
Expand Down Expand Up @@ -767,7 +768,7 @@ def test_try_convert_sqrt_iswap_to_fsim_converts_correctly():
expected, 0.0
)
assert (
try_convert_sqrt_iswap_to_fsim(cirq.PhasedISwapPowGate(exponent=-0.5, phase_exponent=0.1))
try_convert_sqrt_iswap_to_fsim(cirq.PhasedISwapPowGate(exponent=-0.6, phase_exponent=0.1))
is None
)

Expand Down Expand Up @@ -949,11 +950,33 @@ def check(gate: cirq.Gate, expected: PhaseCalibratedFSimGate):
assert try_convert_gate_to_fsim(cirq.CZPowGate(exponent=x)) is None


def test_try_convert_syc_or_sqrt_iswap_to_fsim():
def check_converts(gate: cirq.Gate):
result = try_convert_syc_or_sqrt_iswap_to_fsim(gate)
assert np.allclose(cirq.unitary(gate), cirq.unitary(result))

def check_none(gate: cirq.Gate):
assert try_convert_syc_or_sqrt_iswap_to_fsim(gate) is None

check_converts(cirq_google.ops.SYC)
check_converts(cirq.FSimGate(np.pi / 2, np.pi / 6))
check_none(cirq.FSimGate(0, np.pi))
check_converts(cirq.FSimGate(np.pi / 4, 0.0))
check_none(cirq.FSimGate(0.2, 0.3))
check_converts(cirq.ISwapPowGate(exponent=0.5))
check_converts(cirq.ISwapPowGate(exponent=-0.5))
check_none(cirq.ISwapPowGate(exponent=0.3))
check_converts(cirq.PhasedFSimGate(theta=np.pi / 4, phi=0.0, chi=0.7))
check_none(cirq.PhasedFSimGate(theta=0.3, phi=0.4))
check_converts(cirq.PhasedISwapPowGate(exponent=0.5, phase_exponent=0.75))
check_none(cirq.PhasedISwapPowGate(exponent=0.4, phase_exponent=0.75))
check_none(cirq.ops.CZPowGate(exponent=1.0))
check_none(cirq.CX)


# Test that try_convert_gate_to_fsim is extension of try_convert_sqrt_iswap_to_fsim.
# In other words, that both function return the same result for all gates on which
# try_convert_sqrt_iswap_to_fsim is defined.
# TODO: instead of having multiple gate translators, we should have one, the most general, and
# restrict it to different gate sets.
def test_gate_translators_are_consistent():
def check(gate):
result1 = try_convert_gate_to_fsim(gate)
Expand Down
29 changes: 17 additions & 12 deletions cirq-google/cirq_google/calibration/workflow.py
Expand Up @@ -41,7 +41,7 @@
THETA_ZETA_GAMMA_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
merge_matching_results,
try_convert_gate_to_fsim,
try_convert_sqrt_iswap_to_fsim,
try_convert_syc_or_sqrt_iswap_to_fsim,
PhasedFSimCalibrationOptions,
RequestT,
LocalXEBPhasedFSimCalibrationRequest,
Expand Down Expand Up @@ -73,7 +73,7 @@ def prepare_characterization_for_moment(
*,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
canonicalize_pairs: bool = False,
sort_pairs: bool = False,
) -> Optional[RequestT]:
Expand Down Expand Up @@ -116,7 +116,7 @@ def prepare_floquet_characterization_for_moment(
options: FloquetPhasedFSimCalibrationOptions,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
canonicalize_pairs: bool = False,
sort_pairs: bool = False,
) -> Optional[FloquetPhasedFSimCalibrationRequest]:
Expand Down Expand Up @@ -271,7 +271,7 @@ def prepare_characterization_for_moments(
*,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
merge_subsets: bool = True,
initial: Optional[Sequence[RequestT]] = None,
) -> Tuple[CircuitWithCalibration, List[RequestT]]:
Expand Down Expand Up @@ -345,7 +345,7 @@ def prepare_characterization_for_circuits_moments(
*,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
merge_subsets: bool = True,
initial: Optional[Sequence[RequestT]] = None,
) -> Tuple[List[CircuitWithCalibration], List[RequestT]]:
Expand Down Expand Up @@ -406,7 +406,7 @@ def prepare_floquet_characterization_for_moments(
options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
merge_subsets: bool = True,
initial: Optional[Sequence[FloquetPhasedFSimCalibrationRequest]] = None,
) -> Tuple[CircuitWithCalibration, List[FloquetPhasedFSimCalibrationRequest]]:
Expand Down Expand Up @@ -466,7 +466,7 @@ def prepare_characterization_for_operations(
*,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
permit_mixed_moments: bool = False,
) -> List[RequestT]:
"""Extracts a minimal set of characterization requests necessary to characterize all the
Expand Down Expand Up @@ -531,7 +531,7 @@ def prepare_floquet_characterization_for_operations(
options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
permit_mixed_moments: bool = False,
) -> List[FloquetPhasedFSimCalibrationRequest]:
"""Extracts a minimal set of Floquet characterization requests necessary to characterize all the
Expand Down Expand Up @@ -679,8 +679,12 @@ def _merge_into_calibrations(
"""
new_pairs = set(calibration.pairs)
for index in pairs_map.values():
assert calibration.gate == calibrations[index].gate
assert calibration.options == calibrations[index].options
can_merge = (
calibration.gate == calibrations[index].gate
and calibration.options == calibrations[index].options
)
if not can_merge:
continue
existing_pairs = calibrations[index].pairs
if new_pairs.issubset(existing_pairs):
return index
Expand Down Expand Up @@ -1057,6 +1061,7 @@ def from_characterization(
"""Creates an operation that compensates for zeta, chi and gamma angles of the supplied
gate and characterization.
Args:
Args:
qubits: Qubits that the gate should act on.
gate_calibration: Original, imperfect gate that is supposed to run on the hardware
Expand All @@ -1081,7 +1086,7 @@ def run_floquet_characterization_for_moments(
options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
merge_subsets: bool = True,
max_layers_per_request: int = 1,
progress_func: Optional[Callable[[int, int], None]] = None,
Expand Down Expand Up @@ -1147,7 +1152,7 @@ def run_zeta_chi_gamma_compensation_for_moments(
),
gates_translator: Callable[
[cirq.Gate], Optional[PhaseCalibratedFSimGate]
] = try_convert_sqrt_iswap_to_fsim,
] = try_convert_syc_or_sqrt_iswap_to_fsim,
merge_subsets: bool = True,
max_layers_per_request: int = 1,
progress_func: Optional[Callable[[int, int], None]] = None,
Expand Down
17 changes: 10 additions & 7 deletions cirq-google/cirq_google/calibration/workflow_test.py
Expand Up @@ -45,6 +45,9 @@
SQRT_ISWAP_INV_PARAMETERS = cirq_google.PhasedFSimCharacterization(
theta=np.pi / 4, zeta=0.0, chi=0.0, gamma=0.0, phi=0.0
)
SYCAMORE_PARAMETERS = cirq_google.PhasedFSimCharacterization(
theta=np.pi / 2, zeta=0.0, chi=0.0, gamma=0.0, phi=np.pi / 6
)
SQRT_ISWAP_GATE = cirq.FSimGate(7 * np.pi / 4, 0.0)
SQRT_ISWAP_INV_GATE = cirq.FSimGate(np.pi / 4, 0.0)

Expand Down Expand Up @@ -1462,19 +1465,19 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None:
parameters_cd = cirq_google.PhasedFSimCharacterization(zeta=0.2, chi=0.3, gamma=0.4)

a, b, c, d = cirq.LineQubit.range(4)
engine_simulator = cirq_google.PhasedFSimEngineSimulator.create_from_dictionary_sqrt_iswap(
engine_simulator = cirq_google.PhasedFSimEngineSimulator.create_from_dictionary(
parameters={
(a, b): parameters_ab.merge_with(SQRT_ISWAP_INV_PARAMETERS),
(b, c): parameters_bc.merge_with(SQRT_ISWAP_INV_PARAMETERS),
(c, d): parameters_cd.merge_with(SQRT_ISWAP_INV_PARAMETERS),
(a, b): {SQRT_ISWAP_INV_GATE: parameters_ab.merge_with(SQRT_ISWAP_INV_PARAMETERS)},
(b, c): {cirq_google.ops.SYC: parameters_bc.merge_with(SYCAMORE_PARAMETERS)},
(c, d): {SQRT_ISWAP_INV_GATE: parameters_cd.merge_with(SQRT_ISWAP_INV_PARAMETERS)},
}
)

circuit = cirq.Circuit(
[
[cirq.X(a), cirq.Y(c)],
[SQRT_ISWAP_INV_GATE.on(a, b), SQRT_ISWAP_INV_GATE.on(c, d)],
[SQRT_ISWAP_INV_GATE.on(b, c)],
[cirq_google.ops.SYC.on(b, c)],
]
)

Expand All @@ -1490,7 +1493,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None:
circuit,
engine_simulator,
processor_id=None,
gate_set=cirq_google.SQRT_ISWAP_GATESET,
gate_set=cirq_google.FSIM_GATESET,
options=options,
)

Expand All @@ -1505,7 +1508,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None:
options=options,
),
cirq_google.PhasedFSimCalibrationResult(
gate=SQRT_ISWAP_INV_GATE, parameters={(b, c): parameters_bc}, options=options
gate=cirq_google.ops.SYC, parameters={(b, c): parameters_bc}, options=options
),
]
assert calibrated_circuit.moment_to_calibration == [None, None, 0, None, None, 1, None]
Expand Down

0 comments on commit 09a2ea5

Please sign in to comment.