def __getattr__(self, item):
        # We do this because __setattr__ and __getattr__ are not active until
        # _group_attribute_access_active attribute is set, and if it is set,
        # then __getattr__ will not be called. Therefore, if getattr is called
        # with this name, it is because it hasn't been set yet and so this
        # method should raise an AttributeError to agree that it hasn't been
        # called yet.
        if item == '_group_attribute_access_active':
            raise AttributeError
        if not hasattr(self, '_group_attribute_access_active'):
            raise AttributeError

        mon = self.monitor
        if item == 't':
            return Quantity(mon.variables['t'].get_value(), dim=second.dim)
        elif item == 't_':
            return mon.variables['t'].get_value()
        elif item in mon.record_variables:
            dims = mon.variables[item].dim
            return Quantity(mon.variables[item].get_value().T[self.indices],
                            dim=dims,
                            copy=True)
        elif item.endswith('_') and item[:-1] in mon.record_variables:
            return mon.variables[item[:-1]].get_value().T[self.indices].copy()
        else:
            raise AttributeError('Unknown attribute %s' % item)
 def timed_array_func(t, i):
     # We round according to the current defaultclock.dt
     K = _find_K(float(defaultclock.dt), dt)
     epsilon = dt / K
     time_step = np.clip(np.int_(np.round(np.asarray(t/epsilon)) / K),
                 0, len(values)-1)
     return Quantity(values[time_step, i], dim=dimensions)
            def wrapper_function(*args):
                arg_units = list(self._function._arg_units)

                if self._function.auto_vectorise:
                    arg_units += [DIMENSIONLESS]
                if not len(args) == len(arg_units):
                    raise ValueError(
                        ('Function %s got %d arguments, '
                         'expected %d') % (self._function.pyfunc.__name__,
                                           len(args), len(arg_units)))
                new_args = []
                for arg, arg_unit in zip(args, arg_units):
                    if arg_unit == bool or arg_unit is None or isinstance(
                            arg_unit, str):
                        new_args.append(arg)
                    else:
                        new_args.append(
                            Quantity.with_dimensions(arg,
                                                     get_dimensions(arg_unit)))
                result = orig_func(*new_args)
                if isinstance(self._function._return_unit, Callable):
                    return_unit = self._function._return_unit(
                        *[get_dimensions(a) for a in args])
                else:
                    return_unit = self._function._return_unit
                if return_unit == bool:
                    if not (isinstance(result, bool)
                            or np.asarray(result).dtype == bool):
                        raise TypeError('The function %s returned '
                                        '%s, but it was expected '
                                        'to return a boolean '
                                        'value ' %
                                        (orig_func.__name__, result))
                elif (isinstance(return_unit, int) and return_unit
                      == 1) or return_unit.dim is DIMENSIONLESS:
                    fail_for_dimension_mismatch(result,
                                                return_unit,
                                                'The function %s returned '
                                                '{value}, but it was expected '
                                                'to return a dimensionless '
                                                'quantity' %
                                                orig_func.__name__,
                                                value=result)
                else:
                    fail_for_dimension_mismatch(
                        result,
                        return_unit,
                        ('The function %s returned '
                         '{value}, but it was expected '
                         'to return a quantity with '
                         'units %r') % (orig_func.__name__, return_unit),
                        value=result)
                return np.asarray(result)
