dynamicslab / pysindy

A package for the sparse identification of nonlinear dynamical systems from data
https://pysindy.readthedocs.io/en/latest/
Other
1.36k stars 304 forks source link

ValueError: Could not reshape control input to match the input data. #426

Closed ouslalu closed 8 months ago

ouslalu commented 8 months ago

I got the error "ValueError: Could not reshape control input to match the input data." When using multiple trajectories with different time length, and control. The code works fine without control.

Reproducing code example:

import pysindy
x0s = np.array([36, 48, 41]) * (np.random.rand(n_trajectories, 3) - 0.5) + np.array(
    [0, 0, 25]
)

x_train_multi = []
t_train_multi = []

for i in range(n_trajectories):
    n_samples = np.random.randint(*sample_range)
    t = np.arange(0, n_samples * dt, dt)
    t_span = (t[0], t[-1])
    x_train_multi.append(
        solve_ivp(lorenz, t_span, x0s[i], t_eval=t, **integrator_keywords).y.T
    )
    t_train_multi.append(t)

lengths = [len(i) for i in x_train_multi]

control_arrays = [np.random.rand(length) for length in lengths]

model = ps.SINDy()
model.fit(x_train_multi, t=t_train_multi, u = control_arrays, multiple_trajectories=True)
model.print() 

Error message:

ValueError Traceback (most recent call last) File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:971, in _comprehend_and_validate_inputs(x, t, x_dot, u, feature_library) 970 shape[x[0].ax_coord] = -1 --> 971 u = [np.reshape(u[i], shape) for i in range(len(x))] 972 except Exception:

File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:971, in (.0) 970 shape[x[0].ax_coord] = -1 --> 971 u = [np.reshape(u[i], shape) for i in range(len(x))] 972 except Exception:

File <__array_function__ internals>:180, in reshape(*args, **kwargs)

File ~/anaconda3/lib/python3.10/site-packages/numpy/core/fromnumeric.py:298, in reshape(a, newshape, order) 200 """ 201 Gives a new shape to an array without changing its data. 202 (...) 296 [5, 6]]) 297 """ --> 298 return _wrapfunc(a, 'reshape', newshape, order=order)

File ~/anaconda3/lib/python3.10/site-packages/numpy/core/fromnumeric.py:57, in _wrapfunc(obj, method, *args, *kwds) 56 try: ---> 57 return bound(args, **kwds) 58 except TypeError: 59 # A TypeError occurs if the object does have such a method in its 60 # class, but its signature is not identical to that of NumPy's. This (...) 64 # Call _wrapit from within the except clause to ensure a potential 65 # exception has a traceback chain.

ValueError: cannot reshape array of size 759 into shape (1322,newaxis)

During handling of the above exception, another exception occurred:

ValueError Traceback (most recent call last) File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:978, in _comprehend_and_validate_inputs(x, t, x_dot, u, feature_library) 977 shape[x[0].ax_coord] = len(u[0]) --> 978 u = [np.broadcast_to(u[i], shape) for i in range(len(x))] 979 except Exception:

File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:978, in (.0) 977 shape[x[0].ax_coord] = len(u[0]) --> 978 u = [np.broadcast_to(u[i], shape) for i in range(len(x))] 979 except Exception:

File <__array_function__ internals>:180, in broadcast_to(*args, **kwargs)

File ~/anaconda3/lib/python3.10/site-packages/numpy/lib/stride_tricks.py:413, in broadcast_to(array, shape, subok) 369 """Broadcast an array to a new shape. 370 371 Parameters (...) 411 [1, 2, 3]]) 412 """ --> 413 return _broadcast_to(array, shape, subok=subok, readonly=True)

File ~/anaconda3/lib/python3.10/site-packages/numpy/lib/stride_tricks.py:349, in _broadcast_to(array, shape, subok, readonly) 348 extras = [] --> 349 it = np.nditer( 350 (array,), flags=['multi_index', 'refs_ok', 'zerosize_ok'] + extras, 351 op_flags=['readonly'], itershape=shape, order='C') 352 with it: 353 # never really has writebackifcopy semantics

ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (759,) and requested shape (1322,1322)

