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

Cross resonance protocols #860

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/qibocal/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
chsh_pulses,
correct_virtual_z_phases,
correct_virtual_z_phases_signal,
cross_resonance,
cross_resonance_chevron,
optimize_two_qubit_gate,
)
from .two_qubit_state_tomography import two_qubit_state_tomography
Expand Down Expand Up @@ -148,4 +150,6 @@
"standard_rb_2q",
"standard_rb_2q_inter",
"optimize_two_qubit_gate",
"cross_resonance",
"cross_resonance_chevron",
]
1 change: 1 addition & 0 deletions src/qibocal/protocols/two_qubit_interaction/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .chevron import chevron, chevron_signal
from .chsh import chsh_circuits, chsh_pulses
from .cross_resonance import cross_resonance, cross_resonance_chevron
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .chevron import cross_resonance_chevron
from .cross_resonance import cross_resonance
204 changes: 204 additions & 0 deletions src/qibocal/protocols/two_qubit_interaction/cross_resonance/chevron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
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 plotly.subplots import make_subplots
from qibolab import AcquisitionType, AveragingMode, ExecutionParameters
from qibolab.platform import Platform
from qibolab.pulses import PulseSequence
from qibolab.qubits import QubitId, QubitPairId
from qibolab.sweeper import Parameter, Sweeper, SweeperType

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

CrossResonanceChevronType = np.dtype(
[
("prob", np.float64),
("length", np.int64),
("amp", np.float64),
]
)
"""Custom dtype for resonator spectroscopy."""


@dataclass
class CrossResonanceChevronParameters(Parameters):
"""ResonatorSpectroscopy runcard inputs."""

pulse_duration_start: float
"""Initial pi pulse duration [ns]."""
pulse_duration_end: float
"""Final pi pulse duration [ns]."""
pulse_duration_step: float
"""Step pi pulse duration [ns]."""
amplitude_min_factor: float
"""Amplitude minimum."""
amplitude_max_factor: float
"""Amplitude maximum."""
amplitude_step_factor: float
"""Amplitude step."""

pulse_amplitude: Optional[float] = None

@property
def duration_range(self):
return np.arange(
self.pulse_duration_start, self.pulse_duration_end, self.pulse_duration_step
)

@property
def amplitude_factor_range(self):
return np.arange(
self.amplitude_min_factor,
self.amplitude_max_factor,
self.amplitude_step_factor,
)


@dataclass
class CrossResonanceChevronResults(Results):
"""ResonatorSpectroscopy outputs."""


@dataclass
class CrossResonanceChevronData(Data):
"""Data structure for resonator spectroscopy with attenuation."""

data: dict[QubitId, npt.NDArray[CrossResonanceChevronType]] = field(
default_factory=dict
)
"""Raw data acquired."""

def register_qubit(self, dtype, key, prob, length, amp):
"""Store output for single qubit."""
size = len(length) * len(amp)
amplitude, duration = np.meshgrid(amp, length)
ar = np.empty(size, dtype=dtype)
ar["length"] = duration.ravel()
ar["amp"] = amplitude.ravel()
ar["prob"] = prob.ravel()
self.data[key] = np.rec.array(ar)


def _acquisition(
params: CrossResonanceChevronParameters,
platform: Platform,
targets: list[QubitPairId],
) -> CrossResonanceChevronData:
"""Data acquisition for resonator spectroscopy."""

data = CrossResonanceChevronData()

for pair in targets:
for setup in ["I", "X"]:
target, control = pair
sequence = PulseSequence()
control_drive_freq = platform.qubits[control].native_gates.RX.frequency

if setup == "X":
rx_control = platform.create_RX_pulse(control, 0)
pulse = platform.create_RX_pulse(target, rx_control.finish)
sequence.add(rx_control)
else:
pulse = platform.create_RX_pulse(target, 0)

pulse.frequency = control_drive_freq
if params.pulse_amplitude is not None:
pulse.amplitude = params.pulse_amplitude
sequence.add(pulse)
sequence.add(
platform.create_qubit_readout_pulse(target, start=pulse.finish)
)

sweeper_duration = Sweeper(
Parameter.duration,
params.duration_range,
pulses=[pulse],
type=SweeperType.ABSOLUTE,
)

sweeper_amplitude = Sweeper(
Parameter.amplitude,
pulse.amplitude * params.amplitude_factor_range,
pulses=[pulse],
type=SweeperType.FACTOR,
)

results = platform.sweep(
sequence,
ExecutionParameters(
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.DISCRIMINATION,
averaging_mode=AveragingMode.SINGLESHOT,
),
sweeper_duration,
sweeper_amplitude,
)

# store the results
prob1 = results[target].probability(state=1)
data.register_qubit(
dtype=CrossResonanceChevronType,
key=(target, control, setup),
prob=prob1,
length=params.duration_range,
amp=pulse.amplitude * params.amplitude_factor_range,
)
# finally, save the remaining data
return data


