Source code for qiskit_experiments.library.characterization.t2hahn

# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
T2Hahn Echo Experiment class.
"""

from typing import List, Optional, Union, Sequence
import numpy as np

from qiskit import QuantumCircuit, QiskitError
from qiskit.circuit import Parameter
from qiskit.providers.backend import Backend

from qiskit_experiments.framework import BackendTiming, BaseExperiment, Options
from qiskit_experiments.library.characterization.analysis.t2hahn_analysis import T2HahnAnalysis


[docs] class T2Hahn(BaseExperiment): r"""An experiment to measure the dephasing time insensitive to inhomogeneous broadening using Hahn echos. # section: overview This experiment is used to estimate the :math:`T_2` time of a single qubit. :math:`T_2` is the dephasing time or the transverse relaxation time of the qubit on the Bloch sphere as a result of both energy relaxation and pure dephasing in the transverse plane. Unlike :math:`T_2^*`, which is measured by :class:`.T2Ramsey`, :math:`T_2` is insensitive to inhomogenous broadening. This experiment consists of a series of circuits of the form .. parsed-literal:: ┌─────────┐┌──────────┐┌───────┐┌──────────┐┌─────────┐┌─┐ q_0: ┤ Rx(π/2) ├┤ DELAY(t) ├┤ RX(π) ├┤ DELAY(t) ├┤ RX(π/2) ├┤M├ └─────────┘└──────────┘└───────┘└──────────┘└─────────┘└╥┘ c: 1/════════════════════════════════════════════════════════╩═ 0 for each *t* from the specified delay times and the delays are specified by the user. The delays that are specified are delay for each delay gate while the delay in the metadata is the total delay which is delay * (num_echoes +1) The circuits are run on the device or on a simulator backend. # section: manual :doc:`/manuals/characterization/t2hahn` # section: analysis_ref :class:`T2HahnAnalysis` # section: example .. jupyter-execute:: :hide-code: # backend from qiskit_experiments.test.t2hahn_backend import T2HahnBackend conversion_factor = 1e-6 estimated_t2hahn = 20*conversion_factor backend = T2HahnBackend( t2hahn=[estimated_t2hahn], frequency=[100100], readout0to1 = [0.02], readout1to0 = [0.02], ) .. jupyter-execute:: import numpy as np from qiskit_experiments.library.characterization.t2hahn import T2Hahn delays = np.linspace(0, 50, 51)*1e-6 exp = T2Hahn(physical_qubits=(0, ), delays=delays, backend=backend) exp.analysis.set_options(p0=None, plot=True) exp_data = exp.run().block_for_results() display(exp_data.figure(0)) exp_data.analysis_results(dataframe=True) # section: reference .. ref_arxiv:: 1 1904.06560 """ @classmethod def _default_experiment_options(cls) -> Options: """Default experiment options. Experiment Options: delays (Iterable[float]): Delay times of the experiments. num_echoes (int): The number of echoes to preform. """ options = super()._default_experiment_options() options.delays = None options.num_echoes = 1 return options def __init__( self, physical_qubits: Sequence[int], delays: Union[List[float], np.array], num_echoes: int = 1, backend: Optional[Backend] = None, ): """ Initialize the T2 - Hahn Echo class. Args: physical_qubits: a single-element sequence containing the qubit whose T2 is to be estimated. delays: Total delay times of the experiments. backend: Optional, the backend to run the experiment on. num_echoes: The number of echoes to preform. backend: Optional, the backend to run the experiment on. Raises: QiskitError : Error for invalid input. """ # Initialize base experiment super().__init__(physical_qubits, analysis=T2HahnAnalysis(), backend=backend) # Set experiment options self.set_experiment_options(delays=delays, num_echoes=num_echoes) self._verify_parameters() def _verify_parameters(self): """ Verify input correctness, raise QiskitError if needed. Raises: QiskitError : Error for invalid input. """ if any(delay < 0 for delay in self.experiment_options.delays): raise QiskitError( f"The lengths list {self.experiment_options.delays} should only contain " "non-negative elements." )
[docs] def circuits(self) -> List[QuantumCircuit]: """ Return a list of experiment circuits. Each circuit consists of RX(π/2) followed by a sequence of delay gate, RX(π) for echo and delay gate again. The sequence repeats for the number of echoes and terminates with RX(±π/2). Returns: The experiment circuits. """ timing = BackendTiming(self.backend) delay_param = Parameter("delay") num_echoes = self.experiment_options.num_echoes # First X rotation in 90 degrees template = QuantumCircuit(1, 1) template.rx(np.pi / 2, 0) # Brings the qubit to the X Axis if num_echoes == 0: # if number of echoes is 0 then just apply the delay gate template.delay(delay_param, 0, timing.delay_unit) else: for _ in range(num_echoes): template.delay(delay_param, 0, timing.delay_unit) template.rx(np.pi, 0) template.delay(delay_param, 0, timing.delay_unit) if num_echoes % 2 == 1: template.rx(np.pi / 2, 0) # X90 again since the num of echoes is odd else: template.rx(-np.pi / 2, 0) # X(-90) again since the num of echoes is even template.measure(0, 0) # measure circuits = [] for delay in self.experiment_options.delays: if num_echoes == 0: single_delay = timing.delay_time(time=delay) total_delay = single_delay else: # Equal delay is put before and after each echo, so each echo gets # two delay gates. When there are multiple echoes, the total delay # between echoes is 2 * single_delay, made up of two delay gates. single_delay = timing.delay_time(time=delay / num_echoes / 2) total_delay = single_delay * num_echoes * 2 assigned = template.assign_parameters( {delay_param: timing.round_delay(time=single_delay)}, inplace=False ) assigned.metadata = {"xval": total_delay} circuits.append(assigned) return circuits
def _metadata(self): metadata = super()._metadata() # Store measurement level and meas return if they have been # set for the experiment for run_opt in ["meas_level", "meas_return"]: if hasattr(self.run_options, run_opt): metadata[run_opt] = getattr(self.run_options, run_opt) return metadata