1717"""
1818
1919import warnings
20- from typing import Any , Dict , Tuple , Union
20+ from typing import Any , Dict , Tuple , Union , Optional
21+ from types import MappingProxyType
2122import numpy as np
2223from scipy .optimize import OptimizeResult , least_squares
2324from scipy .linalg import qr
2627from .snapshots import Snapshots
2728
2829
29- OPT_DEF_ARGS : Dict [str , Any ] = { # pylint: disable=unused-variable
30- "method" : "trf" ,
31- "tr_solver" : "exact" ,
32- "loss" : "linear" ,
33- "x_scale" : "jac" ,
34- "gtol" : 1e-8 ,
35- "xtol" : 1e-8 ,
36- "ftol" : 1e-8 ,
37- }
30+ OPT_DEF_ARGS = MappingProxyType (
31+ {
32+ "method" : "trf" ,
33+ "tr_solver" : "exact" ,
34+ "loss" : "linear" ,
35+ "x_scale" : "jac" ,
36+ "gtol" : 1e-8 ,
37+ "xtol" : 1e-8 ,
38+ "ftol" : 1e-8 ,
39+ }
40+ )
3841
3942
40- def __svht (
41- sigma_svd : np .ndarray , # pylint: disable=unused-variable
43+ def _svht (
44+ sigma_svd : np .ndarray ,
4245 rows : int ,
4346 cols : int ,
44- sigma : float = None ,
47+ sigma : Optional [ float ] = None ,
4548) -> int :
4649 """
4750 Determine optimal rank for svd matrix approximation,
4851 based on https://arxiv.org/pdf/1305.5870.pdf.
4952
50- :param sigma_svd: Diagonal matrix of "enonomy" SVD as 1d array.
53+ :param sigma_svd: Diagonal matrix of "enonomy" SVD as 1d array.
5154 :type sigma_svd: np.ndarray
5255 :param rows: Number of rows of original data matrix.
5356 :type cols: int
5457 :param cols: Number of columns of original data matrix.
5558 :type cols: int
5659 :param sigma: Signal noise level if known, defaults to None.
57- :type sigma: float, optional
60+ :type sigma: Optional[ float] , optional
5861 :raises ValueError: sigma_svd must be a 1d-array else exception is raised.
5962 :return: Optimal rank.
6063 :rtype: int
@@ -95,7 +98,7 @@ def __svht(
9598 return rank [- 1 ] + 1
9699
97100
98- def __compute_rank (
101+ def _compute_rank (
99102 sigma_x : np .ndarray ,
100103 rows : int ,
101104 cols : int ,
@@ -133,16 +136,16 @@ def __compute_rank(
133136 cumulative_energy = np .cumsum (sigma_x_square / sigma_x_square .sum ())
134137 __rank = np .searchsorted (cumulative_energy , rank ) + 1
135138 elif rank == 0 :
136- __rank = __svht (sigma_x , rows , cols , sigma )
139+ __rank = _svht (sigma_x , rows , cols , sigma )
137140 elif rank >= 1 :
138141 __rank = int (rank )
139142 else :
140143 raise ValueError (f"Invalid rank specified, provided { rank } !" )
141144 return min (__rank , sigma_x .size )
142145
143146
144- def __compute_dmd_ev (
145- x_current : np .ndarray , # pylint: disable=unused-variable
147+ def _compute_dmd_ev (
148+ x_current : np .ndarray ,
146149 x_next : np .ndarray ,
147150 rank : Union [float , int ] = 0 ,
148151) -> np .ndarray :
@@ -167,7 +170,7 @@ def __compute_dmd_ev(
167170 u_x , sigma_x , v_x_t = np .linalg .svd (
168171 x_current , full_matrices = False , hermitian = False
169172 )
170- __rank = __compute_rank (
173+ __rank = _compute_rank (
171174 sigma_x , x_current .shape [0 ], x_current .shape [1 ], rank
172175 )
173176
@@ -185,7 +188,7 @@ def __compute_dmd_ev(
185188 return np .linalg .eigvals (a_approx )
186189
187190
188- class __OptimizeHelper : # pylint: disable=too-few-public-methods
191+ class _OptimizeHelper : # pylint: disable=too-few-public-methods
189192 """
190193 Helper Class to store intermediate results during the optimization.
191194 """
@@ -201,11 +204,11 @@ def __init__(self, l_in: int, m_in: int, n_in: int) -> None:
201204 self .rho : np .ndarray = np .empty ((m_in , n_in ), dtype = np .complex128 )
202205
203206
204- def __compute_dmd_rho (
207+ def _compute_dmd_rho (
205208 alphas : np .ndarray ,
206209 time : np .ndarray ,
207210 data : np .ndarray ,
208- opthelper : __OptimizeHelper ,
211+ opthelper : _OptimizeHelper ,
209212) -> np .ndarray :
210213 r"""
211214 Compute the real residual vector :math:`\boldsymbol{\rho}` for
@@ -223,7 +226,7 @@ def __compute_dmd_rho(
223226 :type data: np.ndarray
224227 :param opthelper: Optimization helper to speed up computations
225228 mainly for Jacobian.
226- :type opthelper: __OptimizeHelper
229+ :type opthelper: _OptimizeHelper
227230 :return: 1d residual vector for Levenberg-Marquardt update
228231 :math:`\boldsymbol{\rho} \in \mathbb{R}^{2mn}`.
229232 :rtype: np.ndarray
@@ -234,37 +237,37 @@ def __compute_dmd_rho(
234237 __alphas .imag = alphas [alphas .shape [- 1 ] // 2 :]
235238
236239 phi = np .exp (np .outer (time , __alphas ))
237- __u , __s , __v_t = np .linalg .svd (phi , hermitian = False , full_matrices = False )
238- __idx = np .where (__s .real != 0.0 )[0 ]
239- __s_inv = np .zeros_like (__s )
240- __s_inv [ __idx ] = np .reciprocal (__s [ __idx ])
240+ u_phi , s_phi , v_phi_t = np .linalg .svd (phi , full_matrices = False )
241+ idx = np .where (s_phi .real != 0.0 )[0 ]
242+ s_phi_inv = np .zeros_like (s_phi )
243+ s_phi_inv [ idx ] = np .reciprocal (s_phi [ idx ])
241244
242- rho = data - np .linalg .multi_dot ([__u , __u .conj ().T , data ])
245+ rho = data - np .linalg .multi_dot ([u_phi , u_phi .conj ().T , data ])
243246 rho_flat = np .ravel (rho )
244247 rho_out = np .zeros ((2 * rho_flat .shape [- 1 ],), dtype = np .float64 )
245248 rho_out [: rho_flat .shape [- 1 ]] = rho_flat .real
246249 rho_out [rho_flat .shape [- 1 ] :] = rho_flat .imag
247250
248251 opthelper .phi = phi
249- opthelper .u_svd = __u
250- opthelper .s_inv = __s_inv
251- opthelper .v_svd = __v_t .conj ().T
252+ opthelper .u_svd = u_phi
253+ opthelper .s_inv = s_phi_inv
254+ opthelper .v_svd = v_phi_t .conj ().T
252255 opthelper .rho = rho
253256 opthelper .b_matrix = np .linalg .multi_dot (
254257 [
255- opthelper .v_svd * __s_inv . reshape (( 1 , - 1 )) ,
258+ opthelper .v_svd * s_phi_inv [ None ] ,
256259 opthelper .u_svd .conj ().T ,
257260 data ,
258261 ]
259262 )
260263 return rho_out
261264
262265
263- def __compute_dmd_jac (
266+ def _compute_dmd_jac (
264267 alphas : np .ndarray ,
265268 time : np .ndarray ,
266269 data : np .ndarray ,
267- opthelper : __OptimizeHelper ,
270+ opthelper : _OptimizeHelper ,
268271) -> np .ndarray :
269272 r"""
270273 Compute the real Jacobian.
@@ -282,8 +285,8 @@ def __compute_dmd_jac(
282285 For DMD computation we set :math:`\boldsymbol{Y} = \boldsymbol{X}^T`.
283286 :type data: np.ndarray
284287 :param opthelper: Optimization helper to speed up computations
285- mainly for Jacobian. The entities are computed in `__compute_dmd_rho `.
286- :type opthelper: __OptimizeHelper
288+ mainly for Jacobian. The entities are computed in `_compute_dmd_rho `.
289+ :type opthelper: _OptimizeHelper
287290 :return: Jacobian :math:`\boldsymbol{J} \in \mathbb{R}^{mn \times 2l}`.
288291 :rtype: np.ndarray
289292 """
@@ -295,42 +298,40 @@ def __compute_dmd_jac(
295298
296299 for j in range (__alphas .shape [- 1 ]):
297300 d_phi_j = time * opthelper .phi [:, j ]
298- __outer = np .outer (d_phi_j , opthelper .b_matrix [j , : ])
299- __a_j = __outer - np .linalg .multi_dot (
300- [opthelper .u_svd , opthelper .u_svd .conj ().T , __outer ]
301+ outer = np .outer (d_phi_j , opthelper .b_matrix [j ])
302+ a_j = outer - np .linalg .multi_dot (
303+ [opthelper .u_svd , opthelper .u_svd .conj ().T , outer ]
301304 )
302- __g_j = np .linalg .multi_dot (
305+ g_j = np .linalg .multi_dot (
303306 [
304- opthelper .u_svd * opthelper .s_inv . reshape (( 1 , - 1 )) ,
307+ opthelper .u_svd * opthelper .s_inv [ None ] ,
305308 np .outer (
306309 opthelper .v_svd [j , :].conj (), d_phi_j .conj () @ opthelper .rho
307310 ),
308311 ]
309312 )
310313 # Compute the jacobian J_mat_j = - (A_j + G_j).
311- __jac = - __a_j - __g_j
312- __jac_flat = np .ravel (__jac )
314+ jac = - a_j - g_j
315+ jac_flat = np .ravel (jac )
313316
314317 # construct the overall jacobian for optimized
315318 # J_real = |Re{J} -Im{J}|
316319 # |Im{J} Re{J}|
317320
318321 # construct real part for optimization
319- jac_out [: jac_out .shape [0 ] // 2 , j ] = __jac_flat .real
320- jac_out [jac_out .shape [0 ] // 2 :, j ] = __jac_flat .imag
322+ jac_out [: jac_out .shape [0 ] // 2 , j ] = jac_flat .real
323+ jac_out [jac_out .shape [0 ] // 2 :, j ] = jac_flat .imag
321324
322325 # construct imaginary part for optimization
323326 jac_out [
324327 : jac_out .shape [0 ] // 2 , __alphas .shape [- 1 ] + j
325- ] = - __jac_flat .imag
326- jac_out [
327- jac_out .shape [0 ] // 2 :, __alphas .shape [- 1 ] + j
328- ] = __jac_flat .real
328+ ] = - jac_flat .imag
329+ jac_out [jac_out .shape [0 ] // 2 :, __alphas .shape [- 1 ] + j ] = jac_flat .real
329330
330331 return jac_out
331332
332333
333- def __varpro_preprocessing (
334+ def _varpro_preprocessing (
334335 data : np .ndarray ,
335336 time : np .ndarray ,
336337 rank : Union [float , int ] = 0.0 ,
@@ -368,25 +369,25 @@ def __varpro_preprocessing(
368369 """
369370
370371 u_r , s_r , v_r_t = np .linalg .svd (data , full_matrices = False )
371- __rank = __compute_rank (s_r , data .shape [0 ], data .shape [1 ], rank )
372+ __rank = _compute_rank (s_r , data .shape [0 ], data .shape [1 ], rank )
372373 u_r = u_r [:, :__rank ]
373374 s_r = s_r [:__rank ]
374- v_r = v_r_t [:__rank , : ].conj ().T
375- data_out = v_r .conj ().T * s_r . reshape (( - 1 , 1 )) if use_proj else data
375+ v_r = v_r_t [:__rank ].conj ().T
376+ data_out = v_r .conj ().T * s_r [:, None ] if use_proj else data
376377
377378 # trapezoidal derivative approximation
378379 y_out = (data_out [:, :- 1 ] + data_out [:, 1 :]) / 2.0
379380 dt_in = time [1 :] - time [:- 1 ]
380- z_out = (data_out [:, 1 :] - data_out [:, :- 1 ]) / dt_in . reshape (( 1 , - 1 ))
381+ z_out = (data_out [:, 1 :] - data_out [:, :- 1 ]) / dt_in [ None ]
381382
382383 return z_out , y_out , data_out , u_r
383384
384385
385- def __compute_dmd_varpro (
386+ def _compute_dmd_varpro (
386387 alphas_init : np .ndarray ,
387388 time : np .ndarray ,
388389 data : np .ndarray ,
389- opthelper : __OptimizeHelper ,
390+ opthelper : _OptimizeHelper ,
390391 ** optargs ,
391392) -> OptimizeResult :
392393 r"""
@@ -400,17 +401,17 @@ def __compute_dmd_varpro(
400401 For DMD computation we set :math:`\boldsymbol{Y} = \boldsymbol{X}^T`.
401402 :type data: np.ndarray
402403 :param opthelper: Optimization helper to speed up computations
403- mainly for Jacobian. The entities are computed in `__compute_dmd_rho `
404- and are used in `__compute_dmd_jac `.
405- :type opthelper: __OptimizeHelper
404+ mainly for Jacobian. The entities are computed in `_compute_dmd_rho `
405+ and are used in `_compute_dmd_jac `.
406+ :type opthelper: _OptimizeHelper
406407 :return: Optimization result.
407408 :rtype: OptimizeResult
408409 """
409410
410411 return least_squares (
411- __compute_dmd_rho ,
412+ _compute_dmd_rho ,
412413 alphas_init ,
413- __compute_dmd_jac ,
414+ _compute_dmd_jac ,
414415 ** optargs ,
415416 args = [time , data , opthelper ],
416417 )
@@ -442,8 +443,8 @@ def select_best_samples_fast(data: np.ndarray, comp: float = 0.9) -> np.ndarray:
442443
443444 n_samples = int (data .shape [- 1 ] * (1.0 - comp ))
444445 pcolumn = qr (data , mode = "economic" , pivoting = True )[- 1 ]
445- __idx = pcolumn [: n_samples ]
446- return __idx
446+
447+ return pcolumn [: n_samples ]
447448
448449
449450def compute_varprodmd_any ( # pylint: disable=unused-variable
@@ -501,8 +502,8 @@ def compute_varprodmd_any( # pylint: disable=unused-variable
501502 raise ValueError ("time needs to be a 1D array" )
502503
503504 # y_in, z_in, data_in, u_r
504- res = __varpro_preprocessing (data , time , rank , use_proj )
505- omegas = __compute_dmd_ev (res [0 ], res [1 ], res [- 1 ].shape [- 1 ])
505+ res = _varpro_preprocessing (data , time , rank , use_proj )
506+ omegas = _compute_dmd_ev (res [0 ], res [1 ], res [- 1 ].shape [- 1 ])
506507
507508 if compression > 0 :
508509 indices = select_best_samples_fast (res [2 ], compression )
@@ -523,8 +524,8 @@ def compute_varprodmd_any( # pylint: disable=unused-variable
523524 )
524525 )
525526
526- opthelper = __OptimizeHelper (res [- 1 ].shape [- 1 ], * res [2 ].shape )
527- opt = __compute_dmd_varpro (
527+ opthelper = _OptimizeHelper (res [- 1 ].shape [- 1 ], * res [2 ].shape )
528+ opt = _compute_dmd_varpro (
528529 np .concatenate ([omegas .real , omegas .imag ]),
529530 time [indices ],
530531 res [2 ][:, indices ].T ,
@@ -535,10 +536,10 @@ def compute_varprodmd_any( # pylint: disable=unused-variable
535536 omegas .imag = opt .x [opt .x .shape [- 1 ] // 2 :]
536537 xi = res [- 1 ] @ opthelper .b_matrix .T if use_proj else opthelper .b_matrix .T
537538 eigenf = np .linalg .norm (xi , axis = 0 )
538- return xi / eigenf . reshape (( 1 , - 1 )) , omegas , eigenf , indices , opt
539+ return xi / eigenf [ None ] , omegas , eigenf , indices , opt
539540
540541
541- def optdmd_predict ( # pylint: disable=unused-variable
542+ def optdmd_predict (
542543 phi : np .ndarray ,
543544 omegas : np .ndarray ,
544545 eigenf : np .ndarray ,
@@ -565,7 +566,7 @@ def optdmd_predict( # pylint: disable=unused-variable
565566 :rtype: np.ndarray
566567 """
567568
568- return phi @ (np .exp (np .outer (omegas , time )) * eigenf . reshape ( - 1 , 1 ) )
569+ return phi @ (np .exp (np .outer (omegas , time )) * eigenf [:, None ] )
569570
570571
571572class VarProOperator (DMDOperator ):
@@ -927,7 +928,7 @@ def dynamics(self):
927928 """
928929
929930 t_omega = np .exp (np .outer (self .eigs , self ._original_time ))
930- return self .amplitudes . reshape ( - 1 , 1 ) * t_omega
931+ return self .amplitudes [:, None ] * t_omega
931932
932933 @property
933934 def frequency (self ):
0 commit comments