Пример #4
0
 def new_f(*args, **kwds):
     newargs = []
     newkwds = {}
     for arg in args:
         newargs.append(modify_arg(arg))
     for k, v in kwds.items():
         newkwds[k] = modify_arg(v)
     rv = f(*newargs, **newkwds)
     if rv.__class__==b1h.Sound:
         rv.__class__ = BridgeSound
     elif isinstance(rv, b1.Quantity):
         rv = Quantity.with_dimensions(float(rv), rv.dim._dims)
     return rv
 def _values_dict(self, first_pos, sort_indices, used_indices, var):
     sorted_values = self.state(var, use_units=False)[sort_indices]
     dim = self.variables[var].dim
     event_values = {}
     current_pos = 0  # position in the all_indices array
     for idx in range(len(self.source)):
         if current_pos < len(
                 used_indices) and used_indices[current_pos] == idx:
             if current_pos < len(used_indices) - 1:
                 event_values[idx] = Quantity(sorted_values[
                     first_pos[current_pos]:first_pos[current_pos + 1]],
                                              dim=dim,
                                              copy=False)
             else:
                 event_values[idx] = Quantity(
                     sorted_values[first_pos[current_pos]:],
                     dim=dim,
                     copy=False)
             current_pos += 1
         else:
             event_values[idx] = Quantity([], dim=dim)
     return event_values
 def __getattr__(self, item):
     # We do this because __setattr__ and __getattr__ are not active until
     # _group_attribute_access_active attribute is set, and if it is set,
     # then __getattr__ will not be called. Therefore, if getattr is called
     # with this name, it is because it hasn't been set yet and so this
     # method should raise an AttributeError to agree that it hasn't been
     # called yet.
     if item == '_group_attribute_access_active':
         raise AttributeError
     if not hasattr(self, '_group_attribute_access_active'):
         raise AttributeError
     if item in self.record_variables:
         var_dim = self.variables[item].dim
         return Quantity(self.variables[item].get_value().T,
                         dim=var_dim,
                         copy=True)
     elif item.endswith('_') and item[:-1] in self.record_variables:
         return self.variables[item[:-1]].get_value().T
     else:
         return Group.__getattr__(self, item)
    def _check_args(self, indices, times, period, N, sorted, dt):
        times = Quantity(times)
        if len(indices) != len(times):
            raise ValueError(('Length of the indices and times array must '
                              'match, but %d != %d') % (len(indices),
                                                        len(times)))
        if period < 0*second:
            raise ValueError('The period cannot be negative.')
        elif len(times) and period != 0*second:
            period_bins = np.round(period / dt)
            # Note: we have to use the timestep function here, to use the same
            # binning as in the actual simulation
            max_bin = timestep(np.max(times), dt)
            if max_bin >= period_bins:
                raise ValueError('The period has to be greater than the '
                                 'maximum of the spike times')
        if len(times) and np.min(times) < 0*second:
            raise ValueError('Spike times cannot be negative')
        if len(indices) and (np.min(indices) < 0 or np.max(indices) >= N):
            raise ValueError('Indices have to lie in the interval [0, %d[' % N)

        times = np.asarray(times)
        indices = np.asarray(indices)
        if not sorted:
            # sort times and indices first by time, then by indices
            I = np.lexsort((indices, times))
            indices = indices[I]
            times = times[I]

        # We store the indices and times also directly in the Python object,
        # this way we can use them for checks in `before_run` even in standalone
        # TODO: Remove this when the checks in `before_run` have been moved to the template
        self._neuron_index = indices
        self._spike_time = times
        self._spikes_changed = True

        return indices, times
Пример #8
0
def convert_unit_b1_to_b2(val):
    return Quantity.with_dimensions(float(val), arg.dim._dims)
    def smooth_rate(self, window='gaussian', width=None):
        '''
        smooth_rate(self, window='gaussian', width=None)

        Return a smooth version of the population rate.

        Parameters
        ----------
        window : str, ndarray
            The window to use for smoothing. Can be a string to chose a
            predefined window(``'flat'`` for a rectangular, and ``'gaussian'``
            for a Gaussian-shaped window). In this case the width of the window
            is determined by the ``width`` argument. Note that for the Gaussian
            window, the ``width`` parameter specifies the standard deviation of
            the Gaussian, the width of the actual window is ``4*width + dt``
            (rounded to the nearest dt). For the flat window, the width is
            rounded to the nearest odd multiple of dt to avoid shifting the rate
            in time.
            Alternatively, an arbitrary window can be given as a numpy array
            (with an odd number of elements). In this case, the width in units
            of time depends on the ``dt`` of the simulation, and no ``width``
            argument can be specified. The given window will be automatically
            normalized to a sum of 1.
        width : `Quantity`, optional
            The width of the ``window`` in seconds (for a predefined window).

        Returns
        -------
        rate : `Quantity`
            The population rate in Hz, smoothed with the given window. Note that
            the rates are smoothed and not re-binned, i.e. the length of the
            returned array is the same as the length of the ``rate`` attribute
            and can be plotted against the `PopulationRateMonitor` 's ``t``
            attribute.
        '''
        if width is None and isinstance(window, str):
            raise TypeError('Need a width when using a predefined window.')
        if width is not None and not isinstance(window, str):
            raise TypeError('Can only specify a width for a predefined window')

        if isinstance(window, str):
            if window == 'gaussian':
                width_dt = int(np.round(2 * width / self.clock.dt))
                # Rounding only for the size of the window, not for the standard
                # deviation of the Gaussian
                window = np.exp(-np.arange(-width_dt, width_dt + 1)**2 * 1. /
                                (2 * (width / self.clock.dt)**2))
            elif window == 'flat':
                width_dt = int(width / 2 / self.clock.dt) * 2 + 1
                used_width = width_dt * self.clock.dt
                if abs(used_width - width) > 1e-6 * self.clock.dt:
                    logger.info('width adjusted from %s to %s' %
                                (width, used_width),
                                'adjusted_width',
                                once=True)
                window = np.ones(width_dt)
            else:
                raise NotImplementedError('Unknown pre-defined window "%s"' %
                                          window)
        else:
            try:
                window = np.asarray(window)
            except TypeError:
                raise TypeError('Cannot use a window of type %s' %
                                type(window))
            if window.ndim != 1:
                raise TypeError('The provided window has to be '
                                'one-dimensional.')
            if len(window) % 2 != 1:
                raise TypeError('The window has to have an odd number of '
                                'values.')
        return Quantity(np.convolve(self.rate_,
                                    window * 1. / sum(window),
                                    mode='same'),
                        dim=hertz.dim)
