Linear models

pynamics.models.state_space_models.LinearModel(initial_state, initial_control, A, B, C, D, input_labels=None, output_labels=None)

Bases: BaseModel

Linear time-invariant state-space model.

This class implements a generic linear time-invariant state-space model. It is intended to model continuous-time systems. Hybrid and discrete-time systems are not supported by pynamics.

Parameters:

Name Type Description Default
initial_state ndarray

The system's initial state. Should be an array shaped (n, 1), where n is the number of state variables.

required
initial_control ndarray

The inputs' initial value(s). Should be an array shaped (u, 1), where u is the number of input variables.

required
A ndarray

Dynamics matrix. It maps the state vector to the state derivatives.

required
B ndarray

Input matrix. It maps the input vector to the state derivatives.

required
C ndarray

Output matrix. It maps the state vector to the output vector.

required
D ndarray

Direct or feedforward matrix. It maps the input vector to the output vector.

required
input_labels list[str] | None

List of input labels. If None is passed, the inputs will be given generic names.

None
output_labels list[str] | None

List of output labels. If None is passed, the outputs will be given generic names.

None

Attributes:

Name Type Description
x ndarray

The system's state vector. Should be an array shaped (n, 1), where n is the number of state variables.

u ndarray

Input vector. Should be an array shaped (u, 1), where u is the number of input variables.

A ndarray

Dynamics matrix. It maps the state vector to the state derivatives.

B ndarray

Input matrix. It maps the input vector to the state derivatives.

C ndarray

Output matrix. It maps the state vector to the output vector.

D ndarray

Direct or feedforward matrix. It maps the input vector to the output vector.

input_labels list[str]

List of input labels.

output_labels list[str]

List of output labels.

Methods:

Name Description
get_state

Get the current state vector.

get_output

Compute the system's output from the current state.

get_input

Get the current inputs.

set_input

Pass new inputs to the system.

update_state

Assign new values to the system's state vector.

eval

Compute the system's state derivative.

Raises:

Type Description
TypeError

If any of the matrices is not an np.array.

ValueError

If any of the matrices has incorrect dimensions.

See also

NonlinearModel: Implements a nonlinear state-space model. Supports linear time-varying systems and linear parameter-varying systems, as well as generic nonlinear systems.

Notes

A linear state-space model describes the relation between the inputs and outputs of a system as a set of linear, first-order differential equations. These equations may be written in matrix form:

\[ \dot{x}(t) = A \cdot x(t) + B \cdot u(t) \]
\[ y(t) = C \cdot x(t) + D \cdot u(t) \]

where \(x\) is called the state vector, \(u\) is the input vector and \(y\) is the output vector. The first equation is called the state equation, and the second one is the output equation. The former relates the system's current state and the inputs to the change in state, while the latter relates those same quantities to the system's outputs.

If the system is time-invariant (as assumed by this class), then the A, B, C and D matrices are constant.

Source code in pynamics/models/state_space_models.py
def __init__(self, initial_state: np.ndarray, 
             initial_control: np.ndarray | float, 
             A: np.ndarray, 
             B: np.ndarray, 
             C: np.ndarray, 
             D: np.ndarray,
             input_labels: list[str] | None=None, 
             output_labels: list[str] | None=None) -> None:

    """
    Class constructor.
    """

    self._matrix_type_checks(A, B, C, D);
    C, D = self._matrix_reformatting(C, D);
    super().__init__(initial_state, B.shape[1], C.shape[0], input_labels, output_labels);
    self.u = self._control_type_checks(initial_control);
    self._matrix_dim_checks(A, B, C, D);
    self.A = A;
    self.B = B;
    self.C = C;
    self.D = D;

    return;

eval(t, x)

Compute the system's state derivative.

This method computes the system's state derivative via the state equation: \(\dot{x} = A \cdot x + B \cdot u\), where \(x\) is the state vector and \(u\) is the input vector.

Parameters:

Name Type Description Default
t float

Time instant. Used for compatibility reasons. Unused by this method.

required
x ndarray

The current state vector.

required

Returns:

Type Description
ndarray

The system's state derivative.

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.ones((3, 1)), np.zeros((1, 1)), A, B, C, D);
>>>
>>> model.eval(0.0, x=model.get_state())
array([[-1.],
       [-2.],
       [-2.]])
Source code in pynamics/models/state_space_models.py
def eval(self, t: float, x: np.ndarray) -> np.ndarray:
    """
    Compute the system's state derivative.

    This method computes the system's state derivative via the state \
    equation: $\dot{x} = A \cdot x + B \cdot u$, where $x$ is the state vector \
    and $u$ is the input vector.

    Parameters
    ----------
    t : float
        Time instant. Used for compatibility reasons. Unused by this method.

    x : np.ndarray
        The current state vector.

    Returns
    -------
    np.ndarray
        The system's state derivative.

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.ones((3, 1)), np.zeros((1, 1)), A, B, C, D);
    >>>
    >>> model.eval(0.0, x=model.get_state())
    array([[-1.],
           [-2.],
           [-2.]])
    """

    return np.matmul(self.A, x) + np.matmul(self.B, self.u);

get_input()

Access the system's input.

This method can be use to access the current input vector.

Returns:

Type Description
ndarray

