Trajectree 0.0.0__py3-none-any.whl → 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trajectree/__init__.py +3 -0
- 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/quimb/docs/_pygments/_pygments_dark.py +118 -0
- trajectree/quimb/docs/_pygments/_pygments_light.py +118 -0
- trajectree/quimb/docs/conf.py +158 -0
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +62 -0
- trajectree/quimb/quimb/__init__.py +507 -0
- trajectree/quimb/quimb/calc.py +1491 -0
- trajectree/quimb/quimb/core.py +2279 -0
- trajectree/quimb/quimb/evo.py +712 -0
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +129 -0
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +109 -0
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +397 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +653 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +571 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +775 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +537 -0
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +194 -0
- trajectree/quimb/quimb/experimental/cluster_update.py +286 -0
- trajectree/quimb/quimb/experimental/merabuilder.py +865 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +15 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +1631 -0
- trajectree/quimb/quimb/experimental/schematic.py +7 -0
- trajectree/quimb/quimb/experimental/tn_marginals.py +130 -0
- trajectree/quimb/quimb/experimental/tnvmc.py +1483 -0
- trajectree/quimb/quimb/gates.py +36 -0
- trajectree/quimb/quimb/gen/__init__.py +2 -0
- trajectree/quimb/quimb/gen/operators.py +1167 -0
- trajectree/quimb/quimb/gen/rand.py +713 -0
- trajectree/quimb/quimb/gen/states.py +479 -0
- trajectree/quimb/quimb/linalg/__init__.py +6 -0
- trajectree/quimb/quimb/linalg/approx_spectral.py +1109 -0
- trajectree/quimb/quimb/linalg/autoblock.py +258 -0
- trajectree/quimb/quimb/linalg/base_linalg.py +719 -0
- trajectree/quimb/quimb/linalg/mpi_launcher.py +397 -0
- trajectree/quimb/quimb/linalg/numpy_linalg.py +244 -0
- trajectree/quimb/quimb/linalg/rand_linalg.py +514 -0
- trajectree/quimb/quimb/linalg/scipy_linalg.py +293 -0
- trajectree/quimb/quimb/linalg/slepc_linalg.py +892 -0
- trajectree/quimb/quimb/schematic.py +1518 -0
- trajectree/quimb/quimb/tensor/__init__.py +401 -0
- trajectree/quimb/quimb/tensor/array_ops.py +610 -0
- trajectree/quimb/quimb/tensor/circuit.py +4824 -0
- trajectree/quimb/quimb/tensor/circuit_gen.py +411 -0
- trajectree/quimb/quimb/tensor/contraction.py +336 -0
- trajectree/quimb/quimb/tensor/decomp.py +1255 -0
- trajectree/quimb/quimb/tensor/drawing.py +1646 -0
- trajectree/quimb/quimb/tensor/fitting.py +385 -0
- trajectree/quimb/quimb/tensor/geometry.py +583 -0
- trajectree/quimb/quimb/tensor/interface.py +114 -0
- trajectree/quimb/quimb/tensor/networking.py +1058 -0
- trajectree/quimb/quimb/tensor/optimize.py +1818 -0
- trajectree/quimb/quimb/tensor/tensor_1d.py +4778 -0
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +1854 -0
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +662 -0
- trajectree/quimb/quimb/tensor/tensor_2d.py +5954 -0
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +96 -0
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +1230 -0
- trajectree/quimb/quimb/tensor/tensor_3d.py +2869 -0
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +46 -0
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +60 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +3237 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +565 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +1138 -0
- trajectree/quimb/quimb/tensor/tensor_builder.py +5411 -0
- trajectree/quimb/quimb/tensor/tensor_core.py +11179 -0
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +1472 -0
- trajectree/quimb/quimb/tensor/tensor_mera.py +204 -0
- trajectree/quimb/quimb/utils.py +892 -0
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +501 -0
- trajectree/quimb/tests/test_calc.py +788 -0
- trajectree/quimb/tests/test_core.py +847 -0
- trajectree/quimb/tests/test_evo.py +565 -0
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +361 -0
- trajectree/quimb/tests/test_gen/test_rand.py +296 -0
- trajectree/quimb/tests/test_gen/test_states.py +261 -0
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +368 -0
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +351 -0
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +127 -0
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +84 -0
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +134 -0
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +283 -0
- 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 +39 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +67 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +64 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +51 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +142 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +101 -0
- trajectree/quimb/tests/test_tensor/test_circuit.py +816 -0
- trajectree/quimb/tests/test_tensor/test_contract.py +67 -0
- trajectree/quimb/tests/test_tensor/test_decomp.py +40 -0
- trajectree/quimb/tests/test_tensor/test_mera.py +52 -0
- trajectree/quimb/tests/test_tensor/test_optimizers.py +488 -0
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +1171 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +606 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +144 -0
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +123 -0
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +226 -0
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +441 -0
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +2066 -0
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +388 -0
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +63 -0
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +270 -0
- trajectree/quimb/tests/test_utils.py +85 -0
- trajectree/trajectory.py +2 -2
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/METADATA +2 -2
- trajectree-0.0.1.dist-info/RECORD +126 -0
- trajectree-0.0.0.dist-info/RECORD +0 -16
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/WHEEL +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
"""Backend agnostic functions for solving matrices either fully or partially.
|
|
2
|
+
"""
|
|
3
|
+
import functools
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import scipy.linalg as sla
|
|
8
|
+
import scipy.sparse.linalg as spla
|
|
9
|
+
|
|
10
|
+
from ..utils import raise_cant_find_library_function
|
|
11
|
+
from ..core import qarray, dag, issparse, isdense, vdot, ldmul
|
|
12
|
+
from .numpy_linalg import (
|
|
13
|
+
eig_numpy,
|
|
14
|
+
eigs_numpy,
|
|
15
|
+
svds_numpy,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from .scipy_linalg import (
|
|
19
|
+
eigs_scipy,
|
|
20
|
+
eigs_lobpcg,
|
|
21
|
+
eigs_primme,
|
|
22
|
+
svds_scipy,
|
|
23
|
+
svds_primme,
|
|
24
|
+
)
|
|
25
|
+
from . import SLEPC4PY_FOUND
|
|
26
|
+
|
|
27
|
+
if SLEPC4PY_FOUND:
|
|
28
|
+
from .mpi_launcher import (
|
|
29
|
+
eigs_slepc_spawn,
|
|
30
|
+
mfn_multiply_slepc_spawn,
|
|
31
|
+
svds_slepc_spawn,
|
|
32
|
+
)
|
|
33
|
+
from .slepc_linalg import eigs_slepc, svds_slepc, mfn_multiply_slepc
|
|
34
|
+
else: # pragma: no cover
|
|
35
|
+
eigs_slepc = raise_cant_find_library_function("slepc4py")
|
|
36
|
+
eigs_slepc_spawn = raise_cant_find_library_function("slepc4py")
|
|
37
|
+
svds_slepc = raise_cant_find_library_function("slepc4py")
|
|
38
|
+
svds_slepc_spawn = raise_cant_find_library_function("slepc4py")
|
|
39
|
+
mfn_multiply_slepc = raise_cant_find_library_function("slepc4py")
|
|
40
|
+
mfn_multiply_slepc_spawn = raise_cant_find_library_function("slepc4py")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# --------------------------------------------------------------------------- #
|
|
44
|
+
# Partial eigendecomposition #
|
|
45
|
+
# --------------------------------------------------------------------------- #
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def choose_backend(A, k, int_eps=False, B=None):
|
|
49
|
+
"""Pick a backend automatically for partial decompositions."""
|
|
50
|
+
# LinOps -> not possible to simply convert to dense or use MPI processes
|
|
51
|
+
A_is_linop = isinstance(A, spla.LinearOperator)
|
|
52
|
+
B_is_linop = isinstance(B, spla.LinearOperator)
|
|
53
|
+
|
|
54
|
+
# small array or large part of subspace requested
|
|
55
|
+
small_d_big_k = A.shape[0] ** 2 / k < (10000 if int_eps else 2000)
|
|
56
|
+
|
|
57
|
+
if small_d_big_k and not (A_is_linop or B_is_linop):
|
|
58
|
+
return "NUMPY"
|
|
59
|
+
|
|
60
|
+
# slepc seems faster for sparse, dense and LinearOperators
|
|
61
|
+
if SLEPC4PY_FOUND and not B_is_linop:
|
|
62
|
+
# only spool up an mpi pool for big sparse matrices though
|
|
63
|
+
if issparse(A) and A.nnz > 10000:
|
|
64
|
+
return "SLEPC"
|
|
65
|
+
|
|
66
|
+
return "SLEPC-NOMPI"
|
|
67
|
+
|
|
68
|
+
return "SCIPY"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
_EIGS_METHODS = {
|
|
72
|
+
"NUMPY": eigs_numpy,
|
|
73
|
+
"SCIPY": eigs_scipy,
|
|
74
|
+
"PRIMME": eigs_primme,
|
|
75
|
+
"LOBPCG": eigs_lobpcg,
|
|
76
|
+
"SLEPC": eigs_slepc_spawn,
|
|
77
|
+
"SLEPC-NOMPI": eigs_slepc,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def eigensystem_partial(
|
|
82
|
+
A,
|
|
83
|
+
k,
|
|
84
|
+
isherm,
|
|
85
|
+
*,
|
|
86
|
+
B=None,
|
|
87
|
+
which=None,
|
|
88
|
+
return_vecs=True,
|
|
89
|
+
sigma=None,
|
|
90
|
+
ncv=None,
|
|
91
|
+
tol=None,
|
|
92
|
+
v0=None,
|
|
93
|
+
sort=True,
|
|
94
|
+
backend=None,
|
|
95
|
+
fallback_to_scipy=False,
|
|
96
|
+
**backend_opts,
|
|
97
|
+
):
|
|
98
|
+
"""Return a few eigenpairs from an operator.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
A : sparse, dense or linear operator
|
|
103
|
+
The operator to solve for.
|
|
104
|
+
k : int
|
|
105
|
+
Number of eigenpairs to return.
|
|
106
|
+
isherm : bool
|
|
107
|
+
Whether to use hermitian solve or not.
|
|
108
|
+
B : sparse, dense or linear operator, optional
|
|
109
|
+
If given, the RHS operator defining a generalized eigen problem.
|
|
110
|
+
which : {'SA', 'LA', 'LM', 'SM', 'TR'}
|
|
111
|
+
Where in spectrum to take eigenvalues from (see
|
|
112
|
+
:func:``scipy.sparse.linalg.eigsh``)
|
|
113
|
+
return_vecs : bool, optional
|
|
114
|
+
Whether to return the eigenvectors.
|
|
115
|
+
sigma : float, optional
|
|
116
|
+
Which part of spectrum to target, implies which='TR' if which is None.
|
|
117
|
+
ncv : int, optional
|
|
118
|
+
number of lanczos vectors, can use to optimise speed
|
|
119
|
+
tol : None or float
|
|
120
|
+
Tolerance with which to find eigenvalues.
|
|
121
|
+
v0 : None or 1D-array like
|
|
122
|
+
An initial vector guess to iterate with.
|
|
123
|
+
sort : bool, optional
|
|
124
|
+
Whether to explicitly sort by ascending eigenvalue order.
|
|
125
|
+
backend : {'AUTO', 'NUMPY', 'SCIPY',
|
|
126
|
+
'LOBPCG', 'SLEPC', 'SLEPC-NOMPI'}, optional
|
|
127
|
+
Which solver to use.
|
|
128
|
+
fallback_to_scipy : bool, optional
|
|
129
|
+
If an error occurs and scipy is not being used, try using scipy.
|
|
130
|
+
backend_opts
|
|
131
|
+
Supplied to the backend solver.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
elk : (k,) array
|
|
136
|
+
The ``k`` eigenvalues.
|
|
137
|
+
evk : (d, k) array
|
|
138
|
+
Array with ``k`` eigenvectors as columns if ``return_vecs``.
|
|
139
|
+
"""
|
|
140
|
+
settings = {
|
|
141
|
+
"k": k,
|
|
142
|
+
"B": B,
|
|
143
|
+
"which": (
|
|
144
|
+
"SA"
|
|
145
|
+
if (which is None) and (sigma is None)
|
|
146
|
+
else "TR"
|
|
147
|
+
if (which is None) and (sigma is not None)
|
|
148
|
+
else which
|
|
149
|
+
),
|
|
150
|
+
"return_vecs": return_vecs,
|
|
151
|
+
"sigma": sigma,
|
|
152
|
+
"isherm": isherm,
|
|
153
|
+
"ncv": ncv,
|
|
154
|
+
"sort": sort,
|
|
155
|
+
"tol": tol,
|
|
156
|
+
"v0": v0,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Choose backend to perform the decompostion
|
|
160
|
+
bkd = "AUTO" if backend is None else backend.upper()
|
|
161
|
+
if bkd == "AUTO":
|
|
162
|
+
bkd = choose_backend(A, k, sigma is not None, B=B)
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
return _EIGS_METHODS[bkd](A, **settings, **backend_opts)
|
|
166
|
+
|
|
167
|
+
# sometimes e.g. lobpcg fails, worth trying scipy
|
|
168
|
+
except Exception as e: # pragma: no cover
|
|
169
|
+
if fallback_to_scipy and (bkd != "SCIPY"):
|
|
170
|
+
warnings.warn(
|
|
171
|
+
f"`eigensystem_partial` with backend '{bkd}' failed, trying "
|
|
172
|
+
"again with scipy. Set ``fallback_to_scipy=False`` to avoid "
|
|
173
|
+
"this and see the full error."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return eigs_scipy(A, **settings, **backend_opts)
|
|
177
|
+
else:
|
|
178
|
+
raise e
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# --------------------------------------------------------------------------- #
|
|
182
|
+
# Full eigendecomposition #
|
|
183
|
+
# --------------------------------------------------------------------------- #
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def eigensystem(A, isherm, *, k=-1, sort=True, return_vecs=True, **kwargs):
|
|
187
|
+
"""Find all or some eigenpairs of an operator.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
A : operator
|
|
192
|
+
The operator to decompose.
|
|
193
|
+
isherm : bool
|
|
194
|
+
Whether the operator is assumed to be hermitian or not.
|
|
195
|
+
k : int, optional
|
|
196
|
+
If negative, find all eigenpairs, else perform partial
|
|
197
|
+
eigendecomposition and find ``k`` pairs. See
|
|
198
|
+
:func:`~quimb.linalg.base_linalg.eigensystem_partial`.
|
|
199
|
+
sort : bool, optional
|
|
200
|
+
Whether to sort the eigenpairs in ascending eigenvalue order.
|
|
201
|
+
kwargs
|
|
202
|
+
Supplied to the backend function.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
el : (k,) array
|
|
207
|
+
Eigenvalues.
|
|
208
|
+
ev : (d, k) array
|
|
209
|
+
Corresponding eigenvectors as columns of array, such that
|
|
210
|
+
``ev @ diag(el) @ ev.H == A``.
|
|
211
|
+
"""
|
|
212
|
+
if k < 0:
|
|
213
|
+
return eig_numpy(
|
|
214
|
+
A, isherm=isherm, sort=sort, return_vecs=return_vecs, **kwargs
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return eigensystem_partial(
|
|
218
|
+
A, k=k, isherm=isherm, sort=sort, return_vecs=return_vecs, **kwargs
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
eig = functools.partial(eigensystem, isherm=False, return_vecs=True)
|
|
223
|
+
eigh = functools.partial(eigensystem, isherm=True, return_vecs=True)
|
|
224
|
+
eigvals = functools.partial(eigensystem, isherm=False, return_vecs=False)
|
|
225
|
+
eigvalsh = functools.partial(eigensystem, isherm=True, return_vecs=False)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@functools.wraps(eigensystem)
|
|
229
|
+
def eigenvectors(A, isherm, *, sort=True, **kwargs):
|
|
230
|
+
return eigensystem(A, isherm=isherm, sort=sort, **kwargs)[1]
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
eigvecs = functools.partial(eigenvectors, isherm=False)
|
|
234
|
+
eigvecsh = functools.partial(eigenvectors, isherm=True)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def groundstate(ham, **kwargs):
|
|
238
|
+
"""Alias for finding lowest eigenvector only."""
|
|
239
|
+
return eigvecsh(ham, k=1, which="SA", **kwargs)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def groundenergy(ham, **kwargs):
|
|
243
|
+
"""Alias for finding lowest eigenvalue only."""
|
|
244
|
+
return eigvalsh(ham, k=1, which="SA", **kwargs)[0]
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def bound_spectrum(A, backend="auto", **kwargs):
|
|
248
|
+
"""Return the smallest and largest eigenvalue of hermitian operator ``A``."""
|
|
249
|
+
el_min = eigvalsh(A, k=1, which="SA", backend=backend, **kwargs)[0]
|
|
250
|
+
el_max = eigvalsh(A, k=1, which="LA", backend=backend, **kwargs)[0]
|
|
251
|
+
return el_min, el_max
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _rel_window_to_abs_window(el_min, el_max, w_0, w_sz=None):
|
|
255
|
+
"""Convert min/max eigenvalues and relative window to absolute values.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
el_min : float
|
|
260
|
+
Smallest eigenvalue.
|
|
261
|
+
el_max : float
|
|
262
|
+
Largest eigenvalue.
|
|
263
|
+
w_0 : float [0.0 - 1.0]
|
|
264
|
+
Relative window centre.
|
|
265
|
+
w_sz : float, optional
|
|
266
|
+
Relative window width.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
l_0[, l_min, l_max]:
|
|
271
|
+
Absolute value of centre of window, lower and upper intervals if a
|
|
272
|
+
window size is specified.
|
|
273
|
+
"""
|
|
274
|
+
el_range = el_max - el_min
|
|
275
|
+
el_w_0 = el_min + w_0 * el_range
|
|
276
|
+
if w_sz is not None:
|
|
277
|
+
el_w_min = el_w_0 - w_sz * el_range / 2
|
|
278
|
+
el_w_max = el_w_0 + w_sz * el_range / 2
|
|
279
|
+
return el_w_0, el_w_min, el_w_max
|
|
280
|
+
return el_w_0
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def eigh_window(
|
|
284
|
+
A,
|
|
285
|
+
w_0,
|
|
286
|
+
k,
|
|
287
|
+
w_sz=None,
|
|
288
|
+
backend="AUTO",
|
|
289
|
+
return_vecs=True,
|
|
290
|
+
offset_const=1 / 104729,
|
|
291
|
+
**kwargs,
|
|
292
|
+
):
|
|
293
|
+
"""Return mid-spectrum eigenpairs from a hermitian operator.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
A : (d, d) operator
|
|
298
|
+
Operator to retrieve eigenpairs from.
|
|
299
|
+
w_0 : float [0.0, 1.0]
|
|
300
|
+
Relative window centre to retrieve eigenpairs from.
|
|
301
|
+
k : int
|
|
302
|
+
Target number of eigenpairs to retrieve.
|
|
303
|
+
w_sz : float, optional
|
|
304
|
+
Relative maximum window width within which to keep eigenpairs.
|
|
305
|
+
backend : str, optional
|
|
306
|
+
Which :func:`~quimb.eigh` backend to use.
|
|
307
|
+
return_vecs : bool, optional
|
|
308
|
+
Whether to return eigenvectors as well.
|
|
309
|
+
offset_const : float, optional
|
|
310
|
+
Small fudge factor (relative to window range) to avoid 1 / 0 issues.
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
el : (k,) array
|
|
315
|
+
Eigenvalues around w_0.
|
|
316
|
+
ev : (d, k) array
|
|
317
|
+
The eigenvectors, if ``return_vecs=True``.
|
|
318
|
+
"""
|
|
319
|
+
w_sz = w_sz if w_sz is not None else 1.1
|
|
320
|
+
|
|
321
|
+
if isdense(A) or backend.upper() == "NUMPY":
|
|
322
|
+
if return_vecs:
|
|
323
|
+
lk, vk = eigh(A.toarray() if issparse(A) else A, **kwargs)
|
|
324
|
+
else:
|
|
325
|
+
lk = eigvalsh(A.toarray() if issparse(A) else A, **kwargs)
|
|
326
|
+
|
|
327
|
+
lmin, lmax = lk[0], lk[-1]
|
|
328
|
+
l_w0, l_wmin, l_wmax = _rel_window_to_abs_window(lmin, lmax, w_0, w_sz)
|
|
329
|
+
|
|
330
|
+
else:
|
|
331
|
+
lmin, lmax = bound_spectrum(A, backend=backend, **kwargs)
|
|
332
|
+
l_w0, l_wmin, l_wmax = _rel_window_to_abs_window(lmin, lmax, w_0, w_sz)
|
|
333
|
+
l_w0 += (lmax - lmin) * offset_const # for 1/0 issues
|
|
334
|
+
|
|
335
|
+
if return_vecs:
|
|
336
|
+
lk, vk = eigh(A, k=k, sigma=l_w0, backend=backend, **kwargs)
|
|
337
|
+
else:
|
|
338
|
+
lk = eigvalsh(A, k=k, sigma=l_w0, backend=backend, **kwargs)
|
|
339
|
+
|
|
340
|
+
# Trim eigenpairs from beyond window
|
|
341
|
+
in_window = (lk > l_wmin) & (lk < l_wmax)
|
|
342
|
+
|
|
343
|
+
if return_vecs:
|
|
344
|
+
return lk[in_window], vk[:, in_window]
|
|
345
|
+
|
|
346
|
+
return lk[in_window]
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def eigvalsh_window(*args, **kwargs):
|
|
350
|
+
"""Alias for only finding the eigenvalues in a relative window."""
|
|
351
|
+
return eigh_window(*args, return_vecs=False, **kwargs)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def eigvecsh_window(*args, **kwargs):
|
|
355
|
+
"""Alias for only finding the eigenvectors in a relative window."""
|
|
356
|
+
return eigh_window(*args, return_vecs=True, **kwargs)[1]
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# -------------------------------------------------------------------------- #
|
|
360
|
+
# Partial singular value decomposition #
|
|
361
|
+
# -------------------------------------------------------------------------- #
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def svd(A, return_vecs=True):
|
|
365
|
+
"""Compute full singular value decomposition of an operator, using numpy.
|
|
366
|
+
|
|
367
|
+
Parameters
|
|
368
|
+
----------
|
|
369
|
+
A : (m, n) array
|
|
370
|
+
The operator.
|
|
371
|
+
return_vecs : bool, optional
|
|
372
|
+
Whether to return the singular vectors.
|
|
373
|
+
|
|
374
|
+
Returns
|
|
375
|
+
-------
|
|
376
|
+
U : (m, k) array
|
|
377
|
+
Left singular vectors (if ``return_vecs=True``) as columns.
|
|
378
|
+
s : (k,) array
|
|
379
|
+
Singular values.
|
|
380
|
+
VH : (k, n) array
|
|
381
|
+
Right singular vectors (if ``return_vecs=True``) as rows.
|
|
382
|
+
"""
|
|
383
|
+
try:
|
|
384
|
+
return np.linalg.svd(A, full_matrices=False, compute_uv=return_vecs)
|
|
385
|
+
|
|
386
|
+
except np.linalg.linalg.LinAlgError: # pragma: no cover
|
|
387
|
+
warnings.warn("Numpy SVD failed, trying again with different driver.")
|
|
388
|
+
return sla.svd(
|
|
389
|
+
A,
|
|
390
|
+
full_matrices=False,
|
|
391
|
+
compute_uv=return_vecs,
|
|
392
|
+
lapack_driver="gesvd",
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
_SVDS_METHODS = {
|
|
397
|
+
"SLEPC": svds_slepc_spawn,
|
|
398
|
+
"SLEPC-NOMPI": svds_slepc,
|
|
399
|
+
"NUMPY": svds_numpy,
|
|
400
|
+
"SCIPY": svds_scipy,
|
|
401
|
+
"PRIMME": svds_primme,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def svds(A, k, ncv=None, return_vecs=True, backend="AUTO", **kwargs):
|
|
406
|
+
"""Compute the partial singular value decomposition of an operator.
|
|
407
|
+
|
|
408
|
+
Parameters
|
|
409
|
+
----------
|
|
410
|
+
A : dense, sparse or linear operator
|
|
411
|
+
The operator to decompose.
|
|
412
|
+
k : int, optional
|
|
413
|
+
number of singular value (triplets) to retrieve
|
|
414
|
+
ncv : int, optional
|
|
415
|
+
Number of lanczos vectors to use performing decomposition.
|
|
416
|
+
return_vecs : bool, optional
|
|
417
|
+
Whether to return the left and right vectors
|
|
418
|
+
backend : {'AUTO', 'SCIPY', 'SLEPC', 'SLEPC-NOMPI', 'NUMPY'}, optional
|
|
419
|
+
Which solver to use to perform decomposition.
|
|
420
|
+
|
|
421
|
+
Returns
|
|
422
|
+
-------
|
|
423
|
+
(Uk,) sk (, VHk) :
|
|
424
|
+
Singular value(s) (and vectors) such that ``Uk @ np.diag(sk) @ VHk``
|
|
425
|
+
approximates ``A``.
|
|
426
|
+
"""
|
|
427
|
+
settings = {"k": k, "ncv": ncv, "return_vecs": return_vecs}
|
|
428
|
+
|
|
429
|
+
bkd = (
|
|
430
|
+
choose_backend(A, k, False)
|
|
431
|
+
if backend in {"auto", "AUTO"}
|
|
432
|
+
else backend.upper()
|
|
433
|
+
)
|
|
434
|
+
svds_func = _SVDS_METHODS[bkd.upper()]
|
|
435
|
+
|
|
436
|
+
return svds_func(A, **settings, **kwargs)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# -------------------------------------------------------------------------- #
|
|
440
|
+
# Norms and other quantities based on decompositions #
|
|
441
|
+
# -------------------------------------------------------------------------- #
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def norm_2(A, **kwargs):
|
|
445
|
+
"""Return the 2-norm of operator, ``A``, i.e. the largest singular value."""
|
|
446
|
+
return svds(A, k=1, return_vecs=False, **kwargs)[0]
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def norm_fro_dense(A):
|
|
450
|
+
"""Frobenius norm for dense matrices"""
|
|
451
|
+
return vdot(A, A).real ** 0.5
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def norm_fro_sparse(A):
|
|
455
|
+
return vdot(A.data, A.data).real ** 0.5
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def norm_trace_dense(A, isherm=False):
|
|
459
|
+
"""Returns the trace norm of operator ``A``, that is,
|
|
460
|
+
the sum of the absolute eigenvalues.
|
|
461
|
+
"""
|
|
462
|
+
if isherm:
|
|
463
|
+
return abs(eigvalsh(A)).sum()
|
|
464
|
+
else:
|
|
465
|
+
return svd(A, return_vecs=False).sum()
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def norm(A, ntype=2, **kwargs):
|
|
469
|
+
"""Operator norms.
|
|
470
|
+
|
|
471
|
+
Parameters
|
|
472
|
+
----------
|
|
473
|
+
A : operator
|
|
474
|
+
The operator to find norm of.
|
|
475
|
+
ntype : str
|
|
476
|
+
Norm to calculate, if any of:
|
|
477
|
+
|
|
478
|
+
- {2, '2', 'spectral'}: largest singular value
|
|
479
|
+
- {'f', 'fro'}: frobenius norm
|
|
480
|
+
- {'t', 'nuc', 'tr', 'trace'}: sum of singular values
|
|
481
|
+
|
|
482
|
+
Returns
|
|
483
|
+
-------
|
|
484
|
+
x : float
|
|
485
|
+
The operator norm.
|
|
486
|
+
"""
|
|
487
|
+
types = {
|
|
488
|
+
"2": "2",
|
|
489
|
+
2: "2",
|
|
490
|
+
"spectral": "2",
|
|
491
|
+
"f": "f",
|
|
492
|
+
"fro": "f",
|
|
493
|
+
"t": "t",
|
|
494
|
+
"trace": "t",
|
|
495
|
+
"nuc": "t",
|
|
496
|
+
"tr": "t",
|
|
497
|
+
}
|
|
498
|
+
methods = {
|
|
499
|
+
("2", 0): norm_2,
|
|
500
|
+
("2", 1): norm_2,
|
|
501
|
+
("t", 0): norm_trace_dense,
|
|
502
|
+
("f", 0): norm_fro_dense,
|
|
503
|
+
("f", 1): norm_fro_sparse,
|
|
504
|
+
}
|
|
505
|
+
return methods[(types[ntype], issparse(A))](A, **kwargs)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# --------------------------------------------------------------------------- #
|
|
509
|
+
# Matrix functions #
|
|
510
|
+
# --------------------------------------------------------------------------- #
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def expm(A, herm=False):
|
|
514
|
+
"""Matrix exponential, can be accelerated if explicitly hermitian.
|
|
515
|
+
|
|
516
|
+
Parameters
|
|
517
|
+
----------
|
|
518
|
+
A : dense or sparse operator
|
|
519
|
+
Operator to exponentiate.
|
|
520
|
+
herm : bool, optional
|
|
521
|
+
If True (not default), and ``A`` is dense, digonalize the matrix
|
|
522
|
+
in order to perform the exponential.
|
|
523
|
+
"""
|
|
524
|
+
if issparse(A):
|
|
525
|
+
# convert to and from csc to suppress scipy warning
|
|
526
|
+
return spla.expm(A.tocsc()).tocsr()
|
|
527
|
+
elif not herm:
|
|
528
|
+
return qarray(spla.expm(A))
|
|
529
|
+
else:
|
|
530
|
+
evals, evecs = eigh(A)
|
|
531
|
+
return evecs @ ldmul(np.exp(evals), dag(evecs))
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
_EXPM_MULTIPLY_METHODS = {
|
|
535
|
+
"SCIPY": spla.expm_multiply,
|
|
536
|
+
"SLEPC": functools.partial(mfn_multiply_slepc_spawn, fntype="exp"),
|
|
537
|
+
"SLEPC-KRYLOV": functools.partial(
|
|
538
|
+
mfn_multiply_slepc_spawn, fntype="exp", MFNType="KRYLOV"
|
|
539
|
+
),
|
|
540
|
+
"SLEPC-EXPOKIT": functools.partial(
|
|
541
|
+
mfn_multiply_slepc_spawn, fntype="exp", MFNType="EXPOKIT"
|
|
542
|
+
),
|
|
543
|
+
"SLEPC-NOMPI": functools.partial(mfn_multiply_slepc, fntype="exp"),
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def expm_multiply(mat, vec, backend="AUTO", **kwargs):
|
|
548
|
+
"""Compute the action of ``expm(mat)`` on ``vec``.
|
|
549
|
+
|
|
550
|
+
Parameters
|
|
551
|
+
----------
|
|
552
|
+
mat : operator
|
|
553
|
+
Operator with which to act with exponential on ``vec``.
|
|
554
|
+
vec : vector-like
|
|
555
|
+
Vector to act with exponential of operator on.
|
|
556
|
+
backend : {'AUTO', 'SCIPY', 'SLEPC', 'SLEPC-KRYLOV', 'SLEPC-EXPOKIT'}
|
|
557
|
+
Which backend to use.
|
|
558
|
+
kwargs
|
|
559
|
+
Supplied to backend function.
|
|
560
|
+
|
|
561
|
+
Returns
|
|
562
|
+
-------
|
|
563
|
+
vector
|
|
564
|
+
Result of ``expm(mat) @ vec``.
|
|
565
|
+
"""
|
|
566
|
+
if backend == "AUTO":
|
|
567
|
+
if SLEPC4PY_FOUND and vec.size > 2**10:
|
|
568
|
+
backend = "SLEPC"
|
|
569
|
+
else:
|
|
570
|
+
backend = "SCIPY"
|
|
571
|
+
|
|
572
|
+
return _EXPM_MULTIPLY_METHODS[backend.upper()](mat, vec, **kwargs)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def sqrtm(A, herm=True):
|
|
576
|
+
"""Matrix square root, can be accelerated if explicitly hermitian.
|
|
577
|
+
|
|
578
|
+
Parameters
|
|
579
|
+
----------
|
|
580
|
+
A : dense array
|
|
581
|
+
Operator to take square root of.
|
|
582
|
+
herm : bool, optional
|
|
583
|
+
If True (the default), and ``A`` is dense, digonalize the matrix
|
|
584
|
+
in order to take the square root.
|
|
585
|
+
|
|
586
|
+
Returns
|
|
587
|
+
-------
|
|
588
|
+
array
|
|
589
|
+
"""
|
|
590
|
+
if issparse(A):
|
|
591
|
+
raise NotImplementedError("No sparse sqrtm available.")
|
|
592
|
+
elif not herm:
|
|
593
|
+
return qarray(sla.sqrtm(A))
|
|
594
|
+
else:
|
|
595
|
+
evals, evecs = eigh(A)
|
|
596
|
+
return evecs @ ldmul(np.sqrt(evals.astype(complex)), dag(evecs))
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class IdentityLinearOperator(spla.LinearOperator):
|
|
600
|
+
"""Get a ``LinearOperator`` representation of the identity operator,
|
|
601
|
+
scaled by ``factor``.
|
|
602
|
+
|
|
603
|
+
Parameters
|
|
604
|
+
----------
|
|
605
|
+
size : int
|
|
606
|
+
The size of the identity.
|
|
607
|
+
factor : float
|
|
608
|
+
The coefficient of the identity.
|
|
609
|
+
|
|
610
|
+
Examples
|
|
611
|
+
--------
|
|
612
|
+
|
|
613
|
+
>>> I3 = IdentityLinearOperator(100, 1/3)
|
|
614
|
+
>>> p = rand_ket(100)
|
|
615
|
+
>>> np.allclose(I3 @ p, p / 3)
|
|
616
|
+
True
|
|
617
|
+
"""
|
|
618
|
+
|
|
619
|
+
def __init__(self, size, factor=1):
|
|
620
|
+
self.factor = factor
|
|
621
|
+
super().__init__(dtype=np.array(factor).dtype, shape=(size, size))
|
|
622
|
+
|
|
623
|
+
def _matvec(self, vec):
|
|
624
|
+
return self.factor * vec
|
|
625
|
+
|
|
626
|
+
def _rmatvec(self, vec):
|
|
627
|
+
return self.factor * vec
|
|
628
|
+
|
|
629
|
+
def _matmat(self, mat):
|
|
630
|
+
return self.factor * mat
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
class Lazy:
|
|
634
|
+
"""A simple class representing an unconstructed matrix. This can be passed
|
|
635
|
+
to, for example, MPI workers, who can then construct the matrix themselves.
|
|
636
|
+
The main function ``fn`` should ideally take an ``ownership`` keyword to
|
|
637
|
+
avoid forming every row.
|
|
638
|
+
|
|
639
|
+
This is essentially like using ``functools.partial`` and assigning the
|
|
640
|
+
``shape`` attribute.
|
|
641
|
+
|
|
642
|
+
Parameters
|
|
643
|
+
----------
|
|
644
|
+
fn : callable
|
|
645
|
+
A function that constructs an operator.
|
|
646
|
+
shape :
|
|
647
|
+
Shape of the constructed operator.
|
|
648
|
+
args
|
|
649
|
+
Supplied to ``fn``.
|
|
650
|
+
kwargs
|
|
651
|
+
Supplied to ``fn``.
|
|
652
|
+
|
|
653
|
+
Returns
|
|
654
|
+
-------
|
|
655
|
+
Lazy : callable
|
|
656
|
+
|
|
657
|
+
Examples
|
|
658
|
+
--------
|
|
659
|
+
Setup the lazy operator:
|
|
660
|
+
|
|
661
|
+
>>> H_lazy = Lazy(ham_heis, n=10, shape=(2**10, 2**10), sparse=True)
|
|
662
|
+
>>> H_lazy
|
|
663
|
+
<Lazy(ham_heis, shape=(1024, 1024), dtype=None)>
|
|
664
|
+
|
|
665
|
+
Build a matrix slice (usually done automatically by e.g. ``eigs``):
|
|
666
|
+
|
|
667
|
+
>>> H_lazy(ownership=(256, 512))
|
|
668
|
+
<256x1024 sparse matrix of type '<class 'numpy.float64'>'
|
|
669
|
+
with 1664 stored elements in Compressed Sparse Row format>
|
|
670
|
+
"""
|
|
671
|
+
|
|
672
|
+
def __init__(self, fn, *args, shape=None, factor=None, **kwargs):
|
|
673
|
+
if shape is None:
|
|
674
|
+
raise TypeError("`shape` must be specified.")
|
|
675
|
+
self.fn = fn
|
|
676
|
+
self.args = args
|
|
677
|
+
self.kwargs = kwargs
|
|
678
|
+
self.shape = shape
|
|
679
|
+
self.factor = factor
|
|
680
|
+
self.dtype = None
|
|
681
|
+
|
|
682
|
+
def __imul__(self, x):
|
|
683
|
+
if self.factor is None:
|
|
684
|
+
self.factor = x
|
|
685
|
+
else:
|
|
686
|
+
self.factor = self.factor * x
|
|
687
|
+
|
|
688
|
+
def __mul__(self, x):
|
|
689
|
+
if self.factor is not None:
|
|
690
|
+
x = x * self.factor
|
|
691
|
+
return Lazy(
|
|
692
|
+
self.fn, *self.args, shape=self.shape, factor=x, **self.kwargs
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
def __rmul__(self, x):
|
|
696
|
+
return self.__mul__(x)
|
|
697
|
+
|
|
698
|
+
def __call__(self, **kwargs):
|
|
699
|
+
A = self.fn(*self.args, **self.kwargs, **kwargs)
|
|
700
|
+
|
|
701
|
+
# check if any prefactors have been set
|
|
702
|
+
if self.factor is not None:
|
|
703
|
+
# try inplace first
|
|
704
|
+
try:
|
|
705
|
+
A *= self.factor
|
|
706
|
+
except (ValueError, TypeError):
|
|
707
|
+
A = self.factor * A
|
|
708
|
+
|
|
709
|
+
# helpful to store dtype once constructed
|
|
710
|
+
self.dtype = A.dtype
|
|
711
|
+
return A
|
|
712
|
+
|
|
713
|
+
def __repr__(self):
|
|
714
|
+
s = "<Lazy({}, shape={}{}{})>"
|
|
715
|
+
|
|
716
|
+
s_dtype = f", dtype={self.dtype}" if self.dtype is not None else ""
|
|
717
|
+
s_factor = f", factor={self.factor}" if self.factor is not None else ""
|
|
718
|
+
|
|
719
|
+
return s.format(self.fn.__name__, self.shape, s_dtype, s_factor)
|