Skip to content

Commit

Permalink
Merge pull request #808 from qiboteam/detuned_flipping
Browse files Browse the repository at this point in the history
feat: detuning for flipping
  • Loading branch information
andrea-pasquale authored Apr 30, 2024
2 parents 352c59f + a50874d commit 50ad9a7
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 67 deletions.
80 changes: 41 additions & 39 deletions src/qibocal/protocols/characterization/flipping.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
FlippingSignalResults,
_update,
flipping_fit,
flipping_sequence,
)
from .utils import COLORBAND, COLORBAND_LINE, chi2_reduced

Expand All @@ -29,14 +30,6 @@
class FlippingParameters(FlippingSignalParameters):
"""Flipping runcard inputs."""

nflips_max: int
"""Maximum number of flips ([RX(pi) - RX(pi)] sequences). """
nflips_step: int
"""Flip step."""
unrolling: bool = False
"""If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call.
Defaults to ``False``."""


@dataclass
class FlippingResults(FlippingSignalResults):
Expand All @@ -55,7 +48,6 @@ class FlippingResults(FlippingSignalResults):
class FlippingData(FlippingSignalData):
"""Flipping acquisition outputs."""

"""Pi pulse amplitudes for each qubit."""
data: dict[QubitId, npt.NDArray[FlippingType]] = field(default_factory=dict)
"""Raw data acquired."""

