Skip to content

Commit b0d16b6

Browse files
committed
removing redundant code from module varprodmd.py
1 parent ffeba1a commit b0d16b6

File tree

2 files changed

+14
-147
lines changed

2 files changed

+14
-147
lines changed

pydmd/varprodmd.py

Lines changed: 9 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@
1717
"""
1818

1919
import warnings
20-
from typing import Any, Dict, Tuple, Union, Optional
2120
from types import MappingProxyType
21+
from typing import Any, Dict, Tuple, Union
22+
2223
import numpy as np
23-
from scipy.optimize import OptimizeResult, least_squares
2424
from scipy.linalg import qr
25+
from scipy.optimize import OptimizeResult, least_squares
26+
2527
from .dmd import DMDBase
2628
from .dmdoperator import DMDOperator
2729
from .snapshots import Snapshots
28-
30+
from .utils import compute_svd
2931

3032
OPT_DEF_ARGS = MappingProxyType(
3133
{
@@ -40,110 +42,6 @@
4042
)
4143

4244

43-
def _svht(
44-
sigma_svd: np.ndarray,
45-
rows: int,
46-
cols: int,
47-
sigma: Optional[float] = None,
48-
) -> int:
49-
"""
50-
Determine optimal rank for svd matrix approximation,
51-
based on https://arxiv.org/pdf/1305.5870.pdf.
52-
53-
:param sigma_svd: Diagonal matrix of "enonomy" SVD as 1d array.
54-
:type sigma_svd: np.ndarray
55-
:param rows: Number of rows of original data matrix.
56-
:type cols: int
57-
:param cols: Number of columns of original data matrix.
58-
:type cols: int
59-
:param sigma: Signal noise level if known, defaults to None.
60-
:type sigma: Optional[float], optional
61-
:raises ValueError: sigma_svd must be a 1d-array else exception is raised.
62-
:return: Optimal rank.
63-
:rtype: int
64-
"""
65-
66-
if len(sigma_svd.shape) != 1:
67-
raise ValueError("Expected 1d array for diagonal matrix!")
68-
69-
beta = (
70-
float(cols) / float(rows) if rows > cols else float(rows) / float(cols)
71-
)
72-
73-
tau_star = 0
74-
75-
if sigma is not None:
76-
sigma = abs(sigma)
77-
lambda_star = np.sqrt(
78-
2 * (beta + 1)
79-
+ (8 * beta / (beta + 1 + np.sqrt(beta * beta + 14 * beta + 1)))
80-
)
81-
tau_star = (
82-
lambda_star
83-
* sigma
84-
* np.sqrt(float(cols) if cols > rows else float(rows))
85-
)
86-
else:
87-
median = np.median(sigma_svd)
88-
beta_square = beta * beta
89-
beta_cubic = beta * beta_square
90-
omega = 0.56 * beta_cubic - 0.95 * beta_square + 1.82 * beta + 1.43
91-
tau_star = median * omega
92-
93-
rank = np.where(sigma_svd >= tau_star)[0]
94-
95-
if rank.size == 0:
96-
return sigma_svd.shape[-1]
97-
98-
return rank[-1] + 1
99-
100-
101-
def _compute_rank(
102-
sigma_x: np.ndarray,
103-
rows: int,
104-
cols: int,
105-
rank: Union[float, int],
106-
sigma: float = None,
107-
) -> int:
108-
r"""
109-
Compute rank without duplicate SVD computation.
110-
111-
:param sigma_x: Diagonal matrix of SVD as 1d array.
112-
:type sigma_x: np.ndarray
113-
:param rows: Number of rows of the original data matrix.
114-
:type rows: int
115-
:param cols: Number of columns of the original data matrix.
116-
:type cols: int
117-
:param rank: Desired input rank :math:`r`. If rank is an integer,
118-
and :math:`r > 0`, the desired rank is used iff possible.
119-
This depends on the size of parameter sigma_x. If the desired
120-
rank exceeds the size of sigma_x, then the minimum of desired rank
121-
and the size of sigma_x is used. If the rank is a float
122-
and :math:`0 < r < 1`, than the rank is determined by investigating
123-
the cumulative energy of the singular values.
124-
:type rank: Union[float, int]
125-
:param sigma: Noise level for SVHT algorithm. If it is unknown
126-
keep it as None. Defaults to None.
127-
:type sigma: float, optional
128-
:raises ValueError: ValueError is raised if rank is negative
129-
(:math:`r < 0`).
130-
:return: Optimal rank.
131-
:rtype: int
132-
"""
133-
134-
if 0 < rank < 1:
135-
sigma_x_square = np.square(sigma_x)
136-
cumulative_energy = np.cumsum(sigma_x_square / sigma_x_square.sum())
137-
__rank = np.searchsorted(cumulative_energy, rank) + 1
138-
elif rank == 0:
139-
__rank = _svht(sigma_x, rows, cols, sigma)
140-
elif rank >= 1:
141-
__rank = int(rank)
142-
else:
143-
raise ValueError(f"Invalid rank specified, provided {rank}!")
144-
return min(__rank, sigma_x.size)
145-
146-
14745
def _compute_dmd_ev(
14846
x_current: np.ndarray,
14947
x_next: np.ndarray,
@@ -167,22 +65,13 @@ def _compute_dmd_ev(
16765
:rtype: np.ndarray
16866
"""
16967

