Trajectree 0.0.1__py3-none-any.whl → 0.0.2__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.
- trajectree/__init__.py +0 -3
- trajectree/fock_optics/devices.py +1 -1
- trajectree/fock_optics/light_sources.py +2 -2
- trajectree/fock_optics/measurement.py +3 -3
- trajectree/fock_optics/utils.py +6 -6
- trajectree/trajectory.py +2 -2
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/METADATA +2 -3
- trajectree-0.0.2.dist-info/RECORD +16 -0
- trajectree/quimb/docs/_pygments/_pygments_dark.py +0 -118
- trajectree/quimb/docs/_pygments/_pygments_light.py +0 -118
- trajectree/quimb/docs/conf.py +0 -158
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +0 -62
- trajectree/quimb/quimb/__init__.py +0 -507
- trajectree/quimb/quimb/calc.py +0 -1491
- trajectree/quimb/quimb/core.py +0 -2279
- trajectree/quimb/quimb/evo.py +0 -712
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +0 -129
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +0 -109
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +0 -397
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +0 -653
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +0 -571
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +0 -775
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +0 -537
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +0 -194
- trajectree/quimb/quimb/experimental/cluster_update.py +0 -286
- trajectree/quimb/quimb/experimental/merabuilder.py +0 -865
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +0 -15
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +0 -1631
- trajectree/quimb/quimb/experimental/schematic.py +0 -7
- trajectree/quimb/quimb/experimental/tn_marginals.py +0 -130
- trajectree/quimb/quimb/experimental/tnvmc.py +0 -1483
- trajectree/quimb/quimb/gates.py +0 -36
- trajectree/quimb/quimb/gen/__init__.py +0 -2
- trajectree/quimb/quimb/gen/operators.py +0 -1167
- trajectree/quimb/quimb/gen/rand.py +0 -713
- trajectree/quimb/quimb/gen/states.py +0 -479
- trajectree/quimb/quimb/linalg/__init__.py +0 -6
- trajectree/quimb/quimb/linalg/approx_spectral.py +0 -1109
- trajectree/quimb/quimb/linalg/autoblock.py +0 -258
- trajectree/quimb/quimb/linalg/base_linalg.py +0 -719
- trajectree/quimb/quimb/linalg/mpi_launcher.py +0 -397
- trajectree/quimb/quimb/linalg/numpy_linalg.py +0 -244
- trajectree/quimb/quimb/linalg/rand_linalg.py +0 -514
- trajectree/quimb/quimb/linalg/scipy_linalg.py +0 -293
- trajectree/quimb/quimb/linalg/slepc_linalg.py +0 -892
- trajectree/quimb/quimb/schematic.py +0 -1518
- trajectree/quimb/quimb/tensor/__init__.py +0 -401
- trajectree/quimb/quimb/tensor/array_ops.py +0 -610
- trajectree/quimb/quimb/tensor/circuit.py +0 -4824
- trajectree/quimb/quimb/tensor/circuit_gen.py +0 -411
- trajectree/quimb/quimb/tensor/contraction.py +0 -336
- trajectree/quimb/quimb/tensor/decomp.py +0 -1255
- trajectree/quimb/quimb/tensor/drawing.py +0 -1646
- trajectree/quimb/quimb/tensor/fitting.py +0 -385
- trajectree/quimb/quimb/tensor/geometry.py +0 -583
- trajectree/quimb/quimb/tensor/interface.py +0 -114
- trajectree/quimb/quimb/tensor/networking.py +0 -1058
- trajectree/quimb/quimb/tensor/optimize.py +0 -1818
- trajectree/quimb/quimb/tensor/tensor_1d.py +0 -4778
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +0 -1854
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +0 -662
- trajectree/quimb/quimb/tensor/tensor_2d.py +0 -5954
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +0 -96
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +0 -1230
- trajectree/quimb/quimb/tensor/tensor_3d.py +0 -2869
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +0 -46
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +0 -60
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +0 -3237
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +0 -565
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +0 -1138
- trajectree/quimb/quimb/tensor/tensor_builder.py +0 -5411
- trajectree/quimb/quimb/tensor/tensor_core.py +0 -11179
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +0 -1472
- trajectree/quimb/quimb/tensor/tensor_mera.py +0 -204
- trajectree/quimb/quimb/utils.py +0 -892
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +0 -501
- trajectree/quimb/tests/test_calc.py +0 -788
- trajectree/quimb/tests/test_core.py +0 -847
- trajectree/quimb/tests/test_evo.py +0 -565
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +0 -361
- trajectree/quimb/tests/test_gen/test_rand.py +0 -296
- trajectree/quimb/tests/test_gen/test_states.py +0 -261
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +0 -368
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +0 -351
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +0 -127
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +0 -84
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +0 -134
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +0 -283
- trajectree/quimb/tests/test_tensor/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +0 -39
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +0 -67
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +0 -64
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +0 -51
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +0 -142
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +0 -101
- trajectree/quimb/tests/test_tensor/test_circuit.py +0 -816
- trajectree/quimb/tests/test_tensor/test_contract.py +0 -67
- trajectree/quimb/tests/test_tensor/test_decomp.py +0 -40
- trajectree/quimb/tests/test_tensor/test_mera.py +0 -52
- trajectree/quimb/tests/test_tensor/test_optimizers.py +0 -488
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +0 -1171
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +0 -606
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +0 -144
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +0 -123
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +0 -226
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +0 -441
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +0 -2066
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +0 -388
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +0 -63
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +0 -270
- trajectree/quimb/tests/test_utils.py +0 -85
- trajectree-0.0.1.dist-info/RECORD +0 -126
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/WHEEL +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/top_level.txt +0 -0
|
@@ -1,1167 +0,0 @@
|
|
|
1
|
-
"""Functions for generating quantum operators.
|
|
2
|
-
"""
|
|
3
|
-
import math
|
|
4
|
-
import functools
|
|
5
|
-
import itertools
|
|
6
|
-
import operator
|
|
7
|
-
|
|
8
|
-
import numpy as np
|
|
9
|
-
import scipy.sparse as sp
|
|
10
|
-
from scipy.special import comb
|
|
11
|
-
|
|
12
|
-
from ..utils import isiterable, concat, unique
|
|
13
|
-
from ..core import (qarray, make_immutable, get_thread_pool,
|
|
14
|
-
par_reduce, isreal, qu, eye, kron, ikron)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# --------------------------------------------------------------------------- #
|
|
18
|
-
# gates and other simple operators #
|
|
19
|
-
# --------------------------------------------------------------------------- #
|
|
20
|
-
|
|
21
|
-
@functools.lru_cache(maxsize=16)
|
|
22
|
-
def spin_operator(label, S=1 / 2, **kwargs):
|
|
23
|
-
"""Generate a general spin-operator.
|
|
24
|
-
|
|
25
|
-
Parameters
|
|
26
|
-
----------
|
|
27
|
-
label : str
|
|
28
|
-
The type of operator, can be one of six options:
|
|
29
|
-
|
|
30
|
-
- ``{'x', 'X'}``, x-spin operator.
|
|
31
|
-
- ``{'y', 'Y'}``, y-spin operator.
|
|
32
|
-
- ``{'z', 'Z'}``, z-spin operator.
|
|
33
|
-
- ``{'+', 'p'}``, Raising operator.
|
|
34
|
-
- ``{'-', 'm'}``, Lowering operator.
|
|
35
|
-
- ``{'i', 'I'}``, identity operator.
|
|
36
|
-
|
|
37
|
-
S : float, optional
|
|
38
|
-
The spin of particle to act on, default to spin-1/2.
|
|
39
|
-
kwargs
|
|
40
|
-
Passed to :func:`quimbify`.
|
|
41
|
-
|
|
42
|
-
Returns
|
|
43
|
-
-------
|
|
44
|
-
S : immutable operator
|
|
45
|
-
The spin operator.
|
|
46
|
-
|
|
47
|
-
See Also
|
|
48
|
-
--------
|
|
49
|
-
pauli
|
|
50
|
-
|
|
51
|
-
Examples
|
|
52
|
-
--------
|
|
53
|
-
>>> spin_operator('x')
|
|
54
|
-
qarray([[0. +0.j, 0.5+0.j],
|
|
55
|
-
[0.5+0.j, 0. +0.j]])
|
|
56
|
-
|
|
57
|
-
>>> qu.spin_operator('+', S=1)
|
|
58
|
-
qarray([[0. +0.j, 1.41421356+0.j, 0. +0.j],
|
|
59
|
-
[0. +0.j, 0. +0.j, 1.41421356+0.j],
|
|
60
|
-
[0. +0.j, 0. +0.j, 0. +0.j]])
|
|
61
|
-
|
|
62
|
-
>>> qu.spin_operator('Y', sparse=True)
|
|
63
|
-
<2x2 sparse matrix of type '<class 'numpy.complex128'>'
|
|
64
|
-
with 2 stored elements in Compressed Sparse Row format>
|
|
65
|
-
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
D = int(2 * S + 1)
|
|
69
|
-
|
|
70
|
-
op = np.zeros((D, D), dtype=complex)
|
|
71
|
-
ms = np.linspace(S, -S, D)
|
|
72
|
-
|
|
73
|
-
label = label.lower()
|
|
74
|
-
|
|
75
|
-
if label in {'x', 'y'}:
|
|
76
|
-
for i in range(D - 1):
|
|
77
|
-
c = 0.5 * (S * (S + 1) - (ms[i] * ms[i + 1]))**0.5
|
|
78
|
-
op[i, i + 1] = -1.0j * c if (label == 'y') else c
|
|
79
|
-
op[i + 1, i] = 1.0j * c if (label == 'y') else c
|
|
80
|
-
|
|
81
|
-
elif label == 'z':
|
|
82
|
-
for i in range(D):
|
|
83
|
-
op[i, i] = ms[i]
|
|
84
|
-
|
|
85
|
-
elif label in {'+', 'p', '-', 'm'}:
|
|
86
|
-
for i in range(D - 1):
|
|
87
|
-
c = (S * (S + 1) - (ms[i] * ms[i + 1]))**0.5
|
|
88
|
-
if label in {'+', 'p'}:
|
|
89
|
-
op[i, i + 1] = c
|
|
90
|
-
else:
|
|
91
|
-
op[i + 1, i] = c
|
|
92
|
-
|
|
93
|
-
elif label in {'i', 'I'}:
|
|
94
|
-
np.fill_diagonal(op, 1.0)
|
|
95
|
-
|
|
96
|
-
else:
|
|
97
|
-
raise ValueError(f"Label '{label}'' not understood, should be one of "
|
|
98
|
-
"``['X', 'Y', 'Z', '+', '-', 'I']``.")
|
|
99
|
-
|
|
100
|
-
op = qu(np.real_if_close(op), **kwargs)
|
|
101
|
-
|
|
102
|
-
make_immutable(op)
|
|
103
|
-
return op
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@functools.lru_cache(maxsize=8)
|
|
107
|
-
def pauli(xyz, dim=2, **kwargs):
|
|
108
|
-
"""Generates the pauli operators for dimension 2 or 3.
|
|
109
|
-
|
|
110
|
-
Parameters
|
|
111
|
-
----------
|
|
112
|
-
xyz : str
|
|
113
|
-
Which spatial direction, upper or lower case from ``{'I', 'X', 'Y',
|
|
114
|
-
'Z'}``.
|
|
115
|
-
dim : int, optional
|
|
116
|
-
Dimension of spin operator (e.g. 3 for spin-1), defaults to 2 for
|
|
117
|
-
spin half.
|
|
118
|
-
kwargs
|
|
119
|
-
Passed to ``quimbify``.
|
|
120
|
-
|
|
121
|
-
Returns
|
|
122
|
-
-------
|
|
123
|
-
P : immutable operator
|
|
124
|
-
The pauli operator.
|
|
125
|
-
|
|
126
|
-
See Also
|
|
127
|
-
--------
|
|
128
|
-
spin_operator
|
|
129
|
-
"""
|
|
130
|
-
xyzmap = {0: 'i', 'i': 'i', 'I': 'i',
|
|
131
|
-
1: 'x', 'x': 'x', 'X': 'x',
|
|
132
|
-
2: 'y', 'y': 'y', 'Y': 'y',
|
|
133
|
-
3: 'z', 'z': 'z', 'Z': 'z'}
|
|
134
|
-
opmap = {('i', 2): lambda: eye(2, **kwargs),
|
|
135
|
-
('x', 2): lambda: qu([[0, 1],
|
|
136
|
-
[1, 0]], **kwargs),
|
|
137
|
-
('y', 2): lambda: qu([[0, -1j],
|
|
138
|
-
[1j, 0]], **kwargs),
|
|
139
|
-
('z', 2): lambda: qu([[1, 0],
|
|
140
|
-
[0, -1]], **kwargs),
|
|
141
|
-
('i', 3): lambda: eye(3, **kwargs),
|
|
142
|
-
('x', 3): lambda: qu([[0, 1, 0],
|
|
143
|
-
[1, 0, 1],
|
|
144
|
-
[0, 1, 0]], **kwargs) / 2**.5,
|
|
145
|
-
('y', 3): lambda: qu([[0, -1j, 0],
|
|
146
|
-
[1j, 0, -1j],
|
|
147
|
-
[0, 1j, 0]], **kwargs) / 2**.5,
|
|
148
|
-
('z', 3): lambda: qu([[1, 0, 0],
|
|
149
|
-
[0, 0, 0],
|
|
150
|
-
[0, 0, -1]], **kwargs)}
|
|
151
|
-
op = opmap[(xyzmap[xyz], dim)]()
|
|
152
|
-
|
|
153
|
-
# Operator is cached, so make sure it cannot be modified
|
|
154
|
-
make_immutable(op)
|
|
155
|
-
|
|
156
|
-
return op
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@functools.lru_cache(8)
|
|
160
|
-
def hadamard(dtype=complex, sparse=False):
|
|
161
|
-
"""The Hadamard gate.
|
|
162
|
-
"""
|
|
163
|
-
H = qu([[1., 1.],
|
|
164
|
-
[1., -1.]], dtype=dtype, sparse=sparse) / 2**0.5
|
|
165
|
-
make_immutable(H)
|
|
166
|
-
return H
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@functools.lru_cache(128)
|
|
170
|
-
def phase_gate(phi, dtype=complex, sparse=False):
|
|
171
|
-
"""The generalized qubit phase-gate, which adds phase ``phi`` to the
|
|
172
|
-
``|1>`` state.
|
|
173
|
-
"""
|
|
174
|
-
Rp = qu([[1., 0.],
|
|
175
|
-
[0., np.exp(1.0j * phi)]], dtype=dtype, sparse=sparse)
|
|
176
|
-
make_immutable(Rp)
|
|
177
|
-
return Rp
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@functools.lru_cache(8)
|
|
181
|
-
def T_gate(dtype=complex, sparse=False):
|
|
182
|
-
"""The T-gate (pi/8 gate).
|
|
183
|
-
"""
|
|
184
|
-
return phase_gate(math.pi / 4, dtype=dtype, sparse=sparse)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@functools.lru_cache(8)
|
|
188
|
-
def S_gate(dtype=complex, sparse=False):
|
|
189
|
-
"""The S-gate (phase gate).
|
|
190
|
-
"""
|
|
191
|
-
return phase_gate(math.pi / 2, dtype=dtype, sparse=sparse)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@functools.lru_cache(128)
|
|
195
|
-
def rotation(phi, xyz='Z', dtype=complex, sparse=False):
|
|
196
|
-
"""The single qubit rotation gate.
|
|
197
|
-
"""
|
|
198
|
-
R = math.cos(phi / 2) * pauli('I') - 1.0j * math.sin(phi / 2) * pauli(xyz)
|
|
199
|
-
R = qu(R, dtype=dtype, sparse=sparse)
|
|
200
|
-
make_immutable(R)
|
|
201
|
-
return R
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Rx = functools.partial(rotation, xyz='x')
|
|
205
|
-
Ry = functools.partial(rotation, xyz='y')
|
|
206
|
-
Rz = functools.partial(rotation, xyz='z')
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
@functools.lru_cache(128)
|
|
210
|
-
def U_gate(theta, phi, lamda, dtype=complex, sparse=False):
|
|
211
|
-
r"""Arbitrary unitary single qubit gate.
|
|
212
|
-
|
|
213
|
-
.. math::
|
|
214
|
-
|
|
215
|
-
U_3(\theta, \phi, \lambda) =
|
|
216
|
-
\begin{bmatrix}
|
|
217
|
-
\cos(\theta / 2) & - e^{i \lambda} \sin(\theta / 2) \\
|
|
218
|
-
e^{i \phi} \sin(\theta / 2) & e^{i(\lambda + \phi)}\cos(\theta / 2)
|
|
219
|
-
\end{bmatrix}
|
|
220
|
-
|
|
221
|
-
Parameters
|
|
222
|
-
----------
|
|
223
|
-
theta : float
|
|
224
|
-
Angle between 0 and pi.
|
|
225
|
-
phi : float
|
|
226
|
-
Angle between 0 and 2 pi.
|
|
227
|
-
lamba : float
|
|
228
|
-
Angle between 0 and 2 pi.
|
|
229
|
-
|
|
230
|
-
Returns
|
|
231
|
-
-------
|
|
232
|
-
U : (2, 2) array
|
|
233
|
-
The unitary matrix, cached.
|
|
234
|
-
"""
|
|
235
|
-
from cmath import cos, sin, exp
|
|
236
|
-
|
|
237
|
-
c2, s2 = cos(theta / 2), sin(theta / 2)
|
|
238
|
-
U = qu(
|
|
239
|
-
[[c2, -exp(1j * lamda) * s2],
|
|
240
|
-
[exp(1j * phi) * s2, exp(1j * (lamda + phi)) * c2]],
|
|
241
|
-
dtype=dtype, sparse=sparse
|
|
242
|
-
)
|
|
243
|
-
make_immutable(U)
|
|
244
|
-
return U
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
@functools.lru_cache(4)
|
|
248
|
-
def Xsqrt(**qu_opts):
|
|
249
|
-
r"""Rx(pi / 2).
|
|
250
|
-
|
|
251
|
-
.. math::
|
|
252
|
-
|
|
253
|
-
X^{\frac{1}{2}} =
|
|
254
|
-
\frac{1}{\sqrt{2}}
|
|
255
|
-
\begin{bmatrix}
|
|
256
|
-
1 & - i \\
|
|
257
|
-
- i & 1
|
|
258
|
-
\end{bmatrix}
|
|
259
|
-
"""
|
|
260
|
-
return Rx(math.pi / 2, **qu_opts)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
@functools.lru_cache(4)
|
|
264
|
-
def Ysqrt(**qu_opts):
|
|
265
|
-
r"""Ry(pi / 2).
|
|
266
|
-
|
|
267
|
-
.. math::
|
|
268
|
-
|
|
269
|
-
Y^{\frac{1}{2}} =
|
|
270
|
-
\frac{1}{\sqrt{2}}
|
|
271
|
-
\begin{bmatrix}
|
|
272
|
-
1 & - 1 \\
|
|
273
|
-
1 & 1
|
|
274
|
-
\end{bmatrix}
|
|
275
|
-
"""
|
|
276
|
-
return Ry(math.pi / 2, **qu_opts)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
@functools.lru_cache(4)
|
|
280
|
-
def Zsqrt(**qu_opts):
|
|
281
|
-
r"""Rz(pi / 2).
|
|
282
|
-
|
|
283
|
-
.. math::
|
|
284
|
-
|
|
285
|
-
Z^{\frac{1}{2}} =
|
|
286
|
-
\frac{1}{\sqrt{2}}
|
|
287
|
-
\begin{bmatrix}
|
|
288
|
-
1 - i & 0 \\
|
|
289
|
-
0 & 1 + i
|
|
290
|
-
\end{bmatrix}
|
|
291
|
-
"""
|
|
292
|
-
return Rz(math.pi / 2, **qu_opts)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
@functools.lru_cache(4)
|
|
296
|
-
def Wsqrt(**qu_opts):
|
|
297
|
-
r"""R[X + Y](pi / 2).
|
|
298
|
-
|
|
299
|
-
.. math::
|
|
300
|
-
|
|
301
|
-
W^{\frac{1}{2}} =
|
|
302
|
-
\frac{1}{\sqrt{2}}
|
|
303
|
-
\begin{bmatrix}
|
|
304
|
-
1 & -\sqrt{i} \\
|
|
305
|
-
\sqrt{-i} & 1
|
|
306
|
-
\end{bmatrix}
|
|
307
|
-
"""
|
|
308
|
-
return U_gate(math.pi / 2, -math.pi / 4, math.pi / 4, **qu_opts)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
@functools.lru_cache(maxsize=8)
|
|
312
|
-
def swap(dim=2, dtype=complex, **kwargs):
|
|
313
|
-
"""The SWAP operator acting on subsystems of dimension `dim`.
|
|
314
|
-
"""
|
|
315
|
-
S = np.identity(dim**2, dtype=dtype)
|
|
316
|
-
S = (S.reshape([dim, dim, dim, dim])
|
|
317
|
-
.transpose([0, 3, 1, 2])
|
|
318
|
-
.reshape([dim**2, dim**2]))
|
|
319
|
-
S = qu(S, dtype=dtype, **kwargs)
|
|
320
|
-
make_immutable(S)
|
|
321
|
-
return S
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
@functools.lru_cache(maxsize=128)
|
|
325
|
-
def fsim(theta, phi, dtype=complex, **kwargs):
|
|
326
|
-
r"""The 'fermionic simulation' gate:
|
|
327
|
-
|
|
328
|
-
.. math::
|
|
329
|
-
|
|
330
|
-
\mathrm{fsim}(\theta, \phi) =
|
|
331
|
-
\begin{bmatrix}
|
|
332
|
-
1 & 0 & 0 & 0\\
|
|
333
|
-
0 & \cos(\theta) & -i sin(\theta) & 0\\
|
|
334
|
-
0 & -i sin(\theta) & \cos(\theta) & 0\\
|
|
335
|
-
0 & 0 & 0 & \exp(-i \phi)
|
|
336
|
-
\end{bmatrix}
|
|
337
|
-
|
|
338
|
-
Note that ``theta`` and ``phi`` should be specified in radians and the sign
|
|
339
|
-
convention with this gate varies. Here for example,
|
|
340
|
-
``fsim(- pi / 2, 0) == iswap()``.
|
|
341
|
-
"""
|
|
342
|
-
from cmath import cos, sin, exp
|
|
343
|
-
|
|
344
|
-
a = cos(theta)
|
|
345
|
-
b = -1j * sin(theta)
|
|
346
|
-
c = exp(-1j * phi)
|
|
347
|
-
gate = [[1, 0, 0, 0],
|
|
348
|
-
[0, a, b, 0],
|
|
349
|
-
[0, b, a, 0],
|
|
350
|
-
[0, 0, 0, c]]
|
|
351
|
-
|
|
352
|
-
gate = qu(gate, dtype=dtype, **kwargs)
|
|
353
|
-
make_immutable(gate)
|
|
354
|
-
return gate
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
@functools.lru_cache(maxsize=256)
|
|
358
|
-
def fsimg(theta, zeta, chi, gamma, phi, dtype=complex, **kwargs):
|
|
359
|
-
r"""The 'fermionic simulation' gate, with:
|
|
360
|
-
|
|
361
|
-
* :math:`\theta` is the iSWAP angle
|
|
362
|
-
* :math:`\phi` is the controlled-phase angle
|
|
363
|
-
* :math:`\zeta, \chi, \gamma` are single-qubit phase angles.
|
|
364
|
-
|
|
365
|
-
.. math::
|
|
366
|
-
\mathrm{fsimg}(\theta, \zeta, \chi, \gamma, \phi) =
|
|
367
|
-
\begin{bmatrix}
|
|
368
|
-
1 & 0 & 0 & 0\\
|
|
369
|
-
0 & \exp(-i(\gamma +\zeta )) \cos(\theta) &
|
|
370
|
-
-i \exp(-i(\gamma - \chi )) sin(\theta) & 0\\
|
|
371
|
-
0 & -i \exp(-i(\gamma + \chi )) sin(\theta) &
|
|
372
|
-
\exp(-i(\gamma - \zeta )) \cos(\theta) & 0\\
|
|
373
|
-
0 & 0 & 0 & \exp(-i (\phi +2 \gamma))
|
|
374
|
-
\end{bmatrix}
|
|
375
|
-
|
|
376
|
-
See Equation 18 of https://arxiv.org/abs/2010.07965. Note that ``theta``,
|
|
377
|
-
``phi``, ``zeta``, ``chi``, ``gamma`` should be specified in radians and
|
|
378
|
-
the sign convention with this gate varies. Here for example,
|
|
379
|
-
``fsimg(- pi / 2, 0, 0, 0,0) == iswap()``.
|
|
380
|
-
"""
|
|
381
|
-
from cmath import cos, sin, exp
|
|
382
|
-
|
|
383
|
-
a1 = exp(-1j * (gamma + zeta)) * cos(theta)
|
|
384
|
-
a2 = exp(-1j * (gamma - zeta)) * cos(theta)
|
|
385
|
-
|
|
386
|
-
b1 = -1j * exp(-1j * (gamma - chi)) * sin(theta)
|
|
387
|
-
b2 = -1j * exp(-1j * (gamma + chi)) * sin(theta)
|
|
388
|
-
|
|
389
|
-
c = exp(-1j * (phi + 2 * gamma))
|
|
390
|
-
|
|
391
|
-
gate = [[1, 0, 0, 0],
|
|
392
|
-
[0, a1, b1, 0],
|
|
393
|
-
[0, b2, a2, 0],
|
|
394
|
-
[0, 0, 0, c]]
|
|
395
|
-
gate = qu(gate, dtype=dtype, **kwargs)
|
|
396
|
-
make_immutable(gate)
|
|
397
|
-
return gate
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
@functools.lru_cache(maxsize=4)
|
|
401
|
-
def iswap(dtype=complex, **kwargs):
|
|
402
|
-
iswap = qu([[1., 0., 0., 0.],
|
|
403
|
-
[0., 0., 1j, 0.],
|
|
404
|
-
[0., 1j, 0., 0.],
|
|
405
|
-
[0., 0., 0., 1.]], dtype=dtype, **kwargs)
|
|
406
|
-
make_immutable(iswap)
|
|
407
|
-
return iswap
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
def ncontrolled_gate(ncontrol, gate, dtype=complex, sparse=False):
|
|
411
|
-
"""Build an n-qubit controlled gate. The control qubits are the
|
|
412
|
-
first ``ncontrol`` qubits.
|
|
413
|
-
|
|
414
|
-
Parameters
|
|
415
|
-
----------
|
|
416
|
-
ncontrol : int
|
|
417
|
-
The number of control qubits.
|
|
418
|
-
gate : array_like
|
|
419
|
-
The gate to apply to the controlled qubit(s).
|
|
420
|
-
dtype : str
|
|
421
|
-
The data type of the returned matrix.
|
|
422
|
-
sparse : bool
|
|
423
|
-
Whether to return a sparse matrix.
|
|
424
|
-
|
|
425
|
-
Returns
|
|
426
|
-
-------
|
|
427
|
-
C : qarray
|
|
428
|
-
The n-qubit controlled gate.
|
|
429
|
-
"""
|
|
430
|
-
dG = gate.shape[0]
|
|
431
|
-
d = 2**ncontrol * dG
|
|
432
|
-
# build gate dense and dtype='complex128'
|
|
433
|
-
op = np.identity(d, dtype='complex128')
|
|
434
|
-
op[-dG:, -dG:] = gate
|
|
435
|
-
# then convert to desired dtype and format
|
|
436
|
-
op = qu(op, dtype=dtype, sparse=sparse)
|
|
437
|
-
return op
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
@functools.lru_cache(maxsize=16)
|
|
441
|
-
def controlled(s, dtype=complex, sparse=False):
|
|
442
|
-
"""Construct a controlled pauli gate for two qubits.
|
|
443
|
-
|
|
444
|
-
Parameters
|
|
445
|
-
----------
|
|
446
|
-
s : str
|
|
447
|
-
Which pauli to use, including 'not' aliased to 'x'.
|
|
448
|
-
sparse : bool, optional
|
|
449
|
-
Whether to construct a sparse operator.
|
|
450
|
-
|
|
451
|
-
Returns
|
|
452
|
-
-------
|
|
453
|
-
C : qarray
|
|
454
|
-
The controlled two-qubit gate operator.
|
|
455
|
-
"""
|
|
456
|
-
# alias not and NOT to x
|
|
457
|
-
s = {'NOT': 'x', 'not': 'x'}.get(s, s)
|
|
458
|
-
gate = pauli(s)
|
|
459
|
-
op = ncontrolled_gate(1, gate, dtype=dtype, sparse=sparse)
|
|
460
|
-
make_immutable(op)
|
|
461
|
-
return op
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
@functools.lru_cache(8)
|
|
465
|
-
def CNOT(dtype=complex, sparse=False):
|
|
466
|
-
"""The controlled-not gate.
|
|
467
|
-
"""
|
|
468
|
-
return controlled('not', dtype=dtype, sparse=sparse)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
@functools.lru_cache(8)
|
|
472
|
-
def cX(dtype=complex, sparse=False):
|
|
473
|
-
"""The controlled-X gate.
|
|
474
|
-
"""
|
|
475
|
-
return controlled('not', dtype=dtype, sparse=sparse)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
@functools.lru_cache(8)
|
|
479
|
-
def cY(dtype=complex, sparse=False):
|
|
480
|
-
"""The controlled-Y gate.
|
|
481
|
-
"""
|
|
482
|
-
return controlled('Y', dtype=dtype, sparse=sparse)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
@functools.lru_cache(8)
|
|
486
|
-
def cZ(dtype=complex, sparse=False):
|
|
487
|
-
"""The controlled-Z gate.
|
|
488
|
-
"""
|
|
489
|
-
return controlled('Z', dtype=dtype, sparse=sparse)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
@functools.lru_cache(8)
|
|
493
|
-
def ccX(dtype=complex, sparse=False):
|
|
494
|
-
"""The double controlled X gate, or Toffoli gate.
|
|
495
|
-
"""
|
|
496
|
-
op = ncontrolled_gate(2, pauli('X'), dtype=dtype, sparse=sparse)
|
|
497
|
-
make_immutable(op)
|
|
498
|
-
return op
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
@functools.lru_cache(8)
|
|
502
|
-
def ccY(dtype=complex, sparse=False):
|
|
503
|
-
"""The double controlled Y gate.
|
|
504
|
-
"""
|
|
505
|
-
op = ncontrolled_gate(2, pauli('Y'), dtype=dtype, sparse=sparse)
|
|
506
|
-
make_immutable(op)
|
|
507
|
-
return op
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
@functools.lru_cache(8)
|
|
511
|
-
def ccZ(dtype=complex, sparse=False):
|
|
512
|
-
"""The double controlled Z gate.
|
|
513
|
-
"""
|
|
514
|
-
op = ncontrolled_gate(2, pauli('Z'), dtype=dtype, sparse=sparse)
|
|
515
|
-
make_immutable(op)
|
|
516
|
-
return op
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
@functools.lru_cache(8)
|
|
520
|
-
def controlled_swap(dtype=complex, sparse=False):
|
|
521
|
-
"""The controlled swap or Fredkin gate. The control qubit is the first
|
|
522
|
-
qubit, if in state |1> a swap is performed on the last two qubits.
|
|
523
|
-
"""
|
|
524
|
-
op = ncontrolled_gate(1, swap(), dtype=dtype, sparse=sparse)
|
|
525
|
-
make_immutable(op)
|
|
526
|
-
return op
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
cswap = controlled_swap
|
|
530
|
-
fredkin = controlled_swap
|
|
531
|
-
toffoli = ccX
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
# --------------------------------------------------------------------------- #
|
|
535
|
-
# Hamiltonians #
|
|
536
|
-
# --------------------------------------------------------------------------- #
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
def hamiltonian_builder(fn):
|
|
540
|
-
"""Wrap a function to perform some generic postprocessing and take the
|
|
541
|
-
kwargs ``stype`` and ``sparse``. This assumes the core function always
|
|
542
|
-
builds the hamiltonian in sparse form. The wrapper then:
|
|
543
|
-
|
|
544
|
-
1. Checks if the operator is real and, if so, discards imaginary part if no
|
|
545
|
-
explicity `dtype` was given
|
|
546
|
-
2. Converts the operator to dense or the correct sparse form
|
|
547
|
-
3. Makes the operator immutable so it can be safely cached
|
|
548
|
-
"""
|
|
549
|
-
|
|
550
|
-
@functools.wraps(fn)
|
|
551
|
-
def ham_fn(*args, stype='csr', sparse=False, **kwargs):
|
|
552
|
-
H = fn(*args, **kwargs)
|
|
553
|
-
|
|
554
|
-
if kwargs.get('dtype', None) is None and isreal(H):
|
|
555
|
-
H = H.real
|
|
556
|
-
|
|
557
|
-
if not sparse:
|
|
558
|
-
H = qarray(H.toarray())
|
|
559
|
-
elif H.format != stype:
|
|
560
|
-
H = H.asformat(stype)
|
|
561
|
-
|
|
562
|
-
make_immutable(H)
|
|
563
|
-
|
|
564
|
-
return H
|
|
565
|
-
|
|
566
|
-
return ham_fn
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
@functools.lru_cache(maxsize=8)
|
|
570
|
-
@hamiltonian_builder
|
|
571
|
-
def ham_heis(n, j=1.0, b=0.0, cyclic=False,
|
|
572
|
-
parallel=False, nthreads=None, ownership=None):
|
|
573
|
-
"""Constructs the nearest neighbour 1d heisenberg spin-1/2 hamiltonian.
|
|
574
|
-
|
|
575
|
-
Parameters
|
|
576
|
-
----------
|
|
577
|
-
n : int
|
|
578
|
-
Number of spins.
|
|
579
|
-
j : float or tuple(float, float, float), optional
|
|
580
|
-
Coupling constant(s), with convention that positive =
|
|
581
|
-
antiferromagnetic. Can supply scalar for isotropic coupling or
|
|
582
|
-
vector ``(jx, jy, jz)``.
|
|
583
|
-
b : float or tuple(float, float, float), optional
|
|
584
|
-
Magnetic field, defaults to z-direction only if tuple not given.
|
|
585
|
-
cyclic : bool, optional
|
|
586
|
-
Whether to couple the first and last spins.
|
|
587
|
-
sparse : bool, optional
|
|
588
|
-
Whether to return the hamiltonian in sparse form.
|
|
589
|
-
stype : str, optional
|
|
590
|
-
What format of sparse operator to return if ``sparse``.
|
|
591
|
-
parallel : bool, optional
|
|
592
|
-
Whether to build the operator in parallel. By default will do this
|
|
593
|
-
for n > 16.
|
|
594
|
-
nthreads : int optional
|
|
595
|
-
How mny threads to use in parallel to build the operator.
|
|
596
|
-
ownership : (int, int), optional
|
|
597
|
-
If given, which range of rows to generate.
|
|
598
|
-
kwargs
|
|
599
|
-
Supplied to :func:`~quimb.core.quimbify`.
|
|
600
|
-
|
|
601
|
-
Returns
|
|
602
|
-
-------
|
|
603
|
-
H : immutable operator
|
|
604
|
-
The Hamiltonian.
|
|
605
|
-
"""
|
|
606
|
-
dims = (2,) * n
|
|
607
|
-
try:
|
|
608
|
-
jx, jy, jz = j
|
|
609
|
-
except TypeError:
|
|
610
|
-
jx = jy = jz = j
|
|
611
|
-
|
|
612
|
-
try:
|
|
613
|
-
bx, by, bz = b
|
|
614
|
-
except TypeError:
|
|
615
|
-
bz = b
|
|
616
|
-
bx = by = 0.0
|
|
617
|
-
|
|
618
|
-
parallel = (n > 16) if parallel is None else parallel
|
|
619
|
-
|
|
620
|
-
op_kws = {'sparse': True, 'stype': 'coo'}
|
|
621
|
-
ikron_kws = {'sparse': True, 'stype': 'coo',
|
|
622
|
-
'coo_build': True, 'ownership': ownership}
|
|
623
|
-
|
|
624
|
-
# The basic operator (interaction and single b-field) that can be repeated.
|
|
625
|
-
two_site_term = sum(
|
|
626
|
-
j * kron(spin_operator(s, **op_kws), spin_operator(s, **op_kws))
|
|
627
|
-
for j, s in zip((jx, jy, jz), 'xyz')
|
|
628
|
-
) - sum(
|
|
629
|
-
b * kron(spin_operator(s, **op_kws), eye(2, **op_kws))
|
|
630
|
-
for b, s in zip((bx, by, bz), 'xyz') if b != 0.0
|
|
631
|
-
)
|
|
632
|
-
|
|
633
|
-
single_site_b = sum(-b * spin_operator(s, **op_kws)
|
|
634
|
-
for b, s in zip((bx, by, bz), 'xyz') if b != 0.0)
|
|
635
|
-
|
|
636
|
-
def gen_term(i):
|
|
637
|
-
# special case: the last b term needs to be added manually
|
|
638
|
-
if i == -1:
|
|
639
|
-
return ikron(single_site_b, dims, n - 1, **ikron_kws)
|
|
640
|
-
|
|
641
|
-
# special case: the interaction between first and last spins if cyclic
|
|
642
|
-
if i == n - 1:
|
|
643
|
-
return sum(
|
|
644
|
-
j * ikron(spin_operator(s, **op_kws),
|
|
645
|
-
dims, [0, n - 1], **ikron_kws)
|
|
646
|
-
for j, s in zip((jx, jy, jz), 'xyz') if j != 0.0)
|
|
647
|
-
|
|
648
|
-
# General term, on-site b-field plus interaction with next site
|
|
649
|
-
return ikron(two_site_term, dims, [i, i + 1], **ikron_kws)
|
|
650
|
-
|
|
651
|
-
terms_needed = range(0 if not any((bx, by, bz)) else -1,
|
|
652
|
-
n if cyclic else n - 1)
|
|
653
|
-
|
|
654
|
-
if parallel:
|
|
655
|
-
pool = get_thread_pool(nthreads)
|
|
656
|
-
ham = par_reduce(operator.add, pool.map(gen_term, terms_needed))
|
|
657
|
-
else:
|
|
658
|
-
ham = sum(map(gen_term, terms_needed))
|
|
659
|
-
|
|
660
|
-
return ham
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
def ham_ising(n, jz=1.0, bx=1.0, **ham_opts):
|
|
664
|
-
"""Generate the quantum transverse field ising model hamiltonian. This is a
|
|
665
|
-
simple alias for :func:`~quimb.gen.operators.ham_heis` with Z-interactions
|
|
666
|
-
and an X-field.
|
|
667
|
-
"""
|
|
668
|
-
return ham_heis(n, j=(0, 0, jz), b=(bx, 0, 0), **ham_opts)
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
def ham_XY(n, jxy, bz, **ham_opts):
|
|
672
|
-
"""Generate the quantum transverse field XY model hamiltonian. This is a
|
|
673
|
-
simple alias for :func:`~quimb.gen.operators.ham_heis` with
|
|
674
|
-
X- and Y-interactions and a Z-field.
|
|
675
|
-
"""
|
|
676
|
-
return ham_heis(n, j=(jxy, jxy, 0), b=(0, 0, bz), **ham_opts)
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
def ham_XXZ(n, delta, jxy=1.0, **ham_opts):
|
|
680
|
-
"""Generate the XXZ-model hamiltonian. This is a
|
|
681
|
-
simple alias for :func:`~quimb.gen.operators.ham_heis` with matched
|
|
682
|
-
X- and Y-interactions and ``delta`` Z coupling.
|
|
683
|
-
"""
|
|
684
|
-
return ham_heis(n, j=(jxy, jxy, delta), b=0, **ham_opts)
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
@functools.lru_cache(maxsize=8)
|
|
688
|
-
@hamiltonian_builder
|
|
689
|
-
def ham_j1j2(n, j1=1.0, j2=0.5, bz=0.0, cyclic=False, ownership=None):
|
|
690
|
-
"""Generate the j1-j2 hamiltonian, i.e. next nearest neighbour
|
|
691
|
-
interactions.
|
|
692
|
-
|
|
693
|
-
Parameters
|
|
694
|
-
----------
|
|
695
|
-
n : int
|
|
696
|
-
Number of spins.
|
|
697
|
-
j1 : float, optional
|
|
698
|
-
Nearest neighbour coupling strength.
|
|
699
|
-
j2 : float, optional
|
|
700
|
-
Next nearest neighbour coupling strength.
|
|
701
|
-
bz : float, optional
|
|
702
|
-
B-field strength in z-direction.
|
|
703
|
-
cyclic : bool, optional
|
|
704
|
-
Cyclic boundary conditions.
|
|
705
|
-
sparse : bool, optional
|
|
706
|
-
Return hamiltonian as sparse-csr operator.
|
|
707
|
-
ownership : (int, int), optional
|
|
708
|
-
If given, which range of rows to generate.
|
|
709
|
-
kwargs
|
|
710
|
-
Supplied to :func:`~quimb.core.quimbify`.
|
|
711
|
-
|
|
712
|
-
Returns
|
|
713
|
-
-------
|
|
714
|
-
H : immutable operator
|
|
715
|
-
The Hamiltonian.
|
|
716
|
-
"""
|
|
717
|
-
dims = (2,) * n
|
|
718
|
-
|
|
719
|
-
op_kws = {'sparse': True, 'stype': 'coo'}
|
|
720
|
-
ikron_kws = {'sparse': True, 'stype': 'coo',
|
|
721
|
-
'coo_build': True, 'ownership': ownership}
|
|
722
|
-
|
|
723
|
-
sxyz = [spin_operator(i, **op_kws) for i in 'xyz']
|
|
724
|
-
|
|
725
|
-
coosj1 = np.array([(i, i + 1) for i in range(n)])
|
|
726
|
-
coosj2 = np.array([(i, i + 2) for i in range(n)])
|
|
727
|
-
if cyclic:
|
|
728
|
-
coosj1, coosj2 = coosj1 % n, coosj2 % n
|
|
729
|
-
else:
|
|
730
|
-
coosj1 = coosj1[np.all(coosj1 < n, axis=1)]
|
|
731
|
-
coosj2 = coosj2[np.all(coosj2 < n, axis=1)]
|
|
732
|
-
|
|
733
|
-
def j1_terms():
|
|
734
|
-
for coo in coosj1:
|
|
735
|
-
if abs(coo[1] - coo[0]) == 1: # can sum then tensor (faster)
|
|
736
|
-
yield ikron(sum(op & op for op in sxyz),
|
|
737
|
-
dims, coo, **ikron_kws)
|
|
738
|
-
else: # tensor then sum (slower)
|
|
739
|
-
yield sum(ikron(op, dims, coo, **ikron_kws) for op in sxyz)
|
|
740
|
-
|
|
741
|
-
def j2_terms():
|
|
742
|
-
for coo in coosj2:
|
|
743
|
-
if abs(coo[1] - coo[0]) == 2: # can add then tensor (faster)
|
|
744
|
-
yield ikron(sum(op & eye(2, **op_kws) & op for op in sxyz),
|
|
745
|
-
dims, coo, **ikron_kws)
|
|
746
|
-
else:
|
|
747
|
-
yield sum(ikron(op, dims, coo, **ikron_kws) for op in sxyz)
|
|
748
|
-
|
|
749
|
-
ham = j1 * sum(j1_terms()) + j2 * sum(j2_terms())
|
|
750
|
-
|
|
751
|
-
if bz != 0:
|
|
752
|
-
gen_bz = (ikron([sxyz[2]], dims, i, **ikron_kws) for i in range(n))
|
|
753
|
-
ham += bz * sum(gen_bz)
|
|
754
|
-
|
|
755
|
-
return ham
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
def _gen_mbl_random_factors(n, dh, dh_dim, dh_dist, seed=None, beta=None):
|
|
759
|
-
# sort out a vector of noise strengths -> e.g. (0, 0, 1) for z-noise only
|
|
760
|
-
if isinstance(dh, (tuple, list)):
|
|
761
|
-
dhds = dh
|
|
762
|
-
else:
|
|
763
|
-
dh_dim = {0: '', 1: 'z', 2: 'xy', 3: 'xyz'}.get(dh_dim, dh_dim)
|
|
764
|
-
dhds = tuple((dh if d in dh_dim else 0) for d in 'xyz')
|
|
765
|
-
|
|
766
|
-
if seed is not None:
|
|
767
|
-
np.random.seed(seed)
|
|
768
|
-
|
|
769
|
-
# sort out the noise distribution
|
|
770
|
-
if dh_dist in {'g', 'gauss', 'gaussian', 'normal'}:
|
|
771
|
-
rs = np.random.randn(3, n)
|
|
772
|
-
|
|
773
|
-
elif dh_dist in {'s', 'flat', 'square', 'uniform', 'box'}:
|
|
774
|
-
rs = 2.0 * np.random.rand(3, n) - 1.0
|
|
775
|
-
|
|
776
|
-
elif dh_dist in {'qp', 'quasiperiodic', 'qr', 'quasirandom'}:
|
|
777
|
-
if dh_dim != 'z':
|
|
778
|
-
raise ValueError("dh_dim should be 1 or 'z' for dh_dist='qp'.")
|
|
779
|
-
|
|
780
|
-
if beta is None:
|
|
781
|
-
beta = (5**0.5 - 1) / 2
|
|
782
|
-
|
|
783
|
-
# the random phase
|
|
784
|
-
delta = 2 * np.pi * np.random.rand()
|
|
785
|
-
|
|
786
|
-
# make sure get 3 by n different strengths
|
|
787
|
-
inds = np.broadcast_to(range(n), (3, n))
|
|
788
|
-
|
|
789
|
-
rs = np.cos(2 * np.pi * beta * inds + delta)
|
|
790
|
-
|
|
791
|
-
return dhds, rs
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
@hamiltonian_builder
|
|
795
|
-
def ham_mbl(n, dh, j=1.0, bz=0.0, cyclic=False,
|
|
796
|
-
seed=None, dh_dist="s", dh_dim=1, beta=None, ownership=None):
|
|
797
|
-
""" Constructs a heisenberg hamiltonian with isotropic coupling and
|
|
798
|
-
random fields acting on each spin - the many-body localized (MBL)
|
|
799
|
-
spin hamiltonian.
|
|
800
|
-
|
|
801
|
-
Parameters
|
|
802
|
-
----------
|
|
803
|
-
n : int
|
|
804
|
-
Number of spins.
|
|
805
|
-
dh : float or (float, float, float)
|
|
806
|
-
Strength of random fields (stdev of gaussian distribution), can be
|
|
807
|
-
scalar (isotropic noise) or 3-vector for (x, y, z) directions.
|
|
808
|
-
j : float or (float, float, float), optional
|
|
809
|
-
Coupling strength, can be scalar (isotropic) or 3-vector.
|
|
810
|
-
bz : float, optional
|
|
811
|
-
Global magnetic field (in z-direction).
|
|
812
|
-
cyclic : bool, optional
|
|
813
|
-
Whether to use periodic boundary conditions.
|
|
814
|
-
seed : int, optional
|
|
815
|
-
Number to seed random number generator with.
|
|
816
|
-
dh_dist : {'g', 's', 'qr'}, optional
|
|
817
|
-
Type of random distribution for the noise:
|
|
818
|
-
|
|
819
|
-
- "s": square, with bounds ``(-dh, dh)``
|
|
820
|
-
- "g": gaussian, with standard deviation ``dh``
|
|
821
|
-
- "qp": quasi periodic, with amplitude ``dh`` and
|
|
822
|
-
'wavenumber' ``beta`` so that the field at site ``i`` is
|
|
823
|
-
``dh * cos(2 * pi * beta * i + delta)`` with ``delta`` a random
|
|
824
|
-
offset between ``(0, 2 * pi)``, possibly seeded by ``seed``.
|
|
825
|
-
|
|
826
|
-
dh_dim : {1, 2, 3} or str, optional
|
|
827
|
-
The number of dimensions the noise acts in, or string
|
|
828
|
-
specifier like ``'yz'``.
|
|
829
|
-
beta : float, optional
|
|
830
|
-
The wave number if ``dh_dist='qr'``, defaults to the golden
|
|
831
|
-
ratio``(5**0.5 - 1) / 2``.
|
|
832
|
-
sparse : bool, optional
|
|
833
|
-
Whether to construct the hamiltonian in sparse form.
|
|
834
|
-
stype : {'csr', 'csc', 'coo'}, optional
|
|
835
|
-
The sparse format.
|
|
836
|
-
ownership : (int, int), optional
|
|
837
|
-
If given, which range of rows to generate.
|
|
838
|
-
kwargs
|
|
839
|
-
Supplied to :func:`~quimb.core.quimbify`.
|
|
840
|
-
|
|
841
|
-
Returns
|
|
842
|
-
-------
|
|
843
|
-
H : operator
|
|
844
|
-
The MBL hamiltonian for spin-1/2.
|
|
845
|
-
|
|
846
|
-
See Also
|
|
847
|
-
--------
|
|
848
|
-
MPO_ham_mbl
|
|
849
|
-
"""
|
|
850
|
-
dhds, rs = _gen_mbl_random_factors(n, dh, dh_dim, dh_dist, seed, beta)
|
|
851
|
-
|
|
852
|
-
# the base hamiltonian ('csr' is most efficient format to add with)
|
|
853
|
-
ham = ham_heis(n=n, j=j, b=bz, cyclic=cyclic,
|
|
854
|
-
sparse=True, stype='csr', ownership=ownership)
|
|
855
|
-
|
|
856
|
-
op_kws = {'sparse': True, 'stype': 'coo'}
|
|
857
|
-
ikron_kws = {'sparse': True, 'stype': 'coo',
|
|
858
|
-
'coo_build': True, 'ownership': ownership}
|
|
859
|
-
|
|
860
|
-
def dh_terms():
|
|
861
|
-
for i in range(n):
|
|
862
|
-
# dhd - the total strength in direction x, y, or z
|
|
863
|
-
# r - the random strength in direction x, y, or z for site i
|
|
864
|
-
hdh = sum(dhd * r * spin_operator(s, **op_kws)
|
|
865
|
-
for dhd, r, s in zip(dhds, rs[:, i], 'xyz'))
|
|
866
|
-
yield ikron(hdh, (2,) * n, i, **ikron_kws)
|
|
867
|
-
|
|
868
|
-
ham = ham + sum(dh_terms())
|
|
869
|
-
|
|
870
|
-
return ham
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
@hamiltonian_builder
|
|
874
|
-
def ham_heis_2D(n, m, j=1.0, bz=0.0, cyclic=False,
|
|
875
|
-
parallel=False, ownership=None):
|
|
876
|
-
r"""Construct the 2D spin-1/2 heisenberg model hamiltonian:
|
|
877
|
-
|
|
878
|
-
.. math::
|
|
879
|
-
|
|
880
|
-
\hat{H} = \sum_{<i, j>}
|
|
881
|
-
J_X S^X_i S^X_j +
|
|
882
|
-
J_Y S^Y_i S^Y_j +
|
|
883
|
-
J_Z S^Z_i S^Z_j
|
|
884
|
-
|
|
885
|
-
where the sum runs over pairs :math:`<i,j>` on a 2D square lattice.
|
|
886
|
-
|
|
887
|
-
Parameters
|
|
888
|
-
----------
|
|
889
|
-
n : int
|
|
890
|
-
The number of rows.
|
|
891
|
-
m : int
|
|
892
|
-
The number of columns.
|
|
893
|
-
j : float or (float, float, float), optional
|
|
894
|
-
The coupling strength(s). Isotropic if scalar else if
|
|
895
|
-
vector ``(Jx, Jy, Jz) = j``.
|
|
896
|
-
bz : float, optional
|
|
897
|
-
The z direction magnetic field.
|
|
898
|
-
cyclic : bool, optional
|
|
899
|
-
Whether to use periodic boundary conditions.
|
|
900
|
-
sparse : bool, optional
|
|
901
|
-
Whether to construct the hamiltonian in sparse form.
|
|
902
|
-
stype : {'csr', 'csc', 'coo'}, optional
|
|
903
|
-
The sparse format.
|
|
904
|
-
parallel : bool, optional
|
|
905
|
-
Construct the hamiltonian in parallel. Faster but might use more
|
|
906
|
-
memory.
|
|
907
|
-
ownership : (int, int), optional
|
|
908
|
-
If given, which range of rows to generate.
|
|
909
|
-
kwargs
|
|
910
|
-
Supplied to :func:`~quimb.core.quimbify`.
|
|
911
|
-
|
|
912
|
-
Returns
|
|
913
|
-
-------
|
|
914
|
-
H : operator
|
|
915
|
-
The hamiltonian.
|
|
916
|
-
"""
|
|
917
|
-
|
|
918
|
-
# parse interaction strengths
|
|
919
|
-
try:
|
|
920
|
-
jx, jy, jz = j
|
|
921
|
-
except (TypeError, ValueError):
|
|
922
|
-
jx = jy = jz = j
|
|
923
|
-
|
|
924
|
-
js = {s: js for s, js in zip("xyz", [jx, jy, jz]) if js != 0.0}
|
|
925
|
-
|
|
926
|
-
dims = [[2] * m] * n # shape (n, m)
|
|
927
|
-
|
|
928
|
-
sites = tuple(itertools.product(range(n), range(m)))
|
|
929
|
-
|
|
930
|
-
# generate neighbouring pair coordinates
|
|
931
|
-
def gen_pairs():
|
|
932
|
-
for i, j in sites:
|
|
933
|
-
above, right = (i + 1) % n, (j + 1) % m
|
|
934
|
-
# ignore wraparound coordinates if not cyclic
|
|
935
|
-
if cyclic or above != 0:
|
|
936
|
-
yield ((i, j), (above, j))
|
|
937
|
-
if cyclic or right != 0:
|
|
938
|
-
yield ((i, j), (i, right))
|
|
939
|
-
|
|
940
|
-
# generate all pairs of coordinates and directions
|
|
941
|
-
pairs_ss = tuple(itertools.product(gen_pairs(), js))
|
|
942
|
-
|
|
943
|
-
# build the hamiltonian in sparse 'coo' format always for efficiency
|
|
944
|
-
op_kws = {'sparse': True, 'stype': 'coo'}
|
|
945
|
-
ikron_kws = {'sparse': True, 'stype': 'coo',
|
|
946
|
-
'coo_build': True, 'ownership': ownership}
|
|
947
|
-
|
|
948
|
-
# generate XX, YY and ZZ interaction from
|
|
949
|
-
# e.g. arg ([(3, 4), (3, 5)], 'z')
|
|
950
|
-
def interactions(pair_s):
|
|
951
|
-
pair, s = pair_s
|
|
952
|
-
Sxyz = spin_operator(s, **op_kws)
|
|
953
|
-
return ikron([js[s] * Sxyz, Sxyz], dims, inds=pair, **ikron_kws)
|
|
954
|
-
|
|
955
|
-
# generate Z field
|
|
956
|
-
def fields(site):
|
|
957
|
-
Sz = spin_operator('z', **op_kws)
|
|
958
|
-
return ikron(bz * Sz, dims, inds=[site], **ikron_kws)
|
|
959
|
-
|
|
960
|
-
if not parallel:
|
|
961
|
-
# combine all terms
|
|
962
|
-
all_terms = itertools.chain(
|
|
963
|
-
map(interactions, pairs_ss),
|
|
964
|
-
map(fields, sites) if bz != 0.0 else ())
|
|
965
|
-
H = functools.reduce(operator.add, all_terms)
|
|
966
|
-
else:
|
|
967
|
-
pool = get_thread_pool()
|
|
968
|
-
all_terms = itertools.chain(
|
|
969
|
-
pool.map(interactions, pairs_ss),
|
|
970
|
-
pool.map(fields, sites) if bz != 0.0 else ())
|
|
971
|
-
H = par_reduce(operator.add, all_terms)
|
|
972
|
-
|
|
973
|
-
return H
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
def uniq_perms(xs):
|
|
977
|
-
"""Generate all the unique permutations of sequence ``xs``.
|
|
978
|
-
|
|
979
|
-
Examples
|
|
980
|
-
--------
|
|
981
|
-
>>> list(uniq_perms('0011'))
|
|
982
|
-
[('0', '0', '1', '1'),
|
|
983
|
-
('0', '1', '0', '1'),
|
|
984
|
-
('0', '1', '1', '0'),
|
|
985
|
-
('1', '0', '0', '1'),
|
|
986
|
-
('1', '0', '1', '0'),
|
|
987
|
-
('1', '1', '0', '0')]
|
|
988
|
-
"""
|
|
989
|
-
if len(xs) == 1:
|
|
990
|
-
yield (xs[0],)
|
|
991
|
-
else:
|
|
992
|
-
uniq_xs = unique(xs)
|
|
993
|
-
for first_x in uniq_xs:
|
|
994
|
-
rem_xs = list(xs)
|
|
995
|
-
rem_xs.remove(first_x)
|
|
996
|
-
for sub_perm in uniq_perms(rem_xs):
|
|
997
|
-
yield (first_x,) + sub_perm
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
@functools.lru_cache(maxsize=8)
|
|
1001
|
-
def zspin_projector(n, sz=0, stype="csr", dtype=float):
|
|
1002
|
-
"""Construct the projector onto spin-z subpspaces.
|
|
1003
|
-
|
|
1004
|
-
Parameters
|
|
1005
|
-
----------
|
|
1006
|
-
n : int
|
|
1007
|
-
Total size of spin system.
|
|
1008
|
-
sz : float or sequence of floats
|
|
1009
|
-
Spin-z value(s) subspace(s) to find projector for.
|
|
1010
|
-
stype : str
|
|
1011
|
-
Sparse format of the output operator.
|
|
1012
|
-
dtype : {float, complex}, optional
|
|
1013
|
-
The data type of the operator to generate.
|
|
1014
|
-
|
|
1015
|
-
Returns
|
|
1016
|
-
-------
|
|
1017
|
-
prj : immutable sparse operator, shape (2**n, D)
|
|
1018
|
-
The (non-square) projector onto the specified subspace(s). The subspace
|
|
1019
|
-
size ``D`` is given by ``n choose (n / 2 + s)`` for each ``s``
|
|
1020
|
-
specified in ``sz``.
|
|
1021
|
-
|
|
1022
|
-
Examples
|
|
1023
|
-
--------
|
|
1024
|
-
>>> zspin_projector(n=2, sz=0).toarray()
|
|
1025
|
-
array([[0., 0.],
|
|
1026
|
-
[1., 0.],
|
|
1027
|
-
[0., 1.],
|
|
1028
|
-
[0., 0.]]
|
|
1029
|
-
|
|
1030
|
-
Project a 9-spin Heisenberg-Hamiltonian into its spin-1/2 subspace:
|
|
1031
|
-
|
|
1032
|
-
>>> H = ham_heis(9, sparse=True)
|
|
1033
|
-
>>> H.shape
|
|
1034
|
-
(512, 512)
|
|
1035
|
-
|
|
1036
|
-
>>> P = zspin_projector(n=9, sz=1 / 2)
|
|
1037
|
-
>>> H0 = P.T @ H @ P
|
|
1038
|
-
>>> H0.shape
|
|
1039
|
-
(126, 126)
|
|
1040
|
-
"""
|
|
1041
|
-
if not isiterable(sz):
|
|
1042
|
-
sz = (sz,)
|
|
1043
|
-
|
|
1044
|
-
p = 0
|
|
1045
|
-
all_perms = []
|
|
1046
|
-
|
|
1047
|
-
for s in sz:
|
|
1048
|
-
# Number of 'up' spins
|
|
1049
|
-
k = n / 2 + s
|
|
1050
|
-
if not k.is_integer():
|
|
1051
|
-
raise ValueError(f"{s} is not a valid spin half subspace for {n} "
|
|
1052
|
-
"spins.")
|
|
1053
|
-
k = int(round(k))
|
|
1054
|
-
# Size of subspace
|
|
1055
|
-
p += comb(n, k, exact=True)
|
|
1056
|
-
# Find all computational basis states with correct number of 0s and 1s
|
|
1057
|
-
base_perm = '0' * (n - k) + '1' * k
|
|
1058
|
-
all_perms += [uniq_perms(base_perm)]
|
|
1059
|
-
|
|
1060
|
-
# Coordinates
|
|
1061
|
-
cis = tuple(range(p)) # arbitrary basis
|
|
1062
|
-
cjs = tuple(int("".join(perm), 2) for perm in concat(all_perms))
|
|
1063
|
-
|
|
1064
|
-
# Construct matrix which projects only on to these basis states
|
|
1065
|
-
prj = sp.coo_matrix((np.ones(p, dtype=dtype), (cjs, cis)),
|
|
1066
|
-
shape=(2**n, p), dtype=dtype)
|
|
1067
|
-
prj = qu(prj, stype=stype, dtype=dtype)
|
|
1068
|
-
make_immutable(prj)
|
|
1069
|
-
return prj
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
@functools.lru_cache(8)
|
|
1073
|
-
def create(n=2, **qu_opts):
|
|
1074
|
-
"""The creation operator acting on an n-level system.
|
|
1075
|
-
"""
|
|
1076
|
-
data = np.zeros((n, n))
|
|
1077
|
-
|
|
1078
|
-
for i in range(n):
|
|
1079
|
-
data[i, i - 1] = i ** 0.5
|
|
1080
|
-
|
|
1081
|
-
ap = qu(data, **qu_opts)
|
|
1082
|
-
make_immutable(ap)
|
|
1083
|
-
return ap
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
@functools.lru_cache(8)
|
|
1087
|
-
def destroy(n=2, **qu_opts):
|
|
1088
|
-
"""The annihilation operator acting on an n-level system.
|
|
1089
|
-
"""
|
|
1090
|
-
am = create(n, **qu_opts).T.copy()
|
|
1091
|
-
make_immutable(am)
|
|
1092
|
-
return am
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
@functools.lru_cache(8)
|
|
1096
|
-
def num(n, **qu_opts):
|
|
1097
|
-
"""The number operator acting on an n-level system.
|
|
1098
|
-
"""
|
|
1099
|
-
ap, am = create(n, **qu_opts), destroy(n, **qu_opts)
|
|
1100
|
-
an = qu(ap @ am, **qu_opts)
|
|
1101
|
-
make_immutable(an)
|
|
1102
|
-
return an
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
@functools.lru_cache(maxsize=8)
|
|
1106
|
-
@hamiltonian_builder
|
|
1107
|
-
def ham_hubbard_hardcore(n, t=0.5, V=1., mu=1., cyclic=False,
|
|
1108
|
-
parallel=False, ownership=None):
|
|
1109
|
-
"""Generate the spinless fermion hopping hamiltonian.
|
|
1110
|
-
|
|
1111
|
-
Parameters
|
|
1112
|
-
----------
|
|
1113
|
-
n : int
|
|
1114
|
-
The number of sites.
|
|
1115
|
-
t : float, optional
|
|
1116
|
-
The hopping energy.
|
|
1117
|
-
V : float, optional
|
|
1118
|
-
The interaction energy.
|
|
1119
|
-
mu : float, optional
|
|
1120
|
-
The chemical potential - defaults to half-filling.
|
|
1121
|
-
cyclic : bool, optional
|
|
1122
|
-
Whether to use periodic boundary conditions.
|
|
1123
|
-
parallel : bool, optional
|
|
1124
|
-
Construct the hamiltonian in parallel. Faster but might use more
|
|
1125
|
-
memory.
|
|
1126
|
-
ownership : (int, int), optional
|
|
1127
|
-
If given, which range of rows to generate.
|
|
1128
|
-
kwargs
|
|
1129
|
-
Supplied to :func:`~quimb.core.quimbify`.
|
|
1130
|
-
|
|
1131
|
-
Returns
|
|
1132
|
-
-------
|
|
1133
|
-
H : operator
|
|
1134
|
-
The hamiltonian.
|
|
1135
|
-
"""
|
|
1136
|
-
|
|
1137
|
-
op_kws = {'sparse': True, 'stype': 'coo'}
|
|
1138
|
-
ikron_kws = {'sparse': True, 'stype': 'csr',
|
|
1139
|
-
'coo_build': True, 'ownership': ownership}
|
|
1140
|
-
|
|
1141
|
-
cdag, c, cnum = (f(2, **op_kws) for f in (create, destroy, num))
|
|
1142
|
-
neighbor_term = t * ((cdag & c) + (c & cdag)) + V * (cnum & cnum)
|
|
1143
|
-
|
|
1144
|
-
dims = [2] * n
|
|
1145
|
-
|
|
1146
|
-
def terms():
|
|
1147
|
-
# interacting terms
|
|
1148
|
-
for i, j in [(i, i + 1) for i in range(n - 1)]:
|
|
1149
|
-
yield ikron(neighbor_term, dims, (i, j), **ikron_kws)
|
|
1150
|
-
|
|
1151
|
-
if cyclic:
|
|
1152
|
-
# can't sum terms and kron later since identity in middle
|
|
1153
|
-
yield ikron([t * cdag, c], dims, (0, n - 1), **ikron_kws)
|
|
1154
|
-
yield ikron([t * c, cdag], dims, (0, n - 1), **ikron_kws)
|
|
1155
|
-
yield ikron([V * cnum, cnum], dims, (0, n - 1), **ikron_kws)
|
|
1156
|
-
|
|
1157
|
-
# single site terms
|
|
1158
|
-
for i in range(n):
|
|
1159
|
-
yield ikron(-mu * cnum, dims, i, **ikron_kws)
|
|
1160
|
-
|
|
1161
|
-
if parallel is None:
|
|
1162
|
-
parallel = (n >= 14)
|
|
1163
|
-
|
|
1164
|
-
if parallel:
|
|
1165
|
-
return par_reduce(operator.add, terms())
|
|
1166
|
-
|
|
1167
|
-
return functools.reduce(operator.add, terms())
|