 # Sequences

In some cases, one may want to intersperse ideal unitary gates within a sequence of time-dependent operations. This is possible using an object called a Sequence. A Sequence is essentially a list containing PulseSequences, Operations, and unitary operators. When Sequence.run(init_state) is called, the Sequence iterates over its constituent PulseSequences, Operations, and unitaries, applying each to the resulting state of the last.

Sequence is designed to behave like a Python list, so it has the following methods defined:

• append()

• extend()

• insert()

• pop()

• clear()

• __len__()

• __getitem__()

• __iter__()

Notes:

• Just like a PulseSequence or CompiledPulseSequence, a Sequence must be associated with a System.

• Whereas PulseSequence.run() and CompiledPulseSequence.run() return an instance of qutip.solver.Result, Sequence.run() returns a SequenceResult object, which behaves just like qutip.solver.Result. SequenceResult.states stores the quantum states after each stage of the simulation (states is init_state and states[-1] is the final state of the system).

:

%config InlineBackend.figure_formats = ['svg']
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import qutip
from sequencing import Transmon, Cavity, System, Sequence

:

qubit = Transmon('qubit', levels=3, kerr=-200e-3)
cavity = Cavity('cavity', levels=10, kerr=-10e-6)
system = System('system', modes=[qubit, cavity])
system.set_cross_kerr(cavity, qubit, chi=-2e-3)
qubit.gaussian_pulse.drag = 5


## Interleave pulses and unitaries

Here we perform a “$$\pi$$-pulse” composed of $$20$$ interleaved $$\frac{\pi}{40}$$-pulses and unitary rotations.

:

init_state = system.ground_state()
# calculate expectation value of |qubit=1, cavity=0>
e_ops = [system.fock_dm(qubit=1)]
n_rotations = 20
theta = np.pi / n_rotations

seq = Sequence(system)

for _ in range(n_rotations):

# Capture a PulseSequence
qubit.rotate_x(theta/2)

# # Alternatively, we can append an Operation
# operation = qubit.rotate_x(theta/2, capture=False)
# seq.append(operation)

# Append a unitary
seq.append(qubit.Rx(theta/2))

result = seq.run(init_state, e_ops=e_ops, full_evolution=True, progress_bar=True)
states = result.states

100%|██████████| 40/40 [00:00<00:00, 79.17it/s]


### Inspect the sequence

Sequence.plot_coefficients() plots Hamiltonian coefficients vs. time. Instantaneous unitary operations are represented by dashed vertical lines. If multiple unitaries occur at the same time, only a single dashed line is drawn.

:

fig, ax = seq.plot_coefficients(subplots=False)
ax.set_xlabel('Time [ns]')
ax.set_ylabel('Hamiltonian coefficient [GHz]')
fig.set_size_inches(8,4)
fig.tight_layout() :

print('len(states):', len(states))
print(f'state fidelity: {qutip.fidelity(states[-1], qubit.Rx(np.pi) * init_state)**2:.4f}')

len(states): 821
state fidelity: 0.9996


### Plot the results

:

e_pops = result.expect # probability of measuring the state |qubit=1, cavity=0>

fig, ax = plt.subplots(figsize=(8,4))
ax.plot(result.times, e_pops, '.')
ax.scatter(result.times[:1], e_pops[:1], marker='s', color='k', label='init_state')
# draw vertical lines at the location of each unitary rotation
for i in range(1, result.times.size // (2*n_rotations) + 1):
t = 2 * n_rotations * i - 1
label = 'unitaries' if i == 1 else None
ax.axvline(t, color='k', alpha=0.25, ls='--', lw=1.5, label=label)
ax.axhline(0, color='k', lw=1)
ax.axhline(1, color='k', lw=1)
ax.set_ylabel('$P(|e\\rangle)$')
ax.set_xlabel('Times [ns]')
ax.set_title('Interleaved pulses and unitaries')
ax.legend(loc=0); :

print(result)

SequenceResult
--------------
Number of times: 821
states = True
expect = True, num_expect = 1
num_collapse = 0

:

from qutip.ipynbtools import version_table
version_table()

:

SoftwareVersion
QuTiP4.7.0
Numpy1.23.2
SciPy1.9.1
matplotlib3.5.3
Cython0.29.32
Number of CPUs2
BLAS InfoOPENBLAS
IPython8.4.0
Python3.8.6 (default, Oct 19 2020, 15:10:29) [GCC 7.5.0]
OSposix [linux]
Tue Aug 30 19:15:43 2022 UTC
[ ]: