Source code for qibocal.protocols.coherence.utils

import numpy as np
import plotly.graph_objects as go
from qibolab import Delay, Platform, PulseSequence
from scipy.optimize import curve_fit

from qibocal.auto.operation import QubitId
from qibocal.config import log

from ..utils import chi2_reduced, table_dict, table_html

CoherenceType = np.dtype(
    [("wait", np.float64), ("signal", np.float64), ("phase", np.float64)]
)
"""Custom dtype for coherence routines."""

COLORBAND = "rgba(0,100,80,0.2)"
COLORBAND_LINE = "rgba(255,255,255,0)"


[docs]def average_single_shots(data_type, single_shots): """Convert single shot acquisition results of signal routines to averaged. Args: data_type: Type of produced data object (eg. ``T1SignalData``, ``T2SignalData`` etc.). single_shots (dict): Dictionary containing acquired single shot data. """ data = data_type() for qubit, values in single_shots.items(): data.register_qubit( CoherenceType, (qubit), {name: values[name].mean(axis=0) for name in values.dtype.names}, ) return data
[docs]def dynamical_decoupling_sequence( platform: Platform, targets: list[QubitId], wait: int = 0, n: int = 1, kind: str = "CPMG", ) -> tuple[PulseSequence, list[Delay]]: """Create dynamical decoupling sequence. Two sequences are available: - CP: RX90 (wait RX wait )^N RX90 - CPMG: RX90 (wait RY wait )^N RX90 """ assert kind in ["CPMG", "CP"], f"Unknown sequence {kind}, please use CP or CPMG" sequence = PulseSequence() all_delays = [] for qubit in targets: natives = platform.natives.single_qubit[qubit] qd_channel, rx90_pulse = natives.R(theta=np.pi / 2)[0] _, pulse = natives.R(phi=np.pi / 2)[0] if kind == "CPMG" else natives.RX()[0] ro_channel, ro_pulse = natives.MZ()[0] drive_delays = 2 * n * [Delay(duration=wait)] ro_delays = 2 * n * [Delay(duration=wait)] sequence.append((qd_channel, rx90_pulse)) for i in range(n): sequence.append((qd_channel, drive_delays[2 * i])) sequence.append((ro_channel, ro_delays[2 * i])) sequence.append((qd_channel, pulse)) sequence.append((qd_channel, drive_delays[2 * i + 1])) sequence.append((ro_channel, ro_delays[2 * i + 1])) sequence.append((qd_channel, rx90_pulse)) sequence.append( ( ro_channel, Delay(duration=2 * rx90_pulse.duration + n * pulse.duration), ) ) sequence.append((ro_channel, ro_pulse)) all_delays.extend(drive_delays) all_delays.extend(ro_delays) return sequence, all_delays
[docs]def exp_decay(x, *p): return p[0] - p[1] * np.exp(-1 * x / p[2])
[docs]def exponential_fit(data, zeno=False): qubits = data.qubits decay = {} fitted_parameters = {} pcovs = {} for qubit in qubits: voltages = data[qubit].signal times = data[qubit].wait try: y_max = np.max(voltages) y_min = np.min(voltages) y = (voltages - y_min) / (y_max - y_min) x_max = np.max(times) x_min = np.min(times) x = (times - x_min) / (x_max - x_min) p0 = [ 0.5, 0.5, 5, ] popt, pcov = curve_fit( exp_decay, x, y, p0=p0, maxfev=2000000, bounds=( [-2, -2, 0], [2, 2, np.inf], ), ) popt = [ (y_max - y_min) * popt[0] + y_min, (y_max - y_min) * popt[1] * np.exp(x_min / popt[2] / (x_max - x_min)), popt[2] * (x_max - x_min), ] fitted_parameters[qubit] = popt pcovs[qubit] = pcov.tolist() decay[qubit] = [popt[2], np.sqrt(pcov[2, 2]) * (x_max - x_min)] except Exception as e: log.warning(f"Exponential decay fit failed for qubit {qubit} due to {e}") return decay, fitted_parameters, pcovs
[docs]def exponential_fit_probability(data, zeno=False): qubits = data.qubits decay = {} fitted_parameters = {} chi2 = {} pcovs = {} for qubit in qubits: times = data[qubit].wait x_max = np.max(times) x_min = np.min(times) x = (times - x_min) / (x_max - x_min) probability = data[qubit].prob p0 = [ 0.5, 0.5, 5, ] try: popt, pcov = curve_fit( exp_decay, x, probability, p0=p0, maxfev=2000000, bounds=( [-2, -2, 0], [2, 2, np.inf], ), sigma=data[qubit].error, ) popt = [ popt[0], popt[1] * np.exp(x_min / (x_max - x_min) / popt[2]), popt[2] * (x_max - x_min), ] pcovs[qubit] = pcov.tolist() fitted_parameters[qubit] = popt dec = popt[2] decay[qubit] = [dec, np.sqrt(pcov[2, 2]) * (x_max - x_min)] chi2[qubit] = [ chi2_reduced( data[qubit].prob, exp_decay(data[qubit].wait, *fitted_parameters[qubit]), data[qubit].error, ), np.sqrt(2 / len(data[qubit].prob)), ] except Exception as e: log.warning(f"Exponential decay fit failed for qubit {qubit} due to {e}") return decay, fitted_parameters, pcovs, chi2
[docs]def plot(data, target: QubitId, fit=None) -> tuple[list[go.Figure], str]: """Plotting function for spin-echo or CPMG protocol.""" figures = [] fitting_report = "" qubit_data = data[target] waits = qubit_data.wait probs = qubit_data.prob error_bars = qubit_data.error fig = go.Figure( [ go.Scatter( x=waits, y=probs, opacity=1, name="Probability of 1", showlegend=True, legendgroup="Probability of 1", mode="lines", ), go.Scatter( x=np.concatenate((waits, waits[::-1])), y=np.concatenate((probs + error_bars, (probs - error_bars)[::-1])), fill="toself", fillcolor=COLORBAND, line=dict(color=COLORBAND_LINE), showlegend=True, name="Errors", ), ] ) if fit is not None: waitrange = np.linspace( min(waits), max(waits), 2 * len(qubit_data), ) params = fit.fitted_parameters[target] 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( target, ["T2", "chi2 reduced"], [fit.t2[target], fit.chi2[target]], display_error=True, ) ) fig.update_layout( showlegend=True, xaxis_title="Time [ns]", yaxis_title="Probability of State 1", ) figures.append(fig) return figures, fitting_report