Source code for rtgym.agent.sensory.spatial_modulated.boundary_cell

import numpy as np
from .sm_base import SMBase
from scipy.signal import fftconvolve


[docs] class BoundaryCell(SMBase): """Boundary cells that respond based on distance to walls. These cells fire based on the distance to the nearest boundary in any direction. Note: This implementation may not be biologically realistic as it responds to boundaries in all directions simultaneously. Args: arena (Arena): Arena environment object. **kwargs: Additional keyword arguments including: n_cells (int): Number of boundary cells. res_dist (float): Response distance from boundary in spatial units. Defaults to 10. magnitude (float): Maximum magnitude of cell responses. normalize (bool): Whether to normalize cell responses. center_border_ratio (float): Ratio of center to border cells (0-1). Defaults to 0.5. Attributes: sens_type (str): Sensory type identifier 'boundary_cell'. """ sens_type = 'boundary_cell' def __init__(self, arena, **kwargs): super(BoundaryCell, self).__init__(arena, **kwargs) # parameters self.res_dist = kwargs.get('res_dist', 10) / self.arena.spatial_resolution self.magnitude = kwargs.get('magnitude', None) self.normalize = kwargs.get('normalize', False) self.center_border_ratio = kwargs.get('center_border_ratio', 0.5) # check parameters and initialize responses self._check_params() self._init_response_map() def _check_params(self): """ Check parameters """ assert self.n_cells > 0, "n_cells <= 0" # check res_dist assert self.res_dist > 0, "res_dist <= 0" assert self.res_dist < self.arena.dimensions[1], "res_dist >= arena.dimensions[1]" assert self.res_dist < self.arena.dimensions[0], "res_dist >= arena.dimensions[0]" def _init_response_map(self): """ Initialize response_map """ super(BoundaryCell, self)._init_response_map() # initialize response_map cell_res_dist = self.rng.normal(self.res_dist, self.res_dist/4, self.n_cells).astype(int) cell_res_dist = np.clip(cell_res_dist, 0, None) for i in range(self.n_cells): kernal = np.ones((cell_res_dist[i]*2, cell_res_dist[i]*2)) self.response_map[i, :, :] = -fftconvolve(self.arena.inv_arena_map, kernal, mode='same')/kernal.sum() self.response_map[i, :, :] *= self.rng.choice([-1, 1], 1, p=[self.center_border_ratio, 1-self.center_border_ratio]) self.response_map[i, :, :] -= self.response_map[i, :, :].min() # normalize response_map if self.normalize: self.response_map = (self.response_map - self.response_map.min()) / (self.response_map.max() - self.response_map.min()) # set magnitude if self.magnitude is not None: self.response_map = self.magnitude * self.response_map
[docs] def get_specs(self): specs = super().get_specs() specs['cell_max_avg'] = self.response_map.max(axis=(1, 2)).mean() specs['cell_min_avg'] = self.response_map.min(axis=(1, 2)).mean() specs['cell_mean_avg'] = self.response_map.mean(axis=(1, 2)).mean() return specs