Ejemplo n.º 1
0
    def __init__(self, target, variables):
        self.target = target
        for mon_var in variables:
            if not hasattr(target, mon_var):
                raise errors.ModelDefError(
                    f"Item {mon_var} isn't defined in model {target}, "
                    f"so it can not be monitored.")

        item_names = []
        mon_indices = []
        item_content = {}
        if variables is not None:
            if isinstance(variables, (list, tuple)):
                for mon_var in variables:
                    if isinstance(mon_var, str):
                        var_data = getattr(target, mon_var)
                        mon_key = mon_var
                        mon_idx = None
                        mon_shape = ops.shape(var_data)
                    elif isinstance(mon_var, (tuple, list)):
                        mon_key = mon_var[0]
                        var_data = getattr(target, mon_key)
                        mon_idx = mon_var[1]
                        mon_shape = ops.shape(mon_idx)  # TODO: matrix index
                    else:
                        raise errors.ModelUseError(
                            f'Unknown monitor item: {str(mon_var)}')
                    item_names.append(mon_key)
                    mon_indices.append(mon_idx)
                    dtype = var_data.dtype if hasattr(var_data,
                                                      'dtype') else None
                    item_content[mon_var] = ops.zeros((1, ) + mon_shape,
                                                      dtype=dtype)
            elif isinstance(variables, dict):
                for k, v in variables.items():
                    item_names.append(k)
                    mon_indices.append(v)
                    if v is None:
                        shape = ops.shape(getattr(target, k))
                    else:
                        shape = ops.shape(v)
                    val_data = getattr(target, k)
                    dtype = val_data.dtype if hasattr(val_data,
                                                      'dtype') else None
                    item_content[k] = ops.zeros((1, ) + shape, dtype=dtype)
            else:
                raise errors.ModelUseError(
                    f'Unknown monitors type: {type(variables)}')

        self.ts = None
        self.item_names = item_names
        self.item_indices = mon_indices
        self.item_contents = item_content
        self.num_item = len(item_content)
Ejemplo n.º 2
0
def ij2mat(i, j, num_pre=None, num_post=None):
    """Convert i-j connection to matrix connection.

    Parameters
    ----------
    i : list, np.ndarray
        Pre-synaptic neuron index.
    j : list, np.ndarray
        Post-synaptic neuron index.
    num_pre : int
        The number of the pre-synaptic neurons.
    num_post : int
        The number of the post-synaptic neurons.

    Returns
    -------
    conn_mat : np.ndarray
        A 2D ndarray connectivity matrix.
    """
    if len(i) != len(j):
        raise errors.ModelUseError('"i" and "j" must be the equal length.')
    if num_pre is None:
        print(
            'WARNING: "num_pre" is not provided, the result may not be accurate.'
        )
        num_pre = i.max()
    if num_post is None:
        print(
            'WARNING: "num_post" is not provided, the result may not be accurate.'
        )
        num_post = j.max()
    conn_mat = ops.zeros((num_pre, num_post))
    conn_mat[i, j] = 1.
    return conn_mat
Ejemplo n.º 3
0
def ramp_input(c_start, c_end, duration, t_start=0, t_end=None, dt=None):
    """Get the gradually changed input current.

    Parameters
    ----------
    c_start : float
        The minimum (or maximum) current size.
    c_end : float
        The maximum (or minimum) current size.
    duration : int, float
        The total duration.
    t_start : float
        The ramped current start time-point.
    t_end : float
        The ramped current end time-point. Default is the None.
    dt : float, int, optional
        The numerical precision.

    Returns
    -------
    current_and_duration : tuple
        (The formatted current, total duration)
    """
    dt = backend.get_dt() if dt is None else dt
    t_end = duration if t_end is None else t_end

    current = ops.zeros(int(math.ceil(duration / dt)))
    p1 = int(math.ceil(t_start / dt))
    p2 = int(math.ceil(t_end / dt))
    current[p1: p2] = ops.as_tensor(np.linspace(c_start, c_end, p2 - p1))
    return current
