Skip to content

Commit 7c7e8c1

Browse files
authored
Implement bases whose elements have finite trace (#11)
* Implement non-traceless bases When dealing with different subspaces, one might want to generate an operator basis that completely separates into elements that live exclusively on the one and elements that live exclusively on the other subspace. This is not possible when forcing all operator bases to have an identity element (and thus the remaining elements traceless). The core feature affected by this change is the way a complete basis is generated from partial orthogonal elements. A switch now controls whether a traceless or non-traceless is generated. Additionally, the checks at the end of _full_from_partial() have been removed. The n elements from which the basis is constructed now are the first n of the final basis. Including non-traceless bases also shone light on a bug in calculate_error_transfer_matrix for a single qubit and the Pauli basis, which did not include contributions from identity elements in case of a cross-correlated spectrum. This induces non-unitality. * Cleanup and comments * Add more tests * Drop imaginary part of integrands when purely real * Fix bug for U.ndim > 2 * Add comments * Add utf-8 coding hint * Increase atol to prevent test from sometimes failing * Get n_oper_identifiers from pulse if available * Check same dims of control and noise Hamiltonian * Add function to more quickly compute exp(1j*x) This is a major time sink in the calculation of the control matrix using calculate_control_matrix_from_scratch(). Computing real and imaginary part separately and writing to a preallocated array should make the whole calculation faster by a factor of two. * Update notebooks due to deprecation warnings * Make QFT example work again * Update RB example to work with the current version * Add show_progressbar switch to error_transfer_matrix() * Remove test that didn't make sense * Project out subspace identity element in CNOT infid * Simplify oper_equiv * Replace :meth: with more accurate :func: sphinx refs * Catch non-traceless bases in infidelity() In this case, additional terms need to be considered. * Set correct cutoff frequencies and fix scaling in test_infidelity_cnot() * Sort imports
1 parent 33aefd3 commit 7c7e8c1

26 files changed

+14821
-15407
lines changed

doc/source/examples/advanced_concatenation.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@
244244
"name": "python",
245245
"nbconvert_exporter": "python",
246246
"pygments_lexer": "ipython3",
247-
"version": "3.7.4"
247+
"version": "3.7.6"
248248
}
249249
},
250250
"nbformat": 4,

doc/source/examples/calculating_error_transfer_matrices.ipynb

Lines changed: 13777 additions & 36 deletions
Large diffs are not rendered by default.

doc/source/examples/extending_pulses.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"outputs": [],
2222
"source": [
2323
"import numpy as np\n",
24-
"import qutip as qt\n",
24+
"from qutip.qip import operations\n",
2525
"\n",
2626
"import filter_functions as ff\n",
2727
"from filter_functions import util\n",
@@ -139,7 +139,7 @@
139139
"outputs": [],
140140
"source": [
141141
"cnot_12 = hadamard_2 @ cphase_12 @ hadamard_2\n",
142-
"util.oper_equiv(cnot_12.total_Q, qt.cnot(control=0, target=1))"
142+
"util.oper_equiv(cnot_12.total_Q, operations.cnot(control=0, target=1))"
143143
]
144144
},
145145
{
@@ -168,7 +168,7 @@
168168
"\n",
169169
"cnot_21 = ff.remap(cnot_12, (1, 0), oper_identifier_mapping=mapping)\n",
170170
"\n",
171-
"util.oper_equiv(cnot_21.total_Q, qt.cnot(control=1, target=0))\n",
171+
"util.oper_equiv(cnot_21.total_Q, operations.cnot(control=1, target=0))\n",
172172
"print(cnot_21.c_oper_identifiers, cnot_21.n_oper_identifiers, sep='\\t')"
173173
]
174174
},
@@ -230,7 +230,7 @@
230230
"name": "python",
231231
"nbconvert_exporter": "python",
232232
"pygments_lexer": "ipython3",
233-
"version": "3.7.4"
233+
"version": "3.7.6"
234234
}
235235
},
236236
"nbformat": 4,

doc/source/examples/getting_started.ipynb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,13 @@
252252
"source": [
253253
"The discrepancy to the analytical solution is due to the finite pulse width we have chosen here. We can see that, contrary to the FID-like filter function along $\\sigma_x$, the dephasing filter function of the SE pulse drops to (almost) zero for DC noise, affirming that indeed the pulse enhances coherence as it filters out frequencies slower than the pulse duration."
254254
]
255+
},
256+
{
257+
"cell_type": "code",
258+
"execution_count": null,
259+
"metadata": {},
260+
"outputs": [],
261+
"source": []
255262
}
256263
],
257264
"metadata": {
@@ -270,7 +277,7 @@
270277
"name": "python",
271278
"nbconvert_exporter": "python",
272279
"pygments_lexer": "ipython3",
273-
"version": "3.7.4"
280+
"version": "3.7.6"
274281
}
275282
},
276283
"nbformat": 4,

