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,514 @@
|
|
|
1
|
+
"""Randomized iterative methods for decompositions.
|
|
2
|
+
"""
|
|
3
|
+
from numbers import Integral
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import scipy.linalg as sla
|
|
7
|
+
|
|
8
|
+
from ..gen.rand import randn
|
|
9
|
+
from ..core import dag, dot, njit
|
|
10
|
+
from ..utils import identity
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def lu_orthog(X):
|
|
14
|
+
return sla.lu(X, permute_l=True, overwrite_a=True, check_finite=False)[0]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def qr_orthog(X):
|
|
18
|
+
return sla.qr(X, mode="economic", overwrite_a=True, check_finite=False)[0]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def orthog(X, lu=False):
|
|
22
|
+
if lu:
|
|
23
|
+
return lu_orthog(X)
|
|
24
|
+
return qr_orthog(X)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def QB_to_svd(Q, B, compute_uv=True):
|
|
28
|
+
UsV = sla.svd(
|
|
29
|
+
B,
|
|
30
|
+
full_matrices=False,
|
|
31
|
+
compute_uv=compute_uv,
|
|
32
|
+
overwrite_a=True,
|
|
33
|
+
check_finite=False,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not compute_uv:
|
|
37
|
+
return UsV
|
|
38
|
+
|
|
39
|
+
U, s, V = UsV
|
|
40
|
+
return dot(Q, U), s, V
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def trim(arrays, k):
|
|
44
|
+
if isinstance(arrays, tuple) and len(arrays) == 3:
|
|
45
|
+
U, s, VH = arrays
|
|
46
|
+
U, s, VH = U[:, :k], s[:k], VH[:k, :]
|
|
47
|
+
return U, s, VH
|
|
48
|
+
if isinstance(arrays, tuple) and len(arrays) == 2:
|
|
49
|
+
# Q, B factors
|
|
50
|
+
Q, B = arrays
|
|
51
|
+
return Q[:, :k], B[:k, :]
|
|
52
|
+
else:
|
|
53
|
+
# just singular values
|
|
54
|
+
return arrays[:k]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def possibly_extend_randn(G, k, p, A):
|
|
58
|
+
# make sure we are using block of the right size by removing or adding
|
|
59
|
+
kG = G.shape[1]
|
|
60
|
+
if kG > k + p:
|
|
61
|
+
# have too many columns
|
|
62
|
+
G = G[:, : k + p]
|
|
63
|
+
elif kG < k + p:
|
|
64
|
+
# have too few columns
|
|
65
|
+
G_extra = randn((A.shape[1], k + p - kG), dtype=A.dtype)
|
|
66
|
+
G = np.concatenate((G, G_extra), axis=1)
|
|
67
|
+
return G
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def isstring(x, s):
|
|
71
|
+
if not isinstance(x, str):
|
|
72
|
+
return False
|
|
73
|
+
return x == s
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def rsvd_qb(A, k, q, p, state, AH=None):
|
|
77
|
+
if AH is None:
|
|
78
|
+
AH = dag(A)
|
|
79
|
+
|
|
80
|
+
# generate first block
|
|
81
|
+
if isstring(state, "begin-qb"):
|
|
82
|
+
G = randn((A.shape[1], k + p), dtype=A.dtype)
|
|
83
|
+
# block already supplied
|
|
84
|
+
elif len(state) == 1:
|
|
85
|
+
(G,) = state
|
|
86
|
+
# mid-way through adaptive algorithm in QB mode
|
|
87
|
+
if len(state) == 3:
|
|
88
|
+
Q, B, G = state
|
|
89
|
+
else:
|
|
90
|
+
Q = np.empty((A.shape[0], 0), dtype=A.dtype)
|
|
91
|
+
B = np.empty((0, A.shape[1]), dtype=A.dtype)
|
|
92
|
+
|
|
93
|
+
QH, BH = dag(Q), dag(B)
|
|
94
|
+
G = possibly_extend_randn(G, k, p, A)
|
|
95
|
+
|
|
96
|
+
Qi = orthog(dot(A, G) - dot(Q, dot(B, G)), lu=q > 0)
|
|
97
|
+
|
|
98
|
+
for i in range(1, q + 1):
|
|
99
|
+
Qi = orthog(dot(AH, Qi) - dot(BH, dot(QH, Qi)), lu=True)
|
|
100
|
+
Qi = orthog(dot(A, Qi) - dot(Q, dot(B, Qi)), lu=i != q)
|
|
101
|
+
|
|
102
|
+
Qi = orthog(Qi - dot(Q, dot(QH, Qi)))
|
|
103
|
+
Bi = dag(dot(AH, Qi)) - dot(dot(dag(Qi), Q), B)
|
|
104
|
+
|
|
105
|
+
if p > 0:
|
|
106
|
+
Qi, Bi = trim((Qi, Bi), k)
|
|
107
|
+
|
|
108
|
+
Q = np.concatenate((Q, Qi), axis=1)
|
|
109
|
+
B = np.concatenate((B, Bi), axis=0)
|
|
110
|
+
|
|
111
|
+
return Q, B, G
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def rsvd_core(A, k, compute_uv=True, q=2, p=0, state=None, AH=None):
|
|
115
|
+
"""Core R3SVD algorithm.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
A : linear operator, shape (m, n)
|
|
120
|
+
Operator to decompose, assumed m >= n.
|
|
121
|
+
k : int
|
|
122
|
+
Number of singular values to find.
|
|
123
|
+
compute_uv : bool, optional
|
|
124
|
+
Return the left and right singular vectors.
|
|
125
|
+
q : int, optional
|
|
126
|
+
Number of power iterations.
|
|
127
|
+
p : int, optional
|
|
128
|
+
Over sampling factor.
|
|
129
|
+
state : {None, array_like, (), (G0,), (U0, s0, VH0, G0)}, optional
|
|
130
|
+
Iterate based on these previous results:
|
|
131
|
+
|
|
132
|
+
- None: basic mode.
|
|
133
|
+
- array_like: use this as the initial subspace.
|
|
134
|
+
- 'begin-svd': begin block iterations, return U, s, VH, G
|
|
135
|
+
- (G0,) : begin block iterations with this subspace
|
|
136
|
+
- (U0, s0, VH0, G0): continue block iterations, return G
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
iterating = isinstance(state, (tuple, str))
|
|
140
|
+
maybe_project_left = maybe_project_right = identity
|
|
141
|
+
|
|
142
|
+
if AH is None:
|
|
143
|
+
AH = dag(A)
|
|
144
|
+
|
|
145
|
+
# generate first block
|
|
146
|
+
if state is None or isstring(state, "begin-svd"):
|
|
147
|
+
G = randn((A.shape[1], k + p), dtype=A.dtype)
|
|
148
|
+
# initial block supplied
|
|
149
|
+
elif hasattr(state, "shape"):
|
|
150
|
+
G = state
|
|
151
|
+
elif len(state) == 1:
|
|
152
|
+
(G,) = state
|
|
153
|
+
# mid-way through adaptive algorithm in SVD mode
|
|
154
|
+
elif len(state) == 4:
|
|
155
|
+
U0, s0, VH0, G = state
|
|
156
|
+
UH0, V0 = dag(U0), dag(VH0)
|
|
157
|
+
|
|
158
|
+
def maybe_project_left(X):
|
|
159
|
+
X -= dot(U0, dot(UH0, X))
|
|
160
|
+
return X
|
|
161
|
+
|
|
162
|
+
def maybe_project_right(X):
|
|
163
|
+
X -= dot(V0, dot(VH0, X))
|
|
164
|
+
return X
|
|
165
|
+
|
|
166
|
+
G = possibly_extend_randn(G, k, p, A)
|
|
167
|
+
G = maybe_project_right(G)
|
|
168
|
+
|
|
169
|
+
Q = dot(A, G)
|
|
170
|
+
Q = maybe_project_left(Q)
|
|
171
|
+
Q = orthog(Q, lu=q > 0)
|
|
172
|
+
|
|
173
|
+
# power iterations with stabilization
|
|
174
|
+
for i in range(1, q + 1):
|
|
175
|
+
Q = dot(AH, Q)
|
|
176
|
+
Q = maybe_project_right(Q)
|
|
177
|
+
Q = orthog(Q, lu=True)
|
|
178
|
+
|
|
179
|
+
Q = dot(A, Q)
|
|
180
|
+
Q = maybe_project_left(Q)
|
|
181
|
+
Q = orthog(Q, lu=i < q)
|
|
182
|
+
|
|
183
|
+
B = dag(dot(AH, Q))
|
|
184
|
+
UsVH = QB_to_svd(Q, B, compute_uv=compute_uv or iterating)
|
|
185
|
+
if p > 0:
|
|
186
|
+
UsVH = trim(UsVH, k)
|
|
187
|
+
|
|
188
|
+
if not iterating:
|
|
189
|
+
return UsVH
|
|
190
|
+
|
|
191
|
+
U, s, VH = UsVH
|
|
192
|
+
|
|
193
|
+
if isstring(state, "begin-svd") or len(state) == 1:
|
|
194
|
+
# first run -> don't need to project or concatenate anything
|
|
195
|
+
return U, s, VH, G
|
|
196
|
+
|
|
197
|
+
U = orthog(maybe_project_left(U))
|
|
198
|
+
VH = dag(orthog(maybe_project_right(dag(VH))))
|
|
199
|
+
|
|
200
|
+
U = np.concatenate((U0, U), axis=1)
|
|
201
|
+
s = np.concatenate((s0, s))
|
|
202
|
+
VH = np.concatenate((VH0, VH), axis=0)
|
|
203
|
+
|
|
204
|
+
return U, s, VH, G
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@njit
|
|
208
|
+
def is_sorted(x): # pragma: no cover
|
|
209
|
+
for i in range(x.size - 1):
|
|
210
|
+
if x[i + 1] < x[i]:
|
|
211
|
+
return False
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def gen_k_steps(start, incr=1.4):
|
|
216
|
+
yield start
|
|
217
|
+
step = start
|
|
218
|
+
while True:
|
|
219
|
+
yield step
|
|
220
|
+
step = round(incr * step)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def rsvd_iterate(
|
|
224
|
+
A,
|
|
225
|
+
eps,
|
|
226
|
+
compute_uv=True,
|
|
227
|
+
q=2,
|
|
228
|
+
p=0,
|
|
229
|
+
G0=None,
|
|
230
|
+
k_max=None,
|
|
231
|
+
k_start=2,
|
|
232
|
+
k_incr=1.4,
|
|
233
|
+
AH=None,
|
|
234
|
+
use_qb=20,
|
|
235
|
+
):
|
|
236
|
+
"""Handle rank-adaptively calling ``rsvd_core``."""
|
|
237
|
+
|
|
238
|
+
if AH is None:
|
|
239
|
+
AH = dag(A)
|
|
240
|
+
|
|
241
|
+
# perform first iteration and set initial rank
|
|
242
|
+
k_steps = gen_k_steps(k_start, k_incr)
|
|
243
|
+
rank = next(k_steps)
|
|
244
|
+
|
|
245
|
+
if use_qb:
|
|
246
|
+
Q, B, G = rsvd_qb(
|
|
247
|
+
A, rank, q=q, p=p, AH=AH, state="begin-qb" if G0 is None else (G0,)
|
|
248
|
+
)
|
|
249
|
+
U, s, VH = QB_to_svd(Q, B)
|
|
250
|
+
G -= dot(dag(VH), dot(VH, G))
|
|
251
|
+
else:
|
|
252
|
+
U, s, VH, G = rsvd_core(
|
|
253
|
+
A,
|
|
254
|
+
rank,
|
|
255
|
+
q=q,
|
|
256
|
+
p=p,
|
|
257
|
+
AH=AH,
|
|
258
|
+
state="begin-svd" if G0 is None else (G0,),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# perform randomized SVD in small blocks
|
|
262
|
+
while (s[-1] > eps * s[0]) and (rank < k_max):
|
|
263
|
+
# only step k as far as k_max
|
|
264
|
+
new_k = min(next(k_steps), k_max - rank)
|
|
265
|
+
rank += new_k
|
|
266
|
+
|
|
267
|
+
if (rank < use_qb) or (use_qb is True):
|
|
268
|
+
Q, B, G = rsvd_qb(A, new_k, q=q, p=p, state=(Q, B, G), AH=AH)
|
|
269
|
+
U, s, VH = QB_to_svd(Q, B)
|
|
270
|
+
G -= dot(dag(VH), dot(VH, G))
|
|
271
|
+
else:
|
|
272
|
+
# concatenate new U, s, VH orthogonal to current U, s, VH
|
|
273
|
+
U, s, VH, G = rsvd_core(
|
|
274
|
+
A, new_k, q=q, p=p, state=(U, s, VH, G), AH=AH
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# make sure singular values always sorted in decreasing order
|
|
278
|
+
if not is_sorted(s):
|
|
279
|
+
so = np.argsort(s)[::-1]
|
|
280
|
+
U, s, VH = U[:, so], s[so], VH[so, :]
|
|
281
|
+
|
|
282
|
+
return U, s, VH if compute_uv else s
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@njit
|
|
286
|
+
def count_svdvals_needed(s, eps): # pragma: no cover
|
|
287
|
+
n = s.size
|
|
288
|
+
thresh = eps * s[0]
|
|
289
|
+
for i in range(n - 1, 0, -1):
|
|
290
|
+
if s[i - 1] < thresh:
|
|
291
|
+
n -= 1
|
|
292
|
+
else:
|
|
293
|
+
break
|
|
294
|
+
return n
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def isdouble(dtype):
|
|
298
|
+
"""Check if ``dtype`` is double precision."""
|
|
299
|
+
return dtype in ("float64", "complex128")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def estimate_rank(
|
|
303
|
+
A,
|
|
304
|
+
eps,
|
|
305
|
+
k_max=None,
|
|
306
|
+
use_sli=True,
|
|
307
|
+
k_start=2,
|
|
308
|
+
k_incr=1.4,
|
|
309
|
+
q=0,
|
|
310
|
+
p=0,
|
|
311
|
+
get_vectors=False,
|
|
312
|
+
G0=None,
|
|
313
|
+
AH=None,
|
|
314
|
+
use_qb=20,
|
|
315
|
+
):
|
|
316
|
+
"""Estimate the rank of an linear operator. Uses a low quality random
|
|
317
|
+
SVD with a resolution of ~ 10.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
A : linear operator
|
|
322
|
+
The operator to find rank of.
|
|
323
|
+
eps : float
|
|
324
|
+
Find rank to this relative (compared to largest singular value)
|
|
325
|
+
precision.
|
|
326
|
+
k_max : int, optional
|
|
327
|
+
The maximum rank to find.
|
|
328
|
+
use_sli : bool, optional
|
|
329
|
+
Whether to use :func:`scipy.linalg.interpolative.estimate_rank` if
|
|
330
|
+
possible (double precision and no ``k_max`` set).
|
|
331
|
+
k_start : int, optional
|
|
332
|
+
Begin the adaptive SVD with a block of this size.
|
|
333
|
+
k_incr : float, optional
|
|
334
|
+
Adaptive rank increment factor. Increase the k-step (from k_start) by
|
|
335
|
+
this factor each time. Set to 1 to use a constant step.
|
|
336
|
+
q : int, optional
|
|
337
|
+
Number of power iterations.
|
|
338
|
+
get_vectors : bool, optional
|
|
339
|
+
Return the right singular vectors found in the pass.
|
|
340
|
+
G0 : , optional
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
rank : int
|
|
345
|
+
The rank.
|
|
346
|
+
VH : array
|
|
347
|
+
The (adjoint) right singular vectors if ``get_vectors=True``.
|
|
348
|
+
"""
|
|
349
|
+
if k_max is None:
|
|
350
|
+
k_max = min(A.shape)
|
|
351
|
+
if eps <= 0.0:
|
|
352
|
+
return k_max
|
|
353
|
+
|
|
354
|
+
use_sli = (
|
|
355
|
+
use_sli
|
|
356
|
+
and (k_max == min(A.shape))
|
|
357
|
+
and isdouble(A.dtype)
|
|
358
|
+
and not get_vectors
|
|
359
|
+
)
|
|
360
|
+
if use_sli:
|
|
361
|
+
return sla.interpolative.estimate_rank(A, eps)
|
|
362
|
+
|
|
363
|
+
if A.shape[0] < A.shape[1]:
|
|
364
|
+
A = A.T
|
|
365
|
+
if get_vectors:
|
|
366
|
+
raise ValueError
|
|
367
|
+
if AH is None:
|
|
368
|
+
AH = dag(A)
|
|
369
|
+
|
|
370
|
+
_, s, VH = rsvd_iterate(
|
|
371
|
+
A,
|
|
372
|
+
eps,
|
|
373
|
+
q=q,
|
|
374
|
+
p=p,
|
|
375
|
+
G0=G0,
|
|
376
|
+
AH=AH,
|
|
377
|
+
use_qb=use_qb,
|
|
378
|
+
k_start=k_start,
|
|
379
|
+
k_max=k_max,
|
|
380
|
+
k_incr=k_incr,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
rank = count_svdvals_needed(s, eps)
|
|
384
|
+
|
|
385
|
+
if get_vectors:
|
|
386
|
+
return rank, VH[:rank, :]
|
|
387
|
+
return rank
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def maybe_flip(UsV, flipped):
|
|
391
|
+
# if only singular values or only tranposing do nothing
|
|
392
|
+
if not (isinstance(UsV, tuple) and flipped):
|
|
393
|
+
return UsV
|
|
394
|
+
U, s, V = UsV
|
|
395
|
+
return V.T, s, U.T
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def rsvd(
|
|
399
|
+
A,
|
|
400
|
+
eps_or_k,
|
|
401
|
+
compute_uv=True,
|
|
402
|
+
mode="adapt+block",
|
|
403
|
+
use_qb=20,
|
|
404
|
+
q=2,
|
|
405
|
+
p=0,
|
|
406
|
+
k_max=None,
|
|
407
|
+
k_start=2,
|
|
408
|
+
k_incr=1.4,
|
|
409
|
+
G0=None,
|
|
410
|
+
AH=None,
|
|
411
|
+
):
|
|
412
|
+
"""Fast, randomized, iterative SVD. Adaptive variant of method due
|
|
413
|
+
originally to Halko. This scales as ``log(k)`` rather than ``k`` so can be
|
|
414
|
+
more efficient.
|
|
415
|
+
|
|
416
|
+
Parameters
|
|
417
|
+
----------
|
|
418
|
+
A : operator, shape (m, n)
|
|
419
|
+
The operator to decompose.
|
|
420
|
+
eps_or_k : float or int
|
|
421
|
+
Either the relative precision or the number of singular values to
|
|
422
|
+
target. If precision, this is relative to the largest singular value.
|
|
423
|
+
compute_uv : bool, optional
|
|
424
|
+
Whether to return the left and right singular vectors.
|
|
425
|
+
mode : {'adapt+block', 'adapt', 'block'}, optional
|
|
426
|
+
How to perform the randomized SVD. If ``eps_or_k`` is an integer then
|
|
427
|
+
this is implicitly 'block' and ignored. Else:
|
|
428
|
+
|
|
429
|
+
- 'adapt+block', perform an initial low quality pass to estimate
|
|
430
|
+
the rank of ``A``, then use the subspace and rank from that to
|
|
431
|
+
perform an accurate fully blocked RSVD.
|
|
432
|
+
- 'adapt', just perform the adaptive randomized SVD.
|
|
433
|
+
|
|
434
|
+
q : int, optional
|
|
435
|
+
The number of power iterations, increase for accuracy at the expense
|
|
436
|
+
of runtime.
|
|
437
|
+
p : int, optional
|
|
438
|
+
Oversampling factor. Perform projections with this many extra columns
|
|
439
|
+
and then throw then away.
|
|
440
|
+
k_max : int, optional
|
|
441
|
+
Maximum adaptive rank. Default: ``min(A.shape)``.
|
|
442
|
+
k_start : int, optional
|
|
443
|
+
Initial k when increasing rank adaptively.
|
|
444
|
+
k_incr : float, optional
|
|
445
|
+
Adaptive rank increment factor. Increase the k-step (from k_start) by
|
|
446
|
+
this factor each time. Set to 1 to use a constant step.
|
|
447
|
+
G0 : array_like, shape (n, k), optional
|
|
448
|
+
Initial subspace to start iterating on. If not given a random one will
|
|
449
|
+
be generated.
|
|
450
|
+
|
|
451
|
+
Returns
|
|
452
|
+
-------
|
|
453
|
+
U, array, shape (m, k)
|
|
454
|
+
Left singular vectors, if ``compute_uv=True``.
|
|
455
|
+
s, array, shape (k,)
|
|
456
|
+
Singular values.
|
|
457
|
+
V, array, shape (k, n)
|
|
458
|
+
Right singular vectors, if ``compute_uv=True``.
|
|
459
|
+
"""
|
|
460
|
+
|
|
461
|
+
flipped = A.shape[0] < A.shape[1]
|
|
462
|
+
if flipped:
|
|
463
|
+
A = A.T
|
|
464
|
+
|
|
465
|
+
# 'block' mode -> just perform single pass random SVD
|
|
466
|
+
if isinstance(eps_or_k, Integral):
|
|
467
|
+
UsV = rsvd_core(A, eps_or_k, q=q, p=p, state=G0, compute_uv=compute_uv)
|
|
468
|
+
return maybe_flip(UsV, flipped)
|
|
469
|
+
|
|
470
|
+
if k_max is None:
|
|
471
|
+
k_max = min(A.shape)
|
|
472
|
+
k_max = min(max(1, k_max), min(A.shape))
|
|
473
|
+
|
|
474
|
+
if AH is None:
|
|
475
|
+
AH = dag(A)
|
|
476
|
+
|
|
477
|
+
adaptive_opts = {
|
|
478
|
+
"k_start": k_start,
|
|
479
|
+
"k_max": k_max,
|
|
480
|
+
"k_incr": k_incr,
|
|
481
|
+
"use_qb": use_qb,
|
|
482
|
+
"AH": AH,
|
|
483
|
+
"G0": G0,
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
# 'adapt' mode -> rank adaptively perform SVD to low accuracy
|
|
487
|
+
if mode == "adapt":
|
|
488
|
+
UsV = rsvd_iterate(
|
|
489
|
+
A, eps_or_k, q=q, p=p, compute_uv=compute_uv, **adaptive_opts
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# 'adapt+block' mode -> use first pass to find rank, then use blocking mode
|
|
493
|
+
elif mode == "adapt+block":
|
|
494
|
+
# estimate both rank and get approximate spanning vectors
|
|
495
|
+
k, VH = estimate_rank(A, eps_or_k, get_vectors=True, **adaptive_opts)
|
|
496
|
+
|
|
497
|
+
# reuse vectors to effectively boost number of power iterations by one
|
|
498
|
+
UsV = rsvd_core(
|
|
499
|
+
A,
|
|
500
|
+
k,
|
|
501
|
+
q=max(q - 1, 0),
|
|
502
|
+
p=p,
|
|
503
|
+
AH=AH,
|
|
504
|
+
state=dag(VH),
|
|
505
|
+
compute_uv=compute_uv,
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
else:
|
|
509
|
+
raise ValueError(
|
|
510
|
+
"``mode`` must be one of {'adapt+block', 'adapt'} or"
|
|
511
|
+
" ``k`` should be a integer to use 'block' mode."
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
return maybe_flip(UsV, flipped)
|