capytaine 2.3.1__cp310-cp310-win_amd64.whl → 3.0.0a1__cp310-cp310-win_amd64.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.
- capytaine/__about__.py +7 -2
- capytaine/__init__.py +11 -15
- capytaine/bem/engines.py +234 -354
- capytaine/bem/problems_and_results.py +14 -13
- capytaine/bem/solver.py +204 -80
- capytaine/bodies/bodies.py +278 -869
- capytaine/bodies/dofs.py +136 -9
- capytaine/bodies/hydrostatics.py +540 -0
- capytaine/bodies/multibodies.py +216 -0
- capytaine/green_functions/{libs/Delhommeau_float32.cp310-win_amd64.dll.a → Delhommeau_float32.cp310-win_amd64.dll.a} +0 -0
- capytaine/green_functions/Delhommeau_float32.cp310-win_amd64.pyd +0 -0
- capytaine/green_functions/{libs/Delhommeau_float64.cp310-win_amd64.dll.a → Delhommeau_float64.cp310-win_amd64.dll.a} +0 -0
- capytaine/green_functions/Delhommeau_float64.cp310-win_amd64.pyd +0 -0
- capytaine/green_functions/abstract_green_function.py +2 -2
- capytaine/green_functions/delhommeau.py +31 -16
- capytaine/green_functions/hams.py +19 -13
- capytaine/io/legacy.py +3 -103
- capytaine/io/xarray.py +11 -6
- capytaine/meshes/__init__.py +2 -6
- capytaine/meshes/abstract_meshes.py +375 -0
- capytaine/meshes/clean.py +302 -0
- capytaine/meshes/clip.py +347 -0
- capytaine/meshes/export.py +89 -0
- capytaine/meshes/geometry.py +244 -394
- capytaine/meshes/io.py +433 -0
- capytaine/meshes/meshes.py +617 -681
- capytaine/meshes/predefined/cylinders.py +22 -56
- capytaine/meshes/predefined/rectangles.py +26 -85
- capytaine/meshes/predefined/spheres.py +4 -11
- capytaine/meshes/quality.py +118 -407
- capytaine/meshes/surface_integrals.py +48 -29
- capytaine/meshes/symmetric_meshes.py +641 -0
- capytaine/meshes/visualization.py +353 -0
- capytaine/post_pro/free_surfaces.py +1 -4
- capytaine/post_pro/kochin.py +10 -10
- capytaine/tools/block_circulant_matrices.py +275 -0
- capytaine/tools/lists_of_points.py +2 -2
- capytaine/tools/memory_monitor.py +45 -0
- capytaine/tools/symbolic_multiplication.py +13 -1
- capytaine/tools/timer.py +58 -34
- capytaine-3.0.0a1.dist-info/DELVEWHEEL +2 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/METADATA +7 -2
- capytaine-3.0.0a1.dist-info/RECORD +70 -0
- capytaine/bodies/predefined/__init__.py +0 -6
- capytaine/bodies/predefined/cylinders.py +0 -151
- capytaine/bodies/predefined/rectangles.py +0 -111
- capytaine/bodies/predefined/spheres.py +0 -70
- capytaine/green_functions/FinGreen3D/.gitignore +0 -1
- capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +0 -3589
- capytaine/green_functions/FinGreen3D/LICENSE +0 -165
- capytaine/green_functions/FinGreen3D/Makefile +0 -16
- capytaine/green_functions/FinGreen3D/README.md +0 -24
- capytaine/green_functions/FinGreen3D/test_program.f90 +0 -39
- capytaine/green_functions/LiangWuNoblesse/.gitignore +0 -1
- capytaine/green_functions/LiangWuNoblesse/LICENSE +0 -504
- capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +0 -751
- capytaine/green_functions/LiangWuNoblesse/Makefile +0 -16
- capytaine/green_functions/LiangWuNoblesse/README.md +0 -2
- capytaine/green_functions/LiangWuNoblesse/test_program.f90 +0 -28
- capytaine/green_functions/libs/Delhommeau_float32.cp310-win_amd64.pyd +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cp310-win_amd64.pyd +0 -0
- capytaine/green_functions/libs/__init__.py +0 -0
- capytaine/io/mesh_loaders.py +0 -1086
- capytaine/io/mesh_writers.py +0 -692
- capytaine/io/meshio.py +0 -38
- capytaine/matrices/__init__.py +0 -16
- capytaine/matrices/block.py +0 -592
- capytaine/matrices/block_toeplitz.py +0 -325
- capytaine/matrices/builders.py +0 -89
- capytaine/matrices/linear_solvers.py +0 -232
- capytaine/matrices/low_rank.py +0 -395
- capytaine/meshes/clipper.py +0 -465
- capytaine/meshes/collections.py +0 -342
- capytaine/meshes/mesh_like_protocol.py +0 -37
- capytaine/meshes/properties.py +0 -276
- capytaine/meshes/quadratures.py +0 -80
- capytaine/meshes/symmetric.py +0 -462
- capytaine/tools/lru_cache.py +0 -49
- capytaine/ui/vtk/__init__.py +0 -3
- capytaine/ui/vtk/animation.py +0 -329
- capytaine/ui/vtk/body_viewer.py +0 -28
- capytaine/ui/vtk/helpers.py +0 -82
- capytaine/ui/vtk/mesh_viewer.py +0 -461
- capytaine-2.3.1.dist-info/DELVEWHEEL +0 -2
- capytaine-2.3.1.dist-info/RECORD +0 -97
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/LICENSE +0 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/WHEEL +0 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/entry_points.txt +0 -0
capytaine/matrices/low_rank.py
DELETED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
"""This module implements a class to describe a low-rank matrix as the tensor product of two smaller matrices.
|
|
2
|
-
In particular, an implementation of the Adaptive Cross Approximation is used to build such a matrix.
|
|
3
|
-
|
|
4
|
-
It takes inspiration from the following works:
|
|
5
|
-
|
|
6
|
-
* `openHmx module from Gypsilab by Matthieu Aussal (GPL licensed) <https://github.com/matthieuaussal/gypsilab>`_
|
|
7
|
-
* `HierarchicalMatrices by Markus Neumann (GPL licensed) <https://github.com/maekke97/HierarchicalMatrices>`_
|
|
8
|
-
"""
|
|
9
|
-
# Copyright (C) 2017-2021 Matthieu Ancellin
|
|
10
|
-
# See LICENSE file at <https://github.com/mancellin/capytaine>
|
|
11
|
-
|
|
12
|
-
import logging
|
|
13
|
-
|
|
14
|
-
import numpy as np
|
|
15
|
-
|
|
16
|
-
LOG = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class NoConvergenceOfACA(Exception):
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class LowRankMatrix:
|
|
24
|
-
"""Matrix defined as the tensor product of two small matrices.
|
|
25
|
-
|
|
26
|
-
Parameters
|
|
27
|
-
----------
|
|
28
|
-
left_matrix: numpy.array
|
|
29
|
-
Matrix of shape (nb_cols, rank).
|
|
30
|
-
right_matrix: numpy.array
|
|
31
|
-
Matrix of shape (rank, nb_rows).
|
|
32
|
-
|
|
33
|
-
Attributes
|
|
34
|
-
----------
|
|
35
|
-
shape: Tuple[int, int]
|
|
36
|
-
The shape of the full matrix.
|
|
37
|
-
rank: int
|
|
38
|
-
The rank of the full matrix.
|
|
39
|
-
dtype: numpy.dtype
|
|
40
|
-
Type of data in the matrix.
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
ndim = 2
|
|
44
|
-
|
|
45
|
-
##############
|
|
46
|
-
# Creation #
|
|
47
|
-
##############
|
|
48
|
-
|
|
49
|
-
def __init__(self, left_matrix, right_matrix):
|
|
50
|
-
self.left_matrix = left_matrix
|
|
51
|
-
self.right_matrix = right_matrix
|
|
52
|
-
self.shape = left_matrix.shape[0], right_matrix.shape[1]
|
|
53
|
-
assert left_matrix.shape[1] == right_matrix.shape[0], "Sizes of the left and right matrices do not match."
|
|
54
|
-
self.rank = left_matrix.shape[1] # == right_matrix.shape[0]
|
|
55
|
-
assert left_matrix.dtype == right_matrix.dtype, "Left and right matrices should have the same type of data."
|
|
56
|
-
self.dtype = left_matrix.dtype
|
|
57
|
-
|
|
58
|
-
@classmethod
|
|
59
|
-
def from_full_matrix_with_SVD(cls, full_matrix, max_rank):
|
|
60
|
-
"""Create a low rank matrix from a full matrix using Singular Value Decomposition.
|
|
61
|
-
|
|
62
|
-
Parameters
|
|
63
|
-
----------
|
|
64
|
-
full_matrix: numpy array
|
|
65
|
-
The matrix that will be approximated.
|
|
66
|
-
max_rank: int
|
|
67
|
-
Rank of the low-rank approximation.
|
|
68
|
-
|
|
69
|
-
Returns
|
|
70
|
-
-------
|
|
71
|
-
LowRankMatrix
|
|
72
|
-
"""
|
|
73
|
-
u, s, v = np.linalg.svd(full_matrix)
|
|
74
|
-
left_matrix = u[:, 0:max_rank] @ np.diag(s[0:max_rank])
|
|
75
|
-
right_matrix = v[0:max_rank, :]
|
|
76
|
-
return cls(left_matrix, right_matrix)
|
|
77
|
-
|
|
78
|
-
@classmethod
|
|
79
|
-
def from_full_matrix_with_ACA(cls, full_matrix, max_rank=None, tol=0.0):
|
|
80
|
-
"""Create a low rank matrix from a full matrix using Adaptive Cross Approximation.
|
|
81
|
-
The user should provide either the `max_rank` optional argument or the `tol` optional argument.
|
|
82
|
-
|
|
83
|
-
Parameters
|
|
84
|
-
----------
|
|
85
|
-
full_matrix: numpy.array
|
|
86
|
-
The matrix that will be approximated.
|
|
87
|
-
max_rank: int, optional
|
|
88
|
-
The maximum rank allowed for the output low rank matrix.
|
|
89
|
-
The default value is half the size of the full matrix, that is no gain in storage space.
|
|
90
|
-
tol: float, optional
|
|
91
|
-
The tolerance on the relative error (default: 0).
|
|
92
|
-
If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
|
|
93
|
-
If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
|
|
94
|
-
|
|
95
|
-
Returns
|
|
96
|
-
-------
|
|
97
|
-
LowRankMatrix
|
|
98
|
-
"""
|
|
99
|
-
def get_row(i):
|
|
100
|
-
return full_matrix[i, :]
|
|
101
|
-
|
|
102
|
-
def get_col(j):
|
|
103
|
-
return full_matrix[:, j]
|
|
104
|
-
|
|
105
|
-
return cls.from_rows_and_cols_functions_with_ACA(
|
|
106
|
-
get_row, get_col, full_matrix.shape[0], full_matrix.shape[1],
|
|
107
|
-
max_rank=max_rank, tol=tol, dtype=full_matrix.dtype
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
@classmethod
|
|
111
|
-
def from_function_with_ACA(cls, func, nb_rows, nb_cols, max_rank=None, tol=0.0, dtype=np.float64):
|
|
112
|
-
"""Create a low rank matrix from a function using Adaptive Cross Approximation.
|
|
113
|
-
The user should provide either the `max_rank` optional argument or the `tol` optional argument.
|
|
114
|
-
|
|
115
|
-
Parameters
|
|
116
|
-
----------
|
|
117
|
-
func: Function
|
|
118
|
-
Function such that `func(i, j)` returns the value of the (i, j) entry of the full matrix.
|
|
119
|
-
nb_rows: int
|
|
120
|
-
Number of rows in the full matrix.
|
|
121
|
-
nb_cols: int
|
|
122
|
-
Number of cols in the full matrix.
|
|
123
|
-
max_rank: int, optional
|
|
124
|
-
The maximum rank allowed for the output low rank matrix.
|
|
125
|
-
The default value is half the size of the full matrix, that is no gain in storage space.
|
|
126
|
-
tol: float, optional
|
|
127
|
-
The tolerance on the relative error (default: 0).
|
|
128
|
-
If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
|
|
129
|
-
If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
|
|
130
|
-
dtype: numpy.dtype, optional
|
|
131
|
-
Type of the data returned by the function.
|
|
132
|
-
|
|
133
|
-
Returns
|
|
134
|
-
-------
|
|
135
|
-
LowRankMatrix
|
|
136
|
-
"""
|
|
137
|
-
def get_row(i):
|
|
138
|
-
return np.asarray([func(i, j) for j in range(nb_cols)])
|
|
139
|
-
|
|
140
|
-
def get_col(j):
|
|
141
|
-
return np.asarray([func(i, j) for i in range(nb_rows)])
|
|
142
|
-
|
|
143
|
-
return cls.from_rows_and_cols_functions_with_ACA(
|
|
144
|
-
get_row, get_col, nb_rows, nb_cols,
|
|
145
|
-
max_rank=max_rank, tol=tol, dtype=dtype
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
@classmethod
|
|
149
|
-
def from_rows_and_cols_functions_with_ACA(cls, get_row_func, get_col_func, nb_rows, nb_cols, max_rank=None, tol=0.0, dtype=np.float64):
|
|
150
|
-
"""Create a low rank matrix from functions using Adaptive Cross Approximation.
|
|
151
|
-
The user should provide either the `max_rank` optional argument or the `tol` optional argument.
|
|
152
|
-
|
|
153
|
-
Parameters
|
|
154
|
-
----------
|
|
155
|
-
get_row_func: Function
|
|
156
|
-
Function such that `get_row_func(i)` returns the `i`-th row of the full matrix.
|
|
157
|
-
get_col_func: Function
|
|
158
|
-
Function such that `get_col_func(j)` returns the `j`-th column of the full matrix.
|
|
159
|
-
nb_rows: int
|
|
160
|
-
Number of rows in the full matrix.
|
|
161
|
-
nb_cols: int
|
|
162
|
-
Number of columns in the full matrix.
|
|
163
|
-
max_rank: int, optional
|
|
164
|
-
The maximum rank allowed for the output low rank matrix.
|
|
165
|
-
The default value is half the size of the full matrix, that is no gain in storage space.
|
|
166
|
-
tol: float, optional
|
|
167
|
-
The tolerance on the relative error (default: 0).
|
|
168
|
-
If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
|
|
169
|
-
If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
|
|
170
|
-
dtype: numpy.dtype, optional
|
|
171
|
-
The type of data in the low rank matrix (default: float64).
|
|
172
|
-
|
|
173
|
-
Returns
|
|
174
|
-
-------
|
|
175
|
-
LowRankMatrix
|
|
176
|
-
"""
|
|
177
|
-
# Just some wrapping and unwrapping to use the multi-ACA below.
|
|
178
|
-
def get_row(i):
|
|
179
|
-
return [get_row_func(i)]
|
|
180
|
-
|
|
181
|
-
def get_col(j):
|
|
182
|
-
return [get_col_func(j)]
|
|
183
|
-
|
|
184
|
-
return cls.from_rows_and_cols_functions_with_multi_ACA(
|
|
185
|
-
get_row, get_col, nb_rows, nb_cols,
|
|
186
|
-
nb_matrices=1, id_main=0,
|
|
187
|
-
max_rank=max_rank, tol=tol, dtype=dtype
|
|
188
|
-
)[0]
|
|
189
|
-
|
|
190
|
-
@classmethod
|
|
191
|
-
def from_rows_and_cols_functions_with_multi_ACA(cls, get_row, get_col, nb_rows, nb_cols,
|
|
192
|
-
nb_matrices=1, id_main=0,
|
|
193
|
-
max_rank=None, tol=0.0, dtype=np.float64):
|
|
194
|
-
"""Create several low rank matrices while running an Adaptive Cross Approximation.
|
|
195
|
-
The user should provide either the `max_rank` optional argument or the `tol` optional argument.
|
|
196
|
-
|
|
197
|
-
In Capytaine, the routines evaluating the influence matrices return the values of two matrices
|
|
198
|
-
(S and V) at once, because there is a lot of common computations in their evaluation.
|
|
199
|
-
The present function can be used to build the ACA of one of them while getting at the same time
|
|
200
|
-
an approximation of the other for free.
|
|
201
|
-
|
|
202
|
-
Freely adapted from the routine hmxACA.m from Gypsilab.
|
|
203
|
-
|
|
204
|
-
Parameters
|
|
205
|
-
----------
|
|
206
|
-
get_row: Function
|
|
207
|
-
Function such that `get_row(i)` returns the `i`-th row of all the full matrices.
|
|
208
|
-
get_col: Function
|
|
209
|
-
Function such that `get_col(j)` returns the `j`-th column of all the full matrices.
|
|
210
|
-
nb_rows: int
|
|
211
|
-
Number of rows in all full matrices.
|
|
212
|
-
nb_cols: int
|
|
213
|
-
Number of columns in all full matrices.
|
|
214
|
-
nb_matrices: int, optional
|
|
215
|
-
The number of matrices approximated at the same time.
|
|
216
|
-
id_main: int, optional
|
|
217
|
-
The matrix used primarily in the ACA.
|
|
218
|
-
max_rank: int, optional
|
|
219
|
-
The maximum rank allowed for both output low rank matrices.
|
|
220
|
-
The default value is half the size of the full matrix, that is no gain in storage space.
|
|
221
|
-
tol: float, optional
|
|
222
|
-
The tolerance on the relative error (default: 0).
|
|
223
|
-
If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
|
|
224
|
-
If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
|
|
225
|
-
dtype: numpy.dtype, optional
|
|
226
|
-
The type of data in both low rank matrices (default: float64).
|
|
227
|
-
|
|
228
|
-
Returns
|
|
229
|
-
-------
|
|
230
|
-
List[LowRankMatrix]
|
|
231
|
-
"""
|
|
232
|
-
if max_rank is None and tol <= 0.0:
|
|
233
|
-
LOG.warning("No stopping criterion for the Adaptive Cross Approximation."
|
|
234
|
-
"Please provide either max_rank or tol.")
|
|
235
|
-
|
|
236
|
-
if max_rank is None:
|
|
237
|
-
max_rank = min(nb_rows, nb_cols)//2
|
|
238
|
-
|
|
239
|
-
# Initialize work matrices
|
|
240
|
-
left = np.zeros((nb_matrices, nb_rows, max_rank), dtype=dtype)
|
|
241
|
-
right = np.zeros((nb_matrices, max_rank, nb_cols), dtype=dtype)
|
|
242
|
-
|
|
243
|
-
squared_norm_of_low_rank_approximation = 0.0
|
|
244
|
-
|
|
245
|
-
# List of indices of unused entries in the full matrix
|
|
246
|
-
available_rows = list(range(nb_rows))
|
|
247
|
-
available_cols = list(range(nb_cols))
|
|
248
|
-
|
|
249
|
-
for l in range(max_rank):
|
|
250
|
-
# Pick a row
|
|
251
|
-
if l == 0:
|
|
252
|
-
relative_i = 0
|
|
253
|
-
# Could also have been chosen at random.
|
|
254
|
-
else:
|
|
255
|
-
relative_i = int(np.argmax(np.abs(left[id_main, available_rows, l-1])))
|
|
256
|
-
# The "int" is useless except for my type checker...
|
|
257
|
-
|
|
258
|
-
i = available_rows.pop(relative_i)
|
|
259
|
-
# relative_i is the index of the row in the list of remaining rows,
|
|
260
|
-
# e.g. if available_rows = [2, 7, 8] and relative_i = 2, the chosen
|
|
261
|
-
# row has index i = 8 in the full matrix.
|
|
262
|
-
|
|
263
|
-
# Add the chosen row to the approximation of all the matrices
|
|
264
|
-
one_row = get_row(i)
|
|
265
|
-
for id_mat in range(nb_matrices):
|
|
266
|
-
right[id_mat, l, :] = one_row[id_mat] - left[id_mat, i, :l] @ right[id_mat, :l, :]
|
|
267
|
-
|
|
268
|
-
# Pick a column
|
|
269
|
-
relative_j = int(np.argmax(np.abs(right[id_main, l, available_cols])))
|
|
270
|
-
j = available_cols.pop(relative_j)
|
|
271
|
-
# Similar to i above.
|
|
272
|
-
|
|
273
|
-
one_col = get_col(j)
|
|
274
|
-
|
|
275
|
-
# Add the column to the approximations of all matrices.
|
|
276
|
-
for id_mat in range(nb_matrices):
|
|
277
|
-
new_col = one_col[id_mat] - left[id_mat, :, :l] @ right[id_mat, :l, j]
|
|
278
|
-
pivot = new_col[i]
|
|
279
|
-
if abs(pivot) < 1e-12:
|
|
280
|
-
pivot = 1e-12
|
|
281
|
-
left[id_mat, :, l] = new_col/pivot
|
|
282
|
-
|
|
283
|
-
# Update norm of the full matrix
|
|
284
|
-
squared_norm_of_increment = np.real(
|
|
285
|
-
(np.conj(left[id_main, :, l]) @ left[id_main, :, l]) *
|
|
286
|
-
(np.conj(right[id_main, l, :]) @ right[id_main, l, :])
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
crossed_terms = (
|
|
290
|
-
(np.conj(left[id_main, :, l].T) @ left[id_main, :, :l]) @
|
|
291
|
-
(np.conj(right[id_main, l, :]) @ right[id_main, :l, :].T)
|
|
292
|
-
)
|
|
293
|
-
squared_norm_of_low_rank_approximation += squared_norm_of_increment + 2*np.real(crossed_terms)
|
|
294
|
-
|
|
295
|
-
if squared_norm_of_increment <= tol**2*squared_norm_of_low_rank_approximation:
|
|
296
|
-
LOG.debug(f"The ACA has found an approximation of rank {l}.")
|
|
297
|
-
|
|
298
|
-
if l == 0: # Edge case of the zero matrix, ...
|
|
299
|
-
l = 1 # ... we actually return a "rank 1" LowRankMatrix with coefficients equal to zero.
|
|
300
|
-
|
|
301
|
-
return [LowRankMatrix(left[id_mat, :, :l], right[id_mat, :l, :]) for id_mat in range(nb_matrices)]
|
|
302
|
-
|
|
303
|
-
if tol > 0:
|
|
304
|
-
LOG.warning(f"The ACA was unable to find a low rank approximation "
|
|
305
|
-
f"of rank lower or equal to {max_rank} with tolerance {tol:.2e} (latest iteration: "
|
|
306
|
-
f"{np.sqrt(squared_norm_of_increment):.2e}/{np.sqrt(squared_norm_of_low_rank_approximation):.2e}).")
|
|
307
|
-
raise NoConvergenceOfACA()
|
|
308
|
-
|
|
309
|
-
return [LowRankMatrix(left[id_mat, :, :], right[id_mat, :, :]) for id_mat in range(nb_matrices)]
|
|
310
|
-
|
|
311
|
-
####################
|
|
312
|
-
# Representation #
|
|
313
|
-
####################
|
|
314
|
-
|
|
315
|
-
def full_matrix(self, dtype=None):
|
|
316
|
-
if dtype is not None:
|
|
317
|
-
return (self.left_matrix @ self.right_matrix).astype(dtype)
|
|
318
|
-
else:
|
|
319
|
-
return self.left_matrix @ self.right_matrix
|
|
320
|
-
|
|
321
|
-
def __array__(self, dtype=None, copy=True):
|
|
322
|
-
if not copy:
|
|
323
|
-
raise ValueError("Making an ndarray out of a BlockMatrix requires copy")
|
|
324
|
-
return self.full_matrix(dtype=dtype)
|
|
325
|
-
|
|
326
|
-
@property
|
|
327
|
-
def stored_data_size(self):
|
|
328
|
-
return np.prod(self.left_matrix.shape) + np.prod(self.right_matrix.shape)
|
|
329
|
-
|
|
330
|
-
@property
|
|
331
|
-
def density(self):
|
|
332
|
-
return self.stored_data_size/np.prod(self.shape)
|
|
333
|
-
|
|
334
|
-
@property
|
|
335
|
-
def sparcity(self):
|
|
336
|
-
return 1 - self.density
|
|
337
|
-
|
|
338
|
-
####################
|
|
339
|
-
# Transformation #
|
|
340
|
-
####################
|
|
341
|
-
|
|
342
|
-
def recompress(self, tol=None, new_rank=None):
|
|
343
|
-
"""Recompress the matrix to a lower rank. Based on the routine hmxQRSVD.m from Gipsylab."""
|
|
344
|
-
if new_rank is None:
|
|
345
|
-
new_rank = self.rank
|
|
346
|
-
QA, RA = np.linalg.qr(self.left_matrix)
|
|
347
|
-
QB, RB = np.linalg.qr(self.right_matrix.T)
|
|
348
|
-
U, S, V = np.linalg.svd(RA @ RB.T)
|
|
349
|
-
if tol is not None:
|
|
350
|
-
new_rank = np.count_nonzero(S/S[0] >= tol)
|
|
351
|
-
A = QA @ (U[:, :new_rank] @ np.diag(S[:new_rank]))
|
|
352
|
-
B = QB @ V[:, :new_rank]
|
|
353
|
-
return LowRankMatrix(A, B.T)
|
|
354
|
-
|
|
355
|
-
def __add__(self, other):
|
|
356
|
-
if isinstance(other, LowRankMatrix):
|
|
357
|
-
new_left = np.concatenate([self.left_matrix, other.left_matrix], axis=1)
|
|
358
|
-
new_right = np.concatenate([self.right_matrix, other.right_matrix], axis=0)
|
|
359
|
-
return LowRankMatrix(new_left, new_right).recompress(new_rank=min(self.rank, other.rank))
|
|
360
|
-
else:
|
|
361
|
-
return NotImplemented
|
|
362
|
-
|
|
363
|
-
def __neg__(self):
|
|
364
|
-
return LowRankMatrix(-self.left_matrix, self.right_matrix)
|
|
365
|
-
|
|
366
|
-
def __sub__(self, other):
|
|
367
|
-
return self + (-other)
|
|
368
|
-
|
|
369
|
-
def __truediv__(self, other):
|
|
370
|
-
from numbers import Number
|
|
371
|
-
if isinstance(other, Number):
|
|
372
|
-
return LowRankMatrix(self.left_matrix, self.right_matrix/other)
|
|
373
|
-
else:
|
|
374
|
-
return NotImplemented
|
|
375
|
-
|
|
376
|
-
def __matmul__(self, other):
|
|
377
|
-
if isinstance(other, np.ndarray) and len(other.shape) == 1:
|
|
378
|
-
return self._mul_with_vector(other)
|
|
379
|
-
else:
|
|
380
|
-
return NotImplemented
|
|
381
|
-
|
|
382
|
-
def _mul_with_vector(self, other):
|
|
383
|
-
return self.left_matrix @ (self.right_matrix @ other)
|
|
384
|
-
|
|
385
|
-
def __rmatmul__(self, other):
|
|
386
|
-
if isinstance(other, np.ndarray) and len(other.shape) == 1:
|
|
387
|
-
return self._rmul_with_vector(other)
|
|
388
|
-
else:
|
|
389
|
-
return NotImplemented
|
|
390
|
-
|
|
391
|
-
def _rmul_with_vector(self, other):
|
|
392
|
-
return (other @ self.left_matrix) @ self.right_matrix
|
|
393
|
-
|
|
394
|
-
def astype(self, dtype):
|
|
395
|
-
return LowRankMatrix(self.left_matrix.astype(dtype), self.right_matrix.astype(dtype))
|