doc/source/examples/periodic_driving.ipynb

Lines changed: 114 additions & 114 deletions
Large diffs are not rendered by default.

doc/source/examples/quantum_fourier_transform.ipynb

Lines changed: 208 additions & 14762 deletions
Large diffs are not rendered by default.

doc/source/examples/qutip_integration.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
"name": "python",
139139
"nbconvert_exporter": "python",
140140
"pygments_lexer": "ipython3",
141-
"version": "3.7.4"
141+
"version": "3.7.6"
142142
}
143143
},
144144
"nbformat": 4,

examples/qft.py

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,13 @@
2929
Hamiltonians: Example with ion traps, 1(1), 1–7.
3030
Retrieved from http://arxiv.org/abs/1503.08806
3131
"""
32-
import filter_functions as ff
3332
import numpy as np
34-
import qutip as qt
35-
# %% Define some functions
36-
37-
38-
def get_a_k(k, n, N: int = 4):
39-
if k < n:
40-
a_k = 0
41-
elif k == n:
42-
a_k = 1/np.sqrt(2**(N - n + 4))
43-
else:
44-
a_k = 2**(n - k - 1)/get_a_k(n, n, N)
45-
46-
return a_k
4733

34+
import filter_functions as ff
35+
import qutip as qt
36+
from qutip import qip
4837

49-
def get_J_kl(k, l, n, N: int = 4):
50-
return -np.pi*get_a_k(k, n, N)*get_a_k(l, n, N)/2
38+
# %% Define some functions
5139

5240

5341
def R_k_pulse(k, theta, phi, N: int = 4, tau: float = 1):
@@ -63,10 +51,10 @@ def R_k_pulse(k, theta, phi, N: int = 4, tau: float = 1):
6351
X = qt.tensor(X)
6452
Y = qt.tensor(Y)
6553

66-
H_c = [[X*np.cos(phi), [theta/2/tau]],
67-
[Y*np.sin(phi), [theta/2/tau]]]
68-
H_n = [[X/np.sqrt(X.shape[0]), [np.sqrt(X.shape[0])]],
69-
[Y/np.sqrt(Y.shape[0]), [np.sqrt(Y.shape[0])]]]
54+
H_c = [[X, [theta/2/tau*np.cos(phi)], 'I'*k + 'X' + 'I'*(N - k - 1)],
55+
[Y, [theta/2/tau*np.sin(phi)], 'I'*k + 'Y' + 'I'*(N - k - 1)]]
56+
H_n = [[X/np.sqrt(X.shape[0]), [1], 'I'*k + 'X' + 'I'*(N - k - 1)],
57+
[Y/np.sqrt(Y.shape[0]), [1], 'I'*k + 'Y' + 'I'*(N - k - 1)]]
7058
dt = [tau]
7159

7260
return ff.PulseSequence(H_c, H_n, dt)
@@ -76,17 +64,19 @@ def T_I_pulse(N: int = 4, tau: float = 1):
7664
Id = qt.qeye(2)
7765
if N == 1:
7866
T = Id
79-
H_c = [[Id, [0]]]
80-
H_n = [[T/np.sqrt(T.shape[0]), [np.sqrt(T.shape[0])]]]
67+
H_c = [[Id, [0], 'I']]
68+
H_n = [[T/np.sqrt(T.shape[0]), [1], 'I']]
8169
else:
8270
H_c = []
8371
H_n = []
8472
T = [Id]*(N - 1)
8573
T.insert(0, qt.sigmaz())
8674
for k in range(1, N+1):
87-
H_c.append([qt.tensor(np.roll(T, k-1)),
88-
[np.pi/4*(1 - 2**(1 - k))/tau]])
89-
H_n.append([qt.tensor(T)/np.sqrt(2**len(T)), [np.sqrt(2**len(T))]])
75+
H_c.append([qt.tensor(T[-k+1:] + T[:-k+1]),
76+
[np.pi/4*(1 - 2**(1 - k))/tau],
77+
'I'*(k - 1) + 'Z' + 'I'*(N - k)])
78+
H_n.append([qt.tensor(T[-k+1:] + T[:-k+1])/np.sqrt(2**len(T)),
79+
[1], 'I'*(k - 1) + 'Z' + 'I'*(N - k)])
9080

9181
return ff.PulseSequence(H_c, H_n, [tau])
9282

@@ -95,38 +85,35 @@ def T_F_pulse(N: int = 4, tau: float = 1):
9585
Id = qt.qeye(2)
9686
if N == 1:
9787
T = Id
98-
H_c = [[Id, [0]]]
99-
H_n = [[T/np.sqrt(T.shape[0]), [np.sqrt(T.shape[0])]]]
88+
H_c = [[Id, [0], 'I']]
89+
H_n = [[T/np.sqrt(T.shape[0]), [1], 'I']]
10090
else:
10191
H_c = []
10292
H_n = []
10393
T = [Id]*(N - 1)
10494
T.insert(0, qt.sigmaz())
10595
for k in range(1, N+1):
106-
H_c.append([qt.tensor(np.roll(T, k-1)),
107-
[np.pi/4*(1 - 2**(k - N))/tau]])
108-
H_n.append([qt.tensor(T)/np.sqrt(2**len(T)), [np.sqrt(2**len(T))]])
96+
H_c.append([qt.tensor(T[-k+1:] + T[:-k+1]),
97+
[np.pi/4*(1 - 2**(k - N))/tau],
98+
'I'*(k - 1) + 'Z' + 'I'*(N - k)])
99+
H_n.append([qt.tensor(T[-k+1:] + T[:-k+1])/np.sqrt(2**len(T)),
100+
[1], 'I'*(k - 1) + 'Z' + 'I'*(N - k)])
109101

110102
return ff.PulseSequence(H_c, H_n, [tau])
111103

112104

113-
def U_pulse(n, N: int = 4, tau: float = 1):
105+
def P_n_pulse(n, N: int = 4, tau: float = 1):
114106
Id = qt.qeye(2)
115-
if N == 2:
116-
Z = qt.tensor([qt.sigmaz()]*2)
117-
H_c = [[Z, [get_J_kl(1, 2, 1, N)/tau]]]
118-
H_n = [[Z/np.sqrt(Z.shape[0]), [np.sqrt(Z.shape[0])]]]
119-
else:
120-
H_c = []
121-
H_n = []
122-
for k in range(n, N+1):
123-
for l in range(k+1, N+1):
124-
Z = [Id]*(N - 2)
125-
Z.insert(k-1, qt.sigmaz())
126-
Z.insert(l-1, qt.sigmaz())
127-
Z = qt.tensor(Z)
128-
H_c.append([Z, [get_J_kl(k, l, n, N)/tau]])
129-
H_n.append([Z/np.sqrt(Z.shape[0]), [np.sqrt(Z.shape[0])]])
107+
H_c = []
108+
H_n = []
109+
for l in range(n+1, N+1):
110+
Z = [Id]*(N - 2)
111+
Z.insert(n-1, qt.sigmaz())
112+
Z.insert(l-1, qt.sigmaz())
113+
Z = qt.tensor(Z)
114+
identifier = ('I'*(n-1) + 'Z' + 'I'*(l - n - 1) + 'Z' + 'I'*(N - l))
115+
H_c.append([Z, [-np.pi/4*2**(n - l)/tau], identifier])
116+
H_n.append([Z/np.sqrt(Z.shape[0]), [1], identifier])
130117

131118
return ff.PulseSequence(H_c, H_n, [tau])
132119

@@ -140,7 +127,7 @@ def QFT_pulse(N: int = 4, tau: float = 1):
140127
pulses = [T_I_pulse(N, tau)]
141128
for n in range(N-1):
142129
pulses.append(H_k_pulse(n, N, tau))
143-
pulses.append(U_pulse(n+1, N, tau))
130+
pulses.append(P_n_pulse(n+1, N, tau))
144131

145132
pulses.append(H_k_pulse(N-1, N, tau))
146133
pulses.append(T_F_pulse(N, tau))
@@ -150,7 +137,18 @@ def QFT_pulse(N: int = 4, tau: float = 1):
150137

151138
# %% Get a 4-qubit QFT and plot the filter function
152139
omega = np.logspace(-2, 2, 500)
153-
QFT = QFT_pulse(4)
140+
141+
N = 4
142+
QFT = QFT_pulse(N)
143+
144+
# Check the pulse produces the correct propagator after swapping the qubits
145+
swaps = [qip.operations.swap(N, [i, j]).full()
146+
for i, j in zip(range(N//2), range(N-1, N//2-1, -1))]
147+
prop = ff.util.mdot(swaps) @ QFT.total_Q
148+
qt.matrix_histogram_complex(prop)
149+
print('Correct action: ',
150+
ff.util.oper_equiv(prop, qip.algorithms.qft.qft(N), eps=1e-14))
151+
154152
fig, ax, _ = ff.plot_filter_function(QFT, omega)
155153
# Move the legend to the side because of many entries
156-
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
154+
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)

examples/randomized_benchmarking.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,17 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int,
9797
H_n = {}
9898
H_c = {}
9999
dt = {}
100-
H_c['Id'] = [[qt.sigmax().full(), [0]]]
101-
H_n['Id'] = [[qt.sigmax().full(), [1]],
102-
[qt.sigmay().full(), [1]]]
100+
H_c['Id'] = [[qt.sigmax().full(), [0], 'X']]
101+
H_n['Id'] = [[qt.sigmax().full(), [1], 'X'],
102+
[qt.sigmay().full(), [1], 'Y']]
103103
dt['Id'] = [T]
104-
H_c['X2'] = [[qt.sigmax().full(), [np.pi/4/T]]]
105-
H_n['X2'] = [[qt.sigmax().full(), [1]],
106-
[qt.sigmay().full(), [1]]]
104+
H_c['X2'] = [[qt.sigmax().full(), [np.pi/4/T], 'X']]
105+
H_n['X2'] = [[qt.sigmax().full(), [1], 'X'],
106+
[qt.sigmay().full(), [1], 'Y']]
107107
dt['X2'] = [T]
108-
H_c['Y2'] = [[qt.sigmay().full(), [np.pi/4/T]]]
109-
H_n['Y2'] = [[qt.sigmax().full(), [1]],
110-
[qt.sigmay().full(), [1]]]
108+
H_c['Y2'] = [[qt.sigmay().full(), [np.pi/4/T], 'Y']]
109+
H_n['Y2'] = [[qt.sigmax().full(), [1], 'X'],
110+
[qt.sigmay().full(), [1], 'Y']]
111111
dt['Y2'] = [T]
112112

113113
# %% Set up PulseSequences
@@ -169,7 +169,7 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int,
169169
# Scaling factor for the noise so that alpha = 0 and alpha = 0.7 have the same
170170
# power
171171
noise_scaling_factor = {
172-
0.0: 0.39651222009884685,
172+
0.0: 0.4415924985735799,
173173
0.7: 1
174174
}
175175

@@ -200,16 +200,16 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int,
200200
for i, alpha in enumerate((0.0, 0.7)):
201201

202202
means = np.mean(fidelities[alpha], axis=1)
203-
errs = np.std(fidelities[alpha], axis=1)/np.sqrt(N_G)
203+
stds = np.std(fidelities[alpha], axis=1)
204204

205-
popt, pcov = optimize.curve_fit(fitfun, lengths, means, [0, 1], errs,
205+
popt, pcov = optimize.curve_fit(fitfun, lengths, means, [0, 1], stds,
206206
absolute_sigma=True)
207207

208208
for j in range(N_G):
209209
fid = ax[i].plot(lengths, fidelities[alpha][:, j], 'k.', alpha=0.1,
210210
zorder=2)
211211

212-
mean = ax[i].errorbar(lengths, means, errs, fmt='.', zorder=3,
212+
mean = ax[i].errorbar(lengths, means, stds, fmt='.', zorder=3,
213213
color='tab:red')
214214
fit = ax[i].plot(lengths, fitfun(lengths, *popt), '--', zorder=4,
215215
color='tab:red')

filter_functions/__init__.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
# =============================================================================
23
# filter_functions
34
# Copyright (C) 2019 Quantum Technology Group, RWTH Aachen University
@@ -21,21 +22,21 @@
2122

2223
from . import analytic, basis, numeric, plotting, pulse_sequence, util
2324
from .basis import Basis
24-
from .numeric import (infidelity, liouville_representation,
25-
error_transfer_matrix)
26-
from .plotting import (plot_bloch_vector_evolution, plot_filter_function,
27-
plot_pulse_correlation_filter_function,
28-
plot_pulse_train, plot_error_transfer_matrix)
25+
from .numeric import (error_transfer_matrix, infidelity,
26+
liouville_representation)
27+
from .plotting import (
28+
plot_bloch_vector_evolution, plot_error_transfer_matrix,
29+
plot_filter_function, plot_pulse_correlation_filter_function,
30+
plot_pulse_train)
2931
from .pulse_sequence import (PulseSequence, concatenate, concatenate_periodic,
3032
extend, remap)
3133

32-
__all__ = ['PulseSequence', 'concatenate', 'extend', 'Basis', 'numeric',
33-
'plot_filter_function', 'plot_bloch_vector_evolution',
34-
'plot_pulse_train', 'util', 'plotting', 'analytic', 'basis',
35-
'infidelity', 'plot_pulse_correlation_filter_function',
36-
'concatenate_periodic', 'pulse_sequence', 'remap',
37-
'error_transfer_matrix', 'plot_error_transfer_matrix',
38-
'liouville_representation']
34+
__all__ = ['Basis', 'PulseSequence', 'analytic', 'basis', 'concatenate',
35+
'concatenate_periodic', 'error_transfer_matrix', 'extend',
36+
'infidelity', 'liouville_representation', 'numeric',
37+
'plot_bloch_vector_evolution', 'plot_error_transfer_matrix',
38+
'plot_filter_function', 'plot_pulse_correlation_filter_function',
39+
'plot_pulse_train', 'plotting', 'pulse_sequence', 'remap', 'util']
3940

4041
__version__ = '0.1.0'
4142
__license__ = 'GNU GPLv3+'

0 commit comments

Comments
 (0)