Skip to content

Commit

Permalink
Merge pull request #549 from qiboteam/add_prob
Browse files Browse the repository at this point in the history
Adding "coherence"  protocols with probabilities and error bands.
  • Loading branch information
andrea-pasquale authored Oct 18, 2023
2 parents 3de2a0f + 9bdb86e commit 9abd302
Show file tree
Hide file tree
Showing 16 changed files with 1,321 additions and 150 deletions.
10 changes: 10 additions & 0 deletions src/qibocal/protocols/characterization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
from .allxy.drag_pulse_tuning import drag_pulse_tuning
from .classification import single_shot_classification
from .coherence.spin_echo import spin_echo
from .coherence.spin_echo_msr import spin_echo_msr
from .coherence.t1 import t1
from .coherence.t1_msr import t1_msr
from .coherence.t1_sequences import t1_sequences
from .coherence.t2 import t2
from .coherence.t2_msr import t2_msr
from .coherence.t2_sequences import t2_sequences
from .coherence.zeno import zeno
from .coherence.zeno_msr import zeno_msr
from .dispersive_shift import dispersive_shift
from .fast_reset.fast_reset import fast_reset
from .flipping import flipping
Expand All @@ -23,6 +27,7 @@
from .rabi.length import rabi_length
from .rabi.length_sequences import rabi_length_sequences
from .ramsey import ramsey
from .ramsey_msr import ramsey_msr
from .ramsey_sequences import ramsey_sequences
from .randomized_benchmarking.standard_rb import standard_rb
from .readout_characterization import readout_characterization
Expand Down Expand Up @@ -53,14 +58,18 @@ class Operation(Enum):
rabi_length = rabi_length
rabi_length_sequences = rabi_length_sequences
ramsey = ramsey
ramsey_msr = ramsey_msr
ramsey_sequences = ramsey_sequences
t1 = t1
t1_msr = t1_msr
t1_sequences = t1_sequences
t2 = t2
t2_msr = t2_msr
t2_sequences = t2_sequences
time_of_flight_readout = time_of_flight_readout
single_shot_classification = single_shot_classification
spin_echo = spin_echo
spin_echo_msr = spin_echo_msr
allxy = allxy
allxy_drag_pulse_tuning = allxy_drag_pulse_tuning
drag_pulse_tuning = drag_pulse_tuning
Expand All @@ -73,6 +82,7 @@ class Operation(Enum):
resonator_frequency = resonator_frequency
fast_reset = fast_reset
zeno = zeno
zeno_msr = zeno_msr
chsh_pulses = chsh_pulses
chsh_circuits = chsh_circuits
readout_mitigation_matrix = readout_mitigation_matrix
Expand Down
70 changes: 42 additions & 28 deletions src/qibocal/protocols/characterization/coherence/spin_echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from qibocal import update
from qibocal.auto.operation import Parameters, Qubits, Results, Routine

from ..utils import V_TO_UV, table_dict, table_html
from .t1 import T1Data
from .utils import exp_decay, exponential_fit
from ..utils import table_dict, table_html
from . import t1
from .utils import exp_decay, exponential_fit_probability


@dataclass
Expand All @@ -37,7 +37,7 @@ class SpinEchoResults(Results):
"""Raw fitting output."""


class SpinEchoData(T1Data):
class SpinEchoData(t1.T1Data):
"""SpinEcho acquisition outputs."""


Expand Down Expand Up @@ -79,7 +79,7 @@ def _acquisition(
)

data = SpinEchoData()

probs = {qubit: [] for qubit in qubits}
# sweep the parameter
for wait in ro_wait_range:
# save data as often as defined by points
Expand All @@ -95,22 +95,25 @@ def _acquisition(
ExecutionParameters(
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.INTEGRATION,
averaging_mode=AveragingMode.CYCLIC,
acquisition_type=AcquisitionType.DISCRIMINATION,
averaging_mode=AveragingMode.SINGLESHOT,
),
)

for qubit in qubits:
result = results[ro_pulses[qubit].serial]
data.register_qubit(
qubit, wait=wait, msr=result.magnitude, phase=result.phase
)
prob = results[ro_pulses[qubit].serial].probability(state=0)
probs[qubit].append(prob)

for qubit in qubits:
errors = [np.sqrt(prob * (1 - prob) / params.nshots) for prob in probs[qubit]]
data.register_qubit(qubit, wait=ro_wait_range, prob=probs[qubit], error=errors)

return data


def _fit(data: SpinEchoData) -> SpinEchoResults:
"""Post-processing for SpinEcho."""
t2Echos, fitted_parameters = exponential_fit(data)
t2Echos, fitted_parameters = exponential_fit_probability(data)

return SpinEchoResults(t2Echos, fitted_parameters)

Expand All @@ -119,23 +122,35 @@ def _plot(data: SpinEchoData, qubit, fit: SpinEchoResults = None):
"""Plotting for SpinEcho"""

figures = []
fig = go.Figure()

# iterate over multiple data folders
fitting_report = ""

qubit_data = data[qubit]
waits = qubit_data.wait
probs = qubit_data.prob
error_bars = qubit_data.error

fig.add_trace(
go.Scatter(
x=waits,
y=qubit_data.msr * V_TO_UV,
opacity=1,
name="Voltage",
showlegend=True,
legendgroup="Voltage",
),
fig = go.Figure(
[
go.Scatter(
x=waits,
y=probs,
opacity=1,
name="Probability of 0",
showlegend=True,
legendgroup="Probability of 0",
mode="lines",
),
go.Scatter(
x=np.concatenate((waits, waits[::-1])),
y=np.concatenate((probs + error_bars, (probs - error_bars)[::-1])),
fill="toself",
fillcolor=t1.COLORBAND,
line=dict(color=t1.COLORBAND_LINE),
showlegend=True,
name="Errors",
),
]
)

if fit is not None:
Expand All @@ -155,20 +170,19 @@ def _plot(data: SpinEchoData, qubit, fit: SpinEchoResults = None):
line=go.scatter.Line(dash="dot"),
),
)

