Source code for symaware.base.models.dynamical_model

from abc import ABC, abstractmethod
from typing import TypedDict, cast

import numpy as np

from symaware.base.data import Identifier
from symaware.base.utils import NullObject


[docs] class DynamicalModelSubinputs(TypedDict): pass
[docs] class DynamicalModel(ABC): """ Abstract class for dynamical models that will simulate the behaviour of an entity. At each step of the simulation, the model is fed the control input from the :class:`.Controller`. Args ---- ID: Identifier of the agent this model belongs to. A value of -1 indicates that no agent is associated with this model. control_input: Initial control input of the model. Is is also used to check the shape of future control inputs """ def __init__(self, ID: Identifier, control_input: np.ndarray): self._ID = ID self._control_input = control_input @property def id(self) -> Identifier: """ Identifier of the agent this model belongs to. A value of -1 indicates that no agent is associated with this model. """ return self._ID @property def control_input(self) -> np.ndarray: """Last control input received by the model.""" return self._control_input @control_input.setter def control_input(self, value: np.ndarray): if not isinstance(value, np.ndarray): raise TypeError(f"Expected numpy array, got {type(value)}") if not value.shape == self.control_input_shape: raise ValueError(f"Expected numpy array of shape {self.control_input_shape}, got {value.shape}") self._control_input = value
[docs] @abstractmethod def initialise(self, *args, **kwargs): """ Initialise the dynamical model. Called by the associated entity when it is added to the environment. Args ---- args: Additional arguments kwargs: Additional keyword arguments """ pass
[docs] @abstractmethod def step(self, *args, **kwargs) -> np.ndarray: """ Run a simulation step of the dynamical model. Called by the associated entity at each simulation step. Args ---- args: Additional arguments kwargs: Additional keyword arguments """ pass
@property def control_input_size(self) -> int: """Size of the control input vector.""" return self.control_input.size @property def control_input_shape(self) -> tuple[int, ...]: """Shape of the control input vector.""" return self.control_input.shape @property @abstractmethod def subinputs_dict(self) -> DynamicalModelSubinputs: """ The input of a system is the composition of subinputs. Example ------- A car input [vx, vy, vz, s] is composed by the velocity and the steering angle. >>> from symaware.base import DynamicalModel >>> class CarModel(DynamicalModel): ... @property ... def subinputs_dict(self): ... return { ... "velocity": self._control_input[:3], ... "steering_angle": self._control_input[3] ... } ... Important --------- The order of the subinputs in the list must be the same as the order of the subinputs in the input vector """ pass def __str__(self) -> str: sub_inputs = "\n".join( f"key : {key}\nsize : {cast(np.ndarray, subinput).size}" for key, subinput in self.subinputs_dict.items() ) return ( f"{self.__class__.__name__} ({self._ID})(\n" f"control input size: {self.control_input_size}\n" "List of subinputs (subinputs are stacked in the given order to give the input vector):\n" f"{sub_inputs}\n)" )
[docs] class NullDynamicalModel(DynamicalModel, NullObject): """ Default dynamical model used as a placeholder. It is used when no dynamical model has been defined for an entity. The entity will just ignore it and won't have any step function associated with it. An exception is raised if this object is used in any way. """ def __init__(self): super().__init__(-1, np.zeros(0))
[docs] def initialise(self): pass
[docs] def step(self): pass
@property def subinputs_dict(self): pass