Note

This is the documentation for the current state of the development branch of Qiskit Experiments. The documentation or APIs here can change prior to being released.

T2 Hahn Characterization

The purpose of the T2 Hahn Echo experiment is to determine the T2 qubit property.

In this experiment, we would like to get a more precise estimate of the qubit’s decay time. T2 represents the amount of time required for a single qubit’s Bloch vector projection on the XY plane to fall to approximately 37% (1e) of its initial amplitude. Unlike T2, which is measured by T2Ramsey, T2 is insensitive to inhomogenous broadening. Hahn Echo experiment and the Carr-Purcell-Meiboom-Gill (CPMG) sequence are experiments to estimate T2 which are robust to the detuning frequency, or the difference between the qubit frequency and the pulse frequency of the applied rotation. The decay in amplitude causes the probability function to take the following form:

f(t)=AetT2+B

The difference between the Hahn Echo and CPMG sequence is that in the Hahn Echo experiment, there is only one echo sequence while in CPMG there are multiple echo sequences.

Decoherence Time

The decoherence time is the time taken for off-diagonal components of the density matrix to fall to approximately 37% (1e). For tT2, the qubit statistics behave like a random bit, with value 0 with probability of p and value 1 with probability 1p.

Since the qubit is exposed to other types of noise (like T1), we are using Rx(π) pulses for decoupling and to solve our inaccuracy for the qubit frequency estimation.

import qiskit
from qiskit_experiments.library.characterization.t2hahn import T2Hahn

The circuit used for an experiment with N echoes comprises the following components:

  1. Rx(π2) gate

  2. N times echo sequence:

    1. Delay(t0) gate

    2. Rx(π) gate

    3. Delay(t0) gate

  3. Rx(±π2) gate (sign depends on the number of echoes)

  4. Measurement gate

The user provides as input a series of delays in seconds. During the delay, we expect the qubit to precess about the z-axis. Because of the echo gate (Rx(π)) for each echo, the angle after the delay gates will be θnew=θold+π. After waiting the same delay time, the angle will be approximately 0 or π. By varying the extension of the delays, we get a series of decaying measurements. We can draw the graph of the resulting function and can analytically extract the desired values.

qubit = 0
conversion_factor = 1e-6 # our delay will be in micro-sec
delays = list(range(0, 51, 5) )
# Round so that the delay gates in the circuit display does not have trailing 9999's
delays = [round(float(_) * conversion_factor, ndigits=6) for _ in delays]
number_of_echoes = 1

# Create a T2Hahn experiment. Print the first circuit as an example
exp1 = T2Hahn(physical_qubits=(qubit,), delays=delays, num_echoes=number_of_echoes)
print(exp1.circuits()[1])
     ┌─────────┐┌───────────────────┐┌───────┐┌───────────────────┐┌─────────┐»
  q: ┤ Rx(π/2) ├┤ Delay(2.5e-06[s]) ├┤ Rx(π) ├┤ Delay(2.5e-06[s]) ├┤ Rx(π/2) ├»
     └─────────┘└───────────────────┘└───────┘└───────────────────┘└─────────┘»
c: 1/═════════════════════════════════════════════════════════════════════════»
                                                                              »
«     ┌─┐
«  q: ┤M├
«     └╥┘
«c: 1/═╩═
«      0 

We run the experiment on a simple simulated backend tailored specifically for this experiment.

from qiskit_experiments.test.t2hahn_backend import T2HahnBackend

estimated_t2hahn = 20 * conversion_factor
# The behavior of the backend is determined by the following parameters
backend = T2HahnBackend(
    t2hahn=[estimated_t2hahn],
    frequency=[100100],
    initialization_error=[0.0],
    readout0to1=[0.02],
    readout1to0=[0.02],
)

The resulting graph will have the form: f(t)=AetT2+B where t is the delay and T2 is the decay factor.

exp1.analysis.set_options(p0=None, plot=True)
expdata1 = exp1.run(backend=backend, shots=2000, seed_simulator=101)
expdata1.block_for_results()  # Wait for job/analysis to finish.

# Display the figure
display(expdata1.figure(0))
../../_images/t2hahn_3_0.png
# Print results
display(expdata1.analysis_results(dataframe=True))
name experiment components value quality backend run_time chisq unit
8d0f44dd T2 T2Hahn [Q0] (2.11+/-0.16)e-05 good T2Hahn_simulator None 1.062618 s

Providing initial user estimates

The user can provide initial estimates for the parameters to help the analysis process. In the initial guess, the keys {amp, tau, base} correspond to the parameters {A, T_2, B} respectively. Because the curve is expected to decay toward 0.5, the natural choice for parameter B is 0.5. When there is no T2 error, we would expect that the probability to measure 1 is 100%, therefore we will guess that A is 0.5. In this experiment, t2hahn is the parameter of interest. A good estimate for it is the value computed in previous experiments on this qubit or a similar value computed for other qubits.

