From 98d66611acd2781979fed77b11faf68b23b27de9 Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 14:48:41 -0400 Subject: [PATCH 1/7] Change sparse to sparse_output in OneHotEncoder --- pykoop/kernel_approximation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pykoop/kernel_approximation.py b/pykoop/kernel_approximation.py index f7d8cea..0275014 100644 --- a/pykoop/kernel_approximation.py +++ b/pykoop/kernel_approximation.py @@ -426,7 +426,7 @@ def fit( X_hashed = self._hash_samples(X_scaled) encoder_args = { 'categories': 'auto', - 'sparse': False, + 'sparse_output': False, 'handle_unknown': 'ignore', } if self.encoder_kw is not None: From 116508f3c42651e49ac36b71fbbbfaa8c405f5a9 Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 14:52:54 -0400 Subject: [PATCH 2/7] Update minimum sklearn version for sparse_output --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a218077..4cd10b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Requirements to install ``pykoop`` and run examples numpy>=1.21.0 scipy>=1.7.0 -scikit-learn>=1.0.0 +scikit-learn>=1.2.0 PICOS>=2.4.0 optht>=0.2.0 Deprecated>=1.2.13 From 8a512fe8c958cc5be57fe2c090f17fc80f8988e8 Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 15:18:58 -0400 Subject: [PATCH 3/7] Add inputless tests --- tests/test_koopman_pipeline.py | 89 ++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/test_koopman_pipeline.py b/tests/test_koopman_pipeline.py index de04bb5..d93e9d3 100644 --- a/tests/test_koopman_pipeline.py +++ b/tests/test_koopman_pipeline.py @@ -1097,6 +1097,95 @@ def test_strip_initial_conditons(self): np.testing.assert_allclose(X1s, X2s) +@pytest.mark.filterwarnings( + 'ignore:Call to deprecated method predict_multistep') +@pytest.mark.parametrize( + 'kp', + [ + pykoop.KoopmanPipeline( + lifting_functions=None, + regressor=pykoop.Edmd(), + ), + pykoop.KoopmanPipeline( + lifting_functions=[ + ('pl', pykoop.PolynomialLiftingFn(order=2)), + ], + regressor=pykoop.Edmd(), + ), + pykoop.KoopmanPipeline( + lifting_functions=[ + ('dl', + pykoop.DelayLiftingFn( + n_delays_state=2, + n_delays_input=2, + )), + ], + regressor=pykoop.Edmd(), + ), + pykoop.KoopmanPipeline( + lifting_functions=[ + ('sp', + pykoop.SplitPipeline( + lifting_functions_state=[ + ('pl', pykoop.PolynomialLiftingFn(order=2)), + ], + lifting_functions_input=None, + )) + ], + regressor=pykoop.Edmd(), + ), + ], +) +class TestPredictionNoInput: + """Test fit Koopman pipeline prediction without input.""" + + def test_predict_trajectory_no_input( + self, + kp, + mass_spring_damper_no_input, + ): + """Test :func:`predict_trajectory` with no input.""" + msg = 'Test only works when there is no episode feature.' + assert (not mass_spring_damper_no_input['episode_feature']), msg + # Fit estimator + kp.fit( + mass_spring_damper_no_input['X_train'], + n_inputs=mass_spring_damper_no_input['n_inputs'], + episode_feature=False, + ) + # Extract initial conditions + x0 = pykoop.extract_initial_conditions( + mass_spring_damper_no_input['X_train'], + kp.min_samples_, + n_inputs=mass_spring_damper_no_input['n_inputs'], + episode_feature=False, + ) + # Extract input + u = pykoop.extract_input( + mass_spring_damper_no_input['X_train'], + n_inputs=mass_spring_damper_no_input['n_inputs'], + episode_feature=False, + ) + # Predict new states + X_sim = kp.predict_trajectory( + x0, + u, + episode_feature=False, + relift_state=True, + ) + # Predict manually + X_sim_exp = np.zeros(X_sim.shape) + X_sim_exp[:kp.min_samples_, :] = x0 + for k in range(kp.min_samples_, u.shape[0]): + X = np.hstack(( + X_sim_exp[(k - kp.min_samples_):k, :], + u[(k - kp.min_samples_):k, :], + )) + Xp = kp.predict(X) + X_sim_exp[[k], :] = Xp[[-1], :] + np.testing.assert_allclose(X_sim, X_sim_exp) + + @pytest.mark.filterwarnings( 'ignore:Call to deprecated method predict_multistep') @pytest.mark.parametrize( From cdcaa9172a1b5052f9f6bb620a9643021d4bfb68 Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 15:44:01 -0400 Subject: [PATCH 4/7] Add 3.12 to tested Python versions --- .github/workflows/test-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 3865ad3..087a2f8 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{matrix.python-version}} From 56179449279f449db36241848df92eff3dac071f Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 15:44:33 -0400 Subject: [PATCH 5/7] Bump SciPy version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4cd10b1..13885b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # Requirements to install ``pykoop`` and run examples numpy>=1.21.0 -scipy>=1.7.0 +scipy>=1.8.0 scikit-learn>=1.2.0 PICOS>=2.4.0 optht>=0.2.0 From f4e4caa3c96d0835bb758d6cee61c379ee5072bb Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 15:45:01 -0400 Subject: [PATCH 6/7] Add NullHandler to centers --- pykoop/centers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pykoop/centers.py b/pykoop/centers.py index 88801bb..6f9c883 100644 --- a/pykoop/centers.py +++ b/pykoop/centers.py @@ -11,6 +11,7 @@ from scipy import stats log = logging.getLogger(__name__) +log.addHandler(logging.NullHandler()) class Centers(sklearn.base.BaseEstimator, metaclass=abc.ABCMeta): From 015fd72039bc6168d9ed9c2157c13b35119cfd89 Mon Sep 17 00:00:00 2001 From: Steven Dahdah Date: Thu, 2 May 2024 16:02:46 -0400 Subject: [PATCH 7/7] Fix default LMI strictness --- pykoop/lmi_regressors.py | 16 ++++----- tests/test_lmi_regressors.py | 34 ++++++++++++++---- ...ressor4_mass_spring_damper_sine_input_.npz | Bin 1286 -> 1278 bytes 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/pykoop/lmi_regressors.py b/pykoop/lmi_regressors.py index bbfa1a3..94b2d09 100644 --- a/pykoop/lmi_regressors.py +++ b/pykoop/lmi_regressors.py @@ -191,7 +191,7 @@ def __init__( inv_method: str = 'svd', tsvd: Optional[tsvd.Tsvd] = None, square_norm: bool = False, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiEdmd`. @@ -494,7 +494,7 @@ def __init__( tsvd_shifted: Optional[tsvd.Tsvd] = None, reg_method: str = 'tikhonov', square_norm: bool = False, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiDmdc`. @@ -672,7 +672,7 @@ def _create_base_problem( - big_constant.T * U_hat.T, U_hat * Q_bar_Sigma_tld ], [Q_bar_Sigma_tld.T * U_hat.T, m1], - ]) << picos_eps) + ]) << -1 * picos_eps) problem.set_objective('min', picos.trace(W_hat)) return problem @@ -736,7 +736,7 @@ def __init__( alpha: float = 0, inv_method: str = 'svd', tsvd: Optional[tsvd.Tsvd] = None, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiEdmdSpectralRadiusConstr`. @@ -988,7 +988,7 @@ def __init__( alpha: float = 0, tsvd_unshifted: Optional[tsvd.Tsvd] = None, tsvd_shifted: Optional[tsvd.Tsvd] = None, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiDmdcSpectralRadiusConstr`. @@ -1264,7 +1264,7 @@ def __init__( inv_method: str = 'svd', tsvd: Optional[tsvd.Tsvd] = None, square_norm: bool = False, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiEdmdHinfReg`. @@ -1603,7 +1603,7 @@ def __init__( tsvd_unshifted: Optional[tsvd.Tsvd] = None, tsvd_shifted: Optional[tsvd.Tsvd] = None, square_norm: bool = False, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiDmdcHinfReg`. @@ -1924,7 +1924,7 @@ def __init__( iter_rtol: float = 0, inv_method: str = 'svd', tsvd: Optional[tsvd.Tsvd] = None, - picos_eps: float = 0, + picos_eps: float = 1e-6, solver_params: Optional[Dict[str, Any]] = None, ) -> None: """Instantiate :class:`LmiEdmdDissipativityConstr`. diff --git a/tests/test_lmi_regressors.py b/tests/test_lmi_regressors.py index e0734f3..659dfe9 100644 --- a/tests/test_lmi_regressors.py +++ b/tests/test_lmi_regressors.py @@ -499,18 +499,38 @@ class TestSkLearn: """Test ``scikit-learn`` compatibility.""" @sklearn.utils.estimator_checks.parametrize_with_checks([ - pykoop.lmi_regressors.LmiEdmd(alpha=1e-3, ), - pykoop.lmi_regressors.LmiEdmdSpectralRadiusConstr(max_iter=1, ), - pykoop.lmi_regressors.LmiEdmdHinfReg(alpha=1, ratio=1, max_iter=1), - pykoop.lmi_regressors.LmiEdmdDissipativityConstr(max_iter=1, ), - pykoop.lmi_regressors.LmiDmdc(alpha=1e-3, ), - pykoop.lmi_regressors.LmiDmdcSpectralRadiusConstr(max_iter=1, ), - pykoop.lmi_regressors.LmiDmdcHinfReg(alpha=1, ratio=1, max_iter=1), + pykoop.lmi_regressors.LmiEdmd(alpha=1e-3, picos_eps=0), + pykoop.lmi_regressors.LmiEdmdSpectralRadiusConstr( + max_iter=1, + picos_eps=0, + ), + pykoop.lmi_regressors.LmiEdmdHinfReg( + alpha=1, + ratio=1, + max_iter=1, + picos_eps=0, + ), + pykoop.lmi_regressors.LmiEdmdDissipativityConstr( + max_iter=1, + picos_eps=0, + ), + pykoop.lmi_regressors.LmiDmdc(alpha=1e-3, picos_eps=0), + pykoop.lmi_regressors.LmiDmdcSpectralRadiusConstr( + max_iter=1, + picos_eps=0, + ), + pykoop.lmi_regressors.LmiDmdcHinfReg( + alpha=1, + ratio=1, + max_iter=1, + picos_eps=0, + ), pykoop.lmi_regressors.LmiHinfZpkMeta( pykoop.lmi_regressors.LmiEdmdHinfReg( alpha=1, ratio=1, max_iter=1, + picos_eps=0, )), ]) def test_compatible_estimator(self, estimator, check, mosek_solver_params): diff --git a/tests/test_lmi_regressors/test_fit_predict_regressor4_mass_spring_damper_sine_input_.npz b/tests/test_lmi_regressors/test_fit_predict_regressor4_mass_spring_damper_sine_input_.npz index 1a4ca1e6ebcafdc05f905ef4f3097660c9ff6c4a..7d109cbb0a00de093f0b59d0b1ddec9c46b5d7e8 100644 GIT binary patch delta 1069 zcmV+|1k(G43jPTlP)h>@EdT%j2mk;8Apj@TUl0HP|NsC0|Nj#J6qBF?6p=AmPM2SU zGp`Z}B!uY?f5qQ(1hO>L#rPi{4qvOFeu6rycAX z;-MA2!H_HRc^_)3Z%b%v?a18BJ+^<(cMcC+*7kozBT;H~kg8*EbDPybugDK*q!iAd2pSfMufZCnkH5mD zFq?DG(vxPb}`Ke zyEZH}B}T0%^yL(G^35>YaJ}%433NJ=@e_emxjGXlf&1Wk%;3p;LoGdn? zY~WgQriz5;o<*$M36~q=07GUDDvWfCJ?Y*KEK6EXf9ZT&KhNsGJe%xdQin zez6(5F)%$)Q^0b9rfY8ds=|LWSdrgEO~2;^dasBT+W9G_r7%^~WlnGvowG>W#5g=# zo>{rZ2?~AHSbkbFOub2Ta%EQ_CKTYg{2JIP?eNcN9iSa#|5nn{1%sL8ytY;fmdJQh!yUnS3TIR(nFhaoo{f$ z21kGVb4C5ecpegQ(-Rq<7asfDA<3f@`nNPH0pKQiOo|KBn9upN3zc2(3HV8 zahQT5M?XlPtkR>YV~6(PEfNl<&pA>`4M^OW+q5=;gi*#Gclt9!xai#yEL^t13rqK} z)=(xK+SuhQFC6w{tHk`0Rj*$11$gm00;m803iS; n)L##ihXgSZEdwn8000O8001EX(}`+|ZIj6aHwJ4200000@6aWAK2mk;8Apl#N0|XQR000O8 z001EX*y^VNbprqZkOKe!k=P!8bprqZ00000oyh%D(*+#Baf}mrNH7saJ9KjBHiaB$ zZOGgw5bQkJgQ8p&rYBm7bCcC7rlYSeg`anwTL2gSc&7of<2-@Cv5lX{xDf_0mn~FsFeXuP;V-Cj*kk^Exy|M z=8*&bPFeVd@!%}1R330o0tbY%;<9zcN~p=LPF*IkgJNTg;>B1M+*resh94%OO&RYY z@TiA^o9ox9ya-_Hdil}f23S~5U0-p=3W@bwuNhoa&E(I5; zQ<+Jyz&qf7