| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 | 
							- import numpy as np
 
- import scipy.signal as scipysig
 
- from numpy.typing import NDArray
 
- from typing import Optional
 
- from pydeepc import Data
 
- class System(object):
 
-     """
 
-     Represents a dynamical system that can be simulated
 
-     """
 
-     def __init__(self, sys: scipysig.StateSpace, x0: Optional[NDArray[np.float64]] = None, noise_std: float = 0.5):
 
-         """
 
-         :param sys: a linear system
 
-         :param x0: initial state
 
-         :param noise_std: Standard deviation of the measurement noise
 
-         """
 
-         assert x0 is None or sys.A.shape[0] == len(x0), 'Invalid initial condition'
 
-         self.sys = sys
 
-         self.x0 = x0 if x0 is not None else np.zeros(sys.A.shape[0])
 
-         self.u = None
 
-         self.y = None
 
-         self.noise_std = noise_std
 
-     def apply_input(self, u: NDArray[np.float64]) -> Data:
 
-         """
 
-         Applies an input signal to the system.
 
-         
 
-         :param u: input signal. Needs to be of shape T x M, where T is the batch size and M is the number of features
 
-         :return: tuple that contains the (input,output) of the system
 
-         """
 
-         T = len(u)
 
-         if T > 1:
 
-             # If u is a signal of length > 1 use dlsim for quicker computation
 
-             t, y, x0 = scipysig.dlsim(self.sys, u, t = np.arange(T) * self.sys.dt, x0 = self.x0)
 
-             self.x0 = x0[-1]
 
-         else:
 
-             y = self.sys.C @ self.x0
 
-             self.x0 = self.sys.A @ self.x0.flatten() + self.sys.B @ u.flatten()
 
-         y = y + self.noise_std * np.random.normal(size = T).reshape(T, 1)
 
-         self.u = np.vstack([self.u, u]) if self.u is not None else u
 
-         self.y = np.vstack([self.y, y]) if self.y is not None else y
 
-         return Data(u, y)
 
-     def get_last_n_samples(self, n: int) -> Data:
 
-         """
 
-         Returns the last n samples
 
-         :param n: integer value
 
-         """
 
-         assert self.u.shape[0] >= n, 'Not enough samples are available'
 
-         return Data(self.u[-n:], self.y[-n:])
 
-     def get_all_samples(self) -> Data:
 
-         """
 
-         Returns all samples
 
-         """
 
-         return Data(self.u, self.y)
 
-     def reset(self, data_ini: Optional[Data] = None, x0: Optional[NDArray[np.float64]] = None):
 
-         """
 
-         Reset initial state and collected data
 
-         """
 
-         self.u = None if data_ini is None else data_ini.u
 
-         self.y = None if data_ini is None else data_ini.y
 
-         self.x0 = x0 if x0 is not None else np.zeros(self.sys.A.shape[0])
 
 
  |