Ejemplo n.º 4
0
    def __init__(self, v0, delay_len, before_t0=0., t0=0., dt=None):
        # size
        self.size = ops.shape(v0)

        # delay_len
        self.delay_len = delay_len
        self.dt = backend.get_dt() if dt is None else dt
        self.num_delay = int(math.ceil(delay_len / self.dt))

        # other variables
        self._delay_in = self.num_delay - 1
        self._delay_out = 0
        self.current_time = t0

        # before_t0
        self.before_t0 = before_t0

        # delay data
        self.data = ops.zeros((self.num_delay + 1,) + self.size)
        if callable(before_t0):
            for i in range(self.num_delay):
                self.data[i] = before_t0(t0 + (i - self.num_delay) * self.dt)
        else:
            self.data[:-1] = before_t0
        self.data[-1] = v0
Ejemplo n.º 5
0
    def __init__(self, size, delay_time):
        if isinstance(size, int):
            size = (size, )
        self.size = tuple(size)
        self.delay_time = delay_time

        if isinstance(delay_time, (int, float)):
            self.uniform_delay = True
            self.delay_num_step = int(math.ceil(
                delay_time / backend.get_dt())) + 1
            self.delay_data = ops.zeros((self.delay_num_step, ) + self.size)
        else:
            if not len(self.size) == 1:
                raise NotImplementedError(
                    f'Currently, BrainPy only supports 1D heterogeneous delays, while does '
                    f'not implement the heterogeneous delay with {len(self.size)}-dimensions.'
                )
            self.num = size2len(size)
            if isinstance(delay_time, type(ops.as_tensor([1]))):
                assert ops.shape(delay_time) == self.size
            elif callable(delay_time):
                delay_time2 = ops.zeros(size)
                for i in range(size[0]):
                    delay_time2[i] = delay_time()
                delay_time = delay_time2
            else:
                raise NotImplementedError(
                    f'Currently, BrainPy does not support delay type '
                    f'of {type(delay_time)}: {delay_time}')
            self.uniform_delay = False
            delay = delay_time / backend.get_dt()
            dint = ops.as_tensor(delay_time / backend.get_dt(), dtype=int)
            ddiff = (delay - dint) >= 0.5
            self.delay_num_step = ops.as_tensor(delay + ddiff, dtype=int) + 1
            self.delay_data = ops.zeros((max(self.delay_num_step), ) + size)
            self.diag = ops.arange(self.num)

        self.delay_in_idx = self.delay_num_step - 1
        if self.uniform_delay:
            self.delay_out_idx = 0
        else:
            self.delay_out_idx = ops.zeros(self.num, dtype=int)
        self.name = None
Ejemplo n.º 6
0
    def __init__(self, size, freqs, **kwargs):
        self.dt = backend.get_dt() / 1000.
        self.freqs = freqs
        self.size = (size,) if isinstance(size, int) else tuple(size)
        self.num = size2len(size)
        self.spike = ops.zeros(self.num, dtype=bool)
        self.t_last_spike = -1e7 * ops.ones(self.num)

        if backend.get_backend_name() == 'numba-cuda':
            super(PoissonInput, self).__init__(steps={'update': self.numba_cuda_update}, **kwargs)
        else:
            super(PoissonInput, self).__init__(steps={'update': self.non_numba_cuda_update}, **kwargs)