Current input vector.

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.zeros((3, 1)), np.ones((1, 1)), A, B, C, D);
>>>
>>> model.get_input()
array([[1.]])
Source code in pynamics/models/state_space_models.py
def get_input(self) -> np.ndarray:
    """
    Access the system's input.

    This method can be use to access the current input vector.

    Returns
    -------
    np.ndarray
        Current input vector.

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.zeros((3, 1)), np.ones((1, 1)), A, B, C, D);
    >>>
    >>> model.get_input()
    array([[1.]])
    """

    return self.u;

get_output()

Compute the system's output from the current state vector.

This method can be used to compute the output of a linear state-space model from its current state vector. This is done by computing \(y = C \cdot x + D \cdot u\), where \(y\) is the output vector, \(x\) is the state vector, and \(u\) is the input vector.

Returns:

Type Description
ndarray

Output vector.

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.ones((3, 1)), np.zeros((1, 1)), A, B, C, D);
>>>
>>> model.get_output()
array([[1.]])
Source code in pynamics/models/state_space_models.py
def get_output(self) -> np.ndarray:
    """
    Compute the system's output from the current state vector.

    This method can be used to compute the output of a linear state-space \
    model from its current state vector. This is done by computing \
    $y = C \cdot x + D \cdot u$, where $y$ is the output vector, $x$ is the state vector, \
    and $u$ is the input vector.

    Returns
    -------
    np.ndarray
        Output vector.

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.ones((3, 1)), np.zeros((1, 1)), A, B, C, D);
    >>>
    >>> model.get_output()
    array([[1.]])
    """

    return np.matmul(self.C, self.x) + np.matmul(self.D, self.u);

get_state()

Access the system's state.

This method allows one to access the current state vector.

Returns:

Type Description
ndarray

Current state vector.

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.zeros((3, 1)), np.zeros((1, 1)), A, B, C, D);
>>>
>>> model.get_state()
array([[0.],
       [0.],
       [0.]])
Source code in pynamics/models/state_space_models.py
def get_state(self) -> np.ndarray:  
    """
    Access the system's state.

    This method allows one to access the current state vector.

    Returns
    -------
    np.ndarray
        Current state vector.

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.zeros((3, 1)), np.zeros((1, 1)), A, B, C, D);
    >>>
    >>> model.get_state()
    array([[0.],
           [0.],
           [0.]]) 
    """

    return self.x;

set_input(u)

Pass a new set of inputs (references, control actions, etc.) to the system.

This method can be used to update the system's input vector directly.

Parameters:

Name Type Description Default
u ndarray | float

The new set of inputs (i.e. input vector).

required

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.zeros((3, 1)), np.ones((1, 1)), A, B, C, D);
>>>
>>> model.get_input()
array([[1.]])
>>>
>>> model.set_input(np.array([[5]]));
>>> model.get_input()
array([[5.]])

For single-input systems, floats and integers also constitute valid inputs:

>>> model.set_input(2.5);
>>> model.get_input()
array([[2.5]])
Source code in pynamics/models/state_space_models.py
def set_input(self, u: np.ndarray | float) -> None:
    """
    Pass a new set of inputs (references, control actions, etc.) to the system.

    This method can be used to update the system's input vector directly.

    Parameters
    ----------
    u : np.ndarray | float
        The new set of inputs (i.e. input vector).

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.zeros((3, 1)), np.ones((1, 1)), A, B, C, D);
    >>>
    >>> model.get_input()
    array([[1.]])
    >>>
    >>> model.set_input(np.array([[5]]));
    >>> model.get_input()
    array([[5.]])

    For single-input systems, floats and integers also constitute valid inputs:

    >>> model.set_input(2.5);
    >>> model.get_input()
    array([[2.5]])
    """

    self.u = self._control_type_checks(u);

    return;

update_state(state)

Assign new values to the system's state vector.

This method can be used to update the system's state vector directly.

Parameters:

Name Type Description Default
state ndarray

New state vector.

required

Examples:

>>> import numpy as np
>>> from pynamics.models.state_space_models import LinearModel
>>>
>>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
>>> B = np.array([1, -5, 1]).reshape(-1, 1);
>>> C = np.array([0, 0, 1]);
>>> D = np.array([0]);
>>> model = LinearModel(np.zeros((3, 1)), np.zeros((1, 1)), A, B, C, D);
>>> 
>>> model.get_state()
array([[0.],
       [0.],
       [0.]])
>>>
>>> model.update_state(np.ones((3, 1)));
>>> model.get_state()
array([[1.],
       [1.],
       [1.]])
Source code in pynamics/models/state_space_models.py
def update_state(self, state: np.ndarray) -> None:
    """
    Assign new values to the system's state vector.

    This method can be used to update the system's state vector directly.

    Parameters
    ----------
    state : np.ndarray
        New state vector.

    Examples
    --------
    >>> import numpy as np
    >>> from pynamics.models.state_space_models import LinearModel
    >>>
    >>> A = np.array([[0, 0, -1], [1, 0, -3], [0, 1, -3]]);
    >>> B = np.array([1, -5, 1]).reshape(-1, 1);
    >>> C = np.array([0, 0, 1]);
    >>> D = np.array([0]);
    >>> model = LinearModel(np.zeros((3, 1)), np.zeros((1, 1)), A, B, C, D);
    >>> 
    >>> model.get_state()
    array([[0.],
           [0.],
           [0.]])
    >>>
    >>> model.update_state(np.ones((3, 1)));
    >>> model.get_state()
    array([[1.],
           [1.],
           [1.]])
    """

    self.x = state;

    return;