During handling of the above exception, another exception occurred:

ValueError Traceback (most recent call last) Cell In[19], line 25 22 control_arrays = [np.random.rand(length) for length in lengths] 24 model = ps.SINDy() ---> 25 model.fit(x_train_multi, t=t_train_multi, u = control_arrays, multiple_trajectories=True) 26 model.print()

File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:325, in SINDy.fit(self, x, t, x_dot, u, multiple_trajectories, unbias, quiet, ensemble, library_ensemble, replace, n_candidates_to_drop, n_subset, n_models, ensemble_aggregator) 316 elif ( 317 not isinstance(x, Sequence) 318 or (not isinstance(x_dot, Sequence) and x_dot is not None) 319 or (not isinstance(u, Sequence) and u is not None) 320 ): 321 raise TypeError( 322 "If multiple trajectories set, x and if included," 323 "x_dot and u, must be Sequences" 324 ) --> 325 x, x_dot, u = _comprehend_and_validate_inputs( 326 x, t, x_dot, u, self.feature_library 327 ) 329 if (n_models is not None) and n_models <= 0: 330 raise ValueError("n_models must be a positive integer")

File ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py:980, in _comprehend_and_validate_inputs(x, t, x_dot, u, feature_library) 978 u = [np.broadcast_to(u[i], shape) for i in range(len(x))] 979 except Exception: --> 980 raise ( 981 ValueError( 982 "Could not reshape control input to match the input data." 983 ) 984 ) 985 correct_shape = True 986 for i in range(len(x)):

ValueError: Could not reshape control input to match the input data.

PySINDy/Python version information:

'1.7.5'

znicolaou commented 8 months ago

Can reproduce on the current master with a couple of changes:

n_trajectories=3
x0s = np.array([36, 48, 41]) * (np.random.rand(n_trajectories, 3) - 0.5) + np.array(
    [0, 0, 25]
)

x_train_multi = []
t_train_multi = []
sample_range=[0,100]
dt=0.01 

for i in range(n_trajectories):
    n_samples = np.random.randint(*sample_range)
    t = np.arange(0, n_samples * dt, dt)
    t_span = (t[0], t[-1])
    x_train_multi.append(
        solve_ivp(lorenz, t_span, x0s[i], t_eval=t).y.T
    )
    t_train_multi.append(t)

lengths = [len(i) for i in x_train_multi]

control_arrays = [np.random.rand(length) for length in lengths]

model = ps.SINDy()
model.fit(x_train_multi, t=t_train_multi, u = control_arrays)
model.print() 

I'll debug soon...

znicolaou commented 8 months ago

The current _comprehend_and_validate_inputs in pysindy.py uses only the number of timesteps in x[0] to reshape the control data to the correct shape; lines 846-848 in master:

                shape = np.array(x[0].shape)
                shape[x[0].ax_coord] = -1
                u = [np.reshape(u[i], shape) for i in range(len(x))]

The fix is pretty simple; replace with:

                for i in range(len(x)):
                    shape = np.array(x[i].shape)
                    shape[x[i].ax_coord] = -1
                    u[i] = np.reshape(u[i], shape)

I'll make a push soon, but you can edit your file ~/anaconda3/lib/python3.10/site-packages/pysindy/pysindy.py (looks like line ~970 in your version) if you want an immediate fix.

Jacob-Stevens-Haas commented 8 months ago

@znicolaou, please don't make a push.

The error is, strictly speaking, designed behavior. The argument u is described as shape (n_samples, n_control_features). As you discovered, the user can get rid of the error by making the control arrays with np.random.rand(length, 1) instead of np.random.rand(length). That n_trajectories=1 does not raise an issue is odd. The real issue is whether we should assume the shape of 1-D training data, or whether we should disallow it. And then standardizing that behavior across x, x_dot, and u.

Also OP, for guidance on how to write good issues, see here. In particular, a more minimal working example (that actually runs copy and pasted) might have made the problem (and the issue title) more obvious.

ouslalu commented 8 months ago

Thanks. What @znicolaou suggested fixed the problem.