dynamicslab / pysindy

A package for the sparse identification of nonlinear dynamical systems from data

Home Page:https://pysindy.readthedocs.io/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG]SINDy defaults are instances of classes

billtubbs opened this issue · comments

I don't think it's a good idea to use instances of optimizer, feature_library, and differentiation_method as default values in the init method of the SINDy class because they will only be instantiated once and then all future instances of SINDy will be using the same instances of these classes.

To illustrate the problem:

from pysindy import SINDy

model_1 = SINDy()
model_2 = SINDy()

print(model_1.optimizer.threshold, model_2.optimizer.threshold)

model_1.optimizer.threshold = 0.2  # This modifies the default instance

print(model_1.optimizer.threshold, model_2.optimizer.threshold)

Output:

0.1 0.1
0.2 0.2

(model_1 and model_2 are sharing the same default optimizer object, furthermore, all future instances of SINDy will also be).

The problem is in the init method for SINDy:

    def __init__(
        self,
        optimizer=STLSQ(),
        feature_library=PolynomialFeatures(),
        differentiation_method=FiniteDifference(),
        feature_names=None,
        discrete_time=False,
        n_jobs=1,
    ):

The standard solution is to set the default values to None and then instantiate new STLSQ(), PolynomialFeatures() or FiniteDifference() objects if required in the body of the __init__ method.

E.g.

    if optimizer is None:
        self.optimizer = STLSQ()
    else:
        self.optimizer = optimizer

I can submit a pull-request to fix this if you like.

commented

This is a great suggestion, thanks! If you'd like to submit a pull-request, please do. Otherwise I can make the fix sometime in the next 24 hours.

Now I'm getting 1 test fail. Any idea how this change to SINDy() could have caused this:

        optimizers = [SINDyOptimizer(o, unbias=True) for o in optimizers]
    
        for opt in optimizers:
            opt.fit(x, y)
    
        for less_complex, more_complex in zip(optimizers, optimizers[1:]):
            # relax the condition to account for
            # noise and non-normalized threshold parameters
>           assert less_complex.complexity <= more_complex.complexity + 1
E           AssertionError: assert 4 <= (2 + 1)
E            +  where 4 = SINDyOptimizer(optimizer=SR3(copy_X=True, fit_intercept=True, max_iter=30,\n                             normalize=False, nu=1.0, threshold=0.1,\n                             thresholder='l1', tol=1e-05),\n               unbias=True).complexity
E            +  and   2 = SINDyOptimizer(optimizer=Lasso(alpha=1.0, copy_X=True, fit_intercept=True,\n                               max_iter=100...          selection='cyclic', tol=0.0001,\n                               warm_start=False),\n               unbias=True).complexity

test/property_tests/test_optimizers_complexity.py:55: AssertionError

How does this test work?

Actually, this is not related. I just rolled back my changes and this test was failing before. My changes pass pytest test/test_pysindy.py. So I will submit a pull-request. Not sure what the other problem is.

commented

The test failure you saw is troubling. I will have to look into it separately.