fitting_report = table_html(
table_dict(
qubit,
"T2 Spin Echo",
np.round(fit.t2_spin_echo[qubit]),
["T2 Spin Echo [ns]"],
[fit.t2_spin_echo[qubit]],
display_error=True,
)
)

fig.update_layout(
showlegend=True,
uirevision="0", # ``uirevision`` allows zooming while live plotting
xaxis_title="Time (ns)",
yaxis_title="MSR (uV)",
yaxis_title="Probability of State 0",
)

figures.append(fig)
Expand Down
169 changes: 169 additions & 0 deletions src/qibocal/protocols/characterization/coherence/spin_echo_msr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from dataclasses import dataclass

import numpy as np
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

from qibocal import update
from qibocal.auto.operation import Qubits, Routine

from ..utils import V_TO_UV, table_dict, table_html
from . import spin_echo
from .t1_msr import T1MSRData
from .utils import exp_decay, exponential_fit


@dataclass
class SpinEchoMSRParameters(spin_echo.SpinEchoParameters):
"""SpinEcho MSR runcard inputs."""


@dataclass
class SpinEchoMSRResults(spin_echo.SpinEchoResults):
"""SpinEchoMSR outputs."""


class SpinEchoMSRData(T1MSRData):
"""SpinEcho acquisition outputs."""


def _acquisition(
params: SpinEchoMSRParameters,
platform: Platform,
qubits: Qubits,
) -> SpinEchoMSRData:
"""Data acquisition for SpinEcho"""
# create a sequence of pulses for the experiment:
# Spin Echo 3 Pulses: RX(pi/2) - wait t(rotates z) - RX(pi) - wait t(rotates z) - RX(pi/2) - readout
ro_pulses = {}
RX90_pulses1 = {}
RX_pulses = {}
RX90_pulses2 = {}
sequence = PulseSequence()
for qubit in qubits:
RX90_pulses1[qubit] = platform.create_RX90_pulse(qubit, start=0)
RX_pulses[qubit] = platform.create_RX_pulse(
qubit, start=RX90_pulses1[qubit].finish
)
RX90_pulses2[qubit] = platform.create_RX90_pulse(
qubit, start=RX_pulses[qubit].finish
)
ro_pulses[qubit] = platform.create_qubit_readout_pulse(
qubit, start=RX90_pulses2[qubit].finish
)
sequence.add(RX90_pulses1[qubit])
sequence.add(RX_pulses[qubit])
sequence.add(RX90_pulses2[qubit])
sequence.add(ro_pulses[qubit])

# define the parameter to sweep and its range:
# delay between pulses
ro_wait_range = np.arange(
params.delay_between_pulses_start,
params.delay_between_pulses_end,
params.delay_between_pulses_step,
)

data = SpinEchoMSRData()

# sweep the parameter
for wait in ro_wait_range:
# save data as often as defined by points

for qubit in qubits:
RX_pulses[qubit].start = RX90_pulses1[qubit].finish + wait
RX90_pulses2[qubit].start = RX_pulses[qubit].finish + wait
ro_pulses[qubit].start = RX90_pulses2[qubit].finish

# execute the pulse sequence
results = platform.execute_pulse_sequence(
sequence,
ExecutionParameters(
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.INTEGRATION,
averaging_mode=AveragingMode.CYCLIC,
),
)

for qubit in qubits:
result = results[ro_pulses[qubit].serial]
data.register_qubit(
qubit, wait=wait, msr=result.magnitude, phase=result.phase
)
return data


def _fit(data: SpinEchoMSRData) -> SpinEchoMSRResults:
"""Post-processing for SpinEcho."""
t2Echos, fitted_parameters = exponential_fit(data)

return SpinEchoMSRResults(t2Echos, fitted_parameters)


def _plot(data: SpinEchoMSRData, qubit, fit: SpinEchoMSRResults = None):
"""Plotting for SpinEcho"""

figures = []
fig = go.Figure()

# iterate over multiple data folders
fitting_report = None

qubit_data = data[qubit]
waits = qubit_data.wait

fig.add_trace(
go.Scatter(
x=waits,
y=qubit_data.msr * V_TO_UV,
opacity=1,
name="Voltage",
showlegend=True,
legendgroup="Voltage",
),
)

if fit is not None:
# add fitting trace
waitrange = np.linspace(
min(waits),
max(waits),
2 * len(qubit_data),
)
params = fit.fitted_parameters[qubit]

fig.add_trace(
go.Scatter(
x=waitrange,
y=exp_decay(waitrange, *params),
name="Fit",
line=go.scatter.Line(dash="dot"),
),
)

fitting_report = table_html(
table_dict(qubit, "T2 Spin Echo [ns]", np.round(fit.t2_spin_echo[qubit]))
)

fig.update_layout(
showlegend=True,
uirevision="0", # ``uirevision`` allows zooming while live plotting
xaxis_title="Time (ns)",
yaxis_title="MSR (uV)",
)

figures.append(fig)

return figures, fitting_report


def _update(results: SpinEchoMSRResults, platform: Platform, qubit: QubitId):
update.t2_spin_echo(results.t2_spin_echo[qubit], platform, qubit)


spin_echo_msr = Routine(_acquisition, _fit, _plot, _update)
"""SpinEcho Routine object."""
Loading

0 comments on commit 9abd302

Please sign in to comment.