import logging
import numpy as np
from ..utils.config import make_configuration
from ..utils.observer import Observable
from ..utils import rand_bounds, bounds_min_max
from ..exceptions import ExplautoBootstrapError
logger = logging.getLogger(__name__)
[docs]class Agent(Observable):
def __init__(self, conf, sm_model, im_model, n_bootstrap=0):
Observable.__init__(self)
self.conf = conf
self.ms = np.zeros(self.conf.ndims)
self.expl_dims = im_model.expl_dims
self.inf_dims = sorted(list(set(conf.dims) - set(self.expl_dims)))
self.sensorimotor_model = sm_model
self.interest_model = im_model
# self.competence = competence
self.t = 0
self.n_bootstrap = n_bootstrap
@classmethod
[docs] def from_classes(cls,
im_model_cls, im_model_config, expl_dims,
sm_model_cls, sm_model_config, inf_dims,
m_mins, m_maxs, s_mins, s_maxs, n_bootstrap=0):
"""Initialize agent class
:param class im_model_cls: a subclass of InterestedModel, as those registered in the interest_model package
:param dict im_model_config: a configuration dict as those registered in the interest_model package
:param list expl_dims: the sensorimotor dimensions where exploration is driven in the interest model
:param list inf_dims: the output sensorimotor dimensions of the sensorimotor model (input being expl_dims)
:param class sm_model_cls: a subclass of SensorimotorModel, as those registered in the sensorimotor_model package
:param dict sensorimotor_model_config: a configuration dict as those registered in the sensorimotor_model package
:param list m_mins, m_maxs, s_mins, s_max: lower and upper bounds of motor and sensory values on each dimension
"""
conf = make_configuration(m_mins, m_maxs, s_mins, s_maxs)
# self.ms = np.zeros(self.conf.ndims)
# self.expl_dims = expl_dims
# self.inf_dims = inf_dims
sm_model = sm_model_cls(conf, **sm_model_config)
im_model = im_model_cls(conf, expl_dims,
**im_model_config)
return cls(conf, sm_model, im_model)
# # self.competence = competence
# self.t = 0
# self.n_bootstrap = n_bootstrap
# self.state = np.zeros(self.conf.ndims)
[docs] def choose(self):
""" Returns a point chosen by the interest model
"""
try:
x = self.interest_model.sample()
except ExplautoBootstrapError:
logger.warning('Interest model not bootstrapped yet')
x = rand_bounds(self.conf.bounds[:, self.expl_dims]).flatten()
return x
[docs] def infer(self, expl_dims, inf_dims, x):
""" Use the sensorimotor model to compute the expected value on inf_dims given that the value on expl_dims is x.
.. note:: This corresponds to a prediction if expl_dims=self.conf.m_dims and inf_dims=self.conf.s_dims and to inverse prediction if expl_dims=self.conf.s_dims and inf_dims=self.conf.m_dims.
"""
try:
if self.n_bootstrap > 0:
self.n_bootstrap -= 1
raise ExplautoBootstrapError
y = self.sensorimotor_model.infer(expl_dims,
inf_dims,
x.flatten())
except ExplautoBootstrapError:
logger.warning('Sensorimotor model not bootstrapped yet')
y = rand_bounds(self.conf.bounds[:, inf_dims]).flatten()
return y
[docs] def motor_primitive(self, m):
""" Prepare the movement from a command m. To be overridded in order to generate more complex movement (tutorial to come). This version simply bounds the command.
"""
return bounds_min_max(m, self.conf.m_mins, self.conf.m_maxs)
[docs] def sensory_primitive(self, s):
""" Extract features from a sensory effect s. To be overridded in order to process more complex feature extraction (tutorial to come). This version simply bounds the sensory effect.
"""
return bounds_min_max(s, self.conf.s_mins, self.conf.s_maxs)
[docs] def produce(self):
""" Exploration (see the `Explauto introduction <about.html>`__ for more detail):
* Choose a value x on expl_dims according to the interest model
* Infer a value y on inf_dims from x using the :meth:`~explauto.agent.agent.Agent.infer` method
* Extract the motor m and sensory s parts of x, y
* generate a movement from m
:returns: the generated movement
.. note:: This correspond to motor babbling if expl_dims=self.conf.m_dims and inf_dims=self.conf.s_dims and to goal babbling if expl_dims=self.conf.s_dims and inf_dims=self.conf.m_dims.
"""
self.x = self.choose()
self.y = self.infer(self.expl_dims, self.inf_dims, self.x)
self.m, self.s = self.extract_ms(self.x, self.y)
movement = self.motor_primitive(self.m)
self.emit('choice', self.x)
self.emit('inference', self.y)
self.emit('movement', movement)
return movement
[docs] def perceive(self, s_):
""" Learning (see the `Explauto introduction <about.html>`__ for more detail):
* update the sensorimotor model with (m, s)
* update the interest model with (x, y, m, s)
(x, y are stored in self.ms in :meth:`~explauto.agent.agent.Agent.production`)
"""
s = self.sensory_primitive(s_)
self.emit('perception', s)
self.sensorimotor_model.update(self.m, s)
self.interest_model.update(np.hstack((self.m, self.s)), np.hstack((self.m, s)))
self.t += 1