Source code for symaware.base.components.perception_system

from abc import abstractmethod
from typing import TYPE_CHECKING

import numpy as np

from symaware.base.data import AwarenessVector, Identifier, StateObservation
from symaware.base.models import Environment
from symaware.base.utils import NullObject, Tasynclooplock

from .component import Component

if TYPE_CHECKING:
    import sys
    from typing import Any, Callable

    from symaware.base.agent import Agent

    if sys.version_info >= (3, 10):
        from typing import TypeAlias
    else:
        from typing_extensions import TypeAlias
    PerceivingInformationCallback: TypeAlias = Callable[[Agent], Any]
    PerceivedInformationCallback: TypeAlias = Callable[[Agent, dict[Identifier, StateObservation]], Any]


[docs] class PerceptionSystem(Component[Tasynclooplock, "PerceivingInformationCallback", "PerceivedInformationCallback"]): """ Generic perception system of an :class:`.symaware.base.Agent`. It is used to perceive the state of the :class:`.Agent` the :class:`symaware.base.Environment`. The implementation could extend this and include more complex interaction, like sensors or omniscient perception. The information collected is then used to update the awareness vector and knowledge database of the agent. Args ---- agent_id: Identifier of the agent this component belongs to environment: :class:`symaware.base.Environment` the perception system will observe async_loop_lock: Async loop lock to use for the perception system """ def __init__(self, agent_id: int, environment: Environment, async_loop_lock: "Tasynclooplock | None" = None): super().__init__(agent_id, async_loop_lock) self._env = environment @property def environment(self) -> Environment: """Environment""" return self._env
[docs] def perceive_self_state(self) -> np.ndarray: """ Perceive the state of the agent itself Returns ------- State of the agent Raises ------ RuntimeError: The perception system has not been initialised yet """ if not self._initialised: raise RuntimeError("The perception system has not been initialised yet.") return self._env.get_agent_state(self._agent_id)
[docs] @abstractmethod def _compute(self) -> dict[Identifier, StateObservation]: """ Perceive the state of each :class:`.Agent` in the :class:`symaware.base.Environment`. It is used to update the knowledge database of the agent. Example ------- Create a new perception system by subclassing the :class:`.PerceptionSystem` and implementing the :meth:`_compute` method. >>> from symaware.base import PerceptionSystem, StateObservation >>> class MyPerceptionSystem(PerceptionSystem): ... def _compute(self): ... # Your implementation here ... # Example: ... # Get only the agent's own state ... if not self._agent_id in self._env.agent_states: ... return {} ... return {self._agent_id: StateObservation(self._agent_id, self._env.agent_states[self._agent_id])} Returns ------- Information perceived by the agent. Each agent's state is identified by its id. """ pass
[docs] def _update(self, perceived_information: dict[Identifier, StateObservation]): """ Update the agent's model with the new perceived information. Example ------- A new perception system could decide to override the default :meth:`_update` method. >>> from symaware.base import PerceptionSystem, StateObservation, Identifier >>> class MyPerceptionSystem(PerceptionSystem): ... def _update(self, perceived_information: dict[Identifier, StateObservation]): ... # Your implementation here ... # Example: ... # Simply override the state of the agent ... self._agent.awareness_database[self._agent_id].state = perceived_information[self._agent_id].state Args ---- perceived_information: Information perceived by the agent. Each agent's state is identified by its id. """ for agent_id, state_observation in perceived_information.items(): awareness_vector = self._agent.awareness_database.setdefault( agent_id, AwarenessVector(agent_id, state_observation.state) ) awareness_vector.state = state_observation.state
[docs] class NullPerceptionSystem(PerceptionSystem[Tasynclooplock], NullObject): """ Default perception system used as a placeholder. It is used when no perception system is set for an agent. An exception is raised if this object is used in any way. """ def __init__(self): super().__init__(-1, None) # type: ignore
[docs] def _compute(self): pass
[docs] class DefaultPerceptionSystem(PerceptionSystem[Tasynclooplock]): """ Default implementation of perception system. It returns the values collected from the environment. Args ---- agent_id: Identifier of the agent this component belongs to """
[docs] def _compute(self) -> dict[Identifier, StateObservation]: """ Compute the information perceived by the agent. It returns the values collected from the environment. Returns ------- Information perceived by the agent. Each agent's state is identified by its id. """ return {agent_id: StateObservation(agent_id, state) for agent_id, state in self._env.agent_states.items()}