From ed515af11fc9d91613f442a7a3e3f6b5aebbfc1c Mon Sep 17 00:00:00 2001 From: Andrea Date: Sat, 13 Jul 2024 19:55:49 +0400 Subject: [PATCH 1/9] feat: Start implementation of iSWAP protocols --- .../two_qubit_interaction/chevron/chevron.py | 41 ++++++++++++++----- .../chevron/chevron_signal.py | 3 +- .../two_qubit_interaction/chevron/utils.py | 25 ++++++----- .../two_qubit_interaction/cz_virtualz.py | 38 +++++++++-------- src/qibocal/update.py | 22 ++++++++-- tests/test_update.py | 24 ++++++++--- 6 files changed, 105 insertions(+), 48 deletions(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/chevron/chevron.py b/src/qibocal/protocols/two_qubit_interaction/chevron/chevron.py index 94ffe52b0..ee87738b2 100644 --- a/src/qibocal/protocols/two_qubit_interaction/chevron/chevron.py +++ b/src/qibocal/protocols/two_qubit_interaction/chevron/chevron.py @@ -42,6 +42,12 @@ class ChevronParameters(Parameters): """Time delay between flux pulses and readout.""" parking: bool = True """Wether to park non interacting qubits or not.""" + native: str = "CZ" + """Two qubit interaction to be calibrated. + + iSWAP and CZ are the possible options. + + """ @property def amplitude_range(self): @@ -68,9 +74,12 @@ class ChevronResults(Results): """CZ angle.""" duration: dict[QubitPairId, int] """Virtual Z phase correction.""" + native: str = "CZ" + """Two qubit interaction to be calibrated. - def __contains__(self, key: QubitPairId): - return super().__contains__(key) | super().__contains__((key[1], key[0])) + iSWAP and CZ are the possible options. + + """ ChevronType = np.dtype( @@ -90,6 +99,12 @@ class ChevronData(Data): native_amplitude: dict[QubitPairId, float] = field(default_factory=dict) """CZ platform amplitude for qubit pair.""" + native: str = "CZ" + """Two qubit interaction to be calibrated. + + iSWAP and CZ are the possible options. + + """ sweetspot: dict[QubitPairId, float] = field(default_factory=dict) """Sweetspot value for high frequency qubit.""" data: dict[QubitPairId, npt.NDArray[ChevronType]] = field(default_factory=dict) @@ -137,8 +152,8 @@ def _aquisition( ChevronData: Acquisition data. """ - # create a DataUnits object to store the results, - data = ChevronData() + # create a DataUnits object to store the results + data = ChevronData(native=params.native) for pair in targets: # order the qubits so that the low frequency one is the first sequence = chevron_sequence( @@ -147,6 +162,7 @@ def _aquisition( duration_max=params.duration_max, parking=params.parking, dt=params.dt, + native=params.native, ) ordered_pair = order_pair(pair, platform) # TODO: move in function to avoid code duplications @@ -216,20 +232,18 @@ def _fit(data: ChevronData) -> ChevronResults: except Exception as e: log.warning(f"Chevron fit failed for pair {pair} due to {e}") - return ChevronResults(amplitude=amplitudes, duration=durations) + return ChevronResults(amplitude=amplitudes, duration=durations, native=data.native) def _plot(data: ChevronData, fit: ChevronResults, target: QubitPairId): """Plot the experiment result for a single pair.""" if isinstance(target, list): target = tuple(target) - # reverse qubit order if not found in data if target not in data.data: target = (target[1], target[0]) pair_data = data[target] - fig = make_subplots( rows=1, cols=2, @@ -278,7 +292,7 @@ def _plot(data: ChevronData, fit: ChevronResults, target: QubitPairId): color="black", symbol="cross", ), - name="CZ estimate", # Change name from the params + name=f"{data.native} estimate", # Change name from the params showlegend=True if measured_qubit == target[0] else False, legendgroup="Voltage", ), @@ -301,7 +315,7 @@ def _plot(data: ChevronData, fit: ChevronResults, target: QubitPairId): fitting_report = table_html( table_dict( target[1], - ["CZ amplitude", "CZ duration", "Bias point"], + [f"{fit.native} amplitude", f"{fit.native} duration", "Bias point"], [ fit.amplitude[target], fit.duration[target], @@ -319,8 +333,13 @@ def _update(results: ChevronResults, platform: Platform, target: QubitPairId): if target not in results.duration: target = (target[1], target[0]) - update.CZ_duration(results.duration[target], platform, target) - update.CZ_amplitude(results.amplitude[target], platform, target) + + getattr(update, f"{results.native}_duration")( + results.duration[target], platform, target + ) + getattr(update, f"{results.native}_amplitude")( + results.amplitude[target], platform, target + ) chevron = Routine(_aquisition, _fit, _plot, _update, two_qubit_gates=True) diff --git a/src/qibocal/protocols/two_qubit_interaction/chevron/chevron_signal.py b/src/qibocal/protocols/two_qubit_interaction/chevron/chevron_signal.py index 54c364348..ccd21bdc1 100644 --- a/src/qibocal/protocols/two_qubit_interaction/chevron/chevron_signal.py +++ b/src/qibocal/protocols/two_qubit_interaction/chevron/chevron_signal.py @@ -90,7 +90,7 @@ def _aquisition( """ # create a DataUnits object to store the results, - data = ChevronSignalData() + data = ChevronSignalData(native=params.native) for pair in targets: # order the qubits so that the low frequency one is the first ordered_pair = order_pair(pair, platform) @@ -100,6 +100,7 @@ def _aquisition( duration_max=params.duration_max, parking=params.parking, dt=params.dt, + native=params.native, ) data.native_amplitude[ordered_pair] = ( diff --git a/src/qibocal/protocols/two_qubit_interaction/chevron/utils.py b/src/qibocal/protocols/two_qubit_interaction/chevron/utils.py index 9fafef6bb..40ea08213 100644 --- a/src/qibocal/protocols/two_qubit_interaction/chevron/utils.py +++ b/src/qibocal/protocols/two_qubit_interaction/chevron/utils.py @@ -18,6 +18,7 @@ def chevron_sequence( pair: QubitPairId, duration_max: int, parking: bool = False, + native: str = "CZ", dt: int = 0, ): """Chevron pulse sequence.""" @@ -25,33 +26,37 @@ def chevron_sequence( sequence = PulseSequence() ordered_pair = order_pair(pair, platform) # initialize in system in 11 state - initialize_lowfreq = platform.create_RX_pulse( - ordered_pair[0], start=0, relative_phase=0 - ) + + if native == "CZ": + initialize_lowfreq = platform.create_RX_pulse( + ordered_pair[0], start=0, relative_phase=0 + ) + sequence.add(initialize_lowfreq) + initialize_highfreq = platform.create_RX_pulse( ordered_pair[1], start=0, relative_phase=0 ) sequence.add(initialize_highfreq) - sequence.add(initialize_lowfreq) - cz, _ = platform.create_CZ_pulse_sequence( + + flux_sequence, _ = getattr(platform, f"create_{native}_pulse_sequence")( qubits=(ordered_pair[1], ordered_pair[0]), start=initialize_highfreq.finish, ) - sequence.add(cz.get_qubit_pulses(ordered_pair[0])) - sequence.add(cz.get_qubit_pulses(ordered_pair[1])) + sequence.add(flux_sequence.get_qubit_pulses(ordered_pair[0])) + sequence.add(flux_sequence.get_qubit_pulses(ordered_pair[1])) delay_measurement = duration_max if platform.couplers: - coupler_pulse = cz.coupler_pulses( + coupler_pulse = flux_sequence.coupler_pulses( platform.pairs[tuple(ordered_pair)].coupler.name ) sequence.add(coupler_pulse) delay_measurement = max(duration_max, coupler_pulse.duration) if parking: - for pulse in cz: + for pulse in flux_sequence: if pulse.qubit not in ordered_pair: pulse.start = COUPLER_PULSE_START pulse.duration = COUPLER_PULSE_DURATION @@ -60,7 +65,7 @@ def chevron_sequence( # add readout measure_lowfreq = platform.create_qubit_readout_pulse( ordered_pair[0], - start=initialize_lowfreq.finish + delay_measurement + dt, + start=initialize_highfreq.finish + delay_measurement + dt, ) measure_highfreq = platform.create_qubit_readout_pulse( ordered_pair[1], diff --git a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py b/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py index fc5a36da4..8e05d2dd7 100644 --- a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py +++ b/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py @@ -23,7 +23,7 @@ @dataclass -class CZVirtualZParameters(Parameters): +class VirtualPhasesParameters(Parameters): """CzVirtualZ runcard inputs.""" theta_start: float @@ -43,7 +43,7 @@ class CZVirtualZParameters(Parameters): @dataclass -class CZVirtualZResults(Results): +class VirtualPhasesResults(Results): """CzVirtualZ outputs when fitting will be done.""" fitted_parameters: dict[tuple[str, QubitId],] @@ -66,14 +66,14 @@ def __contains__(self, key: QubitPairId): ] -CZVirtualZType = np.dtype([("target", np.float64), ("control", np.float64)]) +VirtualPhasesType = np.dtype([("target", np.float64), ("control", np.float64)]) @dataclass -class CZVirtualZData(Data): - """CZVirtualZ data.""" +class VirtualPhasesData(Data): + """VirtualPhases data.""" - data: dict[tuple, npt.NDArray[CZVirtualZType]] = field(default_factory=dict) + data: dict[tuple, npt.NDArray[VirtualPhasesType]] = field(default_factory=dict) thetas: list = field(default_factory=list) vphases: dict[QubitPairId, dict[QubitId, float]] = field(default_factory=dict) amplitudes: dict[tuple[QubitId, QubitId], float] = field(default_factory=dict) @@ -172,12 +172,12 @@ def create_sequence( def _acquisition( - params: CZVirtualZParameters, + params: VirtualPhasesParameters, platform: Platform, targets: list[QubitPairId], -) -> CZVirtualZData: +) -> VirtualPhasesData: r""" - Acquisition for CZVirtualZ. + Acquisition for VirtualPhases. Check the two-qubit landscape created by a flux pulse of a given duration and amplitude. @@ -194,7 +194,7 @@ def _acquisition( """ theta_absolute = np.arange(params.theta_start, params.theta_end, params.theta_step) - data = CZVirtualZData(thetas=theta_absolute.tolist()) + data = VirtualPhasesData(thetas=theta_absolute.tolist()) for pair in targets: # order the qubits so that the low frequency one is the first ord_pair = order_pair(pair, platform) @@ -248,7 +248,7 @@ def _acquisition( result_control = results[control_q].probability(1) data.register_qubit( - CZVirtualZType, + VirtualPhasesType, (target_q, control_q, setup), dict( target=result_target, @@ -265,8 +265,8 @@ def fit_function(x, amplitude, offset, phase): def _fit( - data: CZVirtualZData, -) -> CZVirtualZResults: + data: VirtualPhasesData, +) -> VirtualPhasesResults: r"""Fitting routine for the experiment. The used model is @@ -332,7 +332,7 @@ def _fit( except KeyError: pass # exception covered above - return CZVirtualZResults( + return VirtualPhasesResults( cz_angle=cz_angle, virtual_phase=virtual_phase, fitted_parameters=fitted_parameters, @@ -341,8 +341,8 @@ def _fit( # TODO: remove str -def _plot(data: CZVirtualZData, fit: CZVirtualZResults, target: QubitPairId): - """Plot routine for CZVirtualZ.""" +def _plot(data: VirtualPhasesData, fit: VirtualPhasesResults, target: QubitPairId): + """Plot routine for VirtualPhases.""" pair_data = data[target] qubits = next(iter(pair_data))[:2] fig1 = make_subplots( @@ -460,12 +460,14 @@ def _plot(data: CZVirtualZData, fit: CZVirtualZResults, target: QubitPairId): return [fig1, fig2], "".join(fitting_report) # target and control qubit -def _update(results: CZVirtualZResults, platform: Platform, target: QubitPairId): +def _update(results: VirtualPhasesResults, platform: Platform, target: QubitPairId): # FIXME: quick fix for qubit order qubit_pair = tuple(sorted(target)) target = tuple(sorted(target)) update.virtual_phases(results.virtual_phase[target], platform, target) -cz_virtualz = Routine(_acquisition, _fit, _plot, _update, two_qubit_gates=True) +correct_virtual_phases = Routine( + _acquisition, _fit, _plot, _update, two_qubit_gates=True +) """CZ virtual Z correction routine.""" diff --git a/src/qibocal/update.py b/src/qibocal/update.py index 9e4d1c730..376cad2c3 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -106,11 +106,13 @@ def assignment_fidelity(fidelity: float, platform: Platform, qubit: QubitId): platform.qubits[qubit].assignment_fidelity = float(fidelity) -def virtual_phases(phases: dict[QubitId, float], platform: Platform, pair: QubitPairId): +def virtual_phases( + phases: dict[QubitId, float], native: str, platform: Platform, pair: QubitPairId +): """Update virtual phases for given qubits in pair in results.""" virtual_z_pulses = { pulse.qubit.name: pulse - for pulse in platform.pairs[pair].native_gates.CZ.pulses + for pulse in getattr(platform.pairs[pair].native_gates, native).pulses if isinstance(pulse, VirtualZPulse) } for qubit_id, phase in phases.items(): @@ -120,7 +122,7 @@ def virtual_phases(phases: dict[QubitId, float], platform: Platform, pair: Qubit virtual_z_pulses[qubit_id] = VirtualZPulse( phase=phase, qubit=platform.qubits[qubit_id] ) - platform.pairs[pair].native_gates.CZ.pulses.append( + getattr(platform.pairs[pair].native_gates, native).pulses.append( virtual_z_pulses[qubit_id] ) @@ -139,6 +141,20 @@ def CZ_amplitude(amp: float, platform: Platform, pair: QubitPairId): pulse.amplitude = float(amp) +def iSWAP_duration(duration: int, platform: Platform, pair: QubitPairId): + """Update iSWAP_duration duration for specific pair.""" + for pulse in platform.pairs[pair].native_gates.iSWAP.pulses: + if pulse.qubit.name == pair[1]: + pulse.duration = int(duration) + + +def iSWAP_amplitude(amp: float, platform: Platform, pair: QubitPairId): + """Update iSWAP_duration amplitude for specific pair.""" + for pulse in platform.pairs[pair].native_gates.iSWAP.pulses: + if pulse.qubit.name == pair[1]: + pulse.amplitude = float(amp) + + def t1(t1: int, platform: Platform, qubit: QubitId): """Update t1 value in platform for specific qubit.""" if isinstance(t1, Iterable): diff --git a/tests/test_update.py b/tests/test_update.py index 6083d2d85..220584215 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -90,14 +90,15 @@ def test_classification_update(qubit): assert qubit.assignment_fidelity == RANDOM_FLOAT +@pytest.mark.parametrize("native", ["CZ", "iSWAP"]) @pytest.mark.parametrize("pair", PAIRS) -def test_virtual_phases_update(pair): - if PLATFORM.pairs[pair].native_gates.CZ is not None: +def test_virtual_phases_update(pair, native): + if getattr(PLATFORM.pairs[pair].native_gates, native) is not None: results = {qubit: RANDOM_FLOAT for qubit in pair} - update.virtual_phases(results, PLATFORM, pair) - if PLATFORM.pairs[pair].native_gates.CZ is not None: - for pulse in PLATFORM.pairs[pair].native_gates.CZ.pulses: + update.virtual_phases(results, native, PLATFORM, pair) + if getattr(PLATFORM.pairs[pair].native_gates, native) is not None: + for pulse in getattr(PLATFORM.pairs[pair].native_gates, native).pulses: if isinstance(pulse, VirtualZPulse): assert pulse == VirtualZPulse( qubit=pulse.qubit, phase=results[pulse.qubit.name] @@ -117,6 +118,19 @@ def test_CZ_params_update(pair): assert pulse.amplitude == RANDOM_FLOAT +@pytest.mark.parametrize("pair", PAIRS) +def test_iSWAP_params_update(pair): + if hasattr(PLATFORM.pairs[pair].native_gates, "iSWAP"): + if PLATFORM.pairs[pair].native_gates.iSWAP is not None: + update.iSWAP_amplitude(RANDOM_FLOAT, PLATFORM, pair) + update.iSWAP_duration(RANDOM_INT, PLATFORM, pair) + + for pulse in PLATFORM.pairs[pair].native_gates.iSWAP.pulses: + if pulse.qubit.name == pair[1]: + assert pulse.duration == RANDOM_INT + assert pulse.amplitude == RANDOM_FLOAT + + @pytest.mark.parametrize("qubit", QUBITS) def drive_duration_update(qubit): update.readout_amplitude(RANDOM_INT, PLATFORM, qubit.name) From 85247c302a3007f98b2402e67a051304b36caf96 Mon Sep 17 00:00:00 2001 From: Andrea Date: Sat, 13 Jul 2024 21:57:28 +0400 Subject: [PATCH 2/9] feat: Implement iSWAP for former cz_virtualz protocols --- src/qibocal/protocols/__init__.py | 8 +- .../two_qubit_interaction/__init__.py | 4 +- .../{cz_virtualz.py => virtual_z_phases.py} | 91 +++++++++++-------- ...z_signal.py => virtual_z_phases_signal.py} | 47 +++++----- tests/runcards/protocols.yml | 4 +- 5 files changed, 87 insertions(+), 67 deletions(-) rename src/qibocal/protocols/two_qubit_interaction/{cz_virtualz.py => virtual_z_phases.py} (86%) rename src/qibocal/protocols/two_qubit_interaction/{cz_virtualz_signal.py => virtual_z_phases_signal.py} (78%) diff --git a/src/qibocal/protocols/__init__.py b/src/qibocal/protocols/__init__.py index 19345fa05..3adf59db0 100644 --- a/src/qibocal/protocols/__init__.py +++ b/src/qibocal/protocols/__init__.py @@ -68,8 +68,8 @@ chevron_signal, chsh_circuits, chsh_pulses, - cz_virtualz, - cz_virtualz_signal, + correct_virtual_z_phases, + correct_virtual_z_phases_signal, ) from .two_qubit_state_tomography import two_qubit_state_tomography @@ -133,8 +133,8 @@ "chevron_signal", "chsh_circuits", "chsh_pulses", - "cz_virtualz", - "cz_virtualz_signal", + "correct_virtual_z_phases", + "correct_virtual_z_phases_signal", "state_tomography", "allxy_resonator_depletion_tuning", "two_qubit_state_tomography", diff --git a/src/qibocal/protocols/two_qubit_interaction/__init__.py b/src/qibocal/protocols/two_qubit_interaction/__init__.py index 9f8f79700..1b8c583bd 100644 --- a/src/qibocal/protocols/two_qubit_interaction/__init__.py +++ b/src/qibocal/protocols/two_qubit_interaction/__init__.py @@ -1,4 +1,4 @@ from .chevron import chevron, chevron_signal from .chsh import chsh_circuits, chsh_pulses -from .cz_virtualz import cz_virtualz -from .cz_virtualz_signal import cz_virtualz_signal +from .virtual_z_phases import correct_virtual_z_phases +from .virtual_z_phases_signal import correct_virtual_z_phases_signal diff --git a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py similarity index 86% rename from src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py rename to src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py index 8e05d2dd7..283ae0fef 100644 --- a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py @@ -23,7 +23,7 @@ @dataclass -class VirtualPhasesParameters(Parameters): +class VirtualZPhasesParameters(Parameters): """CzVirtualZ runcard inputs.""" theta_start: float @@ -32,6 +32,12 @@ class VirtualPhasesParameters(Parameters): """Final angle for the low frequency qubit measurement in radians.""" theta_step: float """Step size for the theta sweep in radians.""" + native: str = "CZ" + """Two qubit interaction to be calibrated. + + iSWAP and CZ are the possible options. + + """ flux_pulse_amplitude: Optional[float] = None """Amplitude of flux pulse implementing CZ.""" flux_pulse_duration: Optional[float] = None @@ -43,13 +49,15 @@ class VirtualPhasesParameters(Parameters): @dataclass -class VirtualPhasesResults(Results): +class VirtualZPhasesResults(Results): """CzVirtualZ outputs when fitting will be done.""" fitted_parameters: dict[tuple[str, QubitId],] """Fitted parameters""" - cz_angle: dict[QubitPairId, float] - """CZ angle.""" + native: str + """Native two qubit gate.""" + angle: dict[QubitPairId, float] + """Native angle.""" virtual_phase: dict[QubitPairId, dict[QubitId, float]] """Virtual Z phase correction.""" leakage: dict[QubitPairId, dict[QubitId, float]] @@ -60,20 +68,20 @@ def __contains__(self, key: QubitPairId): While key is a QubitPairId both chsh and chsh_mitigated contain an additional key which represents the basis chosen. """ - return key in [ (target, control) for target, control, _ in self.fitted_parameters ] -VirtualPhasesType = np.dtype([("target", np.float64), ("control", np.float64)]) +VirtualZPhasesType = np.dtype([("target", np.float64), ("control", np.float64)]) @dataclass -class VirtualPhasesData(Data): - """VirtualPhases data.""" +class VirtualZPhasesData(Data): + """VirtualZPhases data.""" - data: dict[tuple, npt.NDArray[VirtualPhasesType]] = field(default_factory=dict) + data: dict[tuple, npt.NDArray[VirtualZPhasesType]] = field(default_factory=dict) + native: str = "CZ" thetas: list = field(default_factory=list) vphases: dict[QubitPairId, dict[QubitId, float]] = field(default_factory=dict) amplitudes: dict[tuple[QubitId, QubitId], float] = field(default_factory=dict) @@ -93,6 +101,7 @@ def create_sequence( target_qubit: QubitId, control_qubit: QubitId, ordered_pair: list[QubitId, QubitId], + native: str, parking: bool, dt: float, amplitude: float = None, @@ -113,25 +122,27 @@ def create_sequence( ) RX_pulse_start = platform.create_RX_pulse(control_qubit, start=0, relative_phase=0) - cz, virtual_z_phase = platform.create_CZ_pulse_sequence( + flux_sequence, virtual_z_phase = getattr( + platform, f"create_{native}_pulse_sequence" + )( (ordered_pair[1], ordered_pair[0]), start=max(Y90_pulse.finish, RX_pulse_start.finish), ) if amplitude is not None: - cz.get_qubit_pulses(ordered_pair[1])[0].amplitude = amplitude + flux_sequence.get_qubit_pulses(ordered_pair[1])[0].amplitude = amplitude if duration is not None: - cz.get_qubit_pulses(ordered_pair[1])[0].duration = duration + flux_sequence.get_qubit_pulses(ordered_pair[1])[0].duration = duration theta_pulse = platform.create_RX90_pulse( target_qubit, - start=cz.finish + dt, + start=flux_sequence.finish + dt, relative_phase=virtual_z_phase[target_qubit], ) RX_pulse_end = platform.create_RX_pulse( control_qubit, - start=cz.finish + dt, + start=flux_sequence.finish + dt, relative_phase=virtual_z_phase[control_qubit], ) measure_target = platform.create_qubit_readout_pulse( @@ -143,8 +154,8 @@ def create_sequence( sequence.add( Y90_pulse, - cz.get_qubit_pulses(ordered_pair[1]), - cz.cf_pulses, + flux_sequence.get_qubit_pulses(ordered_pair[1]), + flux_sequence.cf_pulses, theta_pulse, measure_target, measure_control, @@ -157,7 +168,7 @@ def create_sequence( ) if parking: - for pulse in cz: + for pulse in flux_sequence: if pulse.qubit not in ordered_pair: pulse.duration = theta_pulse.finish sequence.add(pulse) @@ -166,18 +177,18 @@ def create_sequence( sequence, virtual_z_phase, theta_pulse, - cz.get_qubit_pulses(ordered_pair[1])[0].amplitude, - cz.get_qubit_pulses(ordered_pair[1])[0].duration, + flux_sequence.get_qubit_pulses(ordered_pair[1])[0].amplitude, + flux_sequence.get_qubit_pulses(ordered_pair[1])[0].duration, ) def _acquisition( - params: VirtualPhasesParameters, + params: VirtualZPhasesParameters, platform: Platform, targets: list[QubitPairId], -) -> VirtualPhasesData: +) -> VirtualZPhasesData: r""" - Acquisition for VirtualPhases. + Acquisition for VirtualZPhases. Check the two-qubit landscape created by a flux pulse of a given duration and amplitude. @@ -194,7 +205,7 @@ def _acquisition( """ theta_absolute = np.arange(params.theta_start, params.theta_end, params.theta_step) - data = VirtualPhasesData(thetas=theta_absolute.tolist()) + data = VirtualZPhasesData(thetas=theta_absolute.tolist(), native=params.native) for pair in targets: # order the qubits so that the low frequency one is the first ord_pair = order_pair(pair, platform) @@ -216,6 +227,7 @@ def _acquisition( target_q, control_q, ord_pair, + params.native, params.dt, params.parking, params.flux_pulse_amplitude, @@ -248,7 +260,7 @@ def _acquisition( result_control = results[control_q].probability(1) data.register_qubit( - VirtualPhasesType, + VirtualZPhasesType, (target_q, control_q, setup), dict( target=result_target, @@ -265,8 +277,8 @@ def fit_function(x, amplitude, offset, phase): def _fit( - data: VirtualPhasesData, -) -> VirtualPhasesResults: + data: VirtualZPhasesData, +) -> VirtualZPhasesResults: r"""Fitting routine for the experiment. The used model is @@ -278,7 +290,7 @@ def _fit( fitted_parameters = {} pairs = data.pairs virtual_phase = {} - cz_angle = {} + angle = {} leakage = {} for pair in pairs: virtual_phase[pair] = {} @@ -311,7 +323,7 @@ def _fit( pair, list(pair)[::-1], ): - cz_angle[target_q, control_q] = abs( + angle[target_q, control_q] = abs( fitted_parameters[target_q, control_q, "X"][2] - fitted_parameters[target_q, control_q, "I"][2] ) @@ -332,8 +344,9 @@ def _fit( except KeyError: pass # exception covered above - return VirtualPhasesResults( - cz_angle=cz_angle, + return VirtualZPhasesResults( + native=data.native, + angle=angle, virtual_phase=virtual_phase, fitted_parameters=fitted_parameters, leakage=leakage, @@ -341,8 +354,8 @@ def _fit( # TODO: remove str -def _plot(data: VirtualPhasesData, fit: VirtualPhasesResults, target: QubitPairId): - """Plot routine for VirtualPhases.""" +def _plot(data: VirtualZPhasesData, fit: VirtualZPhasesResults, target: QubitPairId): + """Plot routine for VirtualZPhases.""" pair_data = data[target] qubits = next(iter(pair_data))[:2] fig1 = make_subplots( @@ -411,12 +424,12 @@ def _plot(data: VirtualPhasesData, fit: VirtualPhasesResults, target: QubitPairI table_dict( [target_q, target_q, control_q], [ - "CZ angle [rad]", + f"{fit.native} angle [rad]", "Virtual Z phase [rad]", "Leakage [a.u.]", ], [ - np.round(fit.cz_angle[target_q, control_q], 4), + np.round(fit.angle[target_q, control_q], 4), np.round( fit.virtual_phase[tuple(sorted(target))][target_q], 4 ), @@ -460,14 +473,16 @@ def _plot(data: VirtualPhasesData, fit: VirtualPhasesResults, target: QubitPairI return [fig1, fig2], "".join(fitting_report) # target and control qubit -def _update(results: VirtualPhasesResults, platform: Platform, target: QubitPairId): +def _update(results: VirtualZPhasesResults, platform: Platform, target: QubitPairId): # FIXME: quick fix for qubit order qubit_pair = tuple(sorted(target)) target = tuple(sorted(target)) - update.virtual_phases(results.virtual_phase[target], platform, target) + update.virtual_phases( + results.virtual_phase[target], results.native, platform, target + ) -correct_virtual_phases = Routine( +correct_virtual_z_phases = Routine( _acquisition, _fit, _plot, _update, two_qubit_gates=True ) -"""CZ virtual Z correction routine.""" +"""Virtual phases correction protocol.""" diff --git a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz_signal.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py similarity index 78% rename from src/qibocal/protocols/two_qubit_interaction/cz_virtualz_signal.py rename to src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py index 83b4a1b55..133eb480a 100644 --- a/src/qibocal/protocols/two_qubit_interaction/cz_virtualz_signal.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py @@ -10,43 +10,43 @@ from qibocal.auto.operation import Routine -from .cz_virtualz import ( - CZVirtualZData, - CZVirtualZParameters, - CZVirtualZResults, - CZVirtualZType, +from .utils import order_pair +from .virtual_z_phases import ( + VirtualZPhasesData, + VirtualZPhasesParameters, + VirtualZPhasesResults, + VirtualZPhasesType, _fit, ) -from .cz_virtualz import _plot as _plot_prob -from .cz_virtualz import _update, create_sequence -from .utils import order_pair +from .virtual_z_phases import _plot as _plot_prob +from .virtual_z_phases import _update, create_sequence @dataclass -class CZVirtualZSignalParameters(CZVirtualZParameters): +class VirtualZPhasesSignalParameters(VirtualZPhasesParameters): """CzVirtualZ runcard inputs.""" @dataclass -class CZVirtualZSignalResults(CZVirtualZResults): +class VirtualZPhasesSignalResults(VirtualZPhasesResults): """CzVirtualZ outputs when fitting will be done.""" -CZVirtualZType = np.dtype([("target", np.float64), ("control", np.float64)]) +VirtualZPhasesType = np.dtype([("target", np.float64), ("control", np.float64)]) @dataclass -class CZVirtualZSignalData(CZVirtualZData): - """CZVirtualZ data.""" +class VirtualZPhasesSignalData(VirtualZPhasesData): + """VirtualZPhases data.""" def _acquisition( - params: CZVirtualZSignalParameters, + params: VirtualZPhasesSignalParameters, platform: Platform, targets: list[QubitPairId], -) -> CZVirtualZSignalData: +) -> VirtualZPhasesSignalData: r""" - Acquisition for CZVirtualZ. See https://arxiv.org/pdf/1904.06560.pdf + Acquisition for VirtualZPhases. See https://arxiv.org/pdf/1904.06560.pdf Check the two-qubit landscape created by a flux pulse of a given duration and amplitude. @@ -63,7 +63,7 @@ def _acquisition( """ theta_absolute = np.arange(params.theta_start, params.theta_end, params.theta_step) - data = CZVirtualZData(thetas=theta_absolute.tolist()) + data = VirtualZPhasesData(native=params.native, thetas=theta_absolute.tolist()) for pair in targets: # order the qubits so that the low frequency one is the first ord_pair = order_pair(pair, platform) @@ -85,6 +85,7 @@ def _acquisition( target_q, control_q, ord_pair, + params.native, params.dt, params.parking, params.flux_pulse_amplitude, @@ -117,7 +118,7 @@ def _acquisition( result_control = results[control_q].magnitude data.register_qubit( - CZVirtualZType, + VirtualZPhasesType, (target_q, control_q, setup), dict( target=result_target, @@ -128,9 +129,11 @@ def _acquisition( def _plot( - data: CZVirtualZSignalData, fit: CZVirtualZSignalResults, target: QubitPairId + data: VirtualZPhasesSignalData, + fit: VirtualZPhasesSignalResults, + target: QubitPairId, ): - """Plot routine for CZVirtualZ.""" + """Plot routine for VirtualZPhases.""" figs, fitting_report = _plot_prob(data, fit, target) for fig in figs: @@ -141,5 +144,7 @@ def _plot( return figs, fitting_report -cz_virtualz_signal = Routine(_acquisition, _fit, _plot, _update, two_qubit_gates=True) +correct_virtual_z_phases_signal = Routine( + _acquisition, _fit, _plot, _update, two_qubit_gates=True +) """CZ virtual Z correction routine.""" diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c3ef6a6c2..859de43ea 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -717,7 +717,7 @@ actions: parking: True - id: cz - operation: cz_virtualz + operation: correct_virtual_z_phases targets: [[0, 2],[1,2]] parameters: theta_start: 0 @@ -729,7 +729,7 @@ actions: parking: True - id: cz signal - operation: cz_virtualz_signal + operation: correct_virtual_z_phases_signal targets: [[0, 2],[1,2]] parameters: theta_start: 0 From cb3a8aed0f4b8a2086e99b86d1b9fb5ec0b16d7a Mon Sep 17 00:00:00 2001 From: Andrea Date: Sat, 13 Jul 2024 22:16:48 +0400 Subject: [PATCH 3/9] refactor: Update update function --- .../two_qubit_interaction/virtual_z_phases.py | 15 +++++ tests/runcards/protocols.yml | 66 +++++++++++++++++-- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py index 283ae0fef..39d9f25f7 100644 --- a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py @@ -62,12 +62,17 @@ class VirtualZPhasesResults(Results): """Virtual Z phase correction.""" leakage: dict[QubitPairId, dict[QubitId, float]] """Leakage on control qubit for pair.""" + flux_pulse_amplitude: dict[QubitPairId, float] + """Amplitude of flux pulse implementing CZ.""" + flux_pulse_duration: dict[QubitPairId, int] + """Duration of flux pulse implementing CZ.""" def __contains__(self, key: QubitPairId): """Check if key is in class. While key is a QubitPairId both chsh and chsh_mitigated contain an additional key which represents the basis chosen. """ + # TODO: fix this (failing only for qq report) return key in [ (target, control) for target, control, _ in self.fitted_parameters ] @@ -290,6 +295,8 @@ def _fit( fitted_parameters = {} pairs = data.pairs virtual_phase = {} + amplitudes = {} + durations = {} angle = {} leakage = {} for pair in pairs: @@ -346,6 +353,8 @@ def _fit( return VirtualZPhasesResults( native=data.native, + flux_pulse_amplitude=data.amplitudes, + flux_pulse_duration=data.durations, angle=angle, virtual_phase=virtual_phase, fitted_parameters=fitted_parameters, @@ -480,6 +489,12 @@ def _update(results: VirtualZPhasesResults, platform: Platform, target: QubitPai update.virtual_phases( results.virtual_phase[target], results.native, platform, target ) + getattr(update, f"{results.native}_duration")( + results.flux_pulse_duration[target], platform, target + ) + getattr(update, f"{results.native}_amplitude")( + results.flux_pulse_amplitude[target], platform, target + ) correct_virtual_z_phases = Routine( diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 859de43ea..70e299be6 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -690,7 +690,7 @@ actions: niter: 5 nshots: 50 - - id: chevron id + - id: chevron cz operation: chevron targets: [[0, 2],[1,2]] parameters: @@ -701,9 +701,10 @@ actions: duration_max: 50 duration_step: 10 nshots: 10 + native: CZ parking: True - - id: chevron id signal + - id: chevron cz signal operation: chevron_signal targets: [[0, 2],[1,2]] parameters: @@ -713,10 +714,65 @@ actions: duration_min: 10 duration_max: 50 duration_step: 1 + native: CZ nshots: 1000 parking: True - - id: cz + - id: chevron iSWAP + operation: chevron + targets: [[0, 2],[1,2]] + parameters: + amplitude_min_factor: 0.1 + amplitude_max_factor: 0.6 + amplitude_step_factor: 0.01 + duration_min: 10 + duration_max: 50 + duration_step: 10 + nshots: 10 + native: iSWAP + parking: True + + - id: chevron iSWAP signal + operation: chevron_signal + targets: [[0, 2],[1,2]] + parameters: + amplitude_min_factor: 0.1 + amplitude_max_factor: 0.6 + amplitude_step_factor: 0.01 + duration_min: 10 + duration_max: 50 + duration_step: 1 + native: iSWAP + nshots: 1000 + parking: True + + - id: iswap_virtual_phase + operation: correct_virtual_z_phases + targets: [[0, 2],[1,2]] + parameters: + theta_start: 0 + theta_end: 180 + theta_step: 10 + flux_pulse_amplitude: 0.5 + flux_pulse_duration: 10 + dt: 0 + native: iSWAP + parking: True + + - id: iswap virtual signal + operation: correct_virtual_z_phases_signal + targets: [[0, 2],[1,2]] + parameters: + theta_start: 0 + theta_end: 180 + theta_step: 10 + flux_pulse_amplitude: 0.5 + flux_pulse_duration: 10 + native: iSWAP + dt: 0 + parking: True + + - id: cz_virtual_phase operation: correct_virtual_z_phases targets: [[0, 2],[1,2]] parameters: @@ -726,9 +782,10 @@ actions: flux_pulse_amplitude: 0.5 flux_pulse_duration: 10 dt: 0 + native: CZ parking: True - - id: cz signal + - id: cz virtual signal operation: correct_virtual_z_phases_signal targets: [[0, 2],[1,2]] parameters: @@ -737,6 +794,7 @@ actions: theta_step: 10 flux_pulse_amplitude: 0.5 flux_pulse_duration: 10 + native: CZ dt: 0 parking: True From e646f448dcf0afff14f69061abc1d3f11681bba5 Mon Sep 17 00:00:00 2001 From: Andrea Date: Sat, 13 Jul 2024 22:49:00 +0400 Subject: [PATCH 4/9] fix: Fix test by selecting specific pairs --- tests/test_update.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_update.py b/tests/test_update.py index 220584215..b183cc232 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -16,7 +16,8 @@ PLATFORM = create_platform("dummy") QUBITS = list(PLATFORM.qubits.values()) -PAIRS = list(PLATFORM.pairs) +# TODO: fix error in parameters.json for dummy +PAIRS = [(1, 2)] RANDOM_FLOAT = random.random() RANDOM_INT = random.randint(0, 10) RANDOM_ARRAY = np.random.rand(10) From cb8f5b87d75d6d6449a182d2498e14ba40f95ce1 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Wed, 17 Jul 2024 15:50:00 +0400 Subject: [PATCH 5/9] refactor: Fix docstrings with Juan suggestions Co-authored-by: Juan Cereijo --- .../two_qubit_interaction/virtual_z_phases_signal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py index 133eb480a..2b3f4419e 100644 --- a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases_signal.py @@ -24,12 +24,12 @@ @dataclass class VirtualZPhasesSignalParameters(VirtualZPhasesParameters): - """CzVirtualZ runcard inputs.""" + """VirtualZ runcard inputs.""" @dataclass class VirtualZPhasesSignalResults(VirtualZPhasesResults): - """CzVirtualZ outputs when fitting will be done.""" + """VirtualZ outputs when fitting will be done.""" VirtualZPhasesType = np.dtype([("target", np.float64), ("control", np.float64)]) @@ -147,4 +147,4 @@ def _plot( correct_virtual_z_phases_signal = Routine( _acquisition, _fit, _plot, _update, two_qubit_gates=True ) -"""CZ virtual Z correction routine.""" +"""Virtual Z correction routine.""" From e4ef2c033fd6d528cb416a08cd3ed93bd05eb1c0 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Wed, 17 Jul 2024 15:50:17 +0400 Subject: [PATCH 6/9] Update src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py Co-authored-by: Juan Cereijo --- src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py index 39d9f25f7..dd49feaf5 100644 --- a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py @@ -50,7 +50,7 @@ class VirtualZPhasesParameters(Parameters): @dataclass class VirtualZPhasesResults(Results): - """CzVirtualZ outputs when fitting will be done.""" + """VirtualZ outputs when fitting will be done.""" fitted_parameters: dict[tuple[str, QubitId],] """Fitted parameters""" From c5a04d24e8623cff59b0da8f403355f616b29ac0 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Wed, 17 Jul 2024 15:50:24 +0400 Subject: [PATCH 7/9] Update src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py Co-authored-by: Juan Cereijo --- src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py index dd49feaf5..44291be9e 100644 --- a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py @@ -24,7 +24,7 @@ @dataclass class VirtualZPhasesParameters(Parameters): - """CzVirtualZ runcard inputs.""" + """VirtualZ runcard inputs.""" theta_start: float """Initial angle for the low frequency qubit measurement in radians.""" From 4ca385eb5b0ed340260bfe645709158cd4c27a50 Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 12 Aug 2024 09:20:16 +0400 Subject: [PATCH 8/9] fix: Lint problems --- src/qibocal/protocols/two_qubit_interaction/cz_sweep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py b/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py index 7c2f0a51e..d7f6171b1 100644 --- a/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py +++ b/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py @@ -18,8 +18,8 @@ from qibocal.config import log from qibocal.protocols.utils import table_dict, table_html -from .cz_virtualz import create_sequence, fit_function from .utils import order_pair +from .virtual_z_phases import create_sequence, fit_function @dataclass @@ -462,7 +462,7 @@ def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): def _update(results: CZSweepResults, platform: Platform, target: QubitPairId): # FIXME: quick fix for qubit order target = tuple(sorted(target)) - update.virtual_phases(results.best_virtual_phase[target], platform, target) + update.virtual_phases(results.best_virtual_phase[target], "CZ", platform, target) update.CZ_duration(results.best_dur[target], platform, target) update.CZ_amplitude(results.best_amp[target], platform, target) From ef3089ee15b60de689113a59121aa08321235314 Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 12 Aug 2024 10:02:02 +0400 Subject: [PATCH 9/9] feat: Generalize code CZ sweep code to iSWAP --- src/qibocal/protocols/__init__.py | 4 +- .../two_qubit_interaction/__init__.py | 2 +- .../{cz_sweep.py => optimize.py} | 110 +++++++++++------- .../two_qubit_interaction/virtual_z_phases.py | 2 - tests/runcards/protocols.yml | 21 +++- 5 files changed, 92 insertions(+), 47 deletions(-) rename src/qibocal/protocols/two_qubit_interaction/{cz_sweep.py => optimize.py} (84%) diff --git a/src/qibocal/protocols/__init__.py b/src/qibocal/protocols/__init__.py index 96d8dc90c..a46fb5c07 100644 --- a/src/qibocal/protocols/__init__.py +++ b/src/qibocal/protocols/__init__.py @@ -70,7 +70,7 @@ chsh_pulses, correct_virtual_z_phases, correct_virtual_z_phases_signal, - cz_sweep, + optimize_two_qubit_gate, ) from .two_qubit_state_tomography import two_qubit_state_tomography @@ -145,5 +145,5 @@ "rabi_length_frequency", "rabi_length_frequency_signal", "standard_rb_2q", - "cz_sweep", + "optimize_two_qubit_gate", ] diff --git a/src/qibocal/protocols/two_qubit_interaction/__init__.py b/src/qibocal/protocols/two_qubit_interaction/__init__.py index fb6aa7b7c..e45930b33 100644 --- a/src/qibocal/protocols/two_qubit_interaction/__init__.py +++ b/src/qibocal/protocols/two_qubit_interaction/__init__.py @@ -1,5 +1,5 @@ from .chevron import chevron, chevron_signal from .chsh import chsh_circuits, chsh_pulses -from .cz_sweep import cz_sweep +from .optimize import optimize_two_qubit_gate from .virtual_z_phases import correct_virtual_z_phases from .virtual_z_phases_signal import correct_virtual_z_phases_signal diff --git a/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py b/src/qibocal/protocols/two_qubit_interaction/optimize.py similarity index 84% rename from src/qibocal/protocols/two_qubit_interaction/cz_sweep.py rename to src/qibocal/protocols/two_qubit_interaction/optimize.py index d7f6171b1..020eecb66 100644 --- a/src/qibocal/protocols/two_qubit_interaction/cz_sweep.py +++ b/src/qibocal/protocols/two_qubit_interaction/optimize.py @@ -1,4 +1,4 @@ -"""CZ virtual correction experiment for two qubit gates, tune landscape.""" +"""virtual correction experiment for two qubit gates, tune landscape.""" from dataclasses import dataclass, field from typing import Optional @@ -23,8 +23,8 @@ @dataclass -class CZSweepParameters(Parameters): - """CZSweep runcard inputs.""" +class OptimizeTwoQubitGateParameters(Parameters): + """OptimizeTwoQubitGate runcard inputs.""" theta_start: float """Initial angle for the low frequency qubit measurement in radians.""" @@ -48,26 +48,34 @@ class CZSweepParameters(Parameters): """Time delay between flux pulses and readout.""" parking: bool = True """Wether to park non interacting qubits or not.""" + native: str = "CZ" + """Two qubit interaction to be calibrated. + + iSWAP and CZ are the possible options. + + """ @dataclass -class CZSweepResults(Results): +class OptimizeTwoQubitGateResults(Results): """CzVirtualZ outputs when fitting will be done.""" fitted_parameters: dict[tuple[str, QubitId, float], list] """Fitted parameters""" - cz_angles: dict[tuple[QubitPairId, float], float] - """CZ angle.""" + native: str + """Native two qubit gate.""" + angles: dict[tuple[QubitPairId, float], float] + """Two qubit gate angle.""" virtual_phases: dict[tuple[QubitPairId, float], dict[QubitId, float]] """Virtual Z phase correction.""" leakages: dict[tuple[QubitPairId, float], dict[QubitId, float]] """Leakage on control qubit for pair.""" best_amp: dict[QubitPairId] - """Flux pulse amplitude of best CZ configuration.""" + """Flux pulse amplitude of best configuration.""" best_dur: dict[QubitPairId] - """Flux pulse duration of best CZ configuration.""" + """Flux pulse duration of best configuration.""" best_virtual_phase: dict[QubitPairId] - """Virtual phase to correct best CZ configuration.""" + """Virtual phase to correct best configuration.""" def __contains__(self, key: QubitPairId): """Check if key is in class. @@ -81,7 +89,7 @@ def __contains__(self, key: QubitPairId): return tuple(key) in list(pairs) -CZSweepType = np.dtype( +OptimizeTwoQubitGateType = np.dtype( [ ("amp", np.float64), ("theta", np.float64), @@ -93,13 +101,17 @@ def __contains__(self, key: QubitPairId): @dataclass -class CZSweepData(Data): - """CZSweep data.""" +class OptimizeTwoQubitGateData(Data): + """OptimizeTwoQubitGate data.""" - data: dict[tuple, npt.NDArray[CZSweepType]] = field(default_factory=dict) + data: dict[tuple, npt.NDArray[OptimizeTwoQubitGateType]] = field( + default_factory=dict + ) """Raw data.""" thetas: list = field(default_factory=list) """Angles swept.""" + native: str = "CZ" + """Native two qubit gate.""" vphases: dict[QubitPairId, dict[QubitId, float]] = field(default_factory=dict) """Virtual phases for each qubit.""" amplitudes: dict[tuple[QubitId, QubitId], float] = field(default_factory=dict) @@ -121,7 +133,7 @@ def register_qubit( """Store output for single pair.""" size = len(theta) * len(amp) * len(duration) duration, amplitude, angle = np.meshgrid(duration, amp, theta, indexing="ij") - ar = np.empty(size, dtype=CZSweepType) + ar = np.empty(size, dtype=OptimizeTwoQubitGateType) ar["theta"] = angle.ravel() ar["amp"] = amplitude.ravel() ar["duration"] = duration.ravel() @@ -131,16 +143,18 @@ def register_qubit( def _acquisition( - params: CZSweepParameters, + params: OptimizeTwoQubitGateParameters, platform: Platform, targets: list[QubitPairId], -) -> CZSweepData: +) -> OptimizeTwoQubitGateData: r""" - Repetition of CZVirtualZ experiment for several amplitude and duration values. + Repetition of correct virtual phase experiment for several amplitude and duration values. """ theta_absolute = np.arange(params.theta_start, params.theta_end, params.theta_step) - data = CZSweepData(thetas=theta_absolute.tolist()) + data = OptimizeTwoQubitGateData( + thetas=theta_absolute.tolist(), native=params.native + ) for pair in targets: # order the qubits so that the low frequency one is the first ord_pair = order_pair(pair, platform) @@ -162,6 +176,7 @@ def _acquisition( target_q, control_q, ord_pair, + params.native, params.dt, params.parking, params.flux_pulse_amplitude_min, @@ -241,13 +256,13 @@ def _acquisition( def _fit( - data: CZSweepData, -) -> CZSweepResults: - """Repetition of CZ fit for all configurations.""" + data: OptimizeTwoQubitGateData, +) -> OptimizeTwoQubitGateResults: + """Repetition of correct virtual phase fit for all configurations.""" fitted_parameters = {} pairs = data.pairs virtual_phases = {} - cz_angles = {} + angles = {} leakages = {} best_amp = {} best_dur = {} @@ -293,7 +308,7 @@ def _fit( except Exception as e: log.warning( - f"CZ fit failed for pair ({target, control}) due to {e}." + f"Fit failed for pair ({target, control}) due to {e}." ) try: @@ -301,7 +316,7 @@ def _fit( pair, list(pair)[::-1], ): - cz_angles[target_q, control_q, amplitude, duration] = abs( + angles[target_q, control_q, amplitude, duration] = abs( fitted_parameters[ target_q, control_q, "X", amplitude, duration ][2] @@ -353,16 +368,17 @@ def _fit( ) except KeyError: pass - index = np.argmin(np.abs(np.array(list(cz_angles.values())) - np.pi)) - _, _, amp, dur = np.array(list(cz_angles))[index] + index = np.argmin(np.abs(np.array(list(angles.values())) - np.pi)) + _, _, amp, dur = np.array(list(angles))[index] best_amp[pair] = float(amp) best_dur[pair] = float(dur) best_virtual_phase[pair] = virtual_phases[ ord_pair[0], ord_pair[1], float(amp), float(dur) ] - return CZSweepResults( - cz_angles=cz_angles, + return OptimizeTwoQubitGateResults( + angles=angles, + native=data.native, virtual_phases=virtual_phases, fitted_parameters=fitted_parameters, leakages=leakages, @@ -372,8 +388,12 @@ def _fit( ) -def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): - """Plot routine for CZSweep.""" +def _plot( + data: OptimizeTwoQubitGateData, + fit: OptimizeTwoQubitGateResults, + target: QubitPairId, +): + """Plot routine for OptimizeTwoQubitGate.""" fitting_report = "" qubits = next(iter(data.amplitudes))[:2] @@ -381,9 +401,9 @@ def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): rows=2, cols=2, subplot_titles=( - f"Qubit {qubits[0]} CZ angle", + f"Qubit {qubits[0]} {data.native} angle", f"Qubit {qubits[0]} Leakage", - f"Qubit {qubits[1]} CZ angle", + f"Qubit {qubits[1]} {data.native} angle", f"Qubit {qubits[1]} Leakage", ), ) @@ -400,7 +420,7 @@ def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): for j in data.durations[qubits]: durs.append(j) amps.append(i) - cz.append(fit.cz_angles[target_q, control_q, i, j]) + cz.append(fit.angles[target_q, control_q, i, j]) leakage.append(fit.leakages[qubits[0], qubits[1], i, j][control_q]) condition = [target_q, control_q] == list(target) @@ -412,7 +432,7 @@ def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): z=cz, zmin=np.pi / 2, zmax=3 * np.pi / 2, - name="CZ angle", + name="{fit.native} angle", colorbar_x=-0.1, colorscale="RdBu", showscale=condition, @@ -459,13 +479,23 @@ def _plot(data: CZSweepData, fit: CZSweepResults, target: QubitPairId): return [fig], fitting_report -def _update(results: CZSweepResults, platform: Platform, target: QubitPairId): +def _update( + results: OptimizeTwoQubitGateResults, platform: Platform, target: QubitPairId +): # FIXME: quick fix for qubit order target = tuple(sorted(target)) - update.virtual_phases(results.best_virtual_phase[target], "CZ", platform, target) - update.CZ_duration(results.best_dur[target], platform, target) - update.CZ_amplitude(results.best_amp[target], platform, target) + update.virtual_phases( + results.best_virtual_phase[target], results.native, platform, target + ) + getattr(update, f"{results.native}_duration")( + results.best_dur[target], platform, target + ) + getattr(update, f"{results.native}_amplitude")( + results.best_amp[target], platform, target + ) -cz_sweep = Routine(_acquisition, _fit, _plot, _update, two_qubit_gates=True) -"""CZ sweep protocol""" +optimize_two_qubit_gate = Routine( + _acquisition, _fit, _plot, _update, two_qubit_gates=True +) +"""Optimize two qubit gate protocol""" diff --git a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py index a0a81ee21..01adbc5d1 100644 --- a/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py +++ b/src/qibocal/protocols/two_qubit_interaction/virtual_z_phases.py @@ -295,8 +295,6 @@ def _fit( fitted_parameters = {} pairs = data.pairs virtual_phase = {} - amplitudes = {} - durations = {} angle = {} leakage = {} for pair in pairs: diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 1dba63e0b..03188b0aa 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -922,8 +922,8 @@ actions: bias_step: 0.05 drive_amplitude: 0.5 - - id: cz_sweep - operation: cz_sweep + - id: optimize cz + operation: optimize_two_qubit_gate targets: [[0,2]] parameters: flux_pulse_amplitude_min: -0.249 @@ -932,6 +932,23 @@ actions: duration_max: 32 duration_min: 29 duration_step: 1 + native: CZ + theta_start: 0 + theta_end: 7 + theta_step: 1 + relaxation_time: 50_000 + + - id: optimize iSWAP + operation: optimize_two_qubit_gate + targets: [[0,2]] + parameters: + flux_pulse_amplitude_min: -0.249 + flux_pulse_amplitude_max: -0.248 + flux_pulse_amplitude_step: 0.0001 + duration_max: 32 + duration_min: 29 + duration_step: 1 + native: iSWAP theta_start: 0 theta_end: 7 theta_step: 1