def test_floquet_parse_result_bad_metric(): 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) request = FloquetPhasedFSimCalibrationRequest( gate=gate, pairs=((q_00, q_01), (q_02, q_03)), options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) result = cirq.google.CalibrationResult( code=cirq.google.api.v2.calibration_pb2.SUCCESS, error_message=None, token=None, valid_until=None, metrics=cirq.google.Calibration( cirq.google.api.v2.metrics_pb2.MetricsSnapshot(metrics=[ cirq.google.api.v2.metrics_pb2.Metric( name='angles', targets=[ '1000gerbils', ], values=[ cirq.google.api.v2.metrics_pb2.Value(str_val='100_10'), ], ) ])), ) with pytest.raises(ValueError, match='Unknown metric name 1000gerbils'): _ = request.parse_result(result)
def test_floquet_to_calibration_layer(): 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) request = FloquetPhasedFSimCalibrationRequest( gate=gate, pairs=((q_00, q_01), (q_02, q_03)), options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) assert request.to_calibration_layer() == cirq.google.CalibrationLayer( calibration_type='floquet_phased_fsim_characterization', program=cirq.Circuit([gate.on(q_00, q_01), gate.on(q_02, q_03)]), args={ 'est_theta': True, 'est_zeta': True, 'est_chi': False, 'est_gamma': False, 'est_phi': True, 'readout_corrections': True, }, )
def test_run_characterization_fails_when_invalid_arguments(): with pytest.raises(ValueError): assert workflow.run_characterizations([], None, 'qproc', cirq.google.FSIM_GATESET, max_layers_per_request=0) request = FloquetPhasedFSimCalibrationRequest( gate=SQRT_ISWAP_GATE, pairs=(), options=WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, ) engine = mock.MagicMock(spec=cirq.google.Engine) with pytest.raises(ValueError): assert workflow.run_characterizations([request], engine, None, cirq.google.FSIM_GATESET) with pytest.raises(ValueError): assert workflow.run_characterizations([request], engine, 'qproc', None) with pytest.raises(ValueError): assert workflow.run_characterizations([request], 0, 'qproc', cirq.google.FSIM_GATESET)
def test_from_moment(): q_00, q_01, q_02, q_03 = [cirq.GridQubit(0, index) for index in range(4)] m = cirq.Moment(cirq.ISWAP(q_00, q_01) ** 0.5, cirq.ISWAP(q_02, q_03) ** 0.5) options = FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ) request = FloquetPhasedFSimCalibrationRequest.from_moment(m, options) assert request == FloquetPhasedFSimCalibrationRequest( gate=cirq.ISWAP ** 0.5, pairs=((q_00, q_01), (q_02, q_03)), options=options ) non_identical = cirq.Moment(cirq.ISWAP(q_00, q_01) ** 0.5, cirq.ISWAP(q_02, q_03)) with pytest.raises(ValueError, match='must be identical'): _ = FloquetPhasedFSimCalibrationRequest.from_moment(non_identical, options) sq = cirq.Moment(cirq.X(q_00)) with pytest.raises(ValueError, match='must be two qubit gates'): _ = FloquetPhasedFSimCalibrationRequest.from_moment(sq, options) threeq = cirq.Moment(cirq.TOFFOLI(q_00, q_01, q_02)) with pytest.raises(ValueError, match='must be two qubit gates'): _ = FloquetPhasedFSimCalibrationRequest.from_moment(threeq, options) not_gate = cirq.Moment(cirq.CircuitOperation(cirq.FrozenCircuit())) with pytest.raises(ValueError, match='must be two qubit gates'): _ = FloquetPhasedFSimCalibrationRequest.from_moment(not_gate, options) empty = cirq.Moment() with pytest.raises(ValueError, match='No gates found'): _ = FloquetPhasedFSimCalibrationRequest.from_moment(empty, options)
def _merge_into_calibrations( calibration: FloquetPhasedFSimCalibrationRequest, calibrations: List[FloquetPhasedFSimCalibrationRequest], pairs_map: Dict[Tuple[Tuple[Qid, Qid], ...], int], options: FloquetPhasedFSimCalibrationOptions, ) -> int: """Merges a calibration into list of calibrations. If calibrations contains an item of which pairs could be expanded to include a new calibration pairs, without breaking a moment structure, then those two calibrations will be merged together and used as a calibration for both old and newly added calibration. If no calibration like that exists, the list will be expanded by calibration item. Args: calibration: Calibration to be added. calibrations: List of calibrations to be mutated. pairs_map: Map from pairs parameter of each calibration on the calibrations list to the index on that list. This map will be updated if the calibrations list us updated. options: Calibrations options to use when creating a new requests. Returns: Index of the calibration on the updated calibrations list. If the calibration was added, it points to the last element of a list. If not, it points to already existing element. """ new_pairs = set(calibration.pairs) for index in pairs_map.values(): assert calibration.gate == calibrations[index].gate assert calibration.options == calibrations[index].options existing_pairs = calibrations[index].pairs if new_pairs.issubset(existing_pairs): return index elif new_pairs.issuperset(existing_pairs): calibrations[index] = calibration return index else: new_qubit_pairs = calibration.qubit_to_pair existing_qubit_pairs = calibrations[index].qubit_to_pair if all( ( new_qubit_pairs[q] == existing_qubit_pairs[q] for q in set(new_qubit_pairs.keys()).intersection(existing_qubit_pairs.keys()) ) ): calibrations[index] = FloquetPhasedFSimCalibrationRequest( gate=calibration.gate, pairs=tuple(sorted(new_pairs.union(existing_pairs))), options=options, ) return index index = len(calibrations) calibrations.append(calibration) pairs_map[calibration.pairs] = index return index
def test_run_characterization_with_simulator(): q_00, q_01, q_02, q_03 = [cirq.GridQubit(0, index) for index in range(4)] gate = SQRT_ISWAP_GATE request = FloquetPhasedFSimCalibrationRequest( gate=gate, pairs=((q_00, q_01), (q_02, q_03)), options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) simulator = PhasedFSimEngineSimulator.create_with_ideal_sqrt_iswap() actual = workflow.run_characterizations([request], simulator) assert actual == [ PhasedFSimCalibrationResult( parameters={ (q_00, q_01): PhasedFSimCharacterization(theta=np.pi / 4, zeta=0.0, chi=None, gamma=None, phi=0.0), (q_02, q_03): PhasedFSimCharacterization(theta=np.pi / 4, zeta=0.0, chi=None, gamma=None, phi=0.0), }, gate=SQRT_ISWAP_GATE, options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) ]
def prepare_floquet_characterization_for_moment( moment: Moment, options: FloquetPhasedFSimCalibrationOptions, gates_translator: Callable[ [Gate], Optional[PhaseCalibratedFSimGate]] = try_convert_sqrt_iswap_to_fsim, canonicalize_pairs: bool = False, sort_pairs: bool = False, ) -> Optional[FloquetPhasedFSimCalibrationRequest]: """Describes a given moment in terms of a Floquet characterization request. Args: moment: Moment to characterize. options: Options that are applied to each characterized gate within a moment. gates_translator: Function that translates a gate to a supported FSimGate which will undergo characterization. Defaults to sqrt_iswap_gates_translator. canonicalize_pairs: Whether to sort each of the qubit pair so that the first qubit is always lower than the second. sort_pairs: Whether to sort all the qutibt pairs extracted from the moment which will undergo characterization. Returns: Instance of FloquetPhasedFSimCalibrationRequest that characterizes a given moment, or None when it is an empty, measurement or single-qubit gates only moment. Raises: IncompatibleMomentError when a moment contains operations other than the operations matched by gates_translator, or it mixes a single qubit and two qubit gates. """ pairs_and_gate = _list_moment_pairs_to_characterize( moment, gates_translator, canonicalize_pairs=canonicalize_pairs, permit_mixed_moments=False) if pairs_and_gate is None: return None pairs, gate = pairs_and_gate return FloquetPhasedFSimCalibrationRequest( pairs=tuple(sorted(pairs) if sort_pairs else pairs), gate=gate, options=options)
def test_floquet_parse_result(): 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) request = FloquetPhasedFSimCalibrationRequest( gate=gate, pairs=((q_00, q_01), (q_02, q_03)), options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) result = cirq.google.CalibrationResult( code=cirq.google.api.v2.calibration_pb2.SUCCESS, error_message=None, token=None, valid_until=None, metrics=cirq.google.Calibration( cirq.google.api.v2.metrics_pb2.MetricsSnapshot(metrics=[ cirq.google.api.v2.metrics_pb2.Metric( name='angles', targets=[ '0_qubit_a', '0_qubit_b', '0_theta_est', '0_zeta_est', '0_phi_est', '1_qubit_a', '1_qubit_b', '1_theta_est', '1_zeta_est', '1_phi_est', ], values=[ cirq.google.api.v2.metrics_pb2.Value(str_val='0_0'), cirq.google.api.v2.metrics_pb2.Value(str_val='0_1'), cirq.google.api.v2.metrics_pb2.Value(double_val=0.1), cirq.google.api.v2.metrics_pb2.Value(double_val=0.2), cirq.google.api.v2.metrics_pb2.Value(double_val=0.3), cirq.google.api.v2.metrics_pb2.Value(str_val='0_2'), cirq.google.api.v2.metrics_pb2.Value(str_val='0_3'), cirq.google.api.v2.metrics_pb2.Value(double_val=0.4), cirq.google.api.v2.metrics_pb2.Value(double_val=0.5), cirq.google.api.v2.metrics_pb2.Value(double_val=0.6), ], ) ])), ) assert request.parse_result(result) == PhasedFSimCalibrationResult( parameters={ (q_00, q_01): PhasedFSimCharacterization(theta=0.1, zeta=0.2, chi=None, gamma=None, phi=0.3), (q_02, q_03): PhasedFSimCharacterization(theta=0.4, zeta=0.5, chi=None, gamma=None, phi=0.6), }, gate=gate, options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), )
def prepare_floquet_characterization_for_operations( circuit: Union[Circuit, Iterable[Circuit]], options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, gates_translator: Callable[ [Gate], Optional[PhaseCalibratedFSimGate]] = try_convert_sqrt_iswap_to_fsim, permit_mixed_moments: bool = False, ) -> List[FloquetPhasedFSimCalibrationRequest]: """Extracts a minimal set of Floquet characterization requests necessary to characterize all the operations within a circuit(s). This variant of prepare method works on two-qubit operations of the circuit. The method extracts all the operations and groups them in a way to minimize the number of characterizations requested, depending on the connectivity. Contrary to prepare_floquet_characterization_for_moments, this method ignores moments structure and is less accurate because certain errors caused by cross-talks are ignored. The major advantage of this method is that the number of generated characterization requests is bounded by four for grid-like devices, where for the prepare_floquet_characterization_for_moments the number of characterizations is bounded by number of moments in a circuit. The circuit can only be composed of single qubit operations, wait operations, measurement operations and operations supported by gates_translator. Args: circuit: Circuit or circuits to characterize. Only circuits with qubits of type GridQubit that can be covered by HALF_GRID_STAGGERED_PATTERN are supported options: Options that are applied to each characterized gate within a moment. Defaults to all_except_for_chi_options which is the broadest currently supported choice. gates_translator: Function that translates a gate to a supported FSimGate which will undergo characterization. Defaults to sqrt_iswap_gates_translator. permit_mixed_moments: Whether to allow a mix of two-qubit gates with other irrelevant single-qubit gates. Returns: List of PhasedFSimCalibrationRequest for each group of operations to characterize. Raises: IncompatibleMomentError when circuit contains a moment with operations other than the operations matched by gates_translator, or it mixes a single qubit and two qubit gates. """ circuits = [circuit] if isinstance(circuit, Circuit) else circuit pairs, gate = _extract_all_pairs_to_characterize(circuits, gates_translator, permit_mixed_moments) if gate is None: return [] characterizations = [] for pattern in HALF_GRID_STAGGERED_PATTERN: pattern_pairs = [pair for pair in pairs if pair in pattern] if pattern_pairs: characterizations.append( FloquetPhasedFSimCalibrationRequest(pairs=tuple( sorted(pattern_pairs)), gate=gate, options=options)) if sum((len(characterization.pairs) for characterization in characterizations)) != len(pairs): raise ValueError( 'Unable to cover all interactions with HALF_GRID_STAGGERED_PATTERN' ) return characterizations
def test_run_characterization(): 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) request = FloquetPhasedFSimCalibrationRequest( gate=gate, pairs=((q_00, q_01), (q_02, q_03)), options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) result = cirq.google.CalibrationResult( code=cirq.google.api.v2.calibration_pb2.SUCCESS, error_message=None, token=None, valid_until=None, metrics=cirq.google.Calibration( cirq.google.api.v2.metrics_pb2.MetricsSnapshot(metrics=[ cirq.google.api.v2.metrics_pb2.Metric( name='angles', targets=[ '0_qubit_a', '0_qubit_b', '0_theta_est', '0_zeta_est', '0_phi_est', '1_qubit_a', '1_qubit_b', '1_theta_est', '1_zeta_est', '1_phi_est', ], values=[ cirq.google.api.v2.metrics_pb2.Value(str_val='0_0'), cirq.google.api.v2.metrics_pb2.Value(str_val='0_1'), cirq.google.api.v2.metrics_pb2.Value(double_val=0.1), cirq.google.api.v2.metrics_pb2.Value(double_val=0.2), cirq.google.api.v2.metrics_pb2.Value(double_val=0.3), cirq.google.api.v2.metrics_pb2.Value(str_val='0_2'), cirq.google.api.v2.metrics_pb2.Value(str_val='0_3'), cirq.google.api.v2.metrics_pb2.Value(double_val=0.4), cirq.google.api.v2.metrics_pb2.Value(double_val=0.5), cirq.google.api.v2.metrics_pb2.Value(double_val=0.6), ], ) ])), ) job = cirq.google.engine.EngineJob('', '', '', None) job._calibration_results = [result] engine = mock.MagicMock(spec=cirq.google.Engine) engine.run_calibration.return_value = job progress_calls = [] def progress(step: int, steps: int) -> None: progress_calls.append((step, steps)) actual = workflow.run_characterizations([request], engine, 'qproc', cirq.google.FSIM_GATESET, progress_func=progress) expected = [ PhasedFSimCalibrationResult( parameters={ (q_00, q_01): PhasedFSimCharacterization(theta=0.1, zeta=0.2, chi=None, gamma=None, phi=0.3), (q_02, q_03): PhasedFSimCharacterization(theta=0.4, zeta=0.5, chi=None, gamma=None, phi=0.6), }, gate=gate, options=FloquetPhasedFSimCalibrationOptions( characterize_theta=True, characterize_zeta=True, characterize_chi=False, characterize_gamma=False, characterize_phi=True, ), ) ] assert actual == expected assert progress_calls == [(1, 1)]
def make_floquet_request_for_moment( moment: Moment, options: FloquetPhasedFSimCalibrationOptions, gates_translator: Callable[ [Gate], Optional[FSimGate]] = try_convert_sqrt_iswap_to_fsim, canonicalize_pairs: bool = False, sort_pairs: bool = False, ) -> Optional[FloquetPhasedFSimCalibrationRequest]: """Describes a given moment in terms of a Floquet characterization request. Args: moment: Moment to characterize. options: Options that are applied to each characterized gate within a moment. gates_translator: Function that translates a gate to a supported FSimGate which will undergo characterization. Defaults to sqrt_iswap_gates_translator. canonicalize_pairs: Whether to sort each of the qubit pair so that the first qubit is always lower than the second. sort_pairs: Whether to sort all the qutibt pairs extracted from the moment which will undergo characterization. Returns: Instance of FloquetPhasedFSimCalibrationRequest that characterizes a given moment, or None when it is an empty, measurement or single-qubit gates only moment. Raises: IncompatibleMomentError when a moment contains operations other than the operations matched by gates_translator, or it mixes a single qubit and two qubit gates. """ measurement = False single_qubit = False gate: Optional[FSimGate] = None pairs = [] for op in moment: if not isinstance(op, GateOperation): raise IncompatibleMomentError( 'Moment contains operation different than GateOperation') if isinstance(op.gate, MeasurementGate): measurement = True elif isinstance(op.gate, SingleQubitGate): single_qubit = True else: translated_gate = gates_translator(op.gate) if translated_gate is None: raise IncompatibleMomentError( f'Moment {moment} contains unsupported non-single qubit operation {op}' ) elif gate is not None and gate != translated_gate: raise IncompatibleMomentError( f'Moment {moment} contains operations resolved to two different gates {gate} ' f'and {translated_gate}') else: gate = translated_gate pair = cast( Tuple[Qid, Qid], tuple(sorted(op.qubits) if canonicalize_pairs else op.qubits)) pairs.append(pair) if gate is None: # Either empty, single-qubit or measurement moment. return None if gate is not None and (measurement or single_qubit): raise IncompatibleMomentError( f'Moment contains mixed two-qubit operations and ' f'single-qubit operations or measurement operations.') return FloquetPhasedFSimCalibrationRequest( pairs=tuple(sorted(pairs) if sort_pairs else pairs), gate=gate, options=options)