Expand All @@ -82,6 +74,7 @@ def _acquisition(

data = FlippingData(
resonator_type=platform.resonator_type,
detuning=params.detuning,
pi_pulse_amplitudes={
qubit: platform.qubits[qubit].native_gates.RX.amplitude for qubit in targets
},
Expand All @@ -100,26 +93,13 @@ def _acquisition(
for flips in flips_sweep:
# create a sequence of pulses for the experiment
sequence = PulseSequence()
ro_pulses = {}
for qubit in targets:
RX90_pulse = platform.create_RX90_pulse(qubit, start=0)
sequence.add(RX90_pulse)
# execute sequence RX(pi/2) - [RX(pi) - RX(pi)] from 0...flips times - RO
start1 = RX90_pulse.duration
for _ in range(flips):
RX_pulse1 = platform.create_RX_pulse(qubit, start=start1)
start2 = start1 + RX_pulse1.duration
RX_pulse2 = platform.create_RX_pulse(qubit, start=start2)
sequence.add(RX_pulse1)
sequence.add(RX_pulse2)
start1 = start2 + RX_pulse2.duration

# add ro pulse at the end of the sequence
ro_pulses[qubit] = platform.create_qubit_readout_pulse(qubit, start=start1)
sequence.add(ro_pulses[qubit])
sequence += flipping_sequence(
platform=platform, qubit=qubit, detuning=params.detuning, flips=flips
)

sequences.append(sequence)
all_ro_pulses.append(ro_pulses)
all_ro_pulses.append(sequence.ro_pulses)

# execute the pulse sequence
if params.unrolling:
Expand All @@ -132,7 +112,7 @@ def _acquisition(

for ig, (flips, ro_pulses) in enumerate(zip(flips_sweep, all_ro_pulses)):
for qubit in targets:
serial = ro_pulses[qubit].serial
serial = ro_pulses.get_qubit_pulses(qubit)[0].serial
if params.unrolling:
result = results[serial][0]
else:
Expand Down Expand Up @@ -164,11 +144,12 @@ def _fit(data: FlippingData) -> FlippingResults:
qubits = data.qubits
corrected_amplitudes = {}
fitted_parameters = {}
amplitude_correction_factors = {}
delta_amplitude = {}
delta_amplitude_detuned = {}
chi2 = {}
for qubit in qubits:
qubit_data = data[qubit]
pi_pulse_amplitude = data.pi_pulse_amplitudes[qubit]
detuned_pi_pulse_amplitude = data.pi_pulse_amplitudes[qubit]
y = qubit_data.prob
x = qubit_data.flips

Expand All @@ -185,6 +166,7 @@ def _fit(data: FlippingData) -> FlippingResults:
)
f = x[index] / (x[1] - x[0]) if index is not None else 1
pguess = [0.5, 0.5, 1 / f, np.pi, 0]

try:
popt, perr = curve_fit(
flipping_fit,
Expand All @@ -207,21 +189,36 @@ def _fit(data: FlippingData) -> FlippingResults:
signed_correction = popt[2] / 2
# The amplitude is directly proportional to the rotation angle
corrected_amplitudes[qubit] = (
float((pi_pulse_amplitude * np.pi) / (np.pi + signed_correction)),
float(detuned_pi_pulse_amplitude * np.pi / (np.pi + signed_correction)),
float(
pi_pulse_amplitude
detuned_pi_pulse_amplitude
* np.pi
* 1
/ (np.pi + signed_correction) ** 2
* perr[2]
/ 2
),
)

fitted_parameters[qubit] = popt
amplitude_correction_factors[qubit] = (
float(signed_correction / np.pi * pi_pulse_amplitude),
float(perr[2] * pi_pulse_amplitude / np.pi / 2),

delta_amplitude[qubit] = (
-signed_correction
* detuned_pi_pulse_amplitude
/ (np.pi + signed_correction),
np.abs(
np.pi
* detuned_pi_pulse_amplitude
* np.power(np.pi + signed_correction, -2)
)
* perr[2]
/ 2,
)
delta_amplitude_detuned[qubit] = (
delta_amplitude[qubit][0] - data.detuning,
delta_amplitude[qubit][1],
)

chi2[qubit] = (
chi2_reduced(
y,
Expand All @@ -234,7 +231,11 @@ def _fit(data: FlippingData) -> FlippingResults:
log.warning(f"Error in flipping fit for qubit {qubit} due to {e}.")

return FlippingResults(
corrected_amplitudes, amplitude_correction_factors, fitted_parameters, chi2
corrected_amplitudes,
delta_amplitude,
delta_amplitude_detuned,
fitted_parameters,
chi2,
)


Expand All @@ -243,7 +244,6 @@ def _plot(data: FlippingData, target: QubitId, fit: FlippingResults = None):

figures = []
fig = go.Figure()

fitting_report = ""
qubit_data = data[target]

Expand Down Expand Up @@ -298,13 +298,15 @@ def _plot(data: FlippingData, target: QubitId, fit: FlippingResults = None):
table_dict(
target,
[
"Amplitude correction factor",
"Delta amplitude [a.u.]",
"Delta amplitude (with detuning) [a.u.]",
"Corrected amplitude [a.u.]",
"chi2 reduced",
],
[
np.round(fit.amplitude_factors[target], 4),
np.round(fit.amplitude[target], 4),
fit.delta_amplitude[target],
fit.delta_amplitude_detuned[target],
fit.amplitude[target],
fit.chi2[target],
],
display_error=True,
Expand Down
87 changes: 59 additions & 28 deletions src/qibocal/protocols/characterization/flipping_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class FlippingSignalParameters(Parameters):
unrolling: bool = False
"""If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call.
Defaults to ``False``."""
detuning: float = 0
"""Amplitude detuning."""


@dataclass
Expand All @@ -36,8 +38,10 @@ class FlippingSignalResults(Results):

amplitude: dict[QubitId, tuple[float, Optional[float]]]
"""Drive amplitude for each qubit."""
amplitude_factors: dict[QubitId, tuple[float, Optional[float]]]
"""Drive amplitude correction factor for each qubit."""
delta_amplitude: dict[QubitId, tuple[float, Optional[float]]]
"""Difference in amplitude between initial value and fit."""
delta_amplitude_detuned: dict[QubitId, tuple[float, Optional[float]]]
"""Difference in amplitude between detuned value and fit."""
fitted_parameters: dict[QubitId, dict[str, float]]
"""Raw fitting output."""

Expand All @@ -51,12 +55,39 @@ class FlippingSignalData(Data):

resonator_type: str
"""Resonator type."""
detuning: float
"""Amplitude detuning."""
pi_pulse_amplitudes: dict[QubitId, float]
"""Pi pulse amplitudes for each qubit."""
data: dict[QubitId, npt.NDArray[FlippingType]] = field(default_factory=dict)
"""Raw data acquired."""


def flipping_sequence(platform: Platform, qubit: QubitId, detuning: float, flips: int):

sequence = PulseSequence()
RX90_pulse = platform.create_RX90_pulse(qubit, start=0)
sequence.add(RX90_pulse)
# execute sequence RX(pi/2) - [RX(pi) - RX(pi)] from 0...flips times - RO
start1 = RX90_pulse.duration
drive_frequency = platform.qubits[qubit].native_gates.RX.frequency
for _ in range(flips):
RX_pulse1 = platform.create_RX_pulse(qubit, start=start1)
RX_pulse1.frequency = drive_frequency + detuning
start2 = start1 + RX_pulse1.duration
RX_pulse2 = platform.create_RX_pulse(qubit, start=start2)
RX_pulse2.frequency = drive_frequency + detuning

sequence.add(RX_pulse1)
sequence.add(RX_pulse2)
start1 = start2 + RX_pulse2.duration

# add ro pulse at the end of the sequence
sequence.add(platform.create_qubit_readout_pulse(qubit, start=start1))

return sequence


def _acquisition(
params: FlippingSignalParameters,
platform: Platform,
Expand All @@ -79,6 +110,7 @@ def _acquisition(

data = FlippingSignalData(
resonator_type=platform.resonator_type,
detuning=params.detuning,
pi_pulse_amplitudes={
qubit: platform.qubits[qubit].native_gates.RX.amplitude for qubit in targets
},
Expand All @@ -97,26 +129,13 @@ def _acquisition(
for flips in flips_sweep:
# create a sequence of pulses for the experiment
sequence = PulseSequence()
ro_pulses = {}
for qubit in targets:
RX90_pulse = platform.create_RX90_pulse(qubit, start=0)
sequence.add(RX90_pulse)
# execute sequence RX(pi/2) - [RX(pi) - RX(pi)] from 0...flips times - RO
start1 = RX90_pulse.duration
for _ in range(flips):
RX_pulse1 = platform.create_RX_pulse(qubit, start=start1)
start2 = start1 + RX_pulse1.duration
RX_pulse2 = platform.create_RX_pulse(qubit, start=start2)
sequence.add(RX_pulse1)
sequence.add(RX_pulse2)
start1 = start2 + RX_pulse2.duration

# add ro pulse at the end of the sequence
ro_pulses[qubit] = platform.create_qubit_readout_pulse(qubit, start=start1)
sequence.add(ro_pulses[qubit])
sequence += flipping_sequence(
platform=platform, qubit=qubit, detuning=params.detuning, flips=flips
)

sequences.append(sequence)
all_ro_pulses.append(ro_pulses)
all_ro_pulses.append(sequence.ro_pulses)

# execute the pulse sequence
if params.unrolling:
Expand All @@ -129,7 +148,7 @@ def _acquisition(

for ig, (flips, ro_pulses) in enumerate(zip(flips_sweep, all_ro_pulses)):
for qubit in targets:
serial = ro_pulses[qubit].serial
serial = ro_pulses.get_qubit_pulses(qubit)[0].serial
if params.unrolling:
result = results[serial][0]
else:
Expand Down Expand Up @@ -162,10 +181,11 @@ def _fit(data: FlippingSignalData) -> FlippingSignalResults:
qubits = data.qubits
corrected_amplitudes = {}
fitted_parameters = {}
amplitude_correction_factors = {}
delta_amplitude = {}
delta_amplitude_detuned = {}
for qubit in qubits:
qubit_data = data[qubit]
pi_pulse_amplitude = data.pi_pulse_amplitudes[qubit]
detuned_pi_pulse_amplitude = data.pi_pulse_amplitudes[qubit] + data.detuning
voltages = qubit_data.signal
flips = qubit_data.flips
y_min = np.min(voltages)
Expand Down Expand Up @@ -217,18 +237,24 @@ def _fit(data: FlippingSignalData) -> FlippingSignalResults:
else:
signed_correction = -translated_popt[2] / 2
# The amplitude is directly proportional to the rotation angle
corrected_amplitudes[qubit] = (pi_pulse_amplitude * np.pi) / (
corrected_amplitudes[qubit] = (detuned_pi_pulse_amplitude * np.pi) / (
np.pi + signed_correction
)
fitted_parameters[qubit] = translated_popt
amplitude_correction_factors[qubit] = (
signed_correction / np.pi * pi_pulse_amplitude
delta_amplitude[qubit] = (
-signed_correction
* detuned_pi_pulse_amplitude
/ (np.pi + signed_correction)
)
delta_amplitude_detuned[qubit] = delta_amplitude[qubit] - data.detuning
except Exception as e:
log.warning(f"Error in flipping fit for qubit {qubit} due to {e}.")

return FlippingSignalResults(
corrected_amplitudes, amplitude_correction_factors, fitted_parameters
corrected_amplitudes,
delta_amplitude,
delta_amplitude_detuned,
fitted_parameters,
)


Expand Down Expand Up @@ -276,9 +302,14 @@ def _plot(data: FlippingSignalData, target, fit: FlippingSignalResults = None):
fitting_report = table_html(
table_dict(
target,
["Amplitude correction factor", "Corrected amplitude [a.u.]"],
[
np.round(fit.amplitude_factors[target], 4),
"Delta amplitude [a.u.]",
"Delta amplitude (with detuning) [a.u.]",
"Corrected amplitude [a.u.]",
],
[
np.round(fit.delta_amplitude[target], 4),
np.round(fit.delta_amplitude_detuned[target], 4),
np.round(fit.amplitude[target], 4),
],
)
Expand Down
2 changes: 2 additions & 0 deletions tests/runcards/protocols.yml
Original file line number Diff line number Diff line change
Expand Up @@ -550,13 +550,15 @@ actions:
nflips_max: 10
nflips_step: 1
nshots: 50
detuning: 0.1

- id: flipping_signal
operation: flipping_signal
parameters:
nflips_max: 10
nflips_step: 1
nshots: 50
detuning: 0.1

- id: flipping unrolling
operation: flipping
Expand Down

0 comments on commit 50ad9a7

Please sign in to comment.