Skip to content

Commit

Permalink
Merge pull request #173 from decargroup/bugfix/172-prediction-with-no…
Browse files Browse the repository at this point in the history
…-inputs-bug

Bugfix/172 prediction with no inputs bug
  • Loading branch information
sdahdah committed May 2, 2024
2 parents 25c42a8 + 015fd72 commit fdf5099
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-package.yml
Expand Up @@ -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}}
Expand Down
1 change: 1 addition & 0 deletions pykoop/centers.py
Expand Up @@ -11,6 +11,7 @@
from scipy import stats

log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())


class Centers(sklearn.base.BaseEstimator, metaclass=abc.ABCMeta):
Expand Down
2 changes: 1 addition & 1 deletion pykoop/kernel_approximation.py
Expand Up @@ -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:
Expand Down
16 changes: 8 additions & 8 deletions pykoop/lmi_regressors.py
Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions 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
scipy>=1.8.0
scikit-learn>=1.2.0
PICOS>=2.4.0
optht>=0.2.0
Deprecated>=1.2.13
Expand Down
89 changes: 89 additions & 0 deletions tests/test_koopman_pipeline.py
Expand Up @@ -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(
Expand Down
34 changes: 27 additions & 7 deletions tests/test_lmi_regressors.py
Expand Up @@ -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):
Expand Down
Binary file not shown.

0 comments on commit fdf5099

Please sign in to comment.