Source code for teili.tools.stimulus_generators

# -*- coding: utf-8 -*-
# @Author: Alpha Renner
# @Date:   2018-06-05 09:12:40

"""
The idea is to generate inputs based on a function.
This avoids having to read large datafiles and makes generation of input easier.

How to use them:
See example below.

"""

import numpy as np
import matplotlib.pyplot as plt

from brian2 import device, codegen, defaultclock, NeuronGroup, Synapses, run,\
    SpikeMonitor, StateMonitor, start_scope, Network,\
    implementation, declare_types, prefs,\
    PoissonGroup
from brian2 import mV, mA, ms, ohm, uA, pA, Hz

#import teili
from teili import normal2d_density, ind2x, ind2y, Plotter2d
from teili.core.groups import TeiliGroup, Neurons


[docs]class StimulusSpikeGenerator(TeiliGroup, PoissonGroup): ''' idea: add a run_regularly with dt, that dynamically changes the rates of a PoissonGroup or the input current of an IF neuron The class is the neuron or the generator and can be added to a network You can add equations that specify the trajectory of any pattern-variable. The two variables that are given by default are trajectory_x and trajectory_y But you can always specify the trajectory of any other variable in the trajectory_eq trajectory_eq can either use a TimedArray that contains a prerecorded trajectory or you can also set an equation that describes the trajectory The first 2 args of any pattern function must always be the x/y coordinates Any user provided distance function has to have the brian2 decorators and take the following arguments: i, j, nrows, ncols (i and j are 1d indices in the 2d array) please have a look at the example of a moving gaussian below (if __name__ == '__main__':) ''' def __init__(self, nrows, ncols, dt, trajectory_eq, amplitude=1, spike_generator='poisson', pattern_func=normal2d_density, name=None, **patternkwargs): self.nrows = nrows self.ncols = ncols #self.dt = dt TeiliGroup.__init__(self) if name is None: name = '_teili_stimgen*' if spike_generator == 'poisson': PoissonGroup.__init__(self, nrows * ncols, rates='activity*Hz', name=name) elif spike_generator == 'IFneuron': raise NotImplementedError # This is rouhgly how it could be done; Neurons.__init__(self, nrows * ncols, model='''dV/dt = -V/tau + activity * amp : volt tau : second''', threshold='V>1', reset='V=0', name=name) self.tau = patternkwargs['tau'] else: raise NotImplementedError self.add_state_variable('activity', unit=1, shared=False, constant=False, changeInStandalone=False) self.add_state_variable('x', unit=1, shared=False, constant=False, changeInStandalone=False) self.add_state_variable('y', unit=1, shared=False, constant=False, changeInStandalone=False) self.add_state_variable('amplitude', unit=1, shared=True, constant=False, changeInStandalone=True) self.amplitude = amplitude self.namespace.update({"nrows": nrows, "ncols": ncols, "ind2x": ind2x, "ind2y": ind2y, }) pattern_name = pattern_func.pyfunc.__name__ self.namespace.update({pattern_name: pattern_func}) # the first 2 need to be x and y pattern_orig_arg_names = pattern_func.pyfunc._orig_arg_names[2:] pattern_orig_arg_units = pattern_func.pyfunc._arg_units[2:] pattern_orig_arg_types = pattern_func.pyfunc._arg_types[2:] arg_unit_dict = dict( zip(pattern_orig_arg_names, pattern_orig_arg_units)) arg_type_dict = dict( zip(pattern_orig_arg_names, pattern_orig_arg_types)) if not list(pattern_orig_arg_names) == list(patternkwargs.keys()): raise ValueError('please specify ALL pattern_func arguments as patternkwargs\n' + 'The following arguments are expected: ' + ', '.join(pattern_orig_arg_names) + '\nyou only specified: ' + ', '.join(patternkwargs.keys())) pattern_args = patternkwargs.keys() pattern_args_str = ', '.join(pattern_args) for arg in pattern_args: # booleans cannot be set as statevars like that if arg_type_dict[arg] == 'boolean': self.namespace.update({arg: patternkwargs[arg]}) else: self.add_state_variable(arg, unit=arg_unit_dict[arg], shared=True, constant=False) self.__setattr__(arg, patternkwargs[arg]) run_reg_str = trajectory_eq + ''' x = ind2x(i, nrows, ncols) y = ind2y(i, nrows, ncols) activity=amplitude*{pattern_name}(x, y, {pattern_args}) '''.format(pattern_name=pattern_name, pattern_args=pattern_args_str) self.run_regularly(run_reg_str, dt=dt)
[docs] def plot_rates(self): resh_rates = np.reshape(np.asarray(self.rates), (self.nrows, self.ncols)) plt.figure() plt.imshow(resh_rates)
if __name__ == '__main__': prefs.codegen.target = 'numpy' from teili.tools.cpptools import activate_standalone #activate_standalone(directory='test_stimgen', build_on_run=True) nrows = 80 ncols = 80 # Create a moving Gaussian with increasing sigma # the update that happens every dt is given in the trajectory_eq # the center coordinates move 5 to the right and 2 upwards every dt # the sigma is increased by 0.1 in both directions every dt trajectory_eq = ''' mu_x = (mu_x + 5)%nrows mu_y = (mu_y + 2)%nrows sigma_x += 0.1 sigma_y += 0.1 ''' stimgen = StimulusSpikeGenerator( nrows, ncols, dt=50 * ms, trajectory_eq=trajectory_eq, amplitude=200, spike_generator='poisson', pattern_func=normal2d_density, name="moving_gaussian_stimgen", mu_x=40.0, mu_y=40.0, sigma_x=1.0, sigma_y=1.0, rho=0.0, normalized=False) poissonspmon = SpikeMonitor(stimgen, record=True) net = Network() net.add((stimgen, poissonspmon)) net.run(3000 * ms) plt.plot(poissonspmon.t, poissonspmon.i, ".") plotter2d = Plotter2d(poissonspmon, (nrows, ncols)) imv = plotter2d.plot3d(plot_dt=10 * ms, filtersize=20 * ms) imv.show()