exp_with_p0 = T2Hahn(physical_qubits=(qubit,), delays=delays, num_echoes=number_of_echoes)
exp_with_p0.analysis.set_options(p0={"amp": 0.5, "tau": estimated_t2hahn, "base": 0.5})
expdata_with_p0 = exp_with_p0.run(backend=backend, shots=2000, seed_simulator=101)
expdata_with_p0.block_for_results()

# Display fit figure
display(expdata_with_p0.figure(0))
../../_images/t2hahn_5_0.png
# Print results
display(expdata_with_p0.analysis_results(dataframe=True))
name experiment components value quality backend run_time chisq unit
ad1e02c8 T2 T2Hahn [Q0] (2.11+/-0.16)e-05 good T2Hahn_simulator None 1.062618 s

Number of echoes

The user can provide the number of echoes that the circuit will perform. This will determine the amount of delay and echo gates. The echoes decrease the effects of T1 noise and frequency inaccuracy estimation. Due to that, the Hahn Echo experiment improves our estimate for T2. In the following code, we will compare results of the Hahn experiment with 0 echoes and 1 echo. The analysis should fail for the circuit with 0 echoes. In order to see it, we will add frequency to the qubit and see how it affect the estimated T2.

import numpy as np

qubit2 = 0

num_echoes = 1
estimated_t2hahn2 = 30e-6

# Create a T2Hahn experiment with 0 echoes
exp2_0echoes = T2Hahn((qubit2,), delays, num_echoes=0)
exp2_0echoes.analysis.set_options(p0={"amp": 0.5, "tau": estimated_t2hahn2, "base": 0.5})
print("The second circuit of hahn echo experiment with 0 echoes:")
print(exp2_0echoes.circuits()[1])

# Create a T2Hahn experiment with 1 echo. Print the first circuit as an example
exp2_1echoes = T2Hahn((qubit2,), delays, num_echoes=num_echoes)
exp2_1echoes.analysis.set_options(p0={"amp": 0.5, "tau": estimated_t2hahn2, "base": 0.5})
print("The second circuit of hahn echo experiment with 1 echo:")
print(exp2_1echoes.circuits()[1])
The second circuit of hahn echo experiment with 0 echoes:
     ┌─────────┐┌─────────────────┐┌──────────┐┌─┐
  q: ┤ Rx(π/2) ├┤ Delay(5e-06[s]) ├┤ Rx(-π/2) ├┤M├
     └─────────┘└─────────────────┘└──────────┘└╥┘
c: 1/═══════════════════════════════════════════╩═
                                                0 
The second circuit of hahn echo experiment with 1 echo:
     ┌─────────┐┌───────────────────┐┌───────┐┌───────────────────┐┌─────────┐»
  q: ┤ Rx(π/2) ├┤ Delay(2.5e-06[s]) ├┤ Rx(π) ├┤ Delay(2.5e-06[s]) ├┤ Rx(π/2) ├»
     └─────────┘└───────────────────┘└───────┘└───────────────────┘└─────────┘»
c: 1/═════════════════════════════════════════════════════════════════════════»
                                                                              »
«     ┌─┐
«  q: ┤M├
«     └╥┘
«c: 1/═╩═
«      0 
from qiskit_experiments.test.t2hahn_backend import T2HahnBackend

detuning_frequency = 2 * np.pi * 10000

# The behavior of the backend is determined by the following parameters
backend2 = T2HahnBackend(
    t2hahn=[estimated_t2hahn2],
    frequency=[detuning_frequency],
    initialization_error=[0.0],
    readout0to1=[0.02],
    readout1to0=[0.02],)

# Analysis for Hahn Echo experiment with 0 echoes.
expdata2_0echoes = exp2_0echoes.run(backend=backend2, shots=2000, seed_simulator=101)
expdata2_0echoes.block_for_results()  # Wait for job/analysis to finish.

# Analysis for Hahn Echo experiment with 1 echo
expdata2_1echoes = exp2_1echoes.run(backend=backend2, shots=2000, seed_simulator=101)
expdata2_1echoes.block_for_results()  # Wait for job/analysis to finish.

# Display the figure
print("Hahn Echo with 0 echoes:")
display(expdata2_0echoes.figure(0))
print("Hahn Echo with 1 echo:")
display(expdata2_1echoes.figure(0))
Hahn Echo with 0 echoes:
../../_images/t2hahn_8_1.png
Hahn Echo with 1 echo:
../../_images/t2hahn_8_3.png

We see that the estimate T2 is different in the two plots. The mock backend for this experiment used T2=30[μs], which is close to the estimate of the one echo experiment.

See also