ONV 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ONV-0.0.1.dist-info/METADATA +15 -0
- ONV-0.0.1.dist-info/RECORD +12 -0
- ONV-0.0.1.dist-info/WHEEL +5 -0
- ONV-0.0.1.dist-info/top_level.txt +3 -0
- cce/CCE.py +305 -0
- cce/PCE.py +632 -0
- cce/__init__.py +3 -0
- nv/__init__.py +10 -0
- nv/nv.py +2091 -0
- nv/p1.py +175 -0
- tools/__init__.py +1 -0
- tools/plot_tools.py +136 -0
nv/nv.py
ADDED
|
@@ -0,0 +1,2091 @@
|
|
|
1
|
+
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
3
|
+
# NV_library.py by Oliver Whaites
|
|
4
|
+
# A library of functions that are useful when simulating NV systems under Dynamical Decoupling
|
|
5
|
+
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
6
|
+
#------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import qutip
|
|
10
|
+
import progressbar as pb
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import matplotlib as mpl
|
|
14
|
+
from numpy import random
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import scipy
|
|
17
|
+
from matplotlib.ticker import AutoMinorLocator
|
|
18
|
+
from scipy.signal.windows import gaussian
|
|
19
|
+
|
|
20
|
+
options = qutip.solver.Options()
|
|
21
|
+
options.store_states = True
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
OPERATIONS
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def params():
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
Returns the default parameters that define and evaluate the model.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
-----------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-----------
|
|
38
|
+
|
|
39
|
+
params: dictionary of parameters required to determine and calculate the model
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
params = {}
|
|
44
|
+
|
|
45
|
+
#general physics constants
|
|
46
|
+
params['gamma_e'] = -28.025e9;#Hz/T
|
|
47
|
+
params['gamma_C'] = 10.705e6;#Hz/T
|
|
48
|
+
params['gamma_H'] = 42.577478518e6;#Hz/T
|
|
49
|
+
params['gamma_N'] = 3.077e6;#Hz/T
|
|
50
|
+
params['gamma_14N'] = 3.077e6;#Hz/T
|
|
51
|
+
params['h'] = 6.62607e-34; #kg*m**2/s
|
|
52
|
+
params['mu_red'] = 1e-7;#N/A**2
|
|
53
|
+
params['zfs_NV'] = 2.87e9
|
|
54
|
+
params['k_B'] = 1.380649e-23
|
|
55
|
+
params['Temp'] = 300
|
|
56
|
+
|
|
57
|
+
# model parameters
|
|
58
|
+
params['B0'] = 403e-4#external magnetic field strength
|
|
59
|
+
params['B_mis'] = np.radians(0)
|
|
60
|
+
params['omegaL'] = 2*np.pi*params['B0']*10.705e6#nuclear larmour frequency
|
|
61
|
+
params['N_nuc'] = 1;#the number of nuclei in the system
|
|
62
|
+
params['Overhauser'] = 0
|
|
63
|
+
|
|
64
|
+
params['decoherence'] = False
|
|
65
|
+
params['T2'] = 1e-3
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
#pulse protocol parameters
|
|
69
|
+
params['Tp'] = 32e-9#duration of a pi pulse
|
|
70
|
+
params['Omega'] = np.pi/params['Tp']
|
|
71
|
+
params['tau'] = 0#the wait time of the pulse protocol
|
|
72
|
+
params['t_ev'] = 0
|
|
73
|
+
params['delta'] = 0#any pulse error introduced
|
|
74
|
+
params['protocol'] = 'CPMG'
|
|
75
|
+
params['pulse_profile'] = 'inst'
|
|
76
|
+
params['Np'] = 1#number of pulses of protocol
|
|
77
|
+
|
|
78
|
+
#plotting parameters
|
|
79
|
+
params['Delta'] = 0#the width of a tau sweep
|
|
80
|
+
params['Reps'] = 100
|
|
81
|
+
|
|
82
|
+
#evolution time average parameters
|
|
83
|
+
params['t_wait'] = 0
|
|
84
|
+
params['t_wait_mu'] = 13e-6;
|
|
85
|
+
params['t_wait_sig'] = 0.5e-6
|
|
86
|
+
params['num_avg'] = 10
|
|
87
|
+
|
|
88
|
+
#cystal parameters
|
|
89
|
+
params['vecs'] = [[0,0,0],#basis atom
|
|
90
|
+
[0,0.5,0.5],
|
|
91
|
+
[0.5,0,0.5],
|
|
92
|
+
[0.5,0.5,0],
|
|
93
|
+
[0.25,0.25,0.25],#basis atom
|
|
94
|
+
[0.25,0.75,0.75],
|
|
95
|
+
[0.75,0.25,0.75],
|
|
96
|
+
[0.75,0.75,0.25]]
|
|
97
|
+
#all above are normalised, return to real vectors by *lattice constant
|
|
98
|
+
params['bond_size'] = 1.54e-10;# distance between basis atoms = lattice_constant*sqrt(3)/4
|
|
99
|
+
params['lattice_constant'] = 3.57e-10;
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
return params
|
|
103
|
+
|
|
104
|
+
def plot_params():
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
Returns the default parameters that are used when plotting
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
-----------
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-----------
|
|
115
|
+
|
|
116
|
+
params: dictionary of parameters required to plot figures
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
# key parameters for the model, as described in Spearman 2018
|
|
120
|
+
params = {}
|
|
121
|
+
# model parameters
|
|
122
|
+
params['background_color'] = 'white';
|
|
123
|
+
params['text_color'] = 'black';
|
|
124
|
+
params['line_color'] = 'black';
|
|
125
|
+
params['line_style'] = '-';
|
|
126
|
+
params['line_width'] = 2
|
|
127
|
+
params['tick_color'] = 'black'
|
|
128
|
+
params['line_alpha'] = 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
return params
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
species_spin = {'C':int(1/2*2 + 1),
|
|
136
|
+
'14N':int(1*2 + 1),
|
|
137
|
+
'N':int(1/2*2 + 1),
|
|
138
|
+
'NV':int(1/2*2 + 1),
|
|
139
|
+
'NV_full':int(1*2 + 1),
|
|
140
|
+
'e': int(1/2*2 + 1)}
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
FUNCTIONS FOR GENERAL QM OPERATIONS
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
#a function that rotates a 3D vector around the x axis anticlockwise by theta radians
|
|
148
|
+
def x_rotation(vec,theta):
|
|
149
|
+
|
|
150
|
+
Rx = np.array([[1,0,0],[0,np.cos(theta),-np.sin(theta)],[0,np.sin(theta),np.cos(theta)]]);
|
|
151
|
+
|
|
152
|
+
return np.dot(Rx,vec)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
#a function that rotates a vector around the y axis anticlockwise by theta radians
|
|
156
|
+
def y_rotation(vec,theta,passive = False):
|
|
157
|
+
|
|
158
|
+
Ry = np.array([[np.cos(theta),0,np.sin(theta)],[0,1,0],[-np.sin(theta),0,np.cos(theta)]]);
|
|
159
|
+
if passive == True:
|
|
160
|
+
Ry = Ry.T
|
|
161
|
+
|
|
162
|
+
if vec.shape == (3,):
|
|
163
|
+
|
|
164
|
+
return np.dot(Ry,vec)
|
|
165
|
+
else:
|
|
166
|
+
return np.array([np.dot(Ry,v) for v in vec])
|
|
167
|
+
|
|
168
|
+
def xy_rotation(vec,theta,phase):
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
ux = np.cos(phase)
|
|
172
|
+
uy = -np.sin(phase)
|
|
173
|
+
uz = 0
|
|
174
|
+
|
|
175
|
+
c = np.cos(theta)
|
|
176
|
+
s = np.sin(theta)
|
|
177
|
+
|
|
178
|
+
R = np.array([[c + ux**2*(1 - c),ux*uy*(1 - c) - uz*s,ux*uz*(1 - c) + uy*s],
|
|
179
|
+
[uy*ux*(1 - c) + uz*s,c + uy**2*(1 - c),uy*uz*(1 - c) - ux*s],
|
|
180
|
+
[uz*ux*(1 - c) - uy*s,uz*uy*(1 - c) + ux*s,c + uz**2*(1 - c)]])
|
|
181
|
+
|
|
182
|
+
return R.dot(vec)
|
|
183
|
+
|
|
184
|
+
# a function which changes the basis of the lattice coordinates to one in line with NV-axis
|
|
185
|
+
def change_basis(vec):
|
|
186
|
+
|
|
187
|
+
#Two rotations about the z by pi/4 then the x by ...
|
|
188
|
+
vectemp = z_rotation(vec,np.pi/4);
|
|
189
|
+
vectemp = x_rotation(vectemp,np.arctan(np.sqrt(2)));
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
return vectemp
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
#a function that rotates a vector around the z axis anticlockwise by theta radians
|
|
196
|
+
def z_rotation(vec,theta):
|
|
197
|
+
|
|
198
|
+
Rz = np.array([[np.cos(theta),-np.sin(theta),0],[np.sin(theta),np.cos(theta),0],[0,0,1]]);
|
|
199
|
+
|
|
200
|
+
return np.dot(Rz,vec)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def cc_coupling(spin,ref,params = params(),species = 'C'):
|
|
204
|
+
|
|
205
|
+
x = spin['x'] - ref['x']
|
|
206
|
+
y = spin['y'] - ref['y']
|
|
207
|
+
z = spin['z'] - ref['z']
|
|
208
|
+
|
|
209
|
+
r = np.sqrt(x**2 + y**2 + z**2); #distance between atoms
|
|
210
|
+
|
|
211
|
+
const = -(params['h']/(2*np.pi))*params['mu_red']*(2*np.pi*params['gamma_{}'.format(species)])**2/(r**3);
|
|
212
|
+
|
|
213
|
+
Cij = const*(3*(z**2)/(r**2) - 1);
|
|
214
|
+
|
|
215
|
+
return Cij/(2*np.pi)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
#function to find the hyperfine coupling constants between the vacancy and a particular atom in the lattice
|
|
219
|
+
def hyperfine(r,params = params(),species = 'C'):
|
|
220
|
+
#r must be introdued in Angstoms
|
|
221
|
+
x = r[0]*1e-10;
|
|
222
|
+
y = r[1]*1e-10;
|
|
223
|
+
z = r[2]*1e-10;
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
r = np.sqrt(x**2 + y**2 + z**2); #distance between atoms
|
|
227
|
+
|
|
228
|
+
const = -(params['h']/(2*np.pi))*params['mu_red']*2*np.pi*params['gamma_e']*2*np.pi*params['gamma_{}'.format(species)]/(r**3);
|
|
229
|
+
|
|
230
|
+
#find the coupling constants in units kHz*2pi/T
|
|
231
|
+
Ax = const*(3*z*x/(r**2))*1e-3;
|
|
232
|
+
Ay = const*(3*z*y/(r**2))*1e-3;
|
|
233
|
+
Az = const*(3*(z*z)/(r**2) - 1)*1e-3;
|
|
234
|
+
|
|
235
|
+
A = [Ax/(2*np.pi),Ay/(2*np.pi),Az/(2*np.pi)];
|
|
236
|
+
|
|
237
|
+
return A
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
#returns the density matrix for a qubit up state
|
|
241
|
+
def rhoU():
|
|
242
|
+
|
|
243
|
+
return qutip.Qobj([[1,0],[0,0]]);
|
|
244
|
+
|
|
245
|
+
#returns the density matrix for a qubit down state
|
|
246
|
+
def rhoD():
|
|
247
|
+
|
|
248
|
+
return qutip.Qobj([[0,0],[0,1]]);
|
|
249
|
+
|
|
250
|
+
#returns the density matrix for a qubit Xp state
|
|
251
|
+
def rhoXp():
|
|
252
|
+
|
|
253
|
+
return (1/2)*qutip.Qobj([[1,1],[1,1]]);
|
|
254
|
+
|
|
255
|
+
#returns the density matrix for a qubit X- state
|
|
256
|
+
def rhoXm():
|
|
257
|
+
|
|
258
|
+
return (1/2)*qutip.Qobj([[1,-1],[-1,1]]);
|
|
259
|
+
|
|
260
|
+
#returns the density matrix for a qubit in a thermal state
|
|
261
|
+
def rhoT(params = params(), species = 'C'):
|
|
262
|
+
|
|
263
|
+
s = species_spin[species]
|
|
264
|
+
|
|
265
|
+
r =(-(params['h']*params[f'gamma_{species}']*params['B0']*Iz(s))/(2*np.pi*params['k_B']*params['Temp'])).expm();
|
|
266
|
+
|
|
267
|
+
return r/r.tr()
|
|
268
|
+
#returns the density matrix for a qubit in a thermal state
|
|
269
|
+
def rhoSup(a = np.cos(2*np.pi/3),b = np.sin(2*np.pi/3)):
|
|
270
|
+
|
|
271
|
+
A = (a + b)**2
|
|
272
|
+
B = (a - b)**2
|
|
273
|
+
AB = (a**2 - b**2)
|
|
274
|
+
|
|
275
|
+
return (1/2)*qutip.Qobj([[A,AB],[AB,B]]);
|
|
276
|
+
|
|
277
|
+
#A function which returns the reduced quibit Qobj for Sx
|
|
278
|
+
def qubit_Sx():
|
|
279
|
+
|
|
280
|
+
return qutip.Qobj(np.array([[0,1/2],[1/2,0]]));
|
|
281
|
+
|
|
282
|
+
#A function which returns the reduced quibit Qobj for Sz in ms = {0,-1} space
|
|
283
|
+
def qubit_Szp():
|
|
284
|
+
|
|
285
|
+
return qutip.Qobj(np.array([[0,0],[0,1]]));
|
|
286
|
+
|
|
287
|
+
def qubit_Szm():
|
|
288
|
+
|
|
289
|
+
return qutip.Qobj(np.array([[0,0],[0,-1]]));
|
|
290
|
+
|
|
291
|
+
#A function which returns the reduced quibit Qobj for Sz
|
|
292
|
+
def qubit_Sy():
|
|
293
|
+
|
|
294
|
+
return qutip.Qobj(np.array([[0,-1j/2],[1j/2,0]]));
|
|
295
|
+
|
|
296
|
+
#A function which returns the reduced quibit Qobj for Sx
|
|
297
|
+
def state_3_Sx():
|
|
298
|
+
|
|
299
|
+
return qutip.Qobj((1/np.sqrt(2))*np.array([[0,1,0],[1,0,1],[0,1,0]]));
|
|
300
|
+
|
|
301
|
+
#A function which returns the reduced quibit Qobj for Sz
|
|
302
|
+
def state_3_Sz():
|
|
303
|
+
|
|
304
|
+
return qutip.Qobj(np.array([[1,0,0],[0,0,0],[0,0,-1]]));
|
|
305
|
+
|
|
306
|
+
#A function which returns the reduced quibit Qobj for Sz
|
|
307
|
+
def state_3_Sy():
|
|
308
|
+
|
|
309
|
+
return qutip.Qobj((1/np.sqrt(2))*np.array([[0,-1j,0],[1j,0,-1j],[0,1j,0]]));
|
|
310
|
+
|
|
311
|
+
#A function which returns the quibit Qobj for Ix
|
|
312
|
+
def Ix(s = 2):
|
|
313
|
+
|
|
314
|
+
if s == 2:
|
|
315
|
+
return qutip.Qobj(np.array([[0,1/2],[1/2,0]]));
|
|
316
|
+
|
|
317
|
+
elif s == 3:
|
|
318
|
+
return state_3_Sx()
|
|
319
|
+
|
|
320
|
+
else: raise Exception('s = %s is not supported'%s)
|
|
321
|
+
|
|
322
|
+
#A function which returns the quibit Qobj for Iz
|
|
323
|
+
def Iz(s = 2):
|
|
324
|
+
|
|
325
|
+
if s == 2:
|
|
326
|
+
return qutip.Qobj(np.array([[1/2,0],[0,-1/2]]));
|
|
327
|
+
elif s == 3:
|
|
328
|
+
return state_3_Sz()
|
|
329
|
+
|
|
330
|
+
else: raise Exception('s = %d is not supported'%(s))
|
|
331
|
+
|
|
332
|
+
#A function which returns the reduced quibit Qobj for Sz
|
|
333
|
+
def Iy(s = 2):
|
|
334
|
+
if s == 2:
|
|
335
|
+
return qutip.Qobj(np.array([[0,-1j/2],[1j/2,0]]));
|
|
336
|
+
elif s == 3:
|
|
337
|
+
return state_3_Sy()
|
|
338
|
+
|
|
339
|
+
else: raise Exception('s = %s is not supported'%s)
|
|
340
|
+
|
|
341
|
+
#function that constructs the spin ith operator in a tensor space size 2^NumSpin
|
|
342
|
+
def spin_tensor_operator(NumSpin, i,space_size = None):
|
|
343
|
+
|
|
344
|
+
"""
|
|
345
|
+
function which generates a spin operator of spin i in a NumSpin spin system
|
|
346
|
+
|
|
347
|
+
Parameters
|
|
348
|
+
----------
|
|
349
|
+
|
|
350
|
+
NumSpin: int for the number of spins in tensor space
|
|
351
|
+
i: int for the position of the spin in the tensor space
|
|
352
|
+
|
|
353
|
+
returns
|
|
354
|
+
_______
|
|
355
|
+
|
|
356
|
+
array of spin operators:
|
|
357
|
+
Iz: Qobj for the z-directional spin operator.
|
|
358
|
+
Iy: Qobj for the y-directional spin operator.
|
|
359
|
+
Ix: Qobj for the x-directional spin operator.
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
"""
|
|
363
|
+
if space_size == None:
|
|
364
|
+
space_size = 2*np.ones(NumSpin)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
I = qutip.identity(int(space_size[0]));
|
|
368
|
+
|
|
369
|
+
if i == 0:
|
|
370
|
+
|
|
371
|
+
Ixi = Ix(space_size[i]);
|
|
372
|
+
Iyi = Iy(space_size[i]);
|
|
373
|
+
Izi = Iz(space_size[i]);
|
|
374
|
+
|
|
375
|
+
else:
|
|
376
|
+
|
|
377
|
+
Ixi = I;
|
|
378
|
+
Iyi = I;
|
|
379
|
+
Izi = I;
|
|
380
|
+
|
|
381
|
+
for j in range(1,NumSpin):
|
|
382
|
+
|
|
383
|
+
I = qutip.identity(int(space_size[j]));
|
|
384
|
+
|
|
385
|
+
if j == i:
|
|
386
|
+
|
|
387
|
+
Ixi = qutip.tensor(Ixi,Ix(space_size[j]));
|
|
388
|
+
Iyi = qutip.tensor(Iyi,Iy(space_size[j]));
|
|
389
|
+
Izi = qutip.tensor(Izi,Iz(space_size[j]));
|
|
390
|
+
|
|
391
|
+
else:
|
|
392
|
+
|
|
393
|
+
Ixi = qutip.tensor(Ixi,I);
|
|
394
|
+
Iyi = qutip.tensor(Iyi,I);
|
|
395
|
+
Izi = qutip.tensor(Izi,I);
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
return [Ixi,Iyi,Izi]
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
Rho = {'up':rhoU,
|
|
402
|
+
'down':rhoD,
|
|
403
|
+
'Xp':rhoXp,
|
|
404
|
+
'Xm':rhoXm,
|
|
405
|
+
'Therm':rhoT,
|
|
406
|
+
'Sup':rhoSup}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def gaussianEnv(t,args):
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
#tw = args['tw']
|
|
416
|
+
#t0 = args['t0']
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
#r2sigma2 = 1/(2*tw**2)
|
|
420
|
+
#gaussian_pulse = np.exp(-((t-t0)**2)*r2sigma2)
|
|
421
|
+
if args['finite'] == True:
|
|
422
|
+
gaussian_pulse = gaussian(args['num_t'],args['std'])
|
|
423
|
+
else:
|
|
424
|
+
gaussian_pulse = scipy.signal.gausspulse(t,2*np.pi/args['tw'],retenv = True)[1]
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
if args['plot'] == True:
|
|
428
|
+
|
|
429
|
+
fig,ax = plt.subplots(dpi = 200)
|
|
430
|
+
#ax.plot(x,L)
|
|
431
|
+
y = gaussian_pulse
|
|
432
|
+
ax.plot(np.array(t)*1e9,y,color = 'k',label = 'Gaussian')
|
|
433
|
+
ax.set_ylabel('Amplitude',fontsize = 12)
|
|
434
|
+
ax.set_xlabel('Time (ns)',fontsize = 12)
|
|
435
|
+
i = 0
|
|
436
|
+
for ti, A in zip(t,y):
|
|
437
|
+
if i == 0:
|
|
438
|
+
ax.fill_between(np.array([ti,ti + args['dt']])*1e9,0,A,color = 'blue',alpha = 0.4,edgecolor = 'k',label = 'L Amp')
|
|
439
|
+
i +=1
|
|
440
|
+
else: ax.fill_between(np.array([ti,ti + args['dt']])*1e9,0,A,color = 'blue',alpha = 0.4,edgecolor = 'k')
|
|
441
|
+
ax.legend()
|
|
442
|
+
|
|
443
|
+
return gaussian_pulse
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
"""
|
|
449
|
+
OPERATOR AND HAMILTONIAN GENERATORS
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def generate_operators(Az, Ax,species = None, params = params(), NV_state = 'up', Nuc_state = None,NV_sub = '-'):
|
|
454
|
+
|
|
455
|
+
"""
|
|
456
|
+
function which generates the evolution operators for a particular system
|
|
457
|
+
|
|
458
|
+
Parameters
|
|
459
|
+
----------
|
|
460
|
+
|
|
461
|
+
NV_state: string to describe the initial state of the NV. default is up
|
|
462
|
+
Nuc_state: array of strings with length of the number of spins in the system
|
|
463
|
+
to describe the state of the NV. Default = None which will
|
|
464
|
+
set nuclear states to thermal mixture Therm
|
|
465
|
+
params: dict of system and protocol parameters. Can be retrieved using
|
|
466
|
+
params() function
|
|
467
|
+
Az: array of floats which contains the parallel coupling for the spins in system
|
|
468
|
+
length of this array determines how many spins there are.
|
|
469
|
+
Ax: array of floats which contains the perpendicular couplings
|
|
470
|
+
N14: boo determining whether to include N14 spin
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
returns
|
|
474
|
+
_______
|
|
475
|
+
|
|
476
|
+
dict_operators: dict containing operators under the following labels
|
|
477
|
+
|
|
478
|
+
Iz: array of Qobjs for the z-directional spin operators. size of array
|
|
479
|
+
is that of the number of spins
|
|
480
|
+
Iy: array of Qobjs for the y-directional spin operators. size of array
|
|
481
|
+
is that of the number of spins
|
|
482
|
+
Ix: array of Qobjs for the x-directional spin operators. size of array
|
|
483
|
+
is that of the number of spins
|
|
484
|
+
Sz: Qobj for the psuedo spin NV z-directional operator. This is in subspace
|
|
485
|
+
{-1,0}
|
|
486
|
+
Sy: Qobj for the psuedo spin NV y-directional operator. This is in subspace
|
|
487
|
+
{-1,0}
|
|
488
|
+
Sx: Qobj for the psuedo spin NV x-directional operator. This is in subspace
|
|
489
|
+
{-1,0}
|
|
490
|
+
H0: Qobj for the free Hamiltonian of the system
|
|
491
|
+
rho0: Qobj the density matrix for the initial state of the system
|
|
492
|
+
rho0NV: Qobj for the initial density matrix of the NV
|
|
493
|
+
|
|
494
|
+
params: updated list of parameters, where the N_nuc reflects the length of
|
|
495
|
+
Az array input
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
"""
|
|
499
|
+
omegaL = params['omegaL'];
|
|
500
|
+
|
|
501
|
+
NV_dict = {'-':qubit_Szm(),
|
|
502
|
+
'+':qubit_Szp(),
|
|
503
|
+
'+-':qubit_Szm() + qubit_Szp()}
|
|
504
|
+
|
|
505
|
+
dict_operators = {}
|
|
506
|
+
#compute the number of nuclei in the system
|
|
507
|
+
N_nuc = len(Az)
|
|
508
|
+
|
|
509
|
+
params['N_nuc'] = N_nuc
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
if Nuc_state == None:
|
|
513
|
+
Nuc_state = np.full(N_nuc,'Therm');
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
if species == None:
|
|
517
|
+
species = []
|
|
518
|
+
for i in range(params['N_nuc']):
|
|
519
|
+
species.append('C')
|
|
520
|
+
print(species)
|
|
521
|
+
species.insert(0, 'NV')
|
|
522
|
+
space_size = [species_spin[s] for s in species]
|
|
523
|
+
|
|
524
|
+
rho0 = Rho[NV_state]();
|
|
525
|
+
|
|
526
|
+
dict_operators['rho0NV'] = Rho[NV_state]();
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
Sx,Sy,Sz = spin_tensor_operator(N_nuc + 1,0,space_size);
|
|
530
|
+
Sz = NV_dict[NV_sub];
|
|
531
|
+
|
|
532
|
+
I = qutip.identity(space_size[0]);
|
|
533
|
+
|
|
534
|
+
Ix = [];
|
|
535
|
+
Iy = [];
|
|
536
|
+
Iz = [];
|
|
537
|
+
|
|
538
|
+
I_op = I
|
|
539
|
+
|
|
540
|
+
#construct spin operators for nuclei
|
|
541
|
+
for i in range(1,N_nuc + 1,1):
|
|
542
|
+
|
|
543
|
+
rho0 = qutip.tensor(rho0,Rho[Nuc_state[i - 1]](params,species[i]));
|
|
544
|
+
|
|
545
|
+
Ixtemp,Iytemp,Iztemp = spin_tensor_operator(N_nuc + 1,i,space_size);
|
|
546
|
+
|
|
547
|
+
I = qutip.identity(space_size[i])
|
|
548
|
+
|
|
549
|
+
Ix.append(Ixtemp);
|
|
550
|
+
Iy.append(Iytemp);
|
|
551
|
+
Iz.append(Iztemp);
|
|
552
|
+
|
|
553
|
+
Sz = qutip.tensor(Sz,I)
|
|
554
|
+
I_op = qutip.tensor(I_op,I)
|
|
555
|
+
|
|
556
|
+
dict_operators['rho0'] = rho0;
|
|
557
|
+
|
|
558
|
+
dict_operators['Iz'] = Iz;
|
|
559
|
+
dict_operators['Ix'] = Ix;
|
|
560
|
+
dict_operators['Iy'] = Iy;
|
|
561
|
+
|
|
562
|
+
dict_operators['Sx'] = Sx;
|
|
563
|
+
dict_operators['Sy'] = Sy;
|
|
564
|
+
dict_operators['Sz'] = Sz;
|
|
565
|
+
|
|
566
|
+
dict_operators['I'] = I_op
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
#construct free Hamiltonian
|
|
570
|
+
H0 = params['Overhauser']*Sz
|
|
571
|
+
for i in range(N_nuc):
|
|
572
|
+
|
|
573
|
+
H0 += omegaL*Iz[i] + Az[i]*Sz*Iz[i] + Ax[i]*Sz*Ix[i] #magnetic field term
|
|
574
|
+
|
|
575
|
+
dict_operators['H0'] = H0;
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
return dict_operators,params
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def add_N14(operators,params,couplings,TN = 3):
|
|
583
|
+
"""
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
Sz = state_3_Sz()
|
|
591
|
+
Sy = state_3_Sy()
|
|
592
|
+
Sx = state_3_Sx()
|
|
593
|
+
|
|
594
|
+
Ix,Iy,Iz, = spin_tensor_operator(params['N_nuc'] + 1,params['N_nuc'] + 2)
|
|
595
|
+
|
|
596
|
+
Ix = qutip.tensor(Ix,Sx)
|
|
597
|
+
Iy = qutip.tensor(Iy,Sy)
|
|
598
|
+
Iz = qutip.tensor(Iz,Sz)
|
|
599
|
+
|
|
600
|
+
I = qutip.identity(3)
|
|
601
|
+
|
|
602
|
+
for i in range(params['N_nuc']):
|
|
603
|
+
|
|
604
|
+
operators['Iz'][i] = qutip.tensor(operators['Iz'][i],I)
|
|
605
|
+
operators['Iy'][i] = qutip.tensor(operators['Iy'][i],I)
|
|
606
|
+
operators['Ix'][i] = qutip.tensor(operators['Ix'][i],I)
|
|
607
|
+
|
|
608
|
+
operators['Sz'] = qutip.tensor(operators['Sz'],I)
|
|
609
|
+
operators['Sy'] = qutip.tensor(operators['Sy'],I)
|
|
610
|
+
operators['Sx'] = qutip.tensor(operators['Sx'],I)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
operators['Iz'].append(Iz)
|
|
614
|
+
operators['Iy'].append(Iy)
|
|
615
|
+
operators['Ix'].append(Ix)
|
|
616
|
+
|
|
617
|
+
operators['I'] = qutip.tensor(operators['I'],I)
|
|
618
|
+
|
|
619
|
+
params['N_nuc'] += 1
|
|
620
|
+
|
|
621
|
+
r0 = qutip.Qobj([[1,0,0],[0,1,0],[0,0,1]])
|
|
622
|
+
#r0 = (-state_3_Sz()/TN).expm()
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
operators['rho0'] = qutip.tensor(operators['rho0'],r0/r0.tr())
|
|
626
|
+
|
|
627
|
+
Az = -2*np.pi*2.164689e6
|
|
628
|
+
Ax = -2*np.pi*2.632e6
|
|
629
|
+
|
|
630
|
+
omega = np.sqrt((params['B0']*2*np.pi*params['gamma_N'] + Az/2)**2 + (Ax/2)**2)
|
|
631
|
+
|
|
632
|
+
# couplings = couplings.append({'Species': 'N',
|
|
633
|
+
# 'Ax': Ax,
|
|
634
|
+
# 'Az': Az,
|
|
635
|
+
# 'omega': omega},ignore_index = True)
|
|
636
|
+
|
|
637
|
+
couplings = pd.concat([couplings, pd.DataFrame([{'Species': 'N',
|
|
638
|
+
'Ax': Ax,
|
|
639
|
+
'Az': Az,
|
|
640
|
+
'omega': omega}],index = [0])],ignore_index = True)
|
|
641
|
+
|
|
642
|
+
return operators, params, couplings
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def generate_H0(operators,couplings,params):
|
|
646
|
+
|
|
647
|
+
"""
|
|
648
|
+
a function which re-froms the free Hamiltonian for a system of operators
|
|
649
|
+
|
|
650
|
+
Parameters
|
|
651
|
+
----------
|
|
652
|
+
|
|
653
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
654
|
+
generate operators function
|
|
655
|
+
couplings: dataframe of Az and Ax couplings of nucleat spins
|
|
656
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
657
|
+
using the params() function
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
returns
|
|
661
|
+
-------
|
|
662
|
+
|
|
663
|
+
H0: a Qobj for the free Hamiltonian
|
|
664
|
+
"""
|
|
665
|
+
|
|
666
|
+
N_nuc = params['N_nuc'];
|
|
667
|
+
#omegaL = params['omegaL']
|
|
668
|
+
|
|
669
|
+
Iz = operators['Iz']
|
|
670
|
+
Ix = operators['Ix']
|
|
671
|
+
|
|
672
|
+
Sz = operators['Sz']
|
|
673
|
+
|
|
674
|
+
#construct free Hamiltonian
|
|
675
|
+
H0 = params['Overhauser']*Sz
|
|
676
|
+
for i in range(N_nuc):
|
|
677
|
+
|
|
678
|
+
s = couplings.index.tolist()[i]
|
|
679
|
+
|
|
680
|
+
Ax = couplings['Ax'].loc[s]
|
|
681
|
+
Az = couplings['Az'].loc[s]
|
|
682
|
+
species = couplings['Species'].loc[s]
|
|
683
|
+
|
|
684
|
+
omegaL = 2*np.pi*params['B0']*params['gamma_%s'%species]
|
|
685
|
+
|
|
686
|
+
H0 += omegaL*Iz[i] + Az*Sz*Iz[i] + Ax*Sz*Ix[i] #magnetic field term
|
|
687
|
+
|
|
688
|
+
operators['H0'] = H0;
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
return operators
|
|
693
|
+
|
|
694
|
+
def generate_full_H0(operators,couplings,params,CC_coupling = False):
|
|
695
|
+
|
|
696
|
+
"""
|
|
697
|
+
a function which re-froms the free Hamiltonian for a system of operators
|
|
698
|
+
|
|
699
|
+
Parameters
|
|
700
|
+
----------
|
|
701
|
+
|
|
702
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
703
|
+
generate operators function
|
|
704
|
+
Az: dataframe for couplings
|
|
705
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
706
|
+
using the params() function
|
|
707
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
returns
|
|
711
|
+
-------
|
|
712
|
+
|
|
713
|
+
H0: a Qobj for the free Hamiltonian
|
|
714
|
+
"""
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
N_nuc = params['N_nuc'];
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
Iz = operators['Iz']
|
|
723
|
+
Ix = operators['Ix']
|
|
724
|
+
Iy = operators['Iy']
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
Sz = operators['Sz']
|
|
728
|
+
Sx = operators['Sx']
|
|
729
|
+
#Sy = operators['Sy']
|
|
730
|
+
|
|
731
|
+
#construct free Hamiltonian
|
|
732
|
+
H0 = params['Overhauser']*Sz + 2*np.pi*params['B0']*params['gamma_e']*Sz + 2*np.pi*params['gamma_e']*params['B_mis']*Sx + 2*np.pi*params['zfs_NV']*Sz*Sz
|
|
733
|
+
H0p = params['Overhauser']*Sz
|
|
734
|
+
for i in range(N_nuc):
|
|
735
|
+
s1 = couplings.index.tolist()[i]
|
|
736
|
+
|
|
737
|
+
Ax = couplings['Ax'].loc[s1]
|
|
738
|
+
Az = couplings['Az'].loc[s1]
|
|
739
|
+
|
|
740
|
+
species = couplings['Species'].loc[s1]
|
|
741
|
+
|
|
742
|
+
omegaL = 2*np.pi*params['B0']*params['gamma_%s'%species]
|
|
743
|
+
omegaLx = 2*np.pi*params['B_mis']*params['gamma_%s'%species]
|
|
744
|
+
|
|
745
|
+
H0 += omegaL*Iz[i] +omegaLx*Ix[i] + Az*Sz*Iz[i] + Ax*Sz*Ix[i] #magnetic field term
|
|
746
|
+
H0p += omegaL*Iz[i] + omegaLx*Ix[i] + Az*Sz*Iz[i] + Ax*Sz*Ix[i]
|
|
747
|
+
if CC_coupling == True:
|
|
748
|
+
for j in range(i,N_nuc):
|
|
749
|
+
s2 = couplings.index.tolist()[j]
|
|
750
|
+
|
|
751
|
+
C = couplings['C{}'.format(s2)].loc[s1]
|
|
752
|
+
if i != j and abs(C) > 0:
|
|
753
|
+
H0 += C*(Iz[i]*Iz[j] + Ix[i]*Ix[j] + Iy[i]*Iy[j])
|
|
754
|
+
H0p += C*(Iz[i]*Iz[j] + Ix[i]*Ix[j] + Iy[i]*Iy[j])
|
|
755
|
+
|
|
756
|
+
operators['H0'] = H0;
|
|
757
|
+
|
|
758
|
+
operators['H0_pulse'] = H0p
|
|
759
|
+
|
|
760
|
+
return operators
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
"""
|
|
769
|
+
PULSE PROFILES
|
|
770
|
+
"""
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
def inst_pulse(operators,params,rot = np.pi):
|
|
774
|
+
"""
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
"""
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
#Omega = params['Omega'];
|
|
781
|
+
delta = params['delta'];
|
|
782
|
+
#Tp = (rot + delta)/Omega
|
|
783
|
+
|
|
784
|
+
Tp = params['Tp']
|
|
785
|
+
Omega = (abs(rot + delta))/Tp
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
UX = (-1j*(np.sign(rot)*Omega*operators['Sx'])*Tp).expm();
|
|
789
|
+
UY = (-1j*(np.sign(rot)*Omega*operators['Sy'])*Tp).expm();
|
|
790
|
+
|
|
791
|
+
return UX, UY
|
|
792
|
+
|
|
793
|
+
def square_pulse(operators,params, rot = np.pi):
|
|
794
|
+
"""
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
"""
|
|
798
|
+
|
|
799
|
+
#Omega = params['Omega'];
|
|
800
|
+
delta = params['delta'];
|
|
801
|
+
#Tp = (rot + delta)/Omega
|
|
802
|
+
|
|
803
|
+
Tp = params['Tp']
|
|
804
|
+
Omega = (abs(rot + delta))/Tp
|
|
805
|
+
|
|
806
|
+
H0 = operators['H0']
|
|
807
|
+
if params['full_H0'] == True:
|
|
808
|
+
H0 = operators['H0_pulse']
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
UX = (-1j*(H0 + np.sign(rot)*Omega*operators['Sx'])*Tp).expm();
|
|
812
|
+
UY = (-1j*(H0 + np.sign(rot)*Omega*operators['Sy'])*Tp).expm();
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
return UX, UY
|
|
816
|
+
|
|
817
|
+
def gaussian_pulse(operators,params, rot = np.pi, direction = 'x',dt = 1e-9):
|
|
818
|
+
"""
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
"""
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
Tp = params['Tp']
|
|
826
|
+
tw = Tp/2
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
t = np.linspace(-tw/2,tw/2,100)
|
|
830
|
+
#t = [0 + n*dt for n in range(int(Tp/dt))]
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
H0 = operators['H0']
|
|
834
|
+
if params['full_H0'] == True:
|
|
835
|
+
H0 = operators['H0_pulse']
|
|
836
|
+
Om = 1.6824844873848788/Tp
|
|
837
|
+
Hp = (2*rot/np.pi)*2*np.pi*Om*operators['S%s'%direction]
|
|
838
|
+
|
|
839
|
+
H = [H0,[Hp,gaussianEnv]]
|
|
840
|
+
|
|
841
|
+
output = qutip.mesolve(H,
|
|
842
|
+
operators['rho'],
|
|
843
|
+
t,
|
|
844
|
+
e_ops = [operators['Sz']],
|
|
845
|
+
args = {'finite':False,
|
|
846
|
+
'tw': tw,
|
|
847
|
+
'plot': False},
|
|
848
|
+
options = options)
|
|
849
|
+
|
|
850
|
+
operators['rho'] = output.states[-1]
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
return operators
|
|
854
|
+
|
|
855
|
+
def gaussian_pulse_finite(operators,params, rot = np.pi,dt = 1e-9):
|
|
856
|
+
"""
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
"""
|
|
860
|
+
#print('here')
|
|
861
|
+
|
|
862
|
+
Tp = params['Tp']
|
|
863
|
+
|
|
864
|
+
Omega = params['Omega']
|
|
865
|
+
#f = (np.abs(rot)/np.pi)
|
|
866
|
+
|
|
867
|
+
#t,dt = np.linspace(-Tp*f/2,Tp*f/2,int(Tp*f/dt) + 1,restep = True)
|
|
868
|
+
t = [0 + n*dt for n in range(int(Tp/dt))]
|
|
869
|
+
|
|
870
|
+
args = {'num_t':len(t),
|
|
871
|
+
'std':len(t)/5,
|
|
872
|
+
'finite':True,
|
|
873
|
+
'plot':False,
|
|
874
|
+
'dt': dt}
|
|
875
|
+
|
|
876
|
+
env = gaussianEnv(t, args = args)
|
|
877
|
+
|
|
878
|
+
#A = 2*np.pi*1.0432600729457604/Tp*f
|
|
879
|
+
#A = 2*np.pi/(2*Tp*f)
|
|
880
|
+
A = Omega
|
|
881
|
+
|
|
882
|
+
H0 = operators['H0']
|
|
883
|
+
if params['full_H0'] == True:
|
|
884
|
+
H0 = operators['H0_pulse']
|
|
885
|
+
|
|
886
|
+
UX = operators['I']
|
|
887
|
+
UY = operators['I']
|
|
888
|
+
for i in range(len(t)):
|
|
889
|
+
UX = (-1j*(H0 + A*env[i]*operators['Sx'])*dt).expm()*UX
|
|
890
|
+
UY = (-1j*(H0 + A*env[i]*operators['Sy'])*dt).expm()*UY
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
return UX,UY
|
|
895
|
+
|
|
896
|
+
pulse_profiles = {'inst': inst_pulse,
|
|
897
|
+
'square': square_pulse,
|
|
898
|
+
'gaussian_finite':gaussian_pulse_finite,
|
|
899
|
+
'gaussian':gaussian_pulse}
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
"""
|
|
908
|
+
PULSE PROTOCOLS
|
|
909
|
+
"""
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def Free(operators,params,measure,e_ops = [],c_ops = []):
|
|
913
|
+
|
|
914
|
+
"""
|
|
915
|
+
a function which constructs the free evolution operator
|
|
916
|
+
|
|
917
|
+
Parameters
|
|
918
|
+
----------
|
|
919
|
+
|
|
920
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
921
|
+
generate operators function
|
|
922
|
+
params: float for free evolution time
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
returns
|
|
926
|
+
-------
|
|
927
|
+
|
|
928
|
+
U: Qobj for the free evolution
|
|
929
|
+
"""
|
|
930
|
+
|
|
931
|
+
if params['decoherence'] == False:
|
|
932
|
+
|
|
933
|
+
U0 = (-1j*operators['H0']*params['t_ev']).expm();
|
|
934
|
+
|
|
935
|
+
rho = U0*operators['rho']*U0.dag()
|
|
936
|
+
|
|
937
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
938
|
+
|
|
939
|
+
return coh, rho, U0
|
|
940
|
+
|
|
941
|
+
elif params['decoherence'] == True:
|
|
942
|
+
t = np.linspace(0,params['t_ev'],50)
|
|
943
|
+
|
|
944
|
+
output = qutip.mesolve(operators['H0'],operators['rho'],t, e_ops = e_ops,c_ops = c_ops,options = options)
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
return output.expect[0][-1], output.states[-1]
|
|
948
|
+
|
|
949
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
# a function which takes a free evolution operator and pulse operators and returns a packet of CPMG. Note that UX must be a pi/2 pulse
|
|
954
|
+
def XY8(operators, params,measure):
|
|
955
|
+
|
|
956
|
+
"""
|
|
957
|
+
a function which constructs the pulse packet for a CPMG sequence using free evolution
|
|
958
|
+
and pi/2 pulses. pulse errors, delta, may be included as Tp = (np.pi + delta)/2.
|
|
959
|
+
CPMG sequence here is U0*UX*UX*U0*U0*UX*UX*U0
|
|
960
|
+
|
|
961
|
+
Parameters
|
|
962
|
+
----------
|
|
963
|
+
|
|
964
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
965
|
+
generate operators function
|
|
966
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
967
|
+
using the params() function
|
|
968
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
returns
|
|
972
|
+
-------
|
|
973
|
+
|
|
974
|
+
U: Qobj for the CPMG pulse packet
|
|
975
|
+
"""
|
|
976
|
+
|
|
977
|
+
tau = params['tau'];
|
|
978
|
+
profile = params['pulse_profile']
|
|
979
|
+
|
|
980
|
+
params['t_ev'] = tau/2
|
|
981
|
+
|
|
982
|
+
#construct the pulse operators
|
|
983
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi)
|
|
984
|
+
|
|
985
|
+
if params['decoherence'] == False:
|
|
986
|
+
|
|
987
|
+
U0 = (-1j*operators['H0']*params['t_ev']).expm();
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
U = (U0*UX*U0*U0*UY*U0*U0*UX*U0*U0*UY*U0*U0*UY*U0*U0*UX*U0*U0*UY*U0*U0*UX*U0)**(params['Np'])
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
rho = U*operators['rho0']*U.dag();
|
|
994
|
+
|
|
995
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
return coh,rho, U
|
|
999
|
+
|
|
1000
|
+
elif params['decoherence'] == True:
|
|
1001
|
+
#raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1002
|
+
rho = operators['rho0']
|
|
1003
|
+
params['t_ev'] = tau
|
|
1004
|
+
|
|
1005
|
+
for i in range(params['Np']):
|
|
1006
|
+
|
|
1007
|
+
operators['rho'] = rho
|
|
1008
|
+
|
|
1009
|
+
params['t_ev'] = tau/2
|
|
1010
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1011
|
+
operators['rho'] = UX*rho*UX.dag()
|
|
1012
|
+
|
|
1013
|
+
params['t_ev'] = tau
|
|
1014
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1015
|
+
|
|
1016
|
+
operators['rho'] = UY*rho*UY.dag()
|
|
1017
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1018
|
+
|
|
1019
|
+
operators['rho'] = UX*rho*UX.dag()
|
|
1020
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1021
|
+
|
|
1022
|
+
operators['rho'] = UY*rho*UY.dag()
|
|
1023
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1024
|
+
|
|
1025
|
+
operators['rho'] = UY*rho*UY.dag()
|
|
1026
|
+
cho, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1027
|
+
|
|
1028
|
+
operators['rho'] = UX*rho*UX.dag()
|
|
1029
|
+
cho, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1030
|
+
|
|
1031
|
+
operators['rho'] = UY*rho*UY.dag()
|
|
1032
|
+
cho, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1033
|
+
|
|
1034
|
+
operators['rho'] = UX*rho*UX.dag()
|
|
1035
|
+
params['t_ev'] = tau/2
|
|
1036
|
+
cho, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1037
|
+
|
|
1038
|
+
operators['rho'] = rho
|
|
1039
|
+
|
|
1040
|
+
coh = (operators['rho']*operators['Sz']).tr().real
|
|
1041
|
+
|
|
1042
|
+
return coh, operators['rho']
|
|
1043
|
+
|
|
1044
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
# a function which takes a free evolution operator and pulse operators and returns a packet of CPMG. Note that UX must be a pi/2 pulse
|
|
1050
|
+
def CPMG(operators, params,measure):
|
|
1051
|
+
|
|
1052
|
+
"""
|
|
1053
|
+
a function which constructs the pulse packet for a CPMG sequence using free evolution
|
|
1054
|
+
and pi/2 pulses. pulse errors, delta, may be included as Tp = (np.pi + delta)/2.
|
|
1055
|
+
CPMG sequence here is U0*UX*UX*U0*U0*UX*UX*U0
|
|
1056
|
+
|
|
1057
|
+
Parameters
|
|
1058
|
+
----------
|
|
1059
|
+
|
|
1060
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1061
|
+
generate operators function
|
|
1062
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1063
|
+
using the params() function
|
|
1064
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
returns
|
|
1068
|
+
-------
|
|
1069
|
+
|
|
1070
|
+
U: Qobj for the CPMG pulse packet
|
|
1071
|
+
"""
|
|
1072
|
+
|
|
1073
|
+
tau = params['tau'];
|
|
1074
|
+
profile = params['pulse_profile']
|
|
1075
|
+
|
|
1076
|
+
params['t_ev'] = tau/2
|
|
1077
|
+
|
|
1078
|
+
if params['decoherence'] == False:
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
U0 = (-1j*operators['H0']*params['t_ev']).expm();
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi)
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
U = (U0*UX*U0*U0*UX*U0)**params['Np']
|
|
1088
|
+
|
|
1089
|
+
rho = U*operators['rho0']*U.dag();
|
|
1090
|
+
|
|
1091
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
return coh,rho, U
|
|
1095
|
+
|
|
1096
|
+
elif params['decoherence'] == False:
|
|
1097
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
# a function which takes a free evolution operator and pulse operators and returns a packet of CPMG. Note that UX must be a pi/2 pulse
|
|
1106
|
+
def spin_locking(operators, params,measure):
|
|
1107
|
+
|
|
1108
|
+
"""
|
|
1109
|
+
a function which constructs the pulse packet for a CPMG sequence using free evolution
|
|
1110
|
+
and pi/2 pulses. pulse errors, delta, may be included as Tp = (np.pi + delta)/2.
|
|
1111
|
+
CPMG sequence here is U0*UX*UX*U0*U0*UX*UX*U0
|
|
1112
|
+
|
|
1113
|
+
Parameters
|
|
1114
|
+
----------
|
|
1115
|
+
|
|
1116
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1117
|
+
generate operators function
|
|
1118
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1119
|
+
using the params() function
|
|
1120
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
returns
|
|
1124
|
+
-------
|
|
1125
|
+
|
|
1126
|
+
U: Qobj for the CPMG pulse packet
|
|
1127
|
+
"""
|
|
1128
|
+
|
|
1129
|
+
Omega = params['Omega'];
|
|
1130
|
+
tau = params['tau'];
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
params['t_ev'] = tau/2
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
if params['decoherence'] == False:
|
|
1137
|
+
#construct the pulse operators
|
|
1138
|
+
U = (-1j*(operators['H0'] + Omega*operators['Sx'])*tau).expm();
|
|
1139
|
+
|
|
1140
|
+
rho = U*operators['rho']*U.dag();
|
|
1141
|
+
|
|
1142
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
return coh,rho, U
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
elif params['decoherence'] == False:
|
|
1149
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1150
|
+
|
|
1151
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
# a function which takes a free evolution operator and pulse operators and returns a packet of PulsePol. Note that UX and UY must be pi/2 pulses
|
|
1156
|
+
def PulsePol(operators,params,measure):
|
|
1157
|
+
|
|
1158
|
+
"""
|
|
1159
|
+
a function which constructs the pulse packet for a PulsePol sequence using free evolution
|
|
1160
|
+
and pi/2 pulses.
|
|
1161
|
+
PulsePol sequence here is (UX*U0*UY*UY*U0*UX*UY*U0*UXm*UXm*U0*UY)**2
|
|
1162
|
+
|
|
1163
|
+
Parameters
|
|
1164
|
+
----------
|
|
1165
|
+
|
|
1166
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1167
|
+
generate operators function
|
|
1168
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1169
|
+
using the params() function
|
|
1170
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
1171
|
+
|
|
1172
|
+
returns
|
|
1173
|
+
-------
|
|
1174
|
+
|
|
1175
|
+
U: Qobj for the CPMG pulse packet
|
|
1176
|
+
"""
|
|
1177
|
+
|
|
1178
|
+
tau = params['tau'];
|
|
1179
|
+
profile = params['pulse_profile']
|
|
1180
|
+
|
|
1181
|
+
if params['decoherence'] == False:
|
|
1182
|
+
|
|
1183
|
+
U0 = (-1j*operators['H0']*tau).expm();
|
|
1184
|
+
|
|
1185
|
+
#construct the pulse operators
|
|
1186
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi/2)
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
UXm = (UX).dag()
|
|
1190
|
+
UYm = (UY).dag()
|
|
1191
|
+
U = (UX*U0*UY*UY*U0*UX*UY*U0*UXm*UXm*U0*UY)**params['Np']
|
|
1192
|
+
#U = (UX*U0*UYm*UYm*U0*UX*UY*U0*UX*UX*U0*UY*UX*U0*UY*UY*U0*UX*UY*U0*UXm*UXm*U0*UY)**params['Np']
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
rho = U*operators['rho0']*U.dag();
|
|
1196
|
+
|
|
1197
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1198
|
+
|
|
1199
|
+
return coh,rho, U
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
elif params['decoherence'] == False:
|
|
1203
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1204
|
+
|
|
1205
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
def PulsePol_cam(operators,params,measure):
|
|
1210
|
+
|
|
1211
|
+
"""
|
|
1212
|
+
a function which constructs the pulse packet for a PulsePol sequence using free evolution
|
|
1213
|
+
and pi/2 pulses.
|
|
1214
|
+
PulsePol sequence here is (UX*U0*UY*UY*U0*UX*UY*U0*UXm*UXm*U0*UY)**2
|
|
1215
|
+
|
|
1216
|
+
Parameters
|
|
1217
|
+
----------
|
|
1218
|
+
|
|
1219
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1220
|
+
generate operators function
|
|
1221
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1222
|
+
using the params() function
|
|
1223
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
1224
|
+
|
|
1225
|
+
returns
|
|
1226
|
+
-------
|
|
1227
|
+
|
|
1228
|
+
U: Qobj for the CPMG pulse packet
|
|
1229
|
+
"""
|
|
1230
|
+
|
|
1231
|
+
tau = params['tau'];
|
|
1232
|
+
profile = params['pulse_profile']
|
|
1233
|
+
#Tp = params['Tp']
|
|
1234
|
+
Omega = params['Omega']
|
|
1235
|
+
|
|
1236
|
+
params['Omega'] = Omega/2
|
|
1237
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi/2)
|
|
1238
|
+
|
|
1239
|
+
#params['Tp'] = Tp/2
|
|
1240
|
+
params['Omega'] = Omega
|
|
1241
|
+
UXp, UYp = pulse_profiles[profile](operators,params,rot = np.pi)
|
|
1242
|
+
|
|
1243
|
+
UXm = (UXp).dag()
|
|
1244
|
+
|
|
1245
|
+
if params['decoherence'] == False:
|
|
1246
|
+
|
|
1247
|
+
U0 = (-1j*operators['H0']*tau).expm();
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
U = (UX*U0*UYp*U0*UX*UY*U0*UXm*U0*UY)**params['Np']
|
|
1251
|
+
|
|
1252
|
+
rho = U*operators['rho0']*U.dag();
|
|
1253
|
+
|
|
1254
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
return coh,rho, U
|
|
1258
|
+
|
|
1259
|
+
elif params['decoherence'] == True:
|
|
1260
|
+
rho = operators['rho0']
|
|
1261
|
+
params['t_ev'] = tau
|
|
1262
|
+
|
|
1263
|
+
for i in range(params['Np']):
|
|
1264
|
+
|
|
1265
|
+
operators['rho'] = UY*rho*UY.dag()
|
|
1266
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1267
|
+
operators['rho'] = UXm*rho*UXm.dag()
|
|
1268
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1269
|
+
operators['rho'] = UX*UY*rho*UY.dag()*UX.dag()
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
coh, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1273
|
+
operators['rho'] = UYp*rho*UYp.dag()
|
|
1274
|
+
cho, rho = Free(operators, params, measure,e_ops = [operators['Sz']],c_ops = [(1/np.sqrt(params['T2']))*operators['Sx']])
|
|
1275
|
+
operators['rho'] = UX*rho*UX.dag()
|
|
1276
|
+
|
|
1277
|
+
rho = operators['rho']
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
coh = (operators['rho']*operators['Sz']).tr().real
|
|
1281
|
+
|
|
1282
|
+
return coh, rho
|
|
1283
|
+
|
|
1284
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
def Ramsey(operators,params,measure):
|
|
1289
|
+
"""
|
|
1290
|
+
a function which constructs the Ramsey sequence using free evolution
|
|
1291
|
+
and pi/2 pulses.
|
|
1292
|
+
Ramsey here is (U0*UX)
|
|
1293
|
+
|
|
1294
|
+
Parameters
|
|
1295
|
+
----------
|
|
1296
|
+
|
|
1297
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1298
|
+
generate operators function
|
|
1299
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1300
|
+
using the params() function
|
|
1301
|
+
inst: bool to determine whether pulses are instantaneous. Default is False
|
|
1302
|
+
|
|
1303
|
+
returns
|
|
1304
|
+
-------
|
|
1305
|
+
|
|
1306
|
+
U: Qobj for the Ramsey pulse packet
|
|
1307
|
+
"""
|
|
1308
|
+
|
|
1309
|
+
tau = params['tau'];
|
|
1310
|
+
profile = params['pulse_profile']
|
|
1311
|
+
|
|
1312
|
+
if params['decoherence'] == False:
|
|
1313
|
+
|
|
1314
|
+
U0 = (-1j*operators['H0']*tau).expm();
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
#construct the pulse operators
|
|
1318
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi/2)
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
U = UX*U0*UX
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
rho = U*operators['rho0']*U.dag();
|
|
1325
|
+
|
|
1326
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
return coh,rho, U
|
|
1330
|
+
|
|
1331
|
+
elif params['decoherence'] == False:
|
|
1332
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1333
|
+
|
|
1334
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
def PMC(operators,params,measure,drive_dir = 'x'):
|
|
1338
|
+
"""
|
|
1339
|
+
This is a function which performs PCM (phase modulated control), a Continuous
|
|
1340
|
+
driving control method which modulates the phase of a second MW to engineer
|
|
1341
|
+
interactions
|
|
1342
|
+
|
|
1343
|
+
Parameters
|
|
1344
|
+
----------
|
|
1345
|
+
|
|
1346
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1347
|
+
generate operators function
|
|
1348
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1349
|
+
using the params() function
|
|
1350
|
+
measure: str of which direction to measure NV
|
|
1351
|
+
drive_dir: str for which direction to drive NV. Default = 'x'
|
|
1352
|
+
|
|
1353
|
+
returns
|
|
1354
|
+
-------
|
|
1355
|
+
|
|
1356
|
+
coh: float value of measure NV coherence, in direction {measure}
|
|
1357
|
+
rho: Qobj of the final state of the system
|
|
1358
|
+
U: Qobj for the Ramsey pulse packet
|
|
1359
|
+
"""
|
|
1360
|
+
|
|
1361
|
+
tau = params['tau'];
|
|
1362
|
+
|
|
1363
|
+
if params['decoherence'] == False:
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
Up = (-1j*(operators['H0'] + (params['Omega_R'] + np.exp(-1j*0)*params['Omega'])*operators[f'S{drive_dir}'])*tau/2).expm()
|
|
1367
|
+
Um = (-1j*(operators['H0'] + (params['Omega_R'] + np.exp(-1j*np.pi)*params['Omega'])*operators[f'S{drive_dir}'])*tau).expm()
|
|
1368
|
+
|
|
1369
|
+
U = (Up*Um*Up)**params['Np']
|
|
1370
|
+
|
|
1371
|
+
rho = U*operators['rho0']*U.dag();
|
|
1372
|
+
|
|
1373
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
return coh,rho, U
|
|
1377
|
+
|
|
1378
|
+
elif params['decoherence'] == False:
|
|
1379
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1380
|
+
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
def COSY(operators,params,measure,drive_dir = 'z'):
|
|
1384
|
+
"""
|
|
1385
|
+
This is a function which performs COSY (phase modulated control), a pulsed
|
|
1386
|
+
correlation method which separates two Hahn echos with tc
|
|
1387
|
+
|
|
1388
|
+
Parameters
|
|
1389
|
+
----------
|
|
1390
|
+
|
|
1391
|
+
operators: a dict of operators for the system which can be retrieved using the
|
|
1392
|
+
generate operators function
|
|
1393
|
+
params: dict of system parameters which define the protocol. Can be found
|
|
1394
|
+
using the params() function
|
|
1395
|
+
measure: str of which direction to measure NV
|
|
1396
|
+
drive_dir: str for which direction to drive NV. Default = 'x'
|
|
1397
|
+
|
|
1398
|
+
returns
|
|
1399
|
+
-------
|
|
1400
|
+
|
|
1401
|
+
coh: float value of measure NV coherence, in direction {measure}
|
|
1402
|
+
rho: Qobj of the final state of the system
|
|
1403
|
+
U: Qobj for the Ramsey pulse packet
|
|
1404
|
+
"""
|
|
1405
|
+
|
|
1406
|
+
tau = params['tau'];
|
|
1407
|
+
tc = params['t_corr']
|
|
1408
|
+
profile = params['pulse_profile']
|
|
1409
|
+
|
|
1410
|
+
if params['decoherence'] == False:
|
|
1411
|
+
|
|
1412
|
+
U0 = (-1j*operators['H0']*tau).expm();
|
|
1413
|
+
U0c = (-1j*operators['H0']*tc).expm();
|
|
1414
|
+
|
|
1415
|
+
#construct the pulse operators
|
|
1416
|
+
UX, UY = pulse_profiles[profile](operators,params,rot = np.pi/2)
|
|
1417
|
+
|
|
1418
|
+
UH = UX*U0*UY*UY*U0*UY
|
|
1419
|
+
|
|
1420
|
+
U = (UH*U0c*UH)**params['Np']
|
|
1421
|
+
|
|
1422
|
+
rho = U*operators['rho0']*U.dag();
|
|
1423
|
+
|
|
1424
|
+
coh = (operators['S%s'%measure]*rho).tr().real
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
return coh,rho, U
|
|
1428
|
+
|
|
1429
|
+
elif params['decoherence'] == False:
|
|
1430
|
+
raise Exception('Decoherence has not been initialised with this pulse protocol.')
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
|
|
1434
|
+
"""
|
|
1435
|
+
NV SYSTEM EVOLUTION
|
|
1436
|
+
"""
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
def pulse_NV(operators,params,measure = 'x'):
|
|
1440
|
+
|
|
1441
|
+
"""
|
|
1442
|
+
a function which constructs the evolved density matrix and the coherence of the NV
|
|
1443
|
+
after a particular protocol
|
|
1444
|
+
|
|
1445
|
+
Parameters
|
|
1446
|
+
----------
|
|
1447
|
+
|
|
1448
|
+
operators: dict of quantum objects found using the generate_operators() function
|
|
1449
|
+
params: dict of system and protocol parameters, found using the params() function
|
|
1450
|
+
inst: bool to determine whether pulses are instantaneous. default = True
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
returns
|
|
1454
|
+
-------
|
|
1455
|
+
|
|
1456
|
+
rho: QObj for the final state of the system
|
|
1457
|
+
coh: float for the final coherence of the NV
|
|
1458
|
+
"""
|
|
1459
|
+
|
|
1460
|
+
#Np = params['Np'];
|
|
1461
|
+
protocol = params['protocol']
|
|
1462
|
+
|
|
1463
|
+
protocols_dict = {'PulsePol':PulsePol,
|
|
1464
|
+
'PulsePol_cam':PulsePol_cam,
|
|
1465
|
+
'CPMG':CPMG,
|
|
1466
|
+
'XY8':XY8,
|
|
1467
|
+
'Free':Free,
|
|
1468
|
+
'spin_locking':spin_locking,
|
|
1469
|
+
'Ramsey': Ramsey,
|
|
1470
|
+
'PMC': PMC};
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
#U = protocols_dict[protocol](operators,params)**Np;
|
|
1475
|
+
|
|
1476
|
+
#rho = U*operators['rho0']*U.dag();
|
|
1477
|
+
|
|
1478
|
+
#coh = (operators['S%s'%measure]*rho).tr().real
|
|
1479
|
+
|
|
1480
|
+
if params['decoherence'] == True:
|
|
1481
|
+
coh, rho = protocols_dict[protocol](operators,params,measure);
|
|
1482
|
+
elif params['decoherence'] == False:
|
|
1483
|
+
coh, rho, U = protocols_dict[protocol](operators,params,measure);
|
|
1484
|
+
|
|
1485
|
+
else: raise Exception('Invalid value for params["decoherence"]. Must be bool not {params["decoherence"]}.')
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
|
|
1489
|
+
return coh, rho, U
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
def hyperpol(operators,params,pbar = True, re_ini = True,wait = False,random_seed2 = 1,measure ='z'):
|
|
1494
|
+
|
|
1495
|
+
"""
|
|
1496
|
+
A function which an initial density matrix of rho0 and applys Np pulses of a pulse scheme protocol
|
|
1497
|
+
using inputed free evolution U0 and X/Y pulses UX/Y. The NV is then re-initialised and the protocol repeated.
|
|
1498
|
+
the list of polarisations for each nuclear spin is returned as well as the final density matrix.
|
|
1499
|
+
|
|
1500
|
+
Parameters
|
|
1501
|
+
----------
|
|
1502
|
+
operators: dict of operators of the system found by using function generate_operators()
|
|
1503
|
+
params: dict of parameters set by using function params()
|
|
1504
|
+
inst: bool for whether pulses are instantaneous. default = False
|
|
1505
|
+
re_ini: bool for whether to re-initialise the NV.
|
|
1506
|
+
|
|
1507
|
+
Returns
|
|
1508
|
+
-------
|
|
1509
|
+
rho: Qobj for the final state of the system.
|
|
1510
|
+
Pol_array: numpy array of Pol array size N_nuc and length Reps
|
|
1511
|
+
"""
|
|
1512
|
+
|
|
1513
|
+
#compute the number of nuclei in the system
|
|
1514
|
+
N_nuc = params['N_nuc'];
|
|
1515
|
+
Np = params['Np']
|
|
1516
|
+
protocol = params['protocol'];
|
|
1517
|
+
Reps = params['Reps']
|
|
1518
|
+
|
|
1519
|
+
mu = params['t_wait_mu'];
|
|
1520
|
+
sigma = params['t_wait_sig']
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
Retain = [int(x) for x in range(1,N_nuc + 1)]
|
|
1524
|
+
|
|
1525
|
+
rho0NV = operators['rho0NV'];
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
#compute the evolution operator for one pulse packet
|
|
1529
|
+
protocols_dict = {'PulsePol':PulsePol,'CPMG':CPMG,'PulsePol_cam':PulsePol_cam};
|
|
1530
|
+
if params['decoherence'] == False:
|
|
1531
|
+
coh, rho, U = protocols_dict[protocol](operators,params,measure = measure)
|
|
1532
|
+
else: raise Exception('This decoherence value is not supported yet.')
|
|
1533
|
+
|
|
1534
|
+
np.random.seed(int(random_seed2))
|
|
1535
|
+
a = -1.46*np.log(np.random.random(Reps))*1e-6
|
|
1536
|
+
|
|
1537
|
+
Pol_array = [];
|
|
1538
|
+
for i in range(N_nuc):
|
|
1539
|
+
Pol_array.append([]);
|
|
1540
|
+
|
|
1541
|
+
if pbar == True:
|
|
1542
|
+
#propagate the initial matrix and find the evolution of the nuclear spin evolution
|
|
1543
|
+
rho = operators['rho0']
|
|
1544
|
+
for i in tqdm(range(Reps)):
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
rho = U*rho*U.dag();
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
#any wait time after re-initialisation
|
|
1551
|
+
if wait == True:
|
|
1552
|
+
#params['t_wait'] = random.normal(mu,sigma);
|
|
1553
|
+
params['t_wait'] = 10e-6
|
|
1554
|
+
|
|
1555
|
+
if a[i] > 10e-6:
|
|
1556
|
+
a[i] = 10e-6
|
|
1557
|
+
|
|
1558
|
+
#a[i] = 6e-6
|
|
1559
|
+
t_wait = params['t_wait'];
|
|
1560
|
+
|
|
1561
|
+
h0 = params['omegaL']*operators['Iz'][0]
|
|
1562
|
+
|
|
1563
|
+
U_wait = (-1j*h0*(t_wait - a[i])).expm()
|
|
1564
|
+
rho = U_wait*rho*U_wait.dag()
|
|
1565
|
+
|
|
1566
|
+
#re-initiailse the NV
|
|
1567
|
+
rho = qutip.tensor(rho0NV,rho.ptrace(Retain))
|
|
1568
|
+
|
|
1569
|
+
U_wait = (-1j*h0*(a[i])).expm()
|
|
1570
|
+
rho = U_wait*rho*U_wait.dag()
|
|
1571
|
+
|
|
1572
|
+
else:
|
|
1573
|
+
#re-initiailse the NV
|
|
1574
|
+
rho = qutip.tensor(rho0NV,rho.ptrace(Retain))
|
|
1575
|
+
|
|
1576
|
+
#calculate the polarisation
|
|
1577
|
+
for j in range(N_nuc):
|
|
1578
|
+
rhoTr = rho.ptrace([j + 1])
|
|
1579
|
+
if rhoTr.shape[0] == 2:
|
|
1580
|
+
Ptemp = (2*Iz()*rhoTr).tr().real;
|
|
1581
|
+
Pol_array[j].append(Ptemp);
|
|
1582
|
+
|
|
1583
|
+
elif rhoTr.shape[0] == 3:
|
|
1584
|
+
Ptemp = (2*state_3_Sz()*rhoTr).tr().real;
|
|
1585
|
+
Pol_array[j].append(Ptemp);
|
|
1586
|
+
|
|
1587
|
+
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
elif pbar == False:
|
|
1596
|
+
#propagate the initial matrix and find the evolution of the nuclear spin evolution
|
|
1597
|
+
rho = operators['rho0']
|
|
1598
|
+
for i in range(Reps):
|
|
1599
|
+
|
|
1600
|
+
|
|
1601
|
+
|
|
1602
|
+
rho = U*rho*U.dag();
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
#any wait time after re-initialisation
|
|
1608
|
+
|
|
1609
|
+
if wait == True:
|
|
1610
|
+
#params['t_wait'] = random.normal(mu,sigma);
|
|
1611
|
+
params['t_wait'] = 10e-6
|
|
1612
|
+
|
|
1613
|
+
if a[i] > 10e-6:
|
|
1614
|
+
a[i] = 10e-6
|
|
1615
|
+
t_wait = params['t_wait'];
|
|
1616
|
+
|
|
1617
|
+
#a[i] = 6e-6
|
|
1618
|
+
h0 = params['omegaL']*operators['Iz'][0]
|
|
1619
|
+
U_wait = (-1j*h0*(t_wait - a[i])).expm()
|
|
1620
|
+
rho = U_wait*rho*U_wait.dag()
|
|
1621
|
+
|
|
1622
|
+
#re-initiailse the NV
|
|
1623
|
+
rho = qutip.tensor(rho0NV,rho.ptrace(Retain))
|
|
1624
|
+
|
|
1625
|
+
U_wait = (-1j*h0*(a[i])).expm()
|
|
1626
|
+
rho = U_wait*rho*U_wait.dag()
|
|
1627
|
+
|
|
1628
|
+
else:
|
|
1629
|
+
#re-initiailse the NV
|
|
1630
|
+
rho = qutip.tensor(rho0NV,rho.ptrace(Retain))
|
|
1631
|
+
|
|
1632
|
+
#calculate the polarisation
|
|
1633
|
+
for j in range(N_nuc):
|
|
1634
|
+
rhoTr = rho.ptrace([j + 1])
|
|
1635
|
+
if rhoTr.shape[0] == 2:
|
|
1636
|
+
Ptemp = (2*Iz()*rhoTr).tr().real;
|
|
1637
|
+
Pol_array[j].append(Ptemp);
|
|
1638
|
+
|
|
1639
|
+
elif rhoTr.shape[0] == 3:
|
|
1640
|
+
Ptemp = (2*state_3_Sz()*rhoTr).tr().real;
|
|
1641
|
+
Pol_array[j].append(Ptemp);
|
|
1642
|
+
|
|
1643
|
+
|
|
1644
|
+
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
return rho, Pol_array
|
|
1649
|
+
|
|
1650
|
+
|
|
1651
|
+
|
|
1652
|
+
def hyperpol_avg(operators,params, pbar = True,random_seed = 1):
|
|
1653
|
+
|
|
1654
|
+
|
|
1655
|
+
"""
|
|
1656
|
+
A function which an initial density matrix of rho0 and applys Np pulses of a pulse scheme protocol
|
|
1657
|
+
using inputed free evolution U0 and X/Y pulses UX/Y. The NV is then re-initialised and the protocol repeated.
|
|
1658
|
+
the list of polarisations for each nuclear spin is returned as well as the final density matrix.
|
|
1659
|
+
|
|
1660
|
+
Parameters
|
|
1661
|
+
----------
|
|
1662
|
+
operators: dict of operators of the system found by using function generate_operators()
|
|
1663
|
+
params: dict of parameters set by using function params()
|
|
1664
|
+
num_avg: int for the number of t_waits averaged over. default = 100
|
|
1665
|
+
inst: bool for whether pulses are instantaneous. default = False
|
|
1666
|
+
pbar: bool for whether to include progress bar. default = True
|
|
1667
|
+
|
|
1668
|
+
Returns
|
|
1669
|
+
-------
|
|
1670
|
+
rho: Qobj for the final state of the system.
|
|
1671
|
+
Pol_array: numpy array of Pol array size N_nuc and length Reps
|
|
1672
|
+
"""
|
|
1673
|
+
|
|
1674
|
+
num_avg = params['num_avg']
|
|
1675
|
+
np.random.seed(random_seed)
|
|
1676
|
+
seeds = np.floor(np.random.random(num_avg)*100)
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
if pbar == True:
|
|
1680
|
+
Pol_array = 0
|
|
1681
|
+
for i in pb.progressbar(range(num_avg)):
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
rho,Pol = hyperpol(operators,params,pbar = False, wait = True,random_seed2 = seeds[i])
|
|
1685
|
+
Pol_array += np.array(Pol);
|
|
1686
|
+
|
|
1687
|
+
if pbar == False:
|
|
1688
|
+
Pol_array = 0
|
|
1689
|
+
for i in range(num_avg):
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
rho,Pol = hyperpol(operators,params,pbar = False,wait = True,random_seed2 = seeds[i])
|
|
1693
|
+
Pol_array += np.array(Pol);
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
return rho,Pol_array/num_avg
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
"""
|
|
1700
|
+
PLOTTING FUNCTIONS
|
|
1701
|
+
"""
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
def plot_D_pol(P_df,fig_size = [7,4],dpi = 100,fig = None, ax = None, plot_avg = True,title = None,plot_params = plot_params(),label = None):
|
|
1705
|
+
|
|
1706
|
+
"""
|
|
1707
|
+
A function which plots the polarisation time series of nuclear spins under repeated
|
|
1708
|
+
NV polarisation
|
|
1709
|
+
|
|
1710
|
+
Parameters
|
|
1711
|
+
----------
|
|
1712
|
+
|
|
1713
|
+
P_df: dataframe containing all the polarisation series of the nuclear spins
|
|
1714
|
+
plot_avg: bool which determines whether average polarisation should be plotted
|
|
1715
|
+
title: str which will be displayed as a title
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
Returns
|
|
1719
|
+
--------
|
|
1720
|
+
|
|
1721
|
+
fig: matplotlib object which conatains the plot for the figure
|
|
1722
|
+
|
|
1723
|
+
"""
|
|
1724
|
+
|
|
1725
|
+
rc = {"font.family" : "serif",
|
|
1726
|
+
"mathtext.fontset" : "stix"}
|
|
1727
|
+
plt.rcParams.update(rc)
|
|
1728
|
+
plt.rcParams["font.serif"] = ["Helvectica"] + plt.rcParams["font.serif"]
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
if plot_avg == True:
|
|
1735
|
+
P_df['avg'] = P_df.mean(axis = 1);
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
if fig == None or ax == None:
|
|
1740
|
+
|
|
1741
|
+
fig = plt.figure(figsize = fig_size,dpi = dpi);
|
|
1742
|
+
|
|
1743
|
+
ax = fig.add_subplot(1,1,1);
|
|
1744
|
+
|
|
1745
|
+
mpl.rcParams['xtick.color'] = plot_params['tick_color'];
|
|
1746
|
+
mpl.rcParams['ytick.color'] = plot_params['tick_color'];
|
|
1747
|
+
|
|
1748
|
+
fig.set_facecolor(plot_params['background_color'])
|
|
1749
|
+
ax.patch.set_facecolor(plot_params['background_color'])
|
|
1750
|
+
|
|
1751
|
+
ax.tick_params(which = 'minor', length = 2)
|
|
1752
|
+
ax.tick_params(which = 'major', length = 4,labelsize = 16)
|
|
1753
|
+
|
|
1754
|
+
ax.grid(ls = 'dotted',lw = 0.5,color = 'gray',zorder = 1)
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
|
|
1758
|
+
|
|
1759
|
+
ax.set_ylabel('Polarisation',
|
|
1760
|
+
fontsize = 16,
|
|
1761
|
+
color = plot_params['text_color'])
|
|
1762
|
+
|
|
1763
|
+
ax.set_xlabel(r'$T$ ($\mu$s)',
|
|
1764
|
+
fontsize = 16,
|
|
1765
|
+
color = plot_params['text_color'])
|
|
1766
|
+
|
|
1767
|
+
|
|
1768
|
+
ax.set_ylim(0,1)
|
|
1769
|
+
|
|
1770
|
+
if title != None:
|
|
1771
|
+
fig.text(0.15,0.9,title,
|
|
1772
|
+
fontsize = 16,fontweight = 'bold',
|
|
1773
|
+
color = plot_params['text_color'])
|
|
1774
|
+
|
|
1775
|
+
|
|
1776
|
+
for column in P_df.columns:
|
|
1777
|
+
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
if column == 'avg':
|
|
1781
|
+
ax.plot(P_df.index,P_df[column],
|
|
1782
|
+
linestyle = plot_params['line_style'],
|
|
1783
|
+
label = label,
|
|
1784
|
+
alpha = plot_params['line_alpha'],
|
|
1785
|
+
linewidth = plot_params['line_width'],
|
|
1786
|
+
color = plot_params['line_color'])
|
|
1787
|
+
|
|
1788
|
+
else:
|
|
1789
|
+
ax.plot(P_df.index,P_df[column],
|
|
1790
|
+
linestyle = plot_params['line_style'],
|
|
1791
|
+
label = label,
|
|
1792
|
+
alpha = plot_params['line_alpha'],
|
|
1793
|
+
linewidth = plot_params['line_width'],
|
|
1794
|
+
color = plot_params['line_color'])
|
|
1795
|
+
|
|
1796
|
+
ax.xaxis.set_minor_locator(AutoMinorLocator())
|
|
1797
|
+
ax.yaxis.set_minor_locator(AutoMinorLocator())
|
|
1798
|
+
|
|
1799
|
+
ax.legend(loc = 'lower right',fontsize = 12)
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
return fig,ax
|
|
1803
|
+
|
|
1804
|
+
def plot_D_pol_N(P_df,fig_size = [7,4],fig = None, ax = None, plot_avg = True,title = None,plot_params = plot_params(),dpi = 100):
|
|
1805
|
+
|
|
1806
|
+
"""
|
|
1807
|
+
A function which plots the polarisation time series of nuclear spins under repeated
|
|
1808
|
+
NV polarisation
|
|
1809
|
+
|
|
1810
|
+
Parameters
|
|
1811
|
+
----------
|
|
1812
|
+
|
|
1813
|
+
P_df: dataframe containing all the polarisation series of the nuclear spins
|
|
1814
|
+
plot_avg: bool which determines whether average polarisation should be plotted
|
|
1815
|
+
title: str which will be displayed as a title
|
|
1816
|
+
|
|
1817
|
+
|
|
1818
|
+
Returns
|
|
1819
|
+
--------
|
|
1820
|
+
|
|
1821
|
+
fig: matplotlib object which conatains the plot for the figure
|
|
1822
|
+
|
|
1823
|
+
"""
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
if plot_avg == True:
|
|
1827
|
+
P_df['avg'] = P_df.mean(axis = 1);
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
if fig == None or ax == None:
|
|
1832
|
+
|
|
1833
|
+
fig = plt.figure(figsize = fig_size,dpi = dpi);
|
|
1834
|
+
|
|
1835
|
+
ax = fig.add_subplot(1,1,1);
|
|
1836
|
+
|
|
1837
|
+
mpl.rcParams['xtick.color'] = plot_params['tick_color'];
|
|
1838
|
+
mpl.rcParams['ytick.color'] = plot_params['tick_color'];
|
|
1839
|
+
|
|
1840
|
+
fig.set_facecolor(plot_params['background_color'])
|
|
1841
|
+
ax.patch.set_facecolor(plot_params['background_color'])
|
|
1842
|
+
|
|
1843
|
+
ax.tick_params(which = 'minor', length = 2)
|
|
1844
|
+
ax.tick_params(which = 'major', length = 4,labelsize = 16)
|
|
1845
|
+
|
|
1846
|
+
ax.grid(ls = 'dotted',lw = 0.5,color = 'gray',zorder = 1)
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
|
|
1850
|
+
|
|
1851
|
+
ax.set_ylabel('Polarisation',
|
|
1852
|
+
fontsize = 16,
|
|
1853
|
+
color = plot_params['text_color'])
|
|
1854
|
+
|
|
1855
|
+
ax.set_xlabel(r'$2\tau$ ($\mu$s)',
|
|
1856
|
+
fontsize = 16,
|
|
1857
|
+
color = plot_params['text_color'])
|
|
1858
|
+
|
|
1859
|
+
|
|
1860
|
+
ax.set_ylim(0,0.5)
|
|
1861
|
+
|
|
1862
|
+
if title != None:
|
|
1863
|
+
fig.text(0.15,0.9,title,
|
|
1864
|
+
fontsize = 16,fontweight = 'bold',
|
|
1865
|
+
color = plot_params['text_color'])
|
|
1866
|
+
|
|
1867
|
+
|
|
1868
|
+
for column in P_df.columns:
|
|
1869
|
+
|
|
1870
|
+
if column == 'avg':
|
|
1871
|
+
ax.plot(P_df.index,P_df[column]/2,
|
|
1872
|
+
linestyle = plot_params['line_style'],
|
|
1873
|
+
label = '%s'%column,
|
|
1874
|
+
linewidth = 2)
|
|
1875
|
+
|
|
1876
|
+
else:
|
|
1877
|
+
ax.plot(P_df.index,P_df[column]/2,
|
|
1878
|
+
linestyle = plot_params['line_style'],
|
|
1879
|
+
label = '%s'%column,
|
|
1880
|
+
linewidth = 2)
|
|
1881
|
+
|
|
1882
|
+
ax.legend(loc = 'lower right',fontsize = 12)
|
|
1883
|
+
|
|
1884
|
+
|
|
1885
|
+
return fig,ax
|
|
1886
|
+
|
|
1887
|
+
"""
|
|
1888
|
+
FLOQUET
|
|
1889
|
+
"""
|
|
1890
|
+
|
|
1891
|
+
def floquet_spectrum(operators,params,taus, bound = 1,tan2 = False,inst = True):
|
|
1892
|
+
|
|
1893
|
+
"""
|
|
1894
|
+
A function which takes the free Hamiltonian of a system and a partiuclar pulse
|
|
1895
|
+
protocol structure and finds the Floquet phase structure over a range of tau
|
|
1896
|
+
|
|
1897
|
+
Parameters
|
|
1898
|
+
----------
|
|
1899
|
+
operators: dict of operators for system constructed using generate_operators()
|
|
1900
|
+
function
|
|
1901
|
+
params: dict of parameters for protocol and system found by using function
|
|
1902
|
+
params()
|
|
1903
|
+
taus: array of float points which describe the range of tau for floquet spectrum
|
|
1904
|
+
|
|
1905
|
+
|
|
1906
|
+
Returns
|
|
1907
|
+
-------
|
|
1908
|
+
rho: Qobj for the final state of the system.
|
|
1909
|
+
Pol_array: numpy array of Pol array size N_nuc and length Reps
|
|
1910
|
+
"""
|
|
1911
|
+
protocols_dict = {'PulsePol':PulsePol,'CPMG':CPMG};
|
|
1912
|
+
|
|
1913
|
+
#retrieve protocol parameters
|
|
1914
|
+
N_nuc = params['N_nuc'];
|
|
1915
|
+
protocol = params['protocol']
|
|
1916
|
+
|
|
1917
|
+
|
|
1918
|
+
''' find the unperturbed starting floquet states '''
|
|
1919
|
+
params['tau'] = 1e-9;
|
|
1920
|
+
|
|
1921
|
+
U_ini = protocols_dict[protocol](operators,params,inst);
|
|
1922
|
+
lbda, vecOld = U_ini.eigenstates(sort = 'low');
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
''' initialise arrays '''
|
|
1928
|
+
E = []
|
|
1929
|
+
Vec = []
|
|
1930
|
+
wind = [];
|
|
1931
|
+
for i in range(2**(N_nuc + 1)):
|
|
1932
|
+
|
|
1933
|
+
E.append([]);
|
|
1934
|
+
Vec.append([])
|
|
1935
|
+
wind.append(0);
|
|
1936
|
+
|
|
1937
|
+
''' run through tau array and find all floquet phases and states '''
|
|
1938
|
+
for j in pb.progressbar(range(len(taus))):
|
|
1939
|
+
|
|
1940
|
+
states = []
|
|
1941
|
+
params['tau'] = taus[j]
|
|
1942
|
+
|
|
1943
|
+
|
|
1944
|
+
U = protocols_dict[protocol](operators,params,inst);
|
|
1945
|
+
|
|
1946
|
+
lbda,vectemp = U.eigenstates(sort = 'low');
|
|
1947
|
+
|
|
1948
|
+
if tan2 == True:
|
|
1949
|
+
#convert into floquet phases
|
|
1950
|
+
etemp = np.arctan2(lbda.imag,lbda.real)
|
|
1951
|
+
f = 1/2
|
|
1952
|
+
elif tan2 == False:
|
|
1953
|
+
#convert into floquet phases
|
|
1954
|
+
etemp = np.arctan(lbda.imag/lbda.real)
|
|
1955
|
+
f = 1
|
|
1956
|
+
|
|
1957
|
+
etemp = np.array(etemp)
|
|
1958
|
+
|
|
1959
|
+
|
|
1960
|
+
idx = np.argsort(etemp);
|
|
1961
|
+
etemp = etemp[idx]
|
|
1962
|
+
vectemp = vectemp[idx]
|
|
1963
|
+
#assign the correct floquet state to the correct array to prevent discontinuities
|
|
1964
|
+
for i in range(len(etemp)):
|
|
1965
|
+
|
|
1966
|
+
overlapMax = 0
|
|
1967
|
+
|
|
1968
|
+
for k in range(len(vecOld)):
|
|
1969
|
+
overlap = abs((vectemp[i].overlap(vecOld[k])))**2
|
|
1970
|
+
|
|
1971
|
+
if overlap > overlapMax and k not in states:
|
|
1972
|
+
state = k;
|
|
1973
|
+
overlapMax = overlap;
|
|
1974
|
+
|
|
1975
|
+
states.append(state)
|
|
1976
|
+
|
|
1977
|
+
|
|
1978
|
+
#assign winding number for states to remove discontinuities due to oscillatory wrapping
|
|
1979
|
+
if j > 0:
|
|
1980
|
+
if E[state][-1] - etemp[i] - 2*f*np.pi*wind[state] > np.pi/2*f:
|
|
1981
|
+
|
|
1982
|
+
wind[state] +=1;
|
|
1983
|
+
|
|
1984
|
+
elif E[state][-1] - etemp[i] - 2*f*np.pi*wind[state] < -np.pi/2*f:
|
|
1985
|
+
|
|
1986
|
+
wind[state] -=1;
|
|
1987
|
+
|
|
1988
|
+
E[state].append(etemp[i] + 2*f*np.pi*wind[state]);
|
|
1989
|
+
Vec[state].append(vectemp[i]);
|
|
1990
|
+
|
|
1991
|
+
#store the new floquet states as the old floquet states for the next loop
|
|
1992
|
+
vecNew = [];
|
|
1993
|
+
for n in range(len(states)):
|
|
1994
|
+
vecNew.append([])
|
|
1995
|
+
|
|
1996
|
+
for n in range(len(states)):
|
|
1997
|
+
vecNew[states[n]] = vectemp[n]
|
|
1998
|
+
vecOld = vecNew;
|
|
1999
|
+
|
|
2000
|
+
return E,Vec
|
|
2001
|
+
|
|
2002
|
+
|
|
2003
|
+
|
|
2004
|
+
|
|
2005
|
+
"""
|
|
2006
|
+
NV GENERATOR
|
|
2007
|
+
"""
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
def nv_generator(params = params(),lat = [-10,10],p = 0.011,species = 'C',random_state = None):
|
|
2011
|
+
"""
|
|
2012
|
+
nv_generator is a function which takes in the size of a lattice and generates
|
|
2013
|
+
a random bath of species C.
|
|
2014
|
+
|
|
2015
|
+
Parameters:
|
|
2016
|
+
params: a dict of parameters commonly used for NV centres. This
|
|
2017
|
+
function will use the lattice vectors
|
|
2018
|
+
lat: array of integers for start N_s and end N_f of the number of lattice
|
|
2019
|
+
repetitions. Default = [-10,10]
|
|
2020
|
+
p: the probability that the lattice site is the chosen species.
|
|
2021
|
+
species: The label for the species of impurity. Choice between H or C
|
|
2022
|
+
|
|
2023
|
+
returns:
|
|
2024
|
+
df: dataframe of Nv hyperfine couplings, positions and species label
|
|
2025
|
+
"""
|
|
2026
|
+
|
|
2027
|
+
R = params['vecs']
|
|
2028
|
+
sites = []
|
|
2029
|
+
|
|
2030
|
+
np.random.seed(random_state)
|
|
2031
|
+
|
|
2032
|
+
# for loop for x direction
|
|
2033
|
+
for n in range(lat[0],lat[1] + 1):
|
|
2034
|
+
#for loop for y direction
|
|
2035
|
+
for l in range(lat[0],lat[1] + 1):
|
|
2036
|
+
#for loop for z direction
|
|
2037
|
+
for m in range(lat[0],lat[1] + 1):
|
|
2038
|
+
#loop over vectors
|
|
2039
|
+
for vec in range(len(R)):
|
|
2040
|
+
|
|
2041
|
+
r = random.random()
|
|
2042
|
+
new = np.add(R[vec], [n,l,m])
|
|
2043
|
+
|
|
2044
|
+
if new[0] == 0.25 and new[1] == 0.25 and new[2] == 0.25:
|
|
2045
|
+
|
|
2046
|
+
Val = new;
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
elif new[0] == 0 and new[1] == 0 and new[2] == 0:
|
|
2050
|
+
|
|
2051
|
+
N14 = new;
|
|
2052
|
+
|
|
2053
|
+
|
|
2054
|
+
elif r <= p:
|
|
2055
|
+
temp = np.subtract(new,[0.25,0.25,0.25]);
|
|
2056
|
+
|
|
2057
|
+
#change basis
|
|
2058
|
+
temp = change_basis(temp);
|
|
2059
|
+
|
|
2060
|
+
|
|
2061
|
+
if species == 'p1':
|
|
2062
|
+
#find hyperfine coupling using previously defined function
|
|
2063
|
+
s = '14N' if np.random.random() < 1 else 'N'
|
|
2064
|
+
A = hyperfine(temp*params['lattice_constant']*1e10,params=params,species = s)
|
|
2065
|
+
Apar = A[2];
|
|
2066
|
+
Aper = np.linalg.norm([A[0],A[1]]);
|
|
2067
|
+
sites.append([s,A[0],A[1],A[2],Apar,Aper,(temp*params['lattice_constant']*1e10).round(2)])
|
|
2068
|
+
#find hyperfine coupling using previously defined function
|
|
2069
|
+
|
|
2070
|
+
A = hyperfine(temp*params['lattice_constant']*1e10,params=params,species = 'e')
|
|
2071
|
+
Apar = A[2];
|
|
2072
|
+
Aper = np.linalg.norm([A[0],A[1]]);
|
|
2073
|
+
sites.append(['p1',A[0],A[1],A[2],Apar,Aper,(temp*params['lattice_constant']*1e10).round(2)])
|
|
2074
|
+
else:
|
|
2075
|
+
#find hyperfine coupling using previously defined function
|
|
2076
|
+
A = hyperfine(temp*params['lattice_constant']*1e10,params=params,species = species)
|
|
2077
|
+
Apar = A[2];
|
|
2078
|
+
Aper = np.linalg.norm([A[0],A[1]]);
|
|
2079
|
+
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
sites.append([species,A[0],A[1],A[2],Apar,Aper,(temp*params['lattice_constant']*1e10).round(2)])
|
|
2083
|
+
|
|
2084
|
+
df = pd.DataFrame(sites,columns = ['Species','Ax','Ay','Az','Apar','Aper','pos'])
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
|
|
2088
|
+
|
|
2089
|
+
return df
|
|
2090
|
+
|
|
2091
|
+
|