170-
u_x, sigma_x, v_x_t = np.linalg.svd(
171-
x_current, full_matrices=False, hermitian=False
172-
)
173-
__rank = _compute_rank(
174-
sigma_x, x_current.shape[0], x_current.shape[1], rank
175-
)
68+
u_x, sigma_x, v_x = compute_svd(x_current, rank)
17669

17770
# columns of v need to be multiplicated with inverse sigma
178-
sigma_inv_approx = np.reciprocal(sigma_x[:__rank])
71+
sigma_inv_approx = np.reciprocal(sigma_x)
17972

18073
a_approx = np.linalg.multi_dot(
181-
[
182-
u_x[:, :__rank].conj().T,
183-
x_next,
184-
sigma_inv_approx[None] * v_x_t[:__rank, :].conj().T,
185-
]
74+
[u_x.conj().T, x_next, sigma_inv_approx[None] * v_x]
18675
)
18776

18877
return np.linalg.eigvals(a_approx)
@@ -368,11 +257,7 @@ def _varpro_preprocessing(
368257
np.ndarray]
369258
"""
370259

371-
u_r, s_r, v_r_t = np.linalg.svd(data, full_matrices=False)
372-
__rank = _compute_rank(s_r, data.shape[0], data.shape[1], rank)
373-
u_r = u_r[:, :__rank]
374-
s_r = s_r[:__rank]
375-
v_r = v_r_t[:__rank].conj().T
260+
u_r, s_r, v_r = compute_svd(data, rank)
376261
data_out = v_r.conj().T * s_r[:, None] if use_proj else data
377262

378263
# trapezoidal derivative approximation

tests/test_varprodmd.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@
33
"""
44
import numpy as np
55
import pytest
6-
from pydmd.utils import compute_rank
6+
7+
from pydmd import VarProDMD
78
from pydmd.varprodmd import (
8-
_OptimizeHelper,
9-
_compute_dmd_rho,
9+
OPT_DEF_ARGS,
1010
_compute_dmd_jac,
11-
_compute_rank,
12-
_svht,
11+
_compute_dmd_rho,
12+
_OptimizeHelper,
1313
compute_varprodmd_any,
1414
optdmd_predict,
1515
select_best_samples_fast,
16-
OPT_DEF_ARGS,
1716
)
1817

19-
from pydmd import VarProDMD
20-
2118

2219
def signal(x_loc: np.ndarray, time: np.ndarray) -> np.ndarray:
2320
"""
@@ -34,21 +31,6 @@ def signal(x_loc: np.ndarray, time: np.ndarray) -> np.ndarray:
3431
return __f_1 + __f_2
3532

3633

37-
def test_rank():
38-
"""
39-
Test SVHT rank (no duplicate SVD computation is performed).
40-
"""
41-
time = np.linspace(0, 4 * np.pi, 100)
42-
x_loc = np.linspace(-10, 10, 1024)
43-
__x, __time = np.meshgrid(x_loc, time)
44-
z = signal(__x, __time).T
45-
s = np.linalg.svd(z, full_matrices=False)[1]
46-
assert _compute_rank(s, z.shape[0], z.shape[1], 0) == compute_rank(z, 0)
47-
assert _compute_rank(s, z.shape[0], z.shape[1], 4) == 4
48-
assert _compute_rank(s, z.shape[0], z.shape[1], 0.8) == compute_rank(z, 0.8)
49-
assert _svht(s, z.shape[0], z.shape[1]) == compute_rank(z, 0)
50-
51-
5234
def test_varprodmd_rho():
5335
"""
5436
Unit test for residual vector :math: `\boldsymbol{\rho}`.

0 commit comments

Comments
 (0)