Пример #10
0
class Clock(VariableOwner):
    '''
    An object that holds the simulation time and the time step.
    
    Parameters
    ----------
    dt : float
        The time step of the simulation as a float
    name : str, optional
        An explicit name, if not specified gives an automatically generated name

    Notes
    -----
    Clocks are run in the same `Network.run` iteration if `~Clock.t` is the
    same. The condition for two
    clocks to be considered as having the same time is
    ``abs(t1-t2)<epsilon*abs(t1)``, a standard test for equality of floating
    point values. The value of ``epsilon`` is ``1e-14``.
    '''
    def __init__(self, dt, name='clock*'):
        # We need a name right away because some devices (e.g. cpp_standalone)
        # need a name for the object when creating the variables
        Nameable.__init__(self, name=name)
        self._old_dt = None
        self.variables = Variables(self)
        self.variables.add_array('timestep',
                                 size=1,
                                 dtype=np.int64,
                                 read_only=True,
                                 scalar=True)
        self.variables.add_array('t',
                                 dimensions=second.dim,
                                 size=1,
                                 dtype=np.double,
                                 read_only=True,
                                 scalar=True)
        self.variables.add_array('dt',
                                 dimensions=second.dim,
                                 size=1,
                                 values=float(dt),
                                 dtype=np.float,
                                 read_only=True,
                                 constant=True,
                                 scalar=True)
        self.variables.add_constant('N', value=1)
        self._enable_group_attributes()
        self.dt = dt
        logger.diagnostic("Created clock {name} with dt={dt}".format(
            name=self.name, dt=self.dt))

    @check_units(t=second)
    def _set_t_update_dt(self, target_t=0 * second):
        new_dt = self.dt_
        old_dt = self._old_dt
        target_t = float(target_t)
        if old_dt is not None and new_dt != old_dt:
            self._old_dt = None
            # Only allow a new dt which allows to correctly set the new time step
            check_dt(new_dt, old_dt, target_t)

        new_timestep = self._calc_timestep(target_t)
        # Since these attributes are read-only for normal users, we have to
        # update them via the variables object directly
        self.variables['timestep'].set_value(new_timestep)
        self.variables['t'].set_value(new_timestep * new_dt)
        logger.diagnostic("Setting Clock {name} to t={t}, dt={dt}".format(
            name=self.name, t=self.t, dt=self.dt))

    def _calc_timestep(self, target_t):
        '''
        Calculate the integer time step for the target time. If it cannot be
        exactly represented (up to 0.01% of dt), round up.

        Parameters
        ----------
        target_t : float
            The target time in seconds

        Returns
        -------
        timestep : int
            The target time in integers (based on dt)
        '''
        new_i = np.int64(np.round(target_t / self.dt_))
        new_t = new_i * self.dt_
        if (new_t == target_t
                or np.abs(new_t - target_t) / self.dt_ <= Clock.epsilon_dt):
            new_timestep = new_i
        else:
            new_timestep = np.int64(np.ceil(target_t / self.dt_))
        return new_timestep

    def __repr__(self):
        return 'Clock(dt=%r, name=%r)' % (self.dt, self.name)

    def _get_dt_(self):
        return self.variables['dt'].get_value().item()

    @check_units(dt_=1)
    def _set_dt_(self, dt_):
        self._old_dt = self._get_dt_()
        self.variables['dt'].set_value(dt_)

    @check_units(dt=second)
    def _set_dt(self, dt):
        self._set_dt_(float(dt))

    dt = property(
        fget=lambda self: Quantity(self.dt_, dim=second.dim),
        fset=_set_dt,
        doc='''The time step of the simulation in seconds.''',
    )
    dt_ = property(
        fget=_get_dt_,
        fset=_set_dt_,
        doc='''The time step of the simulation as a float (in seconds)''')

    @check_units(start=second, end=second)
    def set_interval(self, start, end):
        '''
        set_interval(self, start, end)

        Set the start and end time of the simulation.

        Sets the start and end value of the clock precisely if
        possible (using epsilon) or rounding up if not. This assures that
        multiple calls to `Network.run` will not re-run the same time step.      
        '''
        self._set_t_update_dt(target_t=start)
        end = float(end)
        self._i_end = self._calc_timestep(end)
        if self._i_end > 2**40:
            logger.warn(
                'The end time of the simulation has been set to {}, '
                'which based on the dt value of {} means that {} '
                'time steps will be simulated. This can lead to '
                'numerical problems, e.g. the times t will not '
                'correspond to exact multiples of '
                'dt.'.format(str(end * second), str(self.dt), self._i_end),
                'many_timesteps')

    #: The relative difference for times (in terms of dt) so that they are
    #: considered identical.
    epsilon_dt = 1e-4