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
trajectree/quimb/quimb/evo.py
DELETED
|
@@ -1,712 +0,0 @@
|
|
|
1
|
-
"""Easy and efficient time evolutions.
|
|
2
|
-
|
|
3
|
-
Contains an evolution class, Evolution to easily and efficiently manage time
|
|
4
|
-
evolution of quantum states according to the Schrodinger equation,
|
|
5
|
-
and related functions.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import functools
|
|
9
|
-
|
|
10
|
-
import numpy as np
|
|
11
|
-
from scipy.integrate import complex_ode
|
|
12
|
-
from scipy.sparse.linalg import LinearOperator
|
|
13
|
-
|
|
14
|
-
from .core import (
|
|
15
|
-
dag,
|
|
16
|
-
dot,
|
|
17
|
-
explt,
|
|
18
|
-
eye,
|
|
19
|
-
isop,
|
|
20
|
-
issparse,
|
|
21
|
-
ldmul,
|
|
22
|
-
make_immutable,
|
|
23
|
-
qarray,
|
|
24
|
-
qu,
|
|
25
|
-
rdmul,
|
|
26
|
-
)
|
|
27
|
-
from .linalg.base_linalg import eigh, norm, expm_multiply, Lazy
|
|
28
|
-
from .linalg.approx_spectral import norm_fro_approx
|
|
29
|
-
from .utils import continuous_progbar, progbar, ensure_dict
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
CALLABLE_TIME_INDEP_CLASSES = (LinearOperator, Lazy)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# --------------------------------------------------------------------------- #
|
|
36
|
-
# Quantum evolution equations #
|
|
37
|
-
# --------------------------------------------------------------------------- #
|
|
38
|
-
#
|
|
39
|
-
# This are mostly just to be used internally with the integrators
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def schrodinger_eq_ket(ham):
|
|
43
|
-
"""Wavefunction schrodinger equation.
|
|
44
|
-
|
|
45
|
-
Parameters
|
|
46
|
-
----------
|
|
47
|
-
ham : operator
|
|
48
|
-
Time-independant Hamiltonian governing evolution.
|
|
49
|
-
|
|
50
|
-
Returns
|
|
51
|
-
-------
|
|
52
|
-
psi_dot(t, y) : callable
|
|
53
|
-
Function to calculate psi_dot(t) at psi(t).
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
def psi_dot(_, y):
|
|
57
|
-
return -1.0j * dot(ham, y)
|
|
58
|
-
|
|
59
|
-
return psi_dot
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def schrodinger_eq_ket_timedep(ham):
|
|
63
|
-
"""Wavefunction time dependent schrodinger equation.
|
|
64
|
-
|
|
65
|
-
Parameters
|
|
66
|
-
----------
|
|
67
|
-
ham : callable
|
|
68
|
-
Time-dependant Hamiltonian governing evolution, such that ``ham(t)``
|
|
69
|
-
returns an operator representation of the Hamiltonian at time ``t``.
|
|
70
|
-
|
|
71
|
-
Returns
|
|
72
|
-
-------
|
|
73
|
-
psi_dot(t, y) : callable
|
|
74
|
-
Function to calculate psi_dot(t) at psi(t).
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
def psi_dot(t, y):
|
|
78
|
-
return -1.0j * dot(ham(t), y)
|
|
79
|
-
|
|
80
|
-
return psi_dot
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def schrodinger_eq_dop(ham):
|
|
84
|
-
"""Density operator schrodinger equation, but with flattened input/output.
|
|
85
|
-
|
|
86
|
-
Note that this assumes both `ham` and `rho` are hermitian in order to speed
|
|
87
|
-
up the commutator, non-hermitian hamiltonians as used to model loss should
|
|
88
|
-
be treated explicilty or with `schrodinger_eq_dop_vectorized`.
|
|
89
|
-
|
|
90
|
-
Parameters
|
|
91
|
-
----------
|
|
92
|
-
ham : operator
|
|
93
|
-
Time-independant Hamiltonian governing evolution.
|
|
94
|
-
|
|
95
|
-
Returns
|
|
96
|
-
-------
|
|
97
|
-
rho_dot(t, y) : callable
|
|
98
|
-
Function to calculate rho_dot(t) at rho(t), input and
|
|
99
|
-
output both in ravelled (1D form).
|
|
100
|
-
"""
|
|
101
|
-
d = ham.shape[0]
|
|
102
|
-
|
|
103
|
-
def rho_dot(_, y):
|
|
104
|
-
hrho = dot(ham, y.reshape(d, d))
|
|
105
|
-
return -1.0j * (hrho - hrho.T.conj()).reshape(-1)
|
|
106
|
-
|
|
107
|
-
return rho_dot
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def schrodinger_eq_dop_timedep(ham):
|
|
111
|
-
"""Time dependent density operator schrodinger equation, but with flattened
|
|
112
|
-
input/output.
|
|
113
|
-
|
|
114
|
-
Note that this assumes both `ham(t)` and `rho` are hermitian in order to
|
|
115
|
-
speed up the commutator, non-hermitian hamiltonians as used to model loss
|
|
116
|
-
should be treated explicilty or with `schrodinger_eq_dop_vectorized`.
|
|
117
|
-
|
|
118
|
-
Parameters
|
|
119
|
-
----------
|
|
120
|
-
ham : callable
|
|
121
|
-
Time-dependant Hamiltonian governing evolution, such that ``ham(t)``
|
|
122
|
-
returns an operator representation of the Hamiltonian at time ``t``.
|
|
123
|
-
|
|
124
|
-
Returns
|
|
125
|
-
-------
|
|
126
|
-
rho_dot(t, y) : callable
|
|
127
|
-
Function to calculate rho_dot(t) at rho(t), input and
|
|
128
|
-
output both in ravelled (1D form).
|
|
129
|
-
"""
|
|
130
|
-
d = ham(0).shape[0]
|
|
131
|
-
|
|
132
|
-
def rho_dot(t, y):
|
|
133
|
-
hrho = dot(ham(t), y.reshape(d, d))
|
|
134
|
-
return -1.0j * (hrho - hrho.T.conj()).reshape(-1)
|
|
135
|
-
|
|
136
|
-
return rho_dot
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def schrodinger_eq_dop_vectorized(ham):
|
|
140
|
-
"""Density operator schrodinger equation, but with flattened input/output
|
|
141
|
-
and vectorised superoperator mode (no reshaping required).
|
|
142
|
-
|
|
143
|
-
Note that this is probably only more efficient for sparse Hamiltonians.
|
|
144
|
-
|
|
145
|
-
Parameters
|
|
146
|
-
----------
|
|
147
|
-
ham: time-independant hamiltonian governing evolution
|
|
148
|
-
|
|
149
|
-
Returns
|
|
150
|
-
-------
|
|
151
|
-
rho_dot(t, y) : callable
|
|
152
|
-
Function to calculate rho_dot(t) at rho(t), input and
|
|
153
|
-
output both in ravelled (1D form).
|
|
154
|
-
"""
|
|
155
|
-
d = ham.shape[0]
|
|
156
|
-
sparse = issparse(ham)
|
|
157
|
-
idt = eye(d, sparse=sparse)
|
|
158
|
-
evo_superop = -1.0j * ((ham & idt) - (idt & ham.T))
|
|
159
|
-
|
|
160
|
-
def rho_dot(_, y):
|
|
161
|
-
return dot(evo_superop, y)
|
|
162
|
-
|
|
163
|
-
return rho_dot
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def lindblad_eq(ham, ls, gamma):
|
|
167
|
-
"""Lindblad equation, but with flattened input/output.
|
|
168
|
-
|
|
169
|
-
Parameters
|
|
170
|
-
----------
|
|
171
|
-
ham : operator
|
|
172
|
-
Time-independant hamiltonian governing evolution.
|
|
173
|
-
ls : sequence of matrices
|
|
174
|
-
Lindblad operators.
|
|
175
|
-
gamma : float
|
|
176
|
-
Dampening strength.
|
|
177
|
-
|
|
178
|
-
Returns
|
|
179
|
-
-------
|
|
180
|
-
rho_dot(t, y) : callable
|
|
181
|
-
Function to calculate rho_dot(t) at rho(t), input and
|
|
182
|
-
output both in ravelled (1D form).
|
|
183
|
-
"""
|
|
184
|
-
d = ham.shape[0]
|
|
185
|
-
lls = tuple(dot(dag(l), l) for l in ls)
|
|
186
|
-
|
|
187
|
-
def gen_l_terms(rho):
|
|
188
|
-
for l, ll in zip(ls, lls):
|
|
189
|
-
yield (
|
|
190
|
-
dot(l, dot(rho, dag(l))) - 0.5 * (dot(rho, ll) + dot(ll, rho))
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
def rho_dot(_, y):
|
|
194
|
-
rho = y.reshape(d, d)
|
|
195
|
-
rho_d = dot(ham, rho)
|
|
196
|
-
rho_d -= rho_d.T.conj()
|
|
197
|
-
rho_d *= -1.0j
|
|
198
|
-
rho_d += gamma * sum(gen_l_terms(rho))
|
|
199
|
-
return np.asarray(rho_d).reshape(-1)
|
|
200
|
-
|
|
201
|
-
return rho_dot
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def lindblad_eq_vectorized(ham, ls, gamma, sparse=False):
|
|
205
|
-
"""Lindblad equation, but with flattened input/output and vectorised
|
|
206
|
-
superoperation mode (no reshaping required).
|
|
207
|
-
|
|
208
|
-
Parameters
|
|
209
|
-
----------
|
|
210
|
-
ham : operator
|
|
211
|
-
Time-independant hamiltonian governing evolution.
|
|
212
|
-
ls : sequence of matrices
|
|
213
|
-
Lindblad operators.
|
|
214
|
-
gamma : float
|
|
215
|
-
Dampening strength.
|
|
216
|
-
|
|
217
|
-
Returns
|
|
218
|
-
-------
|
|
219
|
-
rho_dot(t, y) : callable
|
|
220
|
-
Function to calculate rho_dot(t) at rho(t), input and
|
|
221
|
-
output both in ravelled (1D form).
|
|
222
|
-
"""
|
|
223
|
-
d = ham.shape[0]
|
|
224
|
-
ham_sparse = issparse(ham) or sparse
|
|
225
|
-
idt = eye(d, sparse=ham_sparse)
|
|
226
|
-
evo_superop = -1.0j * ((ham & idt) - (idt & ham.T))
|
|
227
|
-
|
|
228
|
-
def gen_lb_terms():
|
|
229
|
-
for l in ls:
|
|
230
|
-
lb_sparse = issparse(l) or sparse
|
|
231
|
-
idt = eye(d, sparse=lb_sparse)
|
|
232
|
-
yield (
|
|
233
|
-
(l & l.conj())
|
|
234
|
-
- 0.5 * ((idt & dot(dag(l), l).T) + (dot(dag(l), l) & idt))
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
evo_superop += gamma * sum(gen_lb_terms())
|
|
238
|
-
|
|
239
|
-
def rho_dot(_, y):
|
|
240
|
-
return dot(evo_superop, y)
|
|
241
|
-
|
|
242
|
-
return rho_dot
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def _calc_evo_eq(isdop, issparse, isopen=False, timedep=False):
|
|
246
|
-
"""Choose an appropirate dynamical equation to evolve with."""
|
|
247
|
-
eq_chooser = {
|
|
248
|
-
(0, 0, 0, 0): schrodinger_eq_ket,
|
|
249
|
-
(0, 1, 0, 0): schrodinger_eq_ket,
|
|
250
|
-
(1, 0, 0, 0): schrodinger_eq_dop,
|
|
251
|
-
(1, 1, 0, 0): schrodinger_eq_dop_vectorized,
|
|
252
|
-
(1, 0, 1, 0): lindblad_eq,
|
|
253
|
-
(1, 1, 1, 0): lindblad_eq_vectorized,
|
|
254
|
-
# time-dependent
|
|
255
|
-
(0, 0, 0, 1): schrodinger_eq_ket_timedep,
|
|
256
|
-
(0, 1, 0, 1): schrodinger_eq_ket_timedep,
|
|
257
|
-
(1, 0, 0, 1): schrodinger_eq_dop_timedep,
|
|
258
|
-
(1, 1, 0, 1): schrodinger_eq_dop_timedep,
|
|
259
|
-
}
|
|
260
|
-
return eq_chooser[(isdop, issparse, isopen, timedep)]
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
class Try2Then3Args:
|
|
264
|
-
def __init__(self, fn):
|
|
265
|
-
self.fn = fn
|
|
266
|
-
self.num_args = None
|
|
267
|
-
|
|
268
|
-
def first_call(self, t, p, H):
|
|
269
|
-
try:
|
|
270
|
-
res = self.fn(t, p)
|
|
271
|
-
self.num_args = 2
|
|
272
|
-
except TypeError as e:
|
|
273
|
-
if "positional" in e.args[0]:
|
|
274
|
-
res = self.fn(t, p, H)
|
|
275
|
-
self.num_args = 3
|
|
276
|
-
else:
|
|
277
|
-
raise
|
|
278
|
-
return res
|
|
279
|
-
|
|
280
|
-
def __call__(self, t, p, H):
|
|
281
|
-
if self.num_args is None:
|
|
282
|
-
return self.first_call(t, p, H)
|
|
283
|
-
elif self.num_args == 2:
|
|
284
|
-
return self.fn(t, p)
|
|
285
|
-
elif self.num_args == 3:
|
|
286
|
-
return self.fn(t, p, H)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
# --------------------------------------------------------------------------- #
|
|
290
|
-
# Quantum Evolution Class #
|
|
291
|
-
# --------------------------------------------------------------------------- #
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
class Evolution(object):
|
|
295
|
-
"""A class for evolving quantum systems according to Schrodinger equation.
|
|
296
|
-
|
|
297
|
-
The evolution can be performed in a number of ways:
|
|
298
|
-
|
|
299
|
-
- diagonalise the Hamiltonian (or use already diagonalised system).
|
|
300
|
-
- integrate the complex ODE, that is, the Schrodinger equation, using
|
|
301
|
-
scipy. Here either a mid- or high-order Dormand-Prince adaptive
|
|
302
|
-
time stepping scheme is used (see
|
|
303
|
-
:class:`scipy.integrate.complex_ode`).
|
|
304
|
-
|
|
305
|
-
Parameters
|
|
306
|
-
----------
|
|
307
|
-
p0 : quantum state
|
|
308
|
-
Inital state, either vector or operator. If vector, converted to ket.
|
|
309
|
-
ham : operator, tuple (1d array, operator), or callable
|
|
310
|
-
Governing Hamiltonian, if tuple then assumed to contain
|
|
311
|
-
``(eigvals, eigvecs)`` of presolved system. If callable (but not a
|
|
312
|
-
SciPy ``LinearOperator``), assume a time-dependent hamiltonian such
|
|
313
|
-
that ``ham(t)`` is the Hamiltonian at time ``t``. In this case, the
|
|
314
|
-
latest call to ``ham`` will be cached (and made immutable) in case it
|
|
315
|
-
is needed by callbacks passed to ``compute``.
|
|
316
|
-
t0 : float, optional
|
|
317
|
-
Initial time (i.e. time of state ``p0``), defaults to zero.
|
|
318
|
-
compute : callable, or dict of callable, optional
|
|
319
|
-
Function(s) to compute on the state at each time step. Function(s)
|
|
320
|
-
should take args (t, pt) or (t, pt, ham) if the Hamiltonian is
|
|
321
|
-
required. If ham is required, it will be passed in to the function
|
|
322
|
-
exactly as given to this ``Evolution`` instance, except if ``method``
|
|
323
|
-
is ``'solve'``, in which case it will be passed in as the solved system
|
|
324
|
-
``(eigvals, eigvecs)``. If supplied with:
|
|
325
|
-
|
|
326
|
-
- single callable : ``Evolution.results`` will contain the results
|
|
327
|
-
as a list,
|
|
328
|
-
- dict of callables : ``Evolution.results`` will contain the
|
|
329
|
-
results as a dict of lists with corresponding keys to those
|
|
330
|
-
given in ``compute``.
|
|
331
|
-
|
|
332
|
-
int_stop : callable, optional
|
|
333
|
-
A condition to terminate the integration early if ``method`` is
|
|
334
|
-
``'integrate'``. This callable is called at every successful
|
|
335
|
-
integration step and should take args (t, pt) or (t, pt, ham) similar
|
|
336
|
-
to the function(s) in the ``compute`` argument. It should return
|
|
337
|
-
``-1`` to stop the integration, otherwise it should return ``None``
|
|
338
|
-
or ``0``.
|
|
339
|
-
|
|
340
|
-
method : {'integrate', 'solve', 'expm'}
|
|
341
|
-
How to evolve the system:
|
|
342
|
-
|
|
343
|
-
- ``'integrate'``: use definite integration. Get system at each
|
|
344
|
-
time step, only need action of Hamiltonian on state. Generally
|
|
345
|
-
efficient.
|
|
346
|
-
- ``'solve'``: diagonalise dense hamiltonian. Best for small
|
|
347
|
-
systems and allows arbitrary time steps without loss of
|
|
348
|
-
precision.
|
|
349
|
-
- ``'expm'``: compute the evolved state using the action of the
|
|
350
|
-
operator exponential in a 'single shot' style. Only needs action
|
|
351
|
-
of Hamiltonian, for very large systems can use distributed MPI.
|
|
352
|
-
|
|
353
|
-
int_small_step : bool, optional
|
|
354
|
-
If ``method='integrate'``, whether to use a low or high order
|
|
355
|
-
integrator to give naturally small or large steps.
|
|
356
|
-
expm_backend : {'auto', 'scipy', 'slepc'}
|
|
357
|
-
How to perform the expm_multiply function if ``method='expm'``. Can
|
|
358
|
-
further specifiy ``'slepc-krylov'``, or ``'slepc-expokit'``.
|
|
359
|
-
expm_opts : dict
|
|
360
|
-
Supplied to :func:`~quimb.linalg.base_linalg.expm_multiply`
|
|
361
|
-
function if ``method='expm'``.
|
|
362
|
-
progbar : bool, optional
|
|
363
|
-
Whether to show a progress bar when calling ``at_times`` or integrating
|
|
364
|
-
with the ``update_to`` method.
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
def __init__(
|
|
368
|
-
self,
|
|
369
|
-
p0,
|
|
370
|
-
ham,
|
|
371
|
-
t0=0,
|
|
372
|
-
compute=None,
|
|
373
|
-
int_stop=None,
|
|
374
|
-
method="integrate",
|
|
375
|
-
int_small_step=False,
|
|
376
|
-
expm_backend="AUTO",
|
|
377
|
-
expm_opts=None,
|
|
378
|
-
progbar=False,
|
|
379
|
-
):
|
|
380
|
-
self._p0 = qu(p0)
|
|
381
|
-
self._t = self.t0 = t0
|
|
382
|
-
self._isdop = isop(self._p0) # Density operator evolution?
|
|
383
|
-
self._d = p0.shape[0] # Hilbert space dimension
|
|
384
|
-
self._progbar = progbar
|
|
385
|
-
|
|
386
|
-
self._timedep = callable(ham) and not isinstance(
|
|
387
|
-
ham, CALLABLE_TIME_INDEP_CLASSES
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
if self._timedep:
|
|
391
|
-
# cache the time-dependent Hamiltonian in case callbacks use it
|
|
392
|
-
noncacheing_ham = ham
|
|
393
|
-
|
|
394
|
-
@functools.lru_cache(1)
|
|
395
|
-
def ham(t):
|
|
396
|
-
Ht = noncacheing_ham(t)
|
|
397
|
-
if not isinstance(Ht, LinearOperator):
|
|
398
|
-
make_immutable(Ht)
|
|
399
|
-
return Ht
|
|
400
|
-
|
|
401
|
-
if (int_stop is not None) and (method != "integrate"):
|
|
402
|
-
raise ValueError(
|
|
403
|
-
"You can't provide an integration stopping "
|
|
404
|
-
"condition (int_stop) if the method is not "
|
|
405
|
-
"'integrate'"
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
self._setup_callback(compute, int_stop)
|
|
409
|
-
self._method = method
|
|
410
|
-
|
|
411
|
-
if method == "solve" or isinstance(ham, (tuple, list)):
|
|
412
|
-
if isinstance(ham, LinearOperator):
|
|
413
|
-
raise TypeError(
|
|
414
|
-
"You can't use the 'solve' method "
|
|
415
|
-
"with an abstract linear operator "
|
|
416
|
-
"Hamiltonian."
|
|
417
|
-
)
|
|
418
|
-
elif self._timedep:
|
|
419
|
-
raise TypeError(
|
|
420
|
-
"You can't use the 'solve' method "
|
|
421
|
-
"with a time-dependent Hamiltonian."
|
|
422
|
-
)
|
|
423
|
-
self._ham = ham
|
|
424
|
-
self._setup_solved_ham()
|
|
425
|
-
|
|
426
|
-
elif method == "integrate":
|
|
427
|
-
self._start_integrator(ham, int_small_step)
|
|
428
|
-
self._ham = ham
|
|
429
|
-
elif method == "expm":
|
|
430
|
-
if isinstance(ham, LinearOperator):
|
|
431
|
-
raise TypeError(
|
|
432
|
-
"You can't use the 'expm' method "
|
|
433
|
-
"with an abstract linear operator "
|
|
434
|
-
"Hamiltonian."
|
|
435
|
-
)
|
|
436
|
-
elif self._timedep:
|
|
437
|
-
raise TypeError(
|
|
438
|
-
"You can't use the 'expm' method "
|
|
439
|
-
"with a time-dependent Hamiltonian."
|
|
440
|
-
)
|
|
441
|
-
self._update_method = self._update_to_expm_ket
|
|
442
|
-
self._pt = self._p0
|
|
443
|
-
self.expm_backend = expm_backend
|
|
444
|
-
self.expm_opts = ensure_dict(expm_opts)
|
|
445
|
-
self._ham = ham
|
|
446
|
-
else:
|
|
447
|
-
raise ValueError(
|
|
448
|
-
f"Did not understand evolution method: '{method}'."
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
def _setup_callback(self, fn, int_stop):
|
|
452
|
-
"""Setup callbacks in the correct place to compute into _results"""
|
|
453
|
-
# if fn is None there is no callback
|
|
454
|
-
if fn is None:
|
|
455
|
-
step_callback = None
|
|
456
|
-
# else fn is a dict of callbacks or a single callback
|
|
457
|
-
else:
|
|
458
|
-
# dict of funcs input -> dict of funcs output
|
|
459
|
-
if isinstance(fn, dict):
|
|
460
|
-
fn_try2then3args = {k: Try2Then3Args(v) for k, v in fn.items()}
|
|
461
|
-
self._results = {k: [] for k in fn}
|
|
462
|
-
|
|
463
|
-
def step_callback(t, pt, H):
|
|
464
|
-
for k, v in fn_try2then3args.items():
|
|
465
|
-
fn_result = v(t, pt, H)
|
|
466
|
-
self._results[k].append(fn_result)
|
|
467
|
-
|
|
468
|
-
# else results -> single list of outputs of fn
|
|
469
|
-
else:
|
|
470
|
-
fn_try2then3args = Try2Then3Args(fn)
|
|
471
|
-
self._results = []
|
|
472
|
-
|
|
473
|
-
def step_callback(t, pt, H):
|
|
474
|
-
fn_result = fn_try2then3args(t, pt, H)
|
|
475
|
-
self._results.append(fn_result)
|
|
476
|
-
|
|
477
|
-
# For the integration callback, additionally need to convert
|
|
478
|
-
# back to 'quantum' (column vector) form
|
|
479
|
-
|
|
480
|
-
# if no compute callback, check if there is an int_stop callback
|
|
481
|
-
if step_callback is None:
|
|
482
|
-
# if there is only an int_stop callback, set this up
|
|
483
|
-
if int_stop is not None:
|
|
484
|
-
int_stop_try2then3args = Try2Then3Args(int_stop)
|
|
485
|
-
|
|
486
|
-
def int_step_callback(t, y, H):
|
|
487
|
-
pt = qarray(y.reshape(self._d, -1))
|
|
488
|
-
return int_stop_try2then3args(t, pt, H)
|
|
489
|
-
|
|
490
|
-
# else if there is neither kind of callback but a progbar is
|
|
491
|
-
# needed, set up a dummy callback so it gets updated
|
|
492
|
-
elif self._progbar:
|
|
493
|
-
|
|
494
|
-
def int_step_callback(t, y, H):
|
|
495
|
-
pass
|
|
496
|
-
|
|
497
|
-
# else there are no callbacks and no progbar
|
|
498
|
-
else:
|
|
499
|
-
int_step_callback = None
|
|
500
|
-
|
|
501
|
-
# else there is compute callback, but may need to add int_stop callback
|
|
502
|
-
else:
|
|
503
|
-
# if both kinds of callback, combine them
|
|
504
|
-
if int_stop is not None:
|
|
505
|
-
int_stop_try2then3args = Try2Then3Args(int_stop)
|
|
506
|
-
|
|
507
|
-
def int_step_callback(t, y, H):
|
|
508
|
-
# For the integration callback, additionally need to
|
|
509
|
-
# convert back to 'quantum' (column vector) form
|
|
510
|
-
pt = qarray(y.reshape(self._d, -1))
|
|
511
|
-
step_callback(t, pt, H)
|
|
512
|
-
return int_stop_try2then3args(t, pt, H)
|
|
513
|
-
|
|
514
|
-
# else no int_stop callback, so just set up compute callback
|
|
515
|
-
else:
|
|
516
|
-
|
|
517
|
-
def int_step_callback(t, y, H):
|
|
518
|
-
# For the integration callback, additionally need to
|
|
519
|
-
# convert back to 'quantum' (column vector) form
|
|
520
|
-
pt = qarray(y.reshape(self._d, -1))
|
|
521
|
-
step_callback(t, pt, H)
|
|
522
|
-
|
|
523
|
-
self._step_callback = step_callback
|
|
524
|
-
self._int_step_callback = int_step_callback
|
|
525
|
-
|
|
526
|
-
def _setup_solved_ham(self):
|
|
527
|
-
"""Solve the hamiltonian if needed and find the initial state
|
|
528
|
-
in the energy eigenbasis for quick evolution later.
|
|
529
|
-
"""
|
|
530
|
-
# See if already solved from tuple
|
|
531
|
-
try:
|
|
532
|
-
evals, evecs = self._ham
|
|
533
|
-
self._method = "solve"
|
|
534
|
-
except ValueError:
|
|
535
|
-
evals, evecs = eigh(self._ham.toarray())
|
|
536
|
-
self._ham = (evals, evecs)
|
|
537
|
-
|
|
538
|
-
# Find initial state in energy eigenbasis at t0
|
|
539
|
-
if self._isdop:
|
|
540
|
-
self.pe0 = dot(dag(evecs), dot(self._p0, evecs))
|
|
541
|
-
self._update_method = self._update_to_solved_dop
|
|
542
|
-
else:
|
|
543
|
-
self.pe0 = dot(dag(evecs), self._p0)
|
|
544
|
-
self._update_method = self._update_to_solved_ket
|
|
545
|
-
|
|
546
|
-
# Current state (start with same as initial)
|
|
547
|
-
self._pt = self._p0
|
|
548
|
-
|
|
549
|
-
def _start_integrator(self, ham, small_step):
|
|
550
|
-
"""Initialize a stepping integrator."""
|
|
551
|
-
if self._timedep:
|
|
552
|
-
H0 = ham(0.0)
|
|
553
|
-
else:
|
|
554
|
-
H0 = ham
|
|
555
|
-
|
|
556
|
-
# set complex ode with governing equation
|
|
557
|
-
evo_eq = _calc_evo_eq(self._isdop, issparse(H0), False, self._timedep)
|
|
558
|
-
|
|
559
|
-
self._stepper = complex_ode(evo_eq(ham))
|
|
560
|
-
|
|
561
|
-
# 5th order stpper or 8th order stepper
|
|
562
|
-
int_mthd, step_fct = ("dopri5", 150) if small_step else ("dop853", 50)
|
|
563
|
-
if isinstance(H0, LinearOperator):
|
|
564
|
-
# approx norm doesn't need to be very accurate
|
|
565
|
-
nrm0 = norm_fro_approx(H0, tol=0.1)
|
|
566
|
-
else:
|
|
567
|
-
nrm0 = norm(H0, "f")
|
|
568
|
-
first_step = nrm0 / step_fct
|
|
569
|
-
|
|
570
|
-
self._stepper.set_integrator(int_mthd, nsteps=0, first_step=first_step)
|
|
571
|
-
|
|
572
|
-
# Set step_callback to be evaluated with args (t, y) at each step
|
|
573
|
-
if self._int_step_callback is not None:
|
|
574
|
-
|
|
575
|
-
def solout(t, y):
|
|
576
|
-
res = self._int_step_callback(t, y, self._ham)
|
|
577
|
-
return res
|
|
578
|
-
|
|
579
|
-
self._stepper.set_solout(solout)
|
|
580
|
-
|
|
581
|
-
self._stepper.set_initial_value(
|
|
582
|
-
self._p0.toarray().reshape(-1), self.t0
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
# assign the correct update_to method
|
|
586
|
-
self._update_method = self._update_to_integrate
|
|
587
|
-
|
|
588
|
-
# Methods for updating the simulation ----------------------------------- #
|
|
589
|
-
|
|
590
|
-
def _update_to_expm_ket(self, t):
|
|
591
|
-
"""Update the simulation to time ``t``, without explicitly computing
|
|
592
|
-
the operator exponential itself.
|
|
593
|
-
"""
|
|
594
|
-
factor = -1j * (t - self.t)
|
|
595
|
-
self._pt = expm_multiply(
|
|
596
|
-
factor * self._ham,
|
|
597
|
-
self._pt,
|
|
598
|
-
backend=self.expm_backend,
|
|
599
|
-
**self.expm_opts,
|
|
600
|
-
)
|
|
601
|
-
self._t = t
|
|
602
|
-
|
|
603
|
-
# compute any callbacks into -> self._results
|
|
604
|
-
if self._step_callback is not None:
|
|
605
|
-
self._step_callback(t, self._pt, self._ham)
|
|
606
|
-
|
|
607
|
-
def _update_to_solved_ket(self, t):
|
|
608
|
-
"""Update simulation consisting of a solved hamiltonian and a
|
|
609
|
-
wavefunction to time `t`.
|
|
610
|
-
"""
|
|
611
|
-
self._t = t
|
|
612
|
-
evals, evecs = self._ham
|
|
613
|
-
lt = explt(evals, t - self.t0)
|
|
614
|
-
self._pt = evecs @ ldmul(lt, self.pe0)
|
|
615
|
-
|
|
616
|
-
# compute any callbacks into -> self._results
|
|
617
|
-
if self._step_callback is not None:
|
|
618
|
-
self._step_callback(t, self._pt, self._ham)
|
|
619
|
-
|
|
620
|
-
def _update_to_solved_dop(self, t):
|
|
621
|
-
"""Update simulation consisting of a solved hamiltonian and a
|
|
622
|
-
density operator to time `t`.
|
|
623
|
-
"""
|
|
624
|
-
self._t = t
|
|
625
|
-
evals, evecs = self._ham
|
|
626
|
-
lt = explt(evals, t - self.t0)
|
|
627
|
-
lvpvl = rdmul(ldmul(lt, self.pe0), lt.conj())
|
|
628
|
-
self._pt = evecs @ (lvpvl @ dag(evecs))
|
|
629
|
-
|
|
630
|
-
# compute any callbacks into -> self._results
|
|
631
|
-
if self._step_callback is not None:
|
|
632
|
-
self._step_callback(t, self._pt, self._ham)
|
|
633
|
-
|
|
634
|
-
def _update_to_integrate(self, t):
|
|
635
|
-
"""Update simulation consisting of unsolved hamiltonian."""
|
|
636
|
-
self._stepper.integrate(t)
|
|
637
|
-
|
|
638
|
-
def update_to(self, t):
|
|
639
|
-
"""Update the simulation to time ``t`` using relevant method.
|
|
640
|
-
|
|
641
|
-
Parameters
|
|
642
|
-
----------
|
|
643
|
-
t : float
|
|
644
|
-
Time to update the evolution to.
|
|
645
|
-
"""
|
|
646
|
-
if self._progbar and hasattr(self, "_stepper"):
|
|
647
|
-
with continuous_progbar(self.t, t) as pbar:
|
|
648
|
-
if self._int_step_callback is not None:
|
|
649
|
-
|
|
650
|
-
def solout(t, y):
|
|
651
|
-
int_stop_res = self._int_step_callback(t, y, self._ham)
|
|
652
|
-
pbar.cupdate(t)
|
|
653
|
-
return int_stop_res
|
|
654
|
-
else:
|
|
655
|
-
|
|
656
|
-
def solout(t, _):
|
|
657
|
-
pbar.cupdate(t)
|
|
658
|
-
|
|
659
|
-
self._stepper.set_solout(solout)
|
|
660
|
-
self._update_method(t)
|
|
661
|
-
else:
|
|
662
|
-
self._update_method(t)
|
|
663
|
-
|
|
664
|
-
def at_times(self, ts):
|
|
665
|
-
"""Generator expression to yield state af list of times.
|
|
666
|
-
|
|
667
|
-
Parameters
|
|
668
|
-
----------
|
|
669
|
-
ts : sequence of floats
|
|
670
|
-
Times at which to evolve to, then yield the state.
|
|
671
|
-
|
|
672
|
-
Yields
|
|
673
|
-
------
|
|
674
|
-
pt : quantum state
|
|
675
|
-
Quantum state of evolution at next time in ``ts``.
|
|
676
|
-
|
|
677
|
-
Notes
|
|
678
|
-
-----
|
|
679
|
-
If integrating, currently any compute callbacks will be called at every
|
|
680
|
-
*integration* step, not just the times `ts` -- i.e. in general
|
|
681
|
-
len(Evolution.results) != len(ts) and if the adaptive step times are
|
|
682
|
-
needed they should be added as a callback, e.g.
|
|
683
|
-
``compute['t'] = lambda t, _: return t``.
|
|
684
|
-
"""
|
|
685
|
-
if self._progbar:
|
|
686
|
-
ts = progbar(ts)
|
|
687
|
-
|
|
688
|
-
for t in ts:
|
|
689
|
-
self._update_method(t)
|
|
690
|
-
yield self.pt
|
|
691
|
-
|
|
692
|
-
# Simulation properties ------------------------------------------------- #
|
|
693
|
-
|
|
694
|
-
@property
|
|
695
|
-
def t(self):
|
|
696
|
-
"""float : Current time of simulation."""
|
|
697
|
-
return self._stepper.t if self._method == "integrate" else self._t
|
|
698
|
-
|
|
699
|
-
@property
|
|
700
|
-
def pt(self):
|
|
701
|
-
"""quantum state : State of the system at the current time (t)."""
|
|
702
|
-
if self._method == "integrate":
|
|
703
|
-
return qarray(self._stepper.y.reshape(self._d, -1))
|
|
704
|
-
else:
|
|
705
|
-
return self._pt
|
|
706
|
-
|
|
707
|
-
@property
|
|
708
|
-
def results(self):
|
|
709
|
-
"""list, or dict of lists, optional : Results of the compute
|
|
710
|
-
callback(s) for each time step.
|
|
711
|
-
"""
|
|
712
|
-
return self._results
|
|
File without changes
|