Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cryoscope related protocols #974

Draft
wants to merge 21 commits into
base: 0.2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/qibocal/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
from .dispersive_shift_qutrit import dispersive_shift_qutrit
from .drag import drag_tuning
from .flipping import flipping
from .flux_amplitude_frequency import flux_amplitude_frequency
from .flux_dependence.qubit_crosstalk import qubit_crosstalk
from .flux_dependence.qubit_flux_dependence import qubit_flux
from .flux_dependence.resonator_flux_dependence import resonator_flux
from .flux_gate import flux_gate
from .qubit_power_spectroscopy import qubit_power_spectroscopy
from .qubit_spectroscopy import qubit_spectroscopy
from .qubit_spectroscopy_ef import qubit_spectroscopy_ef
Expand Down Expand Up @@ -49,6 +51,7 @@
chevron,
chevron_signal,
correct_virtual_z_phases,
cryoscope,
optimize_two_qubit_gate,
)
from .two_qubit_state_tomography import two_qubit_state_tomography
Expand Down Expand Up @@ -103,5 +106,8 @@
"standard_rb_2q",
"standard_rb_2q_inter",
"optimize_two_qubit_gate",
"cryoscope",
"ramsey_zz",
"flux_gate",
"flux_amplitude_frequency",
]
239 changes: 239 additions & 0 deletions src/qibocal/protocols/flux_amplitude_frequency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
"""Experiment to compute detuning from flux pulses."""

from dataclasses import dataclass, field

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

from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine


@dataclass
class FluxAmplitudeFrequencyParameters(Parameters):
"""FluxAmplitudeFrequency runcard inputs."""

amplitude_min: int
"""Minimum flux pulse amplitude."""
amplitude_max: int
"""Maximum flux amplitude."""
amplitude_step: int
"""Flux pulse amplitude step."""
duration: float
"""Flux pulse duration."""


@dataclass
class FluxAmplitudeFrequencyResults(Results):
"""FluxAmplitudeFrequency outputs."""

detuning: dict[QubitId, float] = field(default_factory=dict)
"""Frequency detuning."""
fitted_parameters: dict[tuple[QubitId, str], list[float]] = field(
default_factory=dict
)
"""Fitted parameters for every qubit."""

# TODO: to be fixed
def __contains__(self, key):
return True


FluxAmplitudeFrequencyType = np.dtype([("amplitude", float), ("prob_1", np.float64)])
"""Custom dtype for FluxAmplitudeFrequency."""


def ramsey_flux(
platform: Platform,
qubit: QubitId,
amplitude: float,
duration: int,
measure: str,
):
"""Compute sequences at fixed amplitude of flux pulse for <X> and <Y>"""

assert measure in ["X", "Y"]

native = platform.natives.single_qubit[qubit]

drive_channel, ry90 = native.R(theta=np.pi / 2, phi=np.pi / 2)[0]
_, rx90 = native.R(theta=np.pi / 2)[0]
ro_channel, ro_pulse = native.MZ()[0]
flux_channel = platform.qubits[qubit].flux

flux_pulse = Pulse(duration=duration, amplitude=amplitude, envelope=Rectangular())

# create the sequences
sequence = PulseSequence()

if measure == "X":
sequence.extend(
[
(drive_channel, ry90),
(flux_channel, Delay(duration=ry90.duration)),
(flux_channel, flux_pulse),
(drive_channel, Delay(duration=flux_pulse.duration)),
(drive_channel, ry90),
(
ro_channel,
Delay(duration=ry90.duration + flux_pulse.duration + ry90.duration),
),
(ro_channel, ro_pulse),
]
)
else:
sequence.extend(
[
(drive_channel, ry90),
(flux_channel, Delay(duration=rx90.duration)),
(flux_channel, flux_pulse),
(drive_channel, Delay(duration=flux_pulse.duration)),
(drive_channel, rx90),
(
ro_channel,
Delay(duration=ry90.duration + flux_pulse.duration + rx90.duration),
),
(ro_channel, ro_pulse),
]
)
return sequence


@dataclass
class FluxAmplitudeFrequencyData(Data):
"""FluxAmplitudeFrequency acquisition outputs."""

flux_pulse_duration: float
"""Flux pulse amplitude."""
data: dict[tuple[QubitId, str], npt.NDArray[FluxAmplitudeFrequencyType]] = field(
default_factory=dict
)


def _acquisition(
params: FluxAmplitudeFrequencyParameters,
platform: Platform,
targets: list[QubitId],
) -> FluxAmplitudeFrequencyData:

data = FluxAmplitudeFrequencyData(
flux_pulse_duration=params.duration,
)
amplitudes = np.arange(
params.amplitude_min, params.amplitude_max, params.amplitude_step
)

options = dict(
nshots=params.nshots,
acquisition_type=AcquisitionType.DISCRIMINATION,
averaging_mode=AveragingMode.CYCLIC,
)

for measure in ["X", "Y"]:
sequence = PulseSequence()
for qubit in targets:
sequence += ramsey_flux(
platform,
qubit,
duration=params.duration,
amplitude=params.amplitude_max / 2,
measure=measure,
)

sweeper = Sweeper(
parameter=Parameter.amplitude,
range=(params.amplitude_min, params.amplitude_max, params.amplitude_step),
pulses=[
pulse[1]
for pulse in sequence
if pulse[0] in [platform.qubits[target].flux for target in targets]
and isinstance(pulse[1], Pulse)
],
)
result = platform.execute([sequence], [[sweeper]], **options)

for qubit in targets:
ro_pulse = list(sequence.channel(platform.qubits[qubit].acquisition))[-1]
data.register_qubit(
FluxAmplitudeFrequencyType,
(qubit, measure),
dict(
amplitude=amplitudes,
prob_1=result[ro_pulse.id],
),
)

return data


def _fit(data: FluxAmplitudeFrequencyData) -> FluxAmplitudeFrequencyResults:

fitted_parameters = {}
detuning = {}
qubits = np.unique([i[0] for i in data.data]).tolist()

for qubit in qubits:
amplitudes = data[qubit, "X"].amplitude
X_exp = 1 - 2 * data[qubit, "X"].prob_1
Y_exp = 1 - 2 * data[qubit, "Y"].prob_1

phase = np.unwrap(np.angle(X_exp + 1j * Y_exp))
# normalize phase ?
phase -= phase[0]
det = phase / data.flux_pulse_duration / 2 / np.pi

fitted_parameters[qubit] = np.polyfit(amplitudes, det, 2).tolist()
detuning[qubit] = det.tolist()
return FluxAmplitudeFrequencyResults(
detuning=detuning, fitted_parameters=fitted_parameters
)


def _plot(
data: FluxAmplitudeFrequencyData,
fit: FluxAmplitudeFrequencyResults,
target: QubitId,
):
"""FluxAmplitudeFrequency plots."""

fig = go.Figure()

amplitude = data[(target, "X")].amplitude

if fit is not None:
fig.add_trace(
go.Scatter(
x=amplitude,
y=fit.detuning[target],
name="Detuning",
)
)
fig.add_trace(
go.Scatter(
x=amplitude,
y=np.polyval(fit.fitted_parameters[target], amplitude),
name="fit",
)
)

fig.update_layout(
showlegend=True,
xaxis_title="Flux pulse amplitude [a.u.]",
yaxis_title="Detuning [GHz]",
)

return [fig], ""


flux_amplitude_frequency = Routine(_acquisition, _fit, _plot)
Loading