Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Project: topocm
Views: 387
1
import itertools
2
import collections
3
from copy import copy
4
from types import SimpleNamespace
5
6
import numpy as np
7
import holoviews as hv
8
9
from wraparound import wraparound
10
11
if tuple(int(i) for i in np.__version__.split('.')[:3]) <= (1, 8, 0):
12
raise RuntimeError("numpy >= (1, 8, 0) is required")
13
14
__all__ = ['spectrum', 'hamiltonian_array', 'h_k', 'pauli']
15
16
pauli = SimpleNamespace(s0=np.array([[1., 0.], [0., 1.]]),
17
sx=np.array([[0., 1.], [1., 0.]]),
18
sy=np.array([[0., -1j], [1j, 0.]]),
19
sz=np.array([[1., 0.], [0., -1.]]))
20
21
pauli.s0s0 = np.kron(pauli.s0, pauli.s0)
22
pauli.s0sx = np.kron(pauli.s0, pauli.sx)
23
pauli.s0sy = np.kron(pauli.s0, pauli.sy)
24
pauli.s0sz = np.kron(pauli.s0, pauli.sz)
25
pauli.sxs0 = np.kron(pauli.sx, pauli.s0)
26
pauli.sxsx = np.kron(pauli.sx, pauli.sx)
27
pauli.sxsy = np.kron(pauli.sx, pauli.sy)
28
pauli.sxsz = np.kron(pauli.sx, pauli.sz)
29
pauli.sys0 = np.kron(pauli.sy, pauli.s0)
30
pauli.sysx = np.kron(pauli.sy, pauli.sx)
31
pauli.sysy = np.kron(pauli.sy, pauli.sy)
32
pauli.sysz = np.kron(pauli.sy, pauli.sz)
33
pauli.szs0 = np.kron(pauli.sz, pauli.s0)
34
pauli.szsx = np.kron(pauli.sz, pauli.sx)
35
pauli.szsy = np.kron(pauli.sz, pauli.sy)
36
pauli.szsz = np.kron(pauli.sz, pauli.sz)
37
38
39
def spectrum(sys, p=None, k_x=None, k_y=None, k_z=None, title=None, xdim=None,
40
ydim=None, zdim=None, xticks=None, yticks=None, zticks=None,
41
xlims=None, ylims=None, zlims=None, num_bands=None, return_energies=False):
42
"""Function that plots system spectrum for varying parameters or momenta.
43
44
Parameters:
45
-----------
46
sys : kwant.Builder object
47
The un-finalized (in)finite system.
48
p : SimpleNamespace object
49
A container used to store Hamiltonian parameters. The parameters that
50
are sequences are used as plot axes.
51
k_x, k_y, k_z : floats or sequences of floats
52
Real space momenta at which the Hamiltonian has to be evaluated.
53
If the system dimensionality is low, extra momenta are ignored.
54
title : function or str
55
Function that takes p as argument and generates a string. If a string
56
it's used as static title.
57
xdim, ydim, zdim : holoviews.Dimension or string
58
The labels of the axes. Default to best guess, and extra ones
59
are ignored.
60
xticks, yticks zticks : list
61
Lists of axes xticks, extra ones are ignored.
62
xlims, ylims, zlims : tuple
63
Upper and lower plot limit of the axes. If None the upper and lower
64
limits of the ticks are used. Extra ones are ignored.
65
num_bands : int
66
Number of bands that should be plotted, only works for 2D plots. If
67
None all bands are plotted.
68
return_energies : bool
69
If True the function only returns the energies in an array.
70
71
Returns:
72
--------
73
plot : holoviews.Path object
74
Plot of varying parameter vs. spectrum.
75
"""
76
pi_ticks = [(-np.pi, r'$-\pi$'), (0, '$0$'), (np.pi, r'$\pi$')]
77
if p is None:
78
p = SimpleNamespace()
79
dimensionality = sys.symmetry.num_directions
80
k = [k_x, k_y, k_z]
81
k = [(np.linspace(-np.pi, np.pi, 101) if i is None else i)
82
for i in k]
83
k = [(i if j < dimensionality else 0) for (j, i) in enumerate(k)]
84
k_x, k_y, k_z = k
85
86
hamiltonians, variables = hamiltonian_array(sys, p, k_x, k_y, k_z, True)
87
# Don't waste effort calculating eigenvalues if we aren't going to plot
88
# anything.
89
if len(variables) in (1, 2):
90
energies = np.linalg.eigvalsh(hamiltonians)
91
92
if len(variables) == 0:
93
raise ValueError("A 0D plot requested")
94
95
if return_energies:
96
return energies
97
98
elif len(variables) == 1:
99
# 1D plot.
100
if xdim is None:
101
if variables[0][0] in 'k_x k_y k_z'.split():
102
xdim = r'${}$'.format(variables[0][0])
103
else:
104
xdim = variables[0][0]
105
if ydim is None:
106
ydim = r'$E$'
107
108
plot = hv.Path((variables[0][1], energies), kdims=[xdim, ydim])
109
110
ticks = {}
111
if isinstance(xticks, collections.Iterable):
112
ticks['xticks'] = list(xticks)
113
elif xticks is None:
114
pass
115
else:
116
ticks['xticks'] = xticks
117
118
if isinstance(yticks, collections.Iterable):
119
ticks['yticks'] = list(yticks)
120
elif isinstance(yticks, int):
121
ticks['yticks'] = yticks
122
123
xlims = slice(*xlims) if xlims is not None else slice(None)
124
ylims = slice(*ylims) if ylims is not None else slice(None)
125
126
if callable(title):
127
plot = plot.relabel(title(p))
128
elif isinstance(title, str):
129
plot = plot.relabel(title)
130
131
return plot[xlims, ylims](plot={'Path': ticks})
132
133
elif len(variables) == 2:
134
# 2D plot.
135
136
style = {}
137
if xticks is None and variables[0][0] in 'k_x k_y k_z'.split():
138
style['xticks'] = pi_ticks
139
elif xticks is not None:
140
style['xticks'] = list(xticks)
141
if yticks is None and variables[1][0] in 'k_x k_y k_z'.split():
142
style['yticks'] = pi_ticks
143
elif yticks is not None:
144
style['yticks'] = list(yticks)
145
146
if xdim is None:
147
if variables[0][0] in 'k_x k_y k_z'.split():
148
xdim = r'${}$'.format(variables[0][0])
149
else:
150
xdim = variables[0][0]
151
if ydim is None:
152
if variables[1][0] in 'k_x k_y k_z'.split():
153
ydim = r'${}$'.format(variables[1][0])
154
else:
155
ydim = variables[1][0]
156
if zdim is None:
157
zdim = r'$E$'
158
159
if zticks is not None:
160
style['zticks'] = zticks
161
162
if xlims is None:
163
xlims = np.round([min(variables[0][1]), max(variables[0][1])], 2)
164
if ylims is None:
165
ylims = np.round([min(variables[1][1]), max(variables[1][1])], 2)
166
if zlims is None:
167
zlims = (None, None)
168
169
kwargs = {'extents': (xlims[0], ylims[0], zlims[0],
170
xlims[1], ylims[1], zlims[1]),
171
'kdims': [xdim, ydim],
172
'vdims': [zdim]}
173
174
if num_bands is None:
175
plot = hv.Overlay([hv.Surface(energies[:, :, i], **kwargs)(plot=style)
176
for i in range(energies.shape[-1])])
177
else:
178
mid = energies.shape[-1] // 2
179
num_bands //= 2
180
plot = hv.Overlay([hv.Surface(energies[:, :, i], **kwargs)(plot=style)
181
for i in range(mid - num_bands, mid + num_bands)])
182
183
if callable(title):
184
plot = plot.relabel(title(p))
185
elif isinstance(title, str):
186
plot = plot.relabel(title)
187
188
return plot(plot={'Overlay': {'fig_size': 200}})
189
190
else:
191
raise ValueError("Cannot make 4D plots yet.")
192
193
194
195
def h_k(sys, p, momentum):
196
"""Function that returns the Hamiltonian of a kwant 1D system as a momentum.
197
"""
198
return hamiltonian_array(sys, p, momentum)[0]
199
200
201
def hamiltonian_array(sys, p=None, k_x=0, k_y=0, k_z=0, return_grid=False):
202
"""Evaluate the Hamiltonian of a system over a grid of parameters.
203
204
Parameters:
205
-----------
206
sys : kwant.Builder object
207
The un-finalized kwant system whose Hamiltonian is calculated.
208
p : SimpleNamespace object
209
A container of Hamiltonian parameters. The parameters that are
210
sequences are used to loop over.
211
k_x, k_y, k_z : floats or sequences of floats
212
Momenta at which the Hamiltonian has to be evaluated. If the system
213
only has 1 translation symmetry, only `k_x` is used, and interpreted as
214
lattice momentum. Otherwise the momenta are in reciprocal space.
215
return_grid : bool
216
Whether to also return the names of the variables used for expansion,
217
and their values.
218
219
Returns:
220
--------
221
hamiltonians : numpy.ndarray
222
An array with the Hamiltonians. The first n-2 dimensions correspond to
223
the expanded variables.
224
parameters : list of tuples
225
Names and ranges of values that were used in evaluation of the
226
Hamiltonians.
227
228
Examples:
229
---------
230
>>> hamiltonian_array(sys, SimpleNamespace(t=1, mu=np.linspace(-2, 2)),
231
... k_x=np.linspace(-np.pi, np.pi))
232
>>> hamiltonian_array(sys_2d, p, np.linspace(-np.pi, np.pi),
233
... np.linspace(-np.pi, np.pi))
234
235
"""
236
if p is None:
237
p = SimpleNamespace()
238
try:
239
space_dimensionality = sys.symmetry.periods.shape[-1]
240
except AttributeError:
241
space_dimensionality = 0
242
dimensionality = sys.symmetry.num_directions
243
pars = copy(p)
244
if dimensionality == 0:
245
sys = sys.finalized()
246
def momentum_to_lattice(k):
247
return []
248
else:
249
if len(sys.symmetry.periods) == 1:
250
def momentum_to_lattice(k):
251
if any(k[dimensionality:]):
252
raise ValueError("Dispersion is 1D, but more momenta are provided.")
253
return [k[0]]
254
else:
255
B = np.array(sys.symmetry.periods).T
256
A = B.dot(np.linalg.inv(B.T.dot(B)))
257
def momentum_to_lattice(k):
258
k, residuals = np.linalg.lstsq(A, k[:space_dimensionality])[:2]
259
if np.any(abs(residuals) > 1e-7):
260
raise RuntimeError("Requested momentum doesn't correspond"
261
" to any lattice momentum.")
262
return list(k)
263
sys = wraparound(sys).finalized()
264
265
changing = dict()
266
for key, value in pars.__dict__.items():
267
if isinstance(value, collections.Iterable):
268
changing[key] = value
269
270
for key, value in [('k_x', k_x), ('k_y', k_y), ('k_z', k_z)]:
271
if key in changing:
272
raise RuntimeError('One of the system parameters is {}, '
273
'which is reserved for momentum. '
274
'Please rename it.'.format(key))
275
if isinstance(value, collections.Iterable):
276
changing[key] = value
277
278
if not changing:
279
hamiltonians = sys.hamiltonian_submatrix([pars] +
280
momentum_to_lattice([k_x, k_y, k_z]),
281
sparse=False)[None, ...]
282
if return_grid:
283
return hamiltonians, []
284
else:
285
return hamiltonians
286
287
288
def hamiltonian(**values):
289
pars.__dict__.update(values)
290
k = [values.get('k_x', k_x), values.get('k_y', k_y),
291
values.get('k_z', k_z)]
292
k = momentum_to_lattice(k)
293
return sys.hamiltonian_submatrix(args=([pars] + k), sparse=False)
294
295
names, values = zip(*sorted(changing.items()))
296
hamiltonians = [hamiltonian(**dict(zip(names, value)))
297
for value in itertools.product(*values)]
298
size = list(hamiltonians[0].shape)
299
300
hamiltonians = np.array(hamiltonians).reshape([len(value)
301
for value in values] + size)
302
303
if return_grid:
304
return hamiltonians, list(zip(names, values))
305
else:
306
return hamiltonians
307
308