Ejemplo n.º 7
0
def period_input(values, durations, dt=None, return_length=False):
    """Format an input current with different periods.

    For example:

    If you want to get an input where the size is 0 bwteen 0-100 ms,
    and the size is 1. between 100-200 ms.
    >>> import numpy as np
    >>> period_input(values=[0, 1],
    >>>              durations=[100, 100])

    Parameters
    ----------
    values : list, np.ndarray
        The current values for each period duration.
    durations : list, np.ndarray
        The duration for each period.
    dt : float
        Default is None.
    return_length : bool
        Return the final duration length.

    Returns
    -------
    current_and_duration : tuple
        (The formatted current, total duration)
    """
    assert len(durations) == len(values), f'"values" and "durations" must be the same length, while ' \
                                          f'we got {len(values)} != {len(durations)}.'

    dt = backend.get_dt() if dt is None else dt

    # get input current shape, and duration
    I_duration = sum(durations)
    I_shape = ()
    for val in values:
        shape = ops.shape(val)
        if len(shape) > len(I_shape):
            I_shape = shape

    # get the current
    start = 0
    I_current = ops.zeros((int(math.ceil(I_duration / dt)),) + I_shape)
    for c_size, duration in zip(values, durations):
        length = int(duration / dt)
        I_current[start: start + length] = c_size
        start += length

    if return_length:
        return I_current, I_duration
    else:
        return I_current
Ejemplo n.º 8
0
    def __init__(self, size, times, indices, need_sort=True, **kwargs):
        if len(indices) != len(times):
            raise errors.ModelUseError(f'The length of "indices" and "times" must be the same. '
                                       f'However, we got {len(indices)} != {len(times)}.')

        # data about times and indices
        self.idx = 0
        self.times = np.ascontiguousarray(times, dtype=float)
        self.indices = np.ascontiguousarray(indices, dtype=int)
        self.num_times = len(times)
        if need_sort:
            sort_idx = np.argsort(times)
            self.indices = self.indices[sort_idx]
        self.spike = ops.zeros(size2len(size), dtype=bool)

        super(SpikeTimeInput, self).__init__(size=size, **kwargs)
Ejemplo n.º 9
0
def spike_input(points, lengths, sizes, duration, dt=None):
    """Format current input like a series of short-time spikes.

    For example:

    If you want to generate a spike train at 10 ms, 20 ms, 30 ms, 200 ms, 300 ms,
    and each spike lasts 1 ms and the spike current is 0.5, then you can use the
    following funtions:

    >>> spike_input(points=[10, 20, 30, 200, 300],
    >>>             lengths=1.,  # can be a list to specify the spike length at each point
    >>>             sizes=0.5,  # can be a list to specify the current size at each point
    >>>             duration=400.)

    Parameters
    ----------
    points : list, tuple
        The spike time-points. Must be an iterable object.
    lengths : int, float, list, tuple
        The length of each point-current, mimicking the spike durations.
    sizes : int, float, list, tuple
        The current sizes.
    duration : int, float
        The total current duration.
    dt : float
        The default is None.

    Returns
    -------
    current_and_duration : tuple
        (The formatted current, total duration)
    """
    dt = backend.get_dt() if dt is None else dt
    assert isinstance(points, (list, tuple))
    if isinstance(lengths, (float, int)):
        lengths = [lengths] * len(points)
    if isinstance(sizes, (float, int)):
        sizes = [sizes] * len(points)

    current = ops.zeros(int(math.ceil(duration / dt)))
    for time, dur, size in zip(points, lengths, sizes):
        pp = int(time / dt)
        p_len = int(dur / dt)
        current[pp: pp + p_len] = size
    return current
Ejemplo n.º 10
0
def constant_input(I_and_duration, dt=None):
    """Format constant input in durations.

    For example:

    If you want to get an input where the size is 0 bwteen 0-100 ms,
    and the size is 1. between 100-200 ms.
    >>> import numpy as np
    >>> constant_input([(0, 100), (1, 100)])
    >>> constant_input([(np.zeros(100), 100), (np.random.rand(100), 100)])

    Parameters
    ----------
    I_and_duration : list
        This parameter receives the current size and the current
        duration pairs, like `[(Isize1, duration1), (Isize2, duration2)]`.
    dt : float
        Default is None.

    Returns
    -------
    current_and_duration : tuple
        (The formatted current, total duration)
    """
    dt = backend.get_dt() if dt is None else dt

    # get input current dimension, shape, and duration
    I_duration = 0.
    I_dim = 0
    I_shape = ()
    for I in I_and_duration:
        I_duration += I[1]
        shape = ops.shape(I[0])
        if len(shape) > len(I_shape):
            I_shape = shape

    # get the current
    I_current = ops.zeros((int(math.ceil(I_duration / dt)),) + I_shape)
    start = 0
    for c_size, duration in I_and_duration:
        length = int(duration / dt)
        I_current[start: start + length] = c_size
        start += length
    return I_current, I_duration
