Source code for palaestrai.agent.actuator_information
from __future__ import annotations
import logging
import warnings
from typing import TYPE_CHECKING, Optional, Union, Any
import numpy as np
from palaestrai.types import Space
from palaestrai.util.exception import OutOfActionSpaceError
if TYPE_CHECKING:
import palaestrai.types
LOG = logging.getLogger(__name__)
[docs]
class ActuatorInformation:
"""Stores information about a single actuator.
The actuator information class is used to transfer actuator
information. It can be called to set a new value (value):
.. code-block::
a = Actuator(some_space)
a(42) # a.value is now 42
Parameters
----------
value : any, optional
The set value for this actuator. The type is defined by the
:attr:`space`. Can be skipped and set afterwards.
space : :class:`palaestrai.types.Space`
An instance of a palaestrai space that defines the type of
the :attr:`value`.
uid : int or str, optional
A unique identifier for this actuator. The agents use this ID
only to assign the value_ids to the correct actuator. The ID
is not analyzed to gain domain knowledge.
value_ids: list of str or int or None (default: None) if the actuator
has multiple value_ids, the value_ids can be used to identify the
value_ids, e.g., the ids can be the names of the value_ids. This should
be used if value is a list or a numpy array.
setpoint : int or str, optional
*Deprecated* in favor of value
action_space : :class:`palaestrai.types.Space`
*Deprecated* in favor of space
actuator_id : int or str, optional
*Deprecated* in favor of uid
"""
def __init__(
self,
value: Optional[Union[int, float, np.ndarray]] = None,
space: Optional[Space] = None,
uid: Optional[str] = None,
value_ids=None,
setpoint=None,
action_space: Optional[Space] = None,
actuator_id=None,
):
self._uid = uid if uid is not None else actuator_id
self._value = value if value is not None else setpoint
self._space = space if space is not None else action_space
self.value_ids = value_ids
assert self._uid is not None, "Must give ActuatorInformation.uid"
assert self._space is not None
ActuatorInformation._assert_value_in_space(self._value, self._space)
@property
def value_ids(self):
return self._value_ids
@value_ids.setter
def value_ids(self, value):
self._value_ids = value
@property
def setpoint(self):
warnings.warn(
f"setpoint property deprecated in class {self.__class__}. Use "
f"value instead.",
DeprecationWarning,
stacklevel=2,
)
return self.value
@setpoint.setter
def setpoint(self, setpoint):
warnings.warn(
f"setpoint property deprecated in class {self.__class__}. Use "
f"value instead.",
DeprecationWarning,
stacklevel=2,
)
self.value = setpoint
@property
def value(self):
return self._value
@value.setter
def value(self, value):
ActuatorInformation._assert_value_in_space(value, self.space)
self._value = value
[docs]
def flat_value(self, **kwargs):
"""Return a flat vector representation of the value"""
return self.space.to_vector(np.array(self.value), **kwargs)
def __call__(self, setpoint):
self.value = setpoint
def __repr__(self):
return "ActuatorInformation(" "value=%s, space=%s, uid=%s)" % (
self.value,
repr(self.space),
self.uid,
)
def __len__(self):
"""The number of values in the space"""
return len(self.space)
def __eq__(self, other):
return (
isinstance(other, ActuatorInformation)
and self.uid == other.uid
and self.value == other.value
and self.space == other.space
and self.value_ids == other.value_ids
)
def __getstate__(self):
return dict(
uid=self.uid,
value=(
self._value.tolist()
if isinstance(self._value, np.ndarray)
else self._value
),
space=self._space.to_string(),
value_ids=self._value_ids,
)
def __setstate__(self, state):
self._uid = state["uid"]
self._space = Space.from_string(state["space"])
assert self._space is not None
self._value = (
np.array(state["value"], dtype=self._space.dtype)
if isinstance(state["value"], list)
else state["value"]
)
self._value_ids = state["value_ids"]
@property
def space(self) -> Space:
assert self._space is not None
return self._space
@space.setter
def space(self, space: Space):
assert space is not None
ActuatorInformation._assert_value_in_space(self._value, space)
self._space = space
@property
def action_space(self):
warnings.warn(
f"action_space property deprecated in class {self.__class__}. Use "
f"space instead.",
DeprecationWarning,
stacklevel=2,
)
return self.space
@action_space.setter
def action_space(self, space: Space):
warnings.warn(
f"action_space setter deprecated in class {self.__class__}. Use "
f"space instead.",
DeprecationWarning,
stacklevel=2,
)
self.space = space
@property
def id(self):
warnings.warn(
f"id property deprecated in class {self.__class__}. Use "
f"uid instead.",
DeprecationWarning,
stacklevel=2,
)
return self.uid
@id.setter
def id(self, value):
warnings.warn(
f"id property deprecated in class {self.__class__}. Use "
f"uid instead.",
DeprecationWarning,
stacklevel=2,
)
self.uid = value
@property
def actuator_id(self):
warnings.warn(
f"actuator_id property deprecated in class {self.__class__}. Use "
f"uid instead.",
DeprecationWarning,
stacklevel=2,
)
return self.uid
@actuator_id.setter
def actuator_id(self, value):
warnings.warn(
f"actuator_id property deprecated in class {self.__class__}. Use "
f"uid instead.",
DeprecationWarning,
stacklevel=2,
)
self.uid = value
@property
def uid(self) -> str:
return self._uid
@uid.setter
def uid(self, value: str):
self._uid = value
@staticmethod
def _assert_value_in_space(value: Any, space: Space):
if value is None:
LOG.warning(
"The value to be set is None. "
"Its the recommended value if wanting to share meta "
"information about an information class, but "
"be careful when wanting to use it as a value!"
)
elif not space.contains(value):
raise OutOfActionSpaceError(
f'Value "{value}" not contained in space "{space}"'
)