Skip to content

Commit e02153c

Browse files
sichinagamtezzele
authored andcommitted
Initial commit
1 parent b17dc93 commit e02153c

File tree

4 files changed

+140
-21
lines changed

4 files changed

+140
-21
lines changed

pydmd/preprocessing/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44

55
from .hankel import hankel_preprocessing
66
from .pre_post_processing import PrePostProcessingDMD
7+
from .randomized import randomized_preprocessing
78
from .svd_projection import svd_projection_preprocessing
89
from .zero_mean import zero_mean_preprocessing

pydmd/preprocessing/randomized.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
Randomized pre-processing.
3+
"""
4+
5+
from functools import partial
6+
from typing import Dict, Union
7+
8+
import numpy as np
9+
10+
from pydmd.dmdbase import DMDBase
11+
from pydmd.preprocessing import PrePostProcessingDMD
12+
from pydmd.utils import compute_rqb
13+
14+
svd_rank_type = Union[int, float]
15+
seed_type = Union[None, int]
16+
17+
def randomized_preprocessing(
18+
dmd: DMDBase,
19+
svd_rank: svd_rank_type,
20+
oversampling: int,
21+
power_iters: int,
22+
test_matrix: np.ndarray = None,
23+
seed: seed_type = None,
24+
):
25+
"""
26+
Randomized QB pre-processing.
27+
28+
:param dmd: DMD instance to be wrapped.
29+
"""
30+
return PrePostProcessingDMD(
31+
dmd,
32+
partial(
33+
_rand_preprocessing,
34+
svd_rank=svd_rank,
35+
oversampling=oversampling,
36+
power_iters=power_iters,
37+
test_matrix=test_matrix,
38+
seed=seed,
39+
),
40+
_rand_postprocessing,
41+
)
42+
43+
def _rand_preprocessing(
44+
state: Dict,
45+
X: np.ndarray,
46+
svd_rank: svd_rank_type,
47+
oversampling: int,
48+
power_iters: int,
49+
test_matrix: np.ndarray,
50+
seed: seed_type,
51+
**kwargs
52+
):
53+
Q = compute_rqb(
54+
X, svd_rank, oversampling, power_iters, test_matrix, seed
55+
)[0]
56+
state["compression_matrix"] = Q.conj().T
57+
58+
return (state["compression_matrix"].dot(X),) + tuple(kwargs.values())
59+
60+
61+
def _rand_postprocessing(state: Dict, X: np.ndarray) -> np.ndarray:
62+
return state["compression_matrix"].conj().T.dot(X)

pydmd/rdmd.py

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import numpy as np
1111

1212
from .cdmd import CDMD
13-
from .utils import compute_rank
13+
from .utils import compute_rank, compute_rqb
1414

1515

1616
class RDMD(CDMD):
@@ -21,6 +21,9 @@ class RDMD(CDMD):
2121
the Randomized QB Decomposition. If not provided, the `svd_rank` and
2222
`oversampling` parameters will be used to compute the random matrix.
2323
:type test_matrix: numpy.ndarray
24+
:param seed: Seed used to initialize the random generator when computing
25+
random test matrices.
26+
:type seed: int
2427
:param oversampling: Number of additional samples (beyond the desired rank)
2528
to use when computing the random test matrix. Note that values {5,10}
2629
tend to be sufficient.
@@ -34,6 +37,7 @@ class RDMD(CDMD):
3437
def __init__(
3538
self,
3639
test_matrix=None,
40+
seed=None,
3741
oversampling=10,
3842
power_iters=2,
3943
svd_rank=0,
@@ -58,6 +62,7 @@ def __init__(
5862
self._oversampling = oversampling
5963
self._power_iters = power_iters
6064
self._test_matrix = test_matrix
65+
self._seed = seed
6166

6267
def _compress_snapshots(self):
6368
"""
@@ -68,28 +73,39 @@ def _compress_snapshots(self):
6873
:return: the compressed snapshots
6974
:rtype: numpy.ndarray
7075
"""
71-
# Define the random test matrix if not provided.
72-
if self._test_matrix is None:
73-
m = self.snapshots.shape[-1]
74-
r = compute_rank(self.snapshots, self._svd_rank)
75-
self._test_matrix = np.random.randn(m, r + self._oversampling)
76+
Q, B, Omega = compute_rqb(
77+
self.snapshots,
78+
self._svd_rank,
79+
self._oversampling,
80+
self._power_iters,
81+
self._test_matrix,
82+
self._seed,
83+
)
84+
self._compression_matrix = Q.conj().T
85+
self._test_matrix = Omega
7686

77-
# Compute sampling matrix.
78-
Y = self.snapshots.dot(self._test_matrix)
87+
# # Define the random test matrix if not provided.
88+
# if self._test_matrix is None:
89+
# m = self.snapshots.shape[-1]
90+
# r = compute_rank(self.snapshots, self._svd_rank)
91+
# self._test_matrix = np.random.randn(m, r + self._oversampling)
7992

80-
# Perform power iterations.
81-
for _ in range(self._power_iters):
82-
Q = np.linalg.qr(Y)[0]
83-
Z = np.linalg.qr(self.snapshots.conj().T.dot(Q))[0]
84-
Y = self.snapshots.dot(Z)
93+
# # Compute sampling matrix.
94+
# Y = self.snapshots.dot(self._test_matrix)
8595

86-
# Orthonormalize the sampling matrix.
87-
Q = np.linalg.qr(Y)[0]
96+
# # Perform power iterations.
97+
# for _ in range(self._power_iters):
98+
# Q = np.linalg.qr(Y)[0]
99+
# Z = np.linalg.qr(self.snapshots.conj().T.dot(Q))[0]
100+
# Y = self.snapshots.dot(Z)
88101

89-
# Project the snapshot matrix onto the smaller space.
90-
B = Q.conj().T.dot(self.snapshots)
102+
# # Orthonormalize the sampling matrix.
103+
# Q = np.linalg.qr(Y)[0]
91104

92-
# Save the compression matrix.
93-
self._compression_matrix = Q.conj().T
105+
# # Project the snapshot matrix onto the smaller space.
106+
# B = Q.conj().T.dot(self.snapshots)
107+
108+
# # Save the compression matrix.
109+
# self._compression_matrix = Q.conj().T
94110

95111
return B

pydmd/utils.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

33
import warnings
44
from numbers import Number
5-
from typing import NamedTuple
5+
from typing import NamedTuple, Union
66
from collections import namedtuple
77
import numpy as np
88
from numpy.lib.stride_tricks import sliding_window_view
99

1010
# Named tuples used in functions.
1111
# compute_svd uses "SVD",
12-
# compute_tlsq uses "TLSQ".
12+
# compute_tlsq uses "TLSQ",
13+
# compute_rqb uses "RQB".
1314
SVD = namedtuple("SVD", ["U", "s", "V"])
1415
TLSQ = namedtuple("TLSQ", ["X_denoised", "Y_denoised"])
16+
RQB = namedtuple("RQB", ["Q", "B", "Omega"])
1517

1618

1719
def _svht(sigma_svd: np.ndarray, rows: int, cols: int) -> int:
@@ -189,6 +191,44 @@ def compute_svd(
189191
return SVD(U, s, V)
190192

191193

194+
def compute_rqb(
195+
X: np.ndarray,
196+
svd_rank: Number,
197+
oversampling: int = 10,
198+
power_iters: int = 2,
199+
Omega: np.ndarray = None,
200+
seed: Union[None, int] = None,
201+
) -> NamedTuple(
202+
"RQB", [("Q", np.ndarray), ("B", np.ndarray), ("Omega", np.ndarray)]
203+
):
204+
if X.ndim != 2:
205+
raise ValueError("Please ensure that input data is a 2D array.")
206+
207+
# Define the random test matrix if not provided.
208+
if Omega is None:
209+
m = X.shape[-1]
210+
r = compute_rank(X, svd_rank)
211+
rng = np.random.default_rng(seed)
212+
Omega = rng.standard_normal((m, r + oversampling))
213+
214+
# Compute sampling matrix.
215+
Y = X.dot(Omega)
216+
217+
# Perform power iterations.
218+
for _ in range(power_iters):
219+
Q = np.linalg.qr(Y)[0]
220+
Z = np.linalg.qr(X.conj().T.dot(Q))[0]
221+
Y = X.dot(Z)
222+
223+
# Orthonormalize the sampling matrix.
224+
Q = np.linalg.qr(Y)[0]
225+
226+
# Project the snapshot matrix onto the smaller space.
227+
B = Q.conj().T.dot(X)
228+
229+
return RQB(Q, B, Omega)
230+
231+
192232
def pseudo_hankel_matrix(X: np.ndarray, d: int) -> np.ndarray:
193233
"""
194234
Arrange the snapshot in the matrix `X` into the (pseudo) Hankel

0 commit comments

Comments
 (0)