Source code for qibocal.protocols.allxy.allxy

from dataclasses import dataclass, field
from typing import Optional

import numpy as np
import numpy.typing as npt
import plotly.graph_objects as go
from qibolab import (
    AcquisitionType,
    AveragingMode,
    Delay,
    Drag,
    Pulse,
    PulseSequence,
)

from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine
from qibocal.calibration import CalibrationPlatform
from qibocal.update import replace

__all__ = ["allxy", "gatelist", "AllXYType", "allxy_sequence"]


[docs]@dataclass class AllXYParameters(Parameters): """AllXY runcard inputs.""" beta_param: float = None """Beta parameter for drag pulse. If None is given, the native rx pulse in the parameters will be used""" unrolling: bool = False """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. Defaults to ``False``."""
@dataclass class AllXYResults(Results): """AllXY outputs.""" AllXYType = np.dtype([("prob", np.float64), ("gate", "<U5"), ("errors", np.float64)]) @dataclass class AllXYData(Data): """AllXY acquisition outputs.""" beta_param: float = None """Beta parameter for drag pulse.""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) """Raw data acquired.""" gatelist = [ ["I", "I"], ["Xp", "Xp"], ["Yp", "Yp"], ["Xp", "Yp"], ["Yp", "Xp"], ["X9", "I"], ["Y9", "I"], ["X9", "Y9"], ["Y9", "X9"], ["X9", "Yp"], ["Y9", "Xp"], ["Xp", "Y9"], ["Yp", "X9"], ["X9", "Xp"], ["Xp", "X9"], ["Y9", "Yp"], ["Yp", "Y9"], ["Xp", "I"], ["Yp", "I"], ["X9", "X9"], ["Y9", "Y9"], ] def _acquisition( params: AllXYParameters, platform: CalibrationPlatform, targets: list[QubitId], ) -> AllXYData: r""" Data acquisition for allXY experiment. The AllXY experiment is a simple test of the calibration of single qubit gatesThe qubit (initialized in the 0 state) is subjected to two back-to-back single-qubit gates and measured. In each round, we run 21 different gate pairs: ideally, the first 5 return the qubit to 0, the next 12 drive it to superposition state, and the last 4 put the qubit in 1 state. """ # create a Data object to store the results data = AllXYData(beta_param=params.beta_param) sequences, all_ro_pulses = [], [] for gates in gatelist: sequence = PulseSequence() ro_pulses = {} for qubit in targets: qubit_sequence, ro_pulses[qubit] = allxy_sequence( platform, gates, qubit, beta_param=params.beta_param ) sequence += qubit_sequence sequences.append(sequence) all_ro_pulses.append(ro_pulses) # execute the pulse sequence options = dict( nshots=params.nshots, averaging_mode=AveragingMode.CYCLIC, acquisition_type=AcquisitionType.DISCRIMINATION, ) if params.unrolling: results = platform.execute(sequences, **options) else: results = {} for sequence in sequences: results.update(platform.execute([sequence], **options)) for gates, ro_pulses in zip(gatelist, all_ro_pulses): gate = "-".join(gates) for qubit in targets: prob = results[ro_pulses[qubit].id] z_proj = 1 - 2 * prob errors = 2 * np.sqrt(prob * (1 - prob) / params.nshots) data.register_qubit( AllXYType, (qubit), dict( prob=np.array([z_proj]), gate=np.array([gate]), errors=np.array([errors]), ), ) # finally, save the remaining data return data def apply_drag(pulse: Pulse, beta_param: Optional[float] = None) -> Pulse: """Apply Drag with parameter beta.""" if beta_param is None: return pulse return replace( # pragma: no cover pulse, envelope=Drag( rel_sigma=pulse.envelope.rel_sigma, beta=beta_param, ), )
[docs]def allxy_sequence( platform: CalibrationPlatform, gates, qubit, sequence_delay=None, readout_delay=None, beta_param=None, ): natives = platform.natives.single_qubit[qubit] qd_channel, _ = natives.RX()[0] sequence = PulseSequence() if sequence_delay is not None: sequence.append((qd_channel, Delay(duration=sequence_delay))) for gate in gates: if gate == "I": pass if gate == "Xp": qd_channel, rx_pulse = natives.RX()[0] sequence.append((qd_channel, apply_drag(rx_pulse, beta_param))) if gate == "X9": qd_channel, rx90_pulse = natives.R(theta=np.pi / 2)[0] sequence.append((qd_channel, apply_drag(rx90_pulse, beta_param))) if gate == "Yp": qd_channel, ry_pulse = natives.R(phi=np.pi / 2)[0] sequence.append((qd_channel, apply_drag(ry_pulse, beta_param))) if gate == "Y9": qd_channel, ry90_pulse = natives.R(theta=np.pi / 2, phi=np.pi / 2)[0] sequence.append((qd_channel, apply_drag(ry90_pulse, beta_param))) # RO pulse starting just after pair of gates qd_channel = platform.qubits[qubit].drive ro_channel, ro_pulse = natives.MZ()[0] if readout_delay is not None: sequence.append( ( ro_channel, Delay(duration=sequence.channel_duration(qd_channel) + readout_delay), ) ) else: sequence.append( ( ro_channel, Delay(duration=sequence.channel_duration(qd_channel)), ) ) sequence.append((ro_channel, ro_pulse)) return sequence, ro_pulse
def _fit(_data: AllXYData) -> AllXYResults: """Post-Processing for allXY""" return AllXYResults() # allXY def _plot(data: AllXYData, target: QubitId, fit: AllXYResults = None): """Plotting function for allXY.""" figures = [] fitting_report = "" fig = go.Figure() qubit_data = data[target] error_bars = qubit_data.errors probs = qubit_data.prob gates = qubit_data.gate fig.add_trace( go.Scatter( x=gates, y=probs, error_y=dict( type="data", array=error_bars, visible=True, ), mode="markers", text=gatelist, textposition="bottom center", name="Expectation value", showlegend=True, legendgroup="group1", ), ) fig.add_hline( y=0, line_width=2, line_dash="dash", line_color="grey", ) fig.add_hline( y=1, line_width=2, line_dash="dash", line_color="grey", ) fig.add_hline( y=-1, line_width=2, line_dash="dash", line_color="grey", ) fig.update_layout( showlegend=True, xaxis_title="Gate sequence number", yaxis_title="Expectation value of Z", ) figures.append(fig) return figures, fitting_report allxy = Routine(_acquisition, _fit, _plot) """AllXY Routine object."""