Ejemplo n.º 11
0
    def run(self, duration, report=False, report_percent=0.1):
        if isinstance(duration, (int, float)):
            duration = [0, duration]
        elif isinstance(duration, (tuple, list)):
            assert len(duration) == 2
            duration = tuple(duration)
        else:
            raise ValueError

        # get the times
        times = ops.arange(duration[0], duration[1], backend.get_dt())
        # reshape the monitor
        for key in self.mon.keys():
            self.mon[key] = ops.zeros((len(times), ) +
                                      ops.shape(self.mon[key])[1:])
        # run the model
        run_model(run_func=self.run_func,
                  times=times,
                  report=report,
                  report_percent=report_percent)
Ejemplo n.º 12
0
    def __init__(self,
                 size,
                 integrals,
                 target_vars,
                 fixed_vars,
                 pars_update,
                 scope,
                 show_code=False):
        """Trajectory Class.

        Parameters
        ----------
        size : int, tuple, list
            The network size.
        integrals : list of functions, function
            The integral functions.
        target_vars : dict
            The target variables, with the format of "{key: initial_v}".
        fixed_vars : dict
            The fixed variables, with the format of "{key: fixed_v}".
        pars_update : dict
            The parameters to update.
        scope :
        """
        if callable(integrals):
            integrals = (integrals, )
        elif isinstance(integrals, (list, tuple)) and callable(integrals[0]):
            integrals = tuple(integrals)
        else:
            raise ValueError
        self.integrals = integrals
        self.target_vars = target_vars
        self.fixed_vars = fixed_vars
        self.pars_update = pars_update
        self.scope = {key: val for key, val in scope.items()}
        self.show_code = show_code

        # check network size
        if isinstance(size, int):
            size = (size, )
        elif isinstance(size, (tuple, list)):
            assert isinstance(size[0], int)
            size = tuple(size)
        else:
            raise ValueError

        # monitors, variables, parameters
        self.mon = DictPlus()
        self.vars_and_pars = DictPlus()
        for key, val in target_vars.items():
            self.vars_and_pars[key] = ops.ones(size) * val
            self.mon[key] = ops.zeros((1, ) + size)
        for key, val in fixed_vars.items():
            self.vars_and_pars[key] = ops.ones(size) * val
        for key, val in pars_update.items():
            self.vars_and_pars[key] = val
        self.scope['VP'] = self.vars_and_pars
        self.scope['MON'] = self.mon
        self.scope['_fixed_vars'] = fixed_vars

        code_lines = ['def run_func(_t, _i, _dt):']
        for integral in integrals:
            func_name = integral.__name__
            self.scope[func_name] = integral
            # update the step function
            assigns = [f'VP["{var}"]' for var in integral.variables]
            calls = [f'VP["{var}"]' for var in integral.variables]
            calls.append('_t')
            calls.extend([f'VP["{var}"]' for var in integral.parameters[1:]])
            code_lines.append(
                f'  {", ".join(assigns)} = {func_name}({", ".join(calls)})')
            # reassign the fixed variables
            for key, val in fixed_vars.items():
                code_lines.append(f'  VP["{key}"][:] = _fixed_vars["{key}"]')
        # monitor the target variables
        for key in target_vars.keys():
            code_lines.append(f'  MON["{key}"][_i] = VP["{key}"]')
        # compile
        code = '\n'.join(code_lines)
        if show_code:
            print(code)
            print(self.scope)
            print()

        # recompile
        exec(compile(code, '', 'exec'), self.scope)
        self.run_func = self.scope['run_func']