GerbenBeintema / deepSI

Dynamical System Identification using python incorporating numerous powerful deep learning methods. (deepSI = deep System Identification)
Other
43 stars 16 forks source link

fix init_state bug #9

Open Enderdead opened 1 year ago

Enderdead commented 1 year ago

This merge request addresses an issue with the OLS formula in the linear state space class.

The problematic line uses the numpy attribute flat, which is inappropriate in this context: https://github.com/GerbenBeintema/deepSI/blob/b393df4474eb6af64e3585fe90647f8d15b2a22c/deepSI/fit_systems/ss_linear.py#L356

Below is a script to demonstrate the issue:

system = deepSI.fit_systems.SS_linear(A=np.array([[0.9,0.1],[0.0,0.7]]), B=np.array([[1,0.0],[0.0, 2]]),
                                      C=np.array([[1.0,2.]]), k0=3)

u0 = np.array([[1.0,2.0]]).T
u1 = np.array([[0.2,0.0]]).T
u2 = np.array([[-2.0,1.]]).T

system.x = np.array([[1.0],[2.0]])
y0 =system.measure_act(u0)
y1 =system.measure_act(u1)
y2 =system.measure_act(u2)

dataset =  deepSI.System_data(y=np.stack([y0, y1, y2]), u=np.concatenate([u0,u1, u2], axis=1).T, normed=True)
system.init_state(dataset)
print(system.measure_act(u2).shape)
print(system.measure_act(u2).shape)

The expected output is:

(1,)
(1,)

However, the current output is:

()
(2,)

This inconsistency arises because the OLS produces a state of shape (n_x, ) instead of (n_x, 1). When applying the f function, as seen here, numpy produces a state x of shape (n_x, n_x) due to broadcasting, rather than actual matrix multiplication. The state is now in an incorrect shape.

To rectify this, I've replaced the inappropriate use of flat with .reshape(-1, 1), ensuring consistent 2D shaping.

Please let me know if there are any questions or further discussions required.

Thank you for considering this fix. Best regards,

GerbenBeintema commented 1 year ago

Hi Enderdead,

Sorry for my late response, my notifications have been acting up.

Thanks for the input, there indeed seems to be a problem in the case that you showed. However, it originates from that the documentation is missing for what the current functionality is and how the user should use it.

To clear things up the shapes that an SS_linear system expects are

where ny=None is a flag that the output is just a number which is set by default if ny=1 is seen in the C matrix. (you can overwrite this by system.ny=1)

Hence if the input, state or output are given by matrices (e.g. (nx, 1) or (nu,1)) then unexpected behaviours might occur.

Thus a working implementation of the example you give could be

import deepSI
import numpy as np

system = deepSI.fit_systems.SS_linear(A=np.array([[0.9,0.1],[0.0,0.7]]), B=np.array([[1,0.0],[0.0, 2]]),
                                      C=np.array([[1.0,2.]]), k0=3)
# system.ny = 1 # enable to force the output to be of shape (1,)

u0 = np.array([1.0,2.0])
u1 = np.array([0.2,0.0])
u2 = np.array([-2.0,1.])

system.x = np.array([1.0, 2.0])
y0 =system.measure_act(u0)
y1 =system.measure_act(u1)
y2 =system.measure_act(u2)

dataset =  deepSI.System_data(y=np.stack([y0, y1, y2]), u=np.stack([u0, u1, u2], axis=0), normed=True)
system.init_state(dataset)
print(system.x.shape)
print(system.measure_act(u1).shape)
print(system.measure_act(u2).shape)

where the output is

(2,)
()
()

since nx=2, and ny=None.

I hope this clears up the problems that you have observed. If you have any further observations feel free to ask.

best regards, Gerben