def _fit(
data: CrossResonanceChevronData,
) -> CrossResonanceChevronResults:
"""Post-processing function for ResonatorSpectroscopy."""
return CrossResonanceChevronResults()


def _plot(
data: CrossResonanceChevronData,
target: QubitPairId,
fit: CrossResonanceChevronResults,
):
"""Plotting function for ResonatorSpectroscopy."""
# TODO: share colorbar
control_idle_data = data.data[target[0], target[1], "I"]
control_excited_data = data.data[target[0], target[1], "X"]
fig = make_subplots(
rows=1,
cols=2,
subplot_titles=(
f"Qubit {target[1]} - |0>",
f"Qubit {target[1]} - |1>",
),
)
fig.add_trace(
go.Heatmap(
x=control_idle_data.length,
y=control_idle_data.amp,
z=control_idle_data.prob,
name="Control at 0",
),
row=1,
col=1,
)

fig.add_trace(
go.Heatmap(
x=control_excited_data.length,
y=control_excited_data.amp,
z=control_excited_data.prob,
name="Control at 1",
),
row=1,
col=2,
)

return [fig], ""


cross_resonance_chevron = Routine(_acquisition, _fit, _plot)
"""CrossResonanceChevron Routine object."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
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, ExecutionParameters
from qibolab.platform import Platform
from qibolab.pulses import PulseSequence
from qibolab.qubits import QubitId, QubitPairId
from qibolab.sweeper import Parameter, Sweeper, SweeperType

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

CrossResonanceType = np.dtype(
[
("prob", np.float64),
("length", np.int64),
]
)
"""Custom dtype for resonator spectroscopy."""


@dataclass
class CrossResonanceParameters(Parameters):
"""ResonatorSpectroscopy runcard inputs."""

pulse_duration_start: float
"""Initial pi pulse duration [ns]."""
pulse_duration_end: float
"""Final pi pulse duration [ns]."""
pulse_duration_step: float
"""Step pi pulse duration [ns]."""

pulse_amplitude: Optional[float] = None

@property
def duration_range(self):
return np.arange(
self.pulse_duration_start, self.pulse_duration_end, self.pulse_duration_step
)


@dataclass
class CrossResonanceResults(Results):
"""ResonatorSpectroscopy outputs."""


@dataclass
class CrossResonanceData(Data):
"""Data structure for resonator spectroscopy with attenuation."""

data: dict[QubitId, npt.NDArray[CrossResonanceType]] = field(default_factory=dict)
"""Raw data acquired."""


def _acquisition(
params: CrossResonanceParameters, platform: Platform, targets: list[QubitPairId]
) -> CrossResonanceData:
"""Data acquisition for resonator spectroscopy."""

data = CrossResonanceData()

for pair in targets:
for setup in ["I", "X"]:
target, control = pair
sequence = PulseSequence()
control_drive_freq = platform.qubits[control].native_gates.RX.frequency

if setup == "X":
rx_control = platform.create_RX_pulse(control, 0)
pulse = platform.create_RX_pulse(target, rx_control.finish)
sequence.add(rx_control)
else:
pulse = platform.create_RX_pulse(target, 0)

pulse.frequency = control_drive_freq
if params.pulse_amplitude is not None:
pulse.amplitude = params.pulse_amplitude
sequence.add(pulse)
sequence.add(
platform.create_qubit_readout_pulse(target, start=pulse.finish)
)

sweeper_duration = Sweeper(
Parameter.duration,
params.duration_range,
pulses=[pulse],
type=SweeperType.ABSOLUTE,
)

results = platform.sweep(
sequence,
ExecutionParameters(
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.DISCRIMINATION,
averaging_mode=AveragingMode.CYCLIC,
),
sweeper_duration,
)

# store the results
prob1 = results[target].probability(state=0)
data.register_qubit(
CrossResonanceType,
(target, control, setup),
dict(
prob=prob1,
length=params.duration_range,
),
)
# finally, save the remaining data
return data


def _fit(
data: CrossResonanceData,
) -> CrossResonanceResults:
"""Post-processing function for ResonatorSpectroscopy."""
return CrossResonanceResults()


def _plot(data: CrossResonanceData, target: QubitPairId, fit: CrossResonanceResults):
"""Plotting function for ResonatorSpectroscopy."""
# TODO: share colorbar
control_idle_data = data.data[target[0], target[1], "I"]
control_excited_data = data.data[target[0], target[1], "X"]
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=control_idle_data.length, y=control_idle_data.prob, name="Control at 0"
),
)

fig.add_trace(
go.Scatter(
x=control_excited_data.length,
y=control_excited_data.prob,
name="Control at 1",
),
)

return [fig], ""


cross_resonance = Routine(_acquisition, _fit, _plot)
"""CrossResonance Routine object."""
Loading