Example #1
0
    def __init__(self,
                 model,
                 input_var,
                 input,
                 output_var,
                 output,
                 dt,
                 n_samples=30,
                 method=None,
                 reset=None,
                 refractory=False,
                 threshold=None,
                 level=0,
                 param_init=None,
                 t_start=0 * second):
        """Initialize the fitter."""
        super().__init__(dt, model, input, output, input_var, output_var,
                         n_samples, threshold, reset, refractory, method,
                         param_init)

        self.output = Quantity(output)
        self.output_ = array(output)

        if output_var not in self.model.names:
            raise NameError("%s is not a model variable" % output_var)
        if output.shape != input.shape:
            raise ValueError("Input and output must have the same size")

        # Replace input variable by TimedArray
        output_traces = TimedArray(output.transpose(), dt=dt)
        output_dim = get_dimensions(output)
        squared_output_dim = ('1' if output_dim is DIMENSIONLESS else repr(
            output_dim**2))
        error_eqs = Equations('total_error : {}'.format(squared_output_dim))
        self.model = self.model + error_eqs

        self.t_start = t_start

        if param_init:
            for param, val in param_init.items():
                if not (param in self.model.identifiers
                        or param in self.model.names):
                    raise ValueError("%s is not a model variable or an "
                                     "identifier in the model" % param)
            self.param_init = param_init

        self.simulator = None
Example #2
0
def test_get_dtype():
    '''
    Check the utility function get_dtype
    '''
    eqs = Equations('''dv/dt = -v / (10*ms) : volt
                       x : 1
                       b : boolean
                       n : integer''')

    # Test standard dtypes
    assert get_dtype(eqs['v']) == brian_prefs['core.default_float_dtype']
    assert get_dtype(eqs['x']) == brian_prefs['core.default_float_dtype']
    assert get_dtype(eqs['n']) == brian_prefs['core.default_integer_dtype']
    assert get_dtype(eqs['b']) == np.bool

    # Test a changed default (float) dtype
    assert get_dtype(eqs['v'], np.float32) == np.float32, get_dtype(
        eqs['v'], np.float32)
    assert get_dtype(eqs['x'], np.float32) == np.float32
    # integer and boolean variables should be unaffected
    assert get_dtype(eqs['n']) == brian_prefs['core.default_integer_dtype']
    assert get_dtype(eqs['b']) == np.bool

    # Explicitly provide a dtype for some variables
    dtypes = {'v': np.float32, 'x': np.float64, 'n': np.int64}
    for varname in dtypes:
        assert get_dtype(eqs[varname], dtypes) == dtypes[varname]

    # Not setting some dtypes should use the standard dtypes
    dtypes = {'n': np.int64}
    assert get_dtype(eqs['n'], dtypes) == np.int64
    assert get_dtype(eqs['v'],
                     dtypes) == brian_prefs['core.default_float_dtype']

    # Test that incorrect types raise an error
    # incorrect general dtype
    assert_raises(TypeError, lambda: get_dtype(eqs['v'], np.int32))
    # incorrect specific types
    assert_raises(TypeError, lambda: get_dtype(eqs['v'], {'v': np.int32}))
    assert_raises(TypeError, lambda: get_dtype(eqs['n'], {'n': np.float32}))
    assert_raises(TypeError, lambda: get_dtype(eqs['b'], {'b': np.int32}))
Example #3
0
    def __init__(self,
                 N,
                 model,
                 method=('exact', 'euler', 'heun'),
                 method_options=None,
                 threshold=None,
                 reset=None,
                 refractory=False,
                 events=None,
                 namespace=None,
                 dtype=None,
                 dt=None,
                 clock=None,
                 order=0,
                 name='neurongroup*',
                 codeobj_class=None):
        Group.__init__(self,
                       dt=dt,
                       clock=clock,
                       when='start',
                       order=order,
                       name=name)
        if dtype is None:
            dtype = {}
        if isinstance(dtype, collections.MutableMapping):
            dtype['lastspike'] = self._clock.variables['t'].dtype

        self.codeobj_class = codeobj_class

        try:
            self._N = N = int(N)
        except ValueError:
            if isinstance(N, str):
                raise TypeError(
                    "First NeuronGroup argument should be size, not equations."
                )
            raise
        if N < 1:
            raise ValueError("NeuronGroup size should be at least 1, was " +
                             str(N))

        self.start = 0
        self.stop = self._N

        ##### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Check flags
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('unless refractory', ),
            PARAMETER: ('constant', 'shared', 'linked'),
            SUBEXPRESSION: ('shared', 'constant over dt')
        })

        # add refractoriness
        #: The original equations as specified by the user (i.e. without
        #: the multiplied `int(not_refractory)` term for equations marked as
        #: `(unless refractory)`)
        self.user_equations = model
        if refractory is not False:
            model = add_refractoriness(model)
        uses_refractoriness = len(model) and any([
            'unless refractory' in eq.flags
            for eq in model.values() if eq.type == DIFFERENTIAL_EQUATION
        ])

        # Separate subexpressions depending whether they are considered to be
        # constant over a time step or not
        model, constant_over_dt = extract_constant_subexpressions(model)
        self.equations = model

        self._linked_variables = set()
        logger.diagnostic("Creating NeuronGroup of size {self._N}, "
                          "equations {self.equations}.".format(self=self))

        if namespace is None:
            namespace = {}
        #: The group-specific namespace
        self.namespace = namespace

        # All of the following will be created in before_run

        #: The refractory condition or timespan
        self._refractory = refractory
        if uses_refractoriness and refractory is False:
            logger.warn(
                'Model equations use the "unless refractory" flag but '
                'no refractory keyword was given.', 'no_refractory')

        #: The state update method selected by the user
        self.method_choice = method

        if events is None:
            events = {}

        if threshold is not None:
            if 'spike' in events:
                raise ValueError(("The NeuronGroup defines both a threshold "
                                  "and a 'spike' event"))
            events['spike'] = threshold

        # Setup variables
        # Since we have to create _spikespace and possibly other "eventspace"
        # variables, we pass the supported events
        self._create_variables(dtype, events=list(events.keys()))

        #: Events supported by this group
        self.events = events

        #: Code that is triggered on events (e.g. reset)
        self.event_codes = {}

        #: Checks the spike threshold (or abitrary user-defined events)
        self.thresholder = {}

        #: Reset neurons which have spiked (or perform arbitrary actions for
        #: user-defined events)
        self.resetter = {}

        for event_name in events.keys():
            if not isinstance(event_name, basestring):
                raise TypeError(('Keys in the "events" dictionary have to be '
                                 'strings, not type %s.') % type(event_name))
            if not _valid_event_name(event_name):
                raise TypeError(("The name '%s' cannot be used as an event "
                                 "name.") % event_name)
            # By default, user-defined events are checked after the threshold
            when = 'thresholds' if event_name == 'spike' else 'after_thresholds'
            # creating a Thresholder will take care of checking the validity
            # of the condition
            thresholder = Thresholder(self, event=event_name, when=when)
            self.thresholder[event_name] = thresholder
            self.contained_objects.append(thresholder)

        if reset is not None:
            self.run_on_event('spike', reset, when='resets')

        #: Performs numerical integration step
        self.state_updater = StateUpdater(self, method, method_options)
        self.contained_objects.append(self.state_updater)

        #: Update the "constant over a time step" subexpressions
        self.subexpression_updater = None
        if len(constant_over_dt):
            self.subexpression_updater = SubexpressionUpdater(
                self, constant_over_dt)
            self.contained_objects.append(self.subexpression_updater)

        if refractory is not False:
            # Set the refractoriness information
            self.variables['lastspike'].set_value(-1e4 * second)
            self.variables['not_refractory'].set_value(True)

        # Activate name attribute access
        self._enable_group_attributes()
def simulate(to_file=True):

    common_params = {    # Parameters common to all neurons.
        'C': 100*b2.pF,
        'tau_m': 10*b2.ms,
        'EL': -60*b2.mV,
        'DeltaT': 2*b2.mV,
        'Vreset': -65,  # *b2.mV
        'VTmean': -50*b2.mV,
        'VTsd': 2*b2.mV,
        'delay': 0.*b2.ms,
    }

    common_params['gL'] = common_params['C'] / common_params['tau_m']

    E_cell_params = dict(
        common_params,
        **{
            'Ncells': num_E_cells,
            'IXmean': 30. * b2.pA,  # 30
            'IXsd': 20. * b2.pA
        })

    I_cell_params = dict(
        common_params, **{
            'Ncells': num_I_cells,
            'IXmean': 30. * b2.pA,
            'IXsd': 80. * b2.pA,
            'p_rate': 400.0 * b2.Hz
        })

    param_I_syn = {
        "Erev_i": -80.0 * b2.mV,
        "Erev_x": 0.0 * b2.mV,
        "Erev_e": 0.0 * b2.mV,
        "Tau_i": 3.0 * b2.ms,
        "Tau_e": 4.0 * b2.ms,
        "Tau_x": 4.0 * b2.ms,
        "w_i": 1.5,  # *b2.nsiemens,  # Peak conductance
        "w_x": 1.1,  # *b2.nsiemens,  # (0.8 in paper)
        "w_e": 0.2,  # *b2.nsiemens,
        "p_i": 0.05,
        "p_e": 0.05,
    }
    param_E_syn = {
        "Erev_i": -80.0 * b2.mV,
        "Erev_x": 0.0 * b2.mV,
        "Erev_e": 0.0 * b2.mV,
        "Tau_i": 3.5 * b2.ms,
        "Tau_e": 4.0 * b2.ms,
        "Tau_x": 4.0 * b2.ms,
        "w_i": 0.6 * b2.nsiemens,  # *b2.nsiemens,  # Peak conductance
        "w_x": 1.4 * b2.nsiemens,
        "w_e": 0.1 * b2.nsiemens,
        "p_i": 0.1,
        "p_e": 0.05,
    }
    if state == "gamma":
        print('Gamma oscillation state.')
        param_I_syn['w_x'] = 0.3 * b2.nS
        param_I_syn['w_e'] = 0.4 * b2.nS

    elif state == "beta":
        param_I_syn['w_x'] = 0.5 * b2.nS
        param_I_syn['Tau_x'] = 12. * b2.ms
        param_E_syn['w_x'] = 0.55 * b2.nS
        param_E_syn['Tau_x'] = 12. * b2.ms
        param_E_syn['w_e'] = 0.05 * b2.nS
        param_E_syn['Tau_e'] = 12. * b2.ms
        param_I_syn['w_e'] = 0.1 * b2.nS
        param_E_syn['w_i'] = 0.1 * b2.nS
        param_E_syn['Tau_i'] = 15. * b2.ms
        param_I_syn['w_i'] = 0.2 * b2.nS
        param_I_syn['Tau_i'] = 15. * b2.ms

    eqs = Equations("""
        VT : volt
        IX : amp
        I_syn_e = g_syn_e * (Erev_e - vm): amp
        I_syn_i = g_syn_i * (Erev_i - vm): amp
        I_syn_x = g_syn_x * (Erev_x - vm): amp
        Im = IX +
            gL * (EL - vm) +
            gL * DeltaT * exp((vm - VT) / DeltaT) : amp

        ds_e/dt = -s_e / Tau_e : siemens
        dg_syn_e/dt = (s_e - g_syn_e) / Tau_e : siemens

        ds_i/dt = -s_i / Tau_i : siemens
        dg_syn_i/dt = (s_i - g_syn_i) / Tau_i : siemens

        ds_x/dt = -s_x / Tau_x : siemens
        dg_syn_x/dt = (s_x - g_syn_x) / Tau_x : siemens

        dvm/dt = (Im + I_syn_e + I_syn_i + I_syn_x) / C : volt
        """)

    I_cells = b2.NeuronGroup(I_cell_params['Ncells'],
                             model=eqs,
                             dt=dt0,
                             method=integration_method,
                             threshold="vm > 0.*mV",
                             refractory="vm > 0.*mV",
                             reset="vm={}*mV".format(common_params['Vreset']),
                             namespace={
                                 **common_params,
                                 **param_I_syn
                             })

    E_cells = b2.NeuronGroup(E_cell_params['Ncells'],
                             model=eqs,
                             dt=dt0,
                             method=integration_method,
                             threshold="vm > 0.*mV",
                             refractory="vm > 0.*mV",
                             reset="vm={}*mV".format(common_params['Vreset']),
                             namespace={
                                 **common_params,
                                 **param_E_syn
                             })

    # rates = '400.0*(1 + 0.35 * cos(2*pi*sin(2*pi*t/(100*ms)) + pi + 2*pi/N + (1.0*i/N)*2*pi))*Hz'
    Poisson_to_E = b2.PoissonGroup(
        E_cell_params['Ncells'],
        rates='400.0*(1+0.35*cos(2*pi*sin(2*pi*t/({:d}*ms))+pi+'
        '2*pi/{:d} + (1.0*i/{:d})*2*pi))*Hz'.format(sim_duration,
                                                    E_cell_params['Ncells'],
                                                    E_cell_params['Ncells']))

    Poisson_to_I = b2.PoissonGroup(I_cell_params['Ncells'],
                                   rates=I_cell_params["p_rate"])

    # ---------------------------------------------------------------
    cEE = b2.Synapses(E_cells,
                      E_cells,
                      dt=dt0,
                      delay=common_params['delay'],
                      on_pre='s_e+= {}*nS'.format(param_E_syn['w_e']),
                      namespace={
                          **common_params,
                          **param_E_syn
                      })
    cEE.connect(p="{:g}".format(param_E_syn["p_e"]))  #, condition='i!=j'

    cII = b2.Synapses(I_cells,
                      I_cells,
                      dt=dt0,
                      delay=common_params['delay'],
                      method=integration_method,
                      on_pre='s_i+= {}*nS'.format(param_I_syn['w_i']),
                      namespace={
                          **common_params,
                          **param_I_syn
                      })
    cII.connect(p="{:g}".format(param_I_syn["p_e"]))  #, condition='i!=j'

    cIE = b2.Synapses(E_cells,
                      I_cells,
                      dt=dt0,
                      method=integration_method,
                      on_pre='s_e+={}*nsiemens'.format(param_I_syn["w_e"]))
    cIE.connect(p=param_I_syn["p_e"])

    cEI = b2.Synapses(I_cells,
                      E_cells,
                      dt=dt0,
                      delay=common_params['delay'],
                      method=integration_method,
                      on_pre='s_i+={}*nsiemens'.format(param_I_syn["w_i"]))
    cEI.connect(p=param_I_syn["p_i"])

    cEX = b2.Synapses(Poisson_to_E,
                      E_cells,
                      dt=dt0,
                      delay=common_params['delay'],
                      method=integration_method,
                      on_pre="s_x += {}*nS".format(param_E_syn["w_x"]))
    cEX.connect(j='i')

    cIX = b2.Synapses(Poisson_to_I,
                      I_cells,
                      dt=dt0,
                      delay=common_params['delay'],
                      method=integration_method,
                      on_pre="s_x += {}*nS".format(param_I_syn["w_x"]))
    cIX.connect(j='i')

    # Initialise random parameters.----------------------------------
    E_cells.VT = (randn(len(E_cells)) * common_params['VTsd'] +
                  common_params['VTmean'])
    I_cells.VT = (randn(len(I_cells)) * common_params['VTsd'] +
                  common_params['VTmean'])

    E_cells.IX = (randn(len(E_cells)) * E_cell_params['IXsd'] +
                  E_cell_params['IXmean'])
    I_cells.IX = (randn(len(I_cells)) * I_cell_params['IXsd'] +
                  I_cell_params['IXmean'])

    I_cells.vm = randn(len(I_cells)) * 10 * b2.mV - 60 * b2.mV
    E_cells.vm = randn(len(E_cells)) * 10 * b2.mV - 60 * b2.mV

    spike_mon_E = b2.SpikeMonitor(E_cells)
    spike_mon_I = b2.SpikeMonitor(I_cells)

    LFP_E = b2.PopulationRateMonitor(E_cells)
    LFP_I = b2.PopulationRateMonitor(I_cells)

    state_monitor_E = state_monitor_I = None
    if rocord_voltages:
        state_monitor_E = b2.StateMonitor(E_cells, "vm", record=True, dt=dt0)
        state_monitor_I = b2.StateMonitor(I_cells, "vm", record=True, dt=dt0)

    net = b2.Network(E_cells)
    net.add(I_cells)
    net.add(spike_mon_E)
    net.add(spike_mon_I)
    net.add(LFP_E)
    net.add(LFP_I)
    net.add(cEE)
    net.add(cII)
    net.add(cEI)
    net.add(cIE)
    net.add(cIX)
    net.add(cEX)

    if rocord_voltages:
        net.add(state_monitor_E)
        net.add(state_monitor_I)

    # ----------------------------------------------------------------
    print('Simulation running...')

    start_time = time.time()
    b2.run(sim_duration * b2.ms)

    duration = time.time() - start_time
    print('Simulation time:', duration, 'seconds')
    # ----------------------------------------------------------------

    if to_file:
        to_npz(spike_mon_E, LFP_E, "data/E")
        to_npz(spike_mon_I, LFP_I, "data/I")
Example #5
0
    def __init__(self,
                 N,
                 model,
                 method=None,
                 threshold=None,
                 reset=None,
                 refractory=False,
                 namespace=None,
                 dtype=None,
                 clock=None,
                 name='neurongroup*',
                 codeobj_class=None):
        BrianObject.__init__(self, when=clock, name=name)

        self.codeobj_class = codeobj_class

        try:
            self.N = N = int(N)
        except ValueError:
            if isinstance(N, str):
                raise TypeError(
                    "First NeuronGroup argument should be size, not equations."
                )
            raise
        if N < 1:
            raise ValueError("NeuronGroup size should be at least 1, was " +
                             str(N))

        ##### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Check flags
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('unless-refractory'),
            PARAMETER: ('constant')
        })

        # add refractoriness
        model = add_refractoriness(model)
        self.equations = model
        uses_refractoriness = len(model) and any([
            'unless-refractory' in eq.flags
            for eq in model.itervalues() if eq.type == DIFFERENTIAL_EQUATION
        ])

        logger.debug("Creating NeuronGroup of size {self.N}, "
                     "equations {self.equations}.".format(self=self))

        ##### Setup the memory
        self.arrays = self._allocate_memory(dtype=dtype)

        self._spikespace = np.zeros(N + 1, dtype=np.int32)

        # Setup the namespace
        self.namespace = create_namespace(namespace)

        # Setup variables
        self.variables = self._create_variables()

        # All of the following will be created in pre_run

        #: The threshold condition
        self.threshold = threshold

        #: The reset statement(s)
        self.reset = reset

        #: The refractory condition or timespan
        self._refractory = refractory
        if uses_refractoriness and refractory is False:
            logger.warn(
                'Model equations use the "unless-refractory" flag but '
                'no refractory keyword was given.', 'no_refractory')

        #: The state update method selected by the user
        self.method_choice = method

        #: Performs thresholding step, sets the value of `spikes`
        self.thresholder = None
        if self.threshold is not None:
            self.thresholder = Thresholder(self)

        #: Resets neurons which have spiked (`spikes`)
        self.resetter = None
        if self.reset is not None:
            self.resetter = Resetter(self)

        # We try to run a pre_run already now. This might fail because of an
        # incomplete namespace but if the namespace is already complete we
        # can spot unit or syntax errors already here, at creation time.
        try:
            self.pre_run(None)
        except KeyError:
            pass

        #: Performs numerical integration step
        self.state_updater = StateUpdater(self, method)

        # Creation of contained_objects that do the work
        self.contained_objects.append(self.state_updater)
        if self.thresholder is not None:
            self.contained_objects.append(self.thresholder)
        if self.resetter is not None:
            self.contained_objects.append(self.resetter)

        # Activate name attribute access
        Group.__init__(self)

        # Set the refractoriness information
        self.lastspike = -np.inf * second
        self.not_refractory = True
Example #6
0
    def __init__(self,
                 source,
                 target=None,
                 model=None,
                 pre=None,
                 post=None,
                 connect=False,
                 delay=None,
                 namespace=None,
                 dtype=None,
                 codeobj_class=None,
                 clock=None,
                 method=None,
                 name='synapses*'):
        self._N = 0
        Group.__init__(self, when=clock, name=name)

        self.codeobj_class = codeobj_class

        self.source = weakref.proxy(source)
        if target is None:
            self.target = self.source
        else:
            self.target = weakref.proxy(target)

        ##### Prepare and validate equations
        if model is None:
            model = ''

        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Check flags
        model.check_flags({
            DIFFERENTIAL_EQUATION: ['event-driven'],
            SUBEXPRESSION: ['summed', 'scalar'],
            PARAMETER: ['constant', 'scalar']
        })

        # Add the lastupdate variable, needed for event-driven updates
        if 'lastupdate' in model._equations:
            raise SyntaxError('lastupdate is a reserved name.')
        model._equations['lastupdate'] = SingleEquation(
            PARAMETER, 'lastupdate', second)
        self._create_variables(model)

        # Separate the equations into event-driven equations,
        # continuously updated equations and summed variable updates
        event_driven = []
        continuous = []
        summed_updates = []
        for single_equation in model.itervalues():
            if 'event-driven' in single_equation.flags:
                event_driven.append(single_equation)
            elif 'summed' in single_equation.flags:
                summed_updates.append(single_equation)
            else:
                continuous.append(single_equation)

        if len(event_driven):
            self.event_driven = Equations(event_driven)
        else:
            self.event_driven = None

        self.equations = Equations(continuous)

        if namespace is None:
            namespace = {}
        #: The group-specific namespace
        self.namespace = namespace

        #: Set of `Variable` objects that should be resized when the
        #: number of synapses changes
        self._registered_variables = set()

        for varname, var in self.variables.iteritems():
            if isinstance(var, DynamicArrayVariable):
                # Register the array with the `SynapticItemMapping` object so
                # it gets automatically resized
                self.register_variable(var)

        if delay is None:
            delay = {}

        if isinstance(delay, Quantity):
            delay = {'pre': delay}
        elif not isinstance(delay, collections.Mapping):
            raise TypeError('Delay argument has to be a quantity or a '
                            'dictionary, is type %s instead.' % type(delay))

        #: List of names of all updaters, e.g. ['pre', 'post']
        self._synaptic_updaters = []
        #: List of all `SynapticPathway` objects
        self._pathways = []
        for prepost, argument in zip(('pre', 'post'), (pre, post)):
            if not argument:
                continue
            if isinstance(argument, basestring):
                pathway_delay = delay.get(prepost, None)
                self._add_updater(argument, prepost, delay=pathway_delay)
            elif isinstance(argument, collections.Mapping):
                for key, value in argument.iteritems():
                    if not isinstance(key, basestring):
                        err_msg = ('Keys for the "{}" argument'
                                   'have to be strings, got '
                                   '{} instead.').format(prepost, type(key))
                        raise TypeError(err_msg)
                    pathway_delay = delay.get(key, None)
                    self._add_updater(value,
                                      prepost,
                                      objname=key,
                                      delay=pathway_delay)

        # Check whether any delays were specified for pathways that don't exist
        for pathway in delay:
            if not pathway in self._synaptic_updaters:
                raise ValueError(('Cannot set the delay for pathway '
                                  '"%s": unknown pathway.') % pathway)

        # If we have a pathway called "pre" (the most common use case), provide
        # direct access to its delay via a delay attribute (instead of having
        # to use pre.delay)
        if 'pre' in self._synaptic_updaters:
            self.variables.add_reference('delay', self.pre.variables['delay'])

        #: Performs numerical integration step
        self.state_updater = None

        # We only need a state update if we have differential equations
        if len(self.equations.diff_eq_names):
            self.state_updater = StateUpdater(self, method)
            self.contained_objects.append(self.state_updater)

        #: "Summed variable" mechanism -- sum over all synapses of a
        #: pre-/postsynaptic target
        self.summed_updaters = {}
        # We want to raise an error if the same variable is updated twice
        # using this mechanism. This could happen if the Synapses object
        # connected a NeuronGroup to itself since then all variables are
        # accessible as var_pre and var_post.
        summed_targets = set()
        for single_equation in summed_updates:
            varname = single_equation.varname
            if not (varname.endswith('_pre') or varname.endswith('_post')):
                raise ValueError(('The summed variable "%s" does not end '
                                  'in "_pre" or "_post".') % varname)
            if not varname in self.variables:
                raise ValueError(('The summed variable "%s" does not refer'
                                  'do any known variable in the '
                                  'target group.') % varname)
            if varname.endswith('_pre'):
                summed_target = self.source
                orig_varname = varname[:-4]
            else:
                summed_target = self.target
                orig_varname = varname[:-5]

            target_eq = getattr(summed_target, 'equations',
                                {}).get(orig_varname, None)
            if target_eq is None or target_eq.type != PARAMETER:
                raise ValueError(('The summed variable "%s" needs a '
                                  'corresponding parameter "%s" in the '
                                  'target group.') % (varname, orig_varname))

            fail_for_dimension_mismatch(
                self.variables['_summed_' + varname].unit,
                self.variables[varname].unit, ('Summed variables need to have '
                                               'the same units in Synapses '
                                               'and the target group'))
            if self.variables[varname] in summed_targets:
                raise ValueError(('The target variable "%s" is already '
                                  'updated by another summed '
                                  'variable') % orig_varname)
            summed_targets.add(self.variables[varname])
            updater = SummedVariableUpdater(single_equation.expr, varname,
                                            self, summed_target)
            self.summed_updaters[varname] = updater
            self.contained_objects.append(updater)

        # Do an initial connect, if requested
        if not isinstance(connect, (bool, basestring)):
            raise TypeError(
                ('"connect" keyword has to be a boolean value or a '
                 'string, is type %s instead.' % type(connect)))
        self._initial_connect = connect
        if not connect is False:
            self.connect(connect, level=1)

        # Activate name attribute access
        self._enable_group_attributes()
Example #7
0
    def __init__(self,
                 morphology=None,
                 model=None,
                 threshold=None,
                 refractory=False,
                 reset=None,
                 threshold_location=None,
                 dt=None,
                 clock=None,
                 order=0,
                 Cm=0.9 * uF / cm**2,
                 Ri=150 * ohm * cm,
                 name='spatialneuron*',
                 dtype=None,
                 namespace=None,
                 method=('linear', 'exponential_euler', 'rk2', 'heun')):

        # #### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Insert the threshold mechanism at the specified location
        if threshold_location is not None:
            if hasattr(threshold_location,
                       '_indices'):  # assuming this is a method
                threshold_location = threshold_location._indices()
                # for now, only a single compartment allowed
                if len(threshold_location) == 1:
                    threshold_location = threshold_location[0]
                else:
                    raise AttributeError(('Threshold can only be applied on a '
                                          'single location'))
            threshold = '(' + threshold + ') and (i == ' + str(
                threshold_location) + ')'

        # Check flags (we have point currents)
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('point current', ),
            PARAMETER: ('constant', 'shared', 'linked', 'point current'),
            SUBEXPRESSION: ('shared', 'point current')
        })

        # Add the membrane potential
        model += Equations('''
        v:volt # membrane potential
        ''')

        # Extract membrane equation
        if 'Im' in model:
            membrane_eq = model['Im']  # the membrane equation
        else:
            raise TypeError('The transmembrane current Im must be defined')

        # Insert point currents in the membrane equation
        for eq in model.itervalues():
            if 'point current' in eq.flags:
                fail_for_dimension_mismatch(
                    eq.unit, amp,
                    "Point current " + eq.varname + " should be in amp")
                eq.flags.remove('point current')
                membrane_eq.expr = Expression(
                    str(membrane_eq.expr.code) + '+' + eq.varname + '/area')

        ###### Process model equations (Im) to extract total conductance and the remaining current
        # Check conditional linearity with respect to v
        # Match to _A*v+_B
        var = sp.Symbol('v', real=True)
        wildcard = sp.Wild('_A', exclude=[var])
        constant_wildcard = sp.Wild('_B', exclude=[var])
        pattern = wildcard * var + constant_wildcard

        # Expand expressions in the membrane equation
        membrane_eq.type = DIFFERENTIAL_EQUATION
        for var, expr in model._get_substituted_expressions(
        ):  # this returns substituted expressions for diff eqs
            if var == 'Im':
                Im_expr = expr
        membrane_eq.type = SUBEXPRESSION

        # Factor out the variable
        s_expr = sp.collect(Im_expr.sympy_expr.expand(), var)
        matches = s_expr.match(pattern)

        if matches is None:
            raise TypeError, "The membrane current must be linear with respect to v"
        a, b = (matches[wildcard], matches[constant_wildcard])

        # Extracts the total conductance from Im, and the remaining current
        minusa_str, b_str = sympy_to_str(-a), sympy_to_str(b)
        # Add correct units if necessary
        if minusa_str == '0':
            minusa_str += '*siemens/meter**2'
        if b_str == '0':
            b_str += '*amp/meter**2'
        gtot_str = "gtot__private=" + minusa_str + ": siemens/meter**2"
        I0_str = "I0__private=" + b_str + ": amp/meter**2"
        model += Equations(gtot_str + "\n" + I0_str)

        # Equations for morphology
        # TODO: check whether Cm and Ri are already in the equations
        #       no: should be shared instead of constant
        #       yes: should be constant (check)
        eqs_constants = Equations("""
        diameter : meter (constant)
        length : meter (constant)
        x : meter (constant)
        y : meter (constant)
        z : meter (constant)
        distance : meter (constant)
        area : meter**2 (constant)
        Cm : farad/meter**2 (constant)
        Ri : ohm*meter (constant, shared)
        space_constant = (diameter/(4*Ri*gtot__private))**.5 : meter # Not so sure about the name

        ### Parameters and intermediate variables for solving the cable equation
        ab_star0 : siemens/meter**2
        ab_plus0 : siemens/meter**2
        ab_minus0 : siemens/meter**2
        ab_star1 : siemens/meter**2
        ab_plus1 : siemens/meter**2
        ab_minus1 : siemens/meter**2
        ab_star2 : siemens/meter**2
        ab_plus2 : siemens/meter**2
        ab_minus2 : siemens/meter**2
        b_plus : siemens/meter**2
        b_minus : siemens/meter**2
        v_star : volt
        u_plus : 1
        u_minus : 1
        # The following two are only necessary for C code where we cannot deal
        # with scalars and arrays interchangeably
        gtot_all : siemens/meter**2
        I0_all : amp/meter**2
        """)
        # Possibilities for the name: characteristic_length, electrotonic_length, length_constant, space_constant

        # Insert morphology
        self.morphology = morphology

        # Link morphology variables to neuron's state variables
        self.morphology_data = MorphologyData(len(morphology))
        self.morphology.compress(self.morphology_data)

        NeuronGroup.__init__(self,
                             len(morphology),
                             model=model + eqs_constants,
                             threshold=threshold,
                             refractory=refractory,
                             reset=reset,
                             method=method,
                             dt=dt,
                             clock=clock,
                             order=order,
                             namespace=namespace,
                             dtype=dtype,
                             name=name)

        self.Cm = Cm
        self.Ri = Ri
        # TODO: View instead of copy for runtime?
        self.diameter_ = self.morphology_data.diameter
        self.distance_ = self.morphology_data.distance
        self.length_ = self.morphology_data.length
        self.area_ = self.morphology_data.area
        self.x_ = self.morphology_data.x
        self.y_ = self.morphology_data.y
        self.z_ = self.morphology_data.z

        # Performs numerical integration step
        self.add_attribute('diffusion_state_updater')
        self.diffusion_state_updater = SpatialStateUpdater(self,
                                                           method,
                                                           clock=self.clock,
                                                           order=order)

        # Creation of contained_objects that do the work
        self.contained_objects.extend([self.diffusion_state_updater])
Example #8
0
    def __init__(self,
                 morphology=None,
                 model=None,
                 threshold=None,
                 refractory=False,
                 reset=None,
                 events=None,
                 threshold_location=None,
                 dt=None,
                 clock=None,
                 order=0,
                 Cm=0.9 * uF / cm**2,
                 Ri=150 * ohm * cm,
                 name='spatialneuron*',
                 dtype=None,
                 namespace=None,
                 method=('linear', 'exponential_euler', 'rk2', 'heun')):

        # #### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Insert the threshold mechanism at the specified location
        if threshold_location is not None:
            if hasattr(threshold_location,
                       '_indices'):  # assuming this is a method
                threshold_location = threshold_location._indices()
                # for now, only a single compartment allowed
                if len(threshold_location) == 1:
                    threshold_location = threshold_location[0]
                else:
                    raise AttributeError(('Threshold can only be applied on a '
                                          'single location'))
            threshold = '(' + threshold + ') and (i == ' + str(
                threshold_location) + ')'

        # Check flags (we have point currents)
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('point current', ),
            PARAMETER: ('constant', 'shared', 'linked', 'point current'),
            SUBEXPRESSION: ('shared', 'point current', 'constant over dt')
        })
        #: The original equations as specified by the user (i.e. before
        #: inserting point-currents into the membrane equation, before adding
        #: all the internally used variables and constants, etc.).
        self.user_equations = model

        # Separate subexpressions depending whether they are considered to be
        # constant over a time step or not (this would also be done by the
        # NeuronGroup initializer later, but this would give incorrect results
        # for the linearity check)
        model, constant_over_dt = extract_constant_subexpressions(model)

        # Extract membrane equation
        if 'Im' in model:
            if len(model['Im'].flags):
                raise TypeError(
                    'Cannot specify any flags for the transmembrane '
                    'current Im.')
            membrane_expr = model['Im'].expr  # the membrane equation
        else:
            raise TypeError('The transmembrane current Im must be defined')

        model_equations = []
        # Insert point currents in the membrane equation
        for eq in model.itervalues():
            if eq.varname == 'Im':
                continue  # ignore -- handled separately
            if 'point current' in eq.flags:
                fail_for_dimension_mismatch(
                    eq.dim, amp,
                    "Point current " + eq.varname + " should be in amp")
                membrane_expr = Expression(
                    str(membrane_expr.code) + '+' + eq.varname + '/area')
                eq = SingleEquation(
                    eq.type,
                    eq.varname,
                    eq.dim,
                    expr=eq.expr,
                    flags=list(set(eq.flags) - set(['point current'])))
            model_equations.append(eq)

        model_equations.append(
            SingleEquation(SUBEXPRESSION,
                           'Im',
                           dimensions=(amp / meter**2).dim,
                           expr=membrane_expr))
        model_equations.append(SingleEquation(PARAMETER, 'v', volt.dim))
        model = Equations(model_equations)

        ###### Process model equations (Im) to extract total conductance and the remaining current
        # Expand expressions in the membrane equation
        for var, expr in model.get_substituted_expressions(
                include_subexpressions=True):
            if var == 'Im':
                Im_expr = expr
                break
        else:
            raise AssertionError('Model equations did not contain Im!')

        # Differentiate Im with respect to v
        Im_sympy_exp = str_to_sympy(Im_expr.code)
        v_sympy = sp.Symbol('v', real=True)
        diffed = sp.diff(Im_sympy_exp, v_sympy)

        unevaled_derivatives = diffed.atoms(sp.Derivative)
        if len(unevaled_derivatives):
            raise TypeError(
                'Cannot take the derivative of "{Im}" with respect '
                'to v.'.format(Im=Im_expr.code))

        gtot_str = sympy_to_str(sp.simplify(-diffed))
        I0_str = sympy_to_str(sp.simplify(Im_sympy_exp - diffed * v_sympy))

        if gtot_str == '0':
            gtot_str += '*siemens/meter**2'
        if I0_str == '0':
            I0_str += '*amp/meter**2'
        gtot_str = "gtot__private=" + gtot_str + ": siemens/meter**2"
        I0_str = "I0__private=" + I0_str + ": amp/meter**2"

        model += Equations(gtot_str + "\n" + I0_str)

        # Insert morphology (store a copy)
        self.morphology = copy.deepcopy(morphology)

        # Flatten the morphology
        self.flat_morphology = FlatMorphology(morphology)

        # Equations for morphology
        # TODO: check whether Cm and Ri are already in the equations
        #       no: should be shared instead of constant
        #       yes: should be constant (check)
        eqs_constants = Equations("""
        length : meter (constant)
        distance : meter (constant)
        area : meter**2 (constant)
        volume : meter**3
        Ic : amp/meter**2
        diameter : meter (constant)
        Cm : farad/meter**2 (constant)
        Ri : ohm*meter (constant, shared)
        r_length_1 : meter (constant)
        r_length_2 : meter (constant)
        time_constant = Cm/gtot__private : second
        space_constant = (2/pi)**(1.0/3.0) * (area/(1/r_length_1 + 1/r_length_2))**(1.0/6.0) /
                         (2*(Ri*gtot__private)**(1.0/2.0)) : meter
        """)
        if self.flat_morphology.has_coordinates:
            eqs_constants += Equations('''
            x : meter (constant)
            y : meter (constant)
            z : meter (constant)
            ''')

        NeuronGroup.__init__(self,
                             morphology.total_compartments,
                             model=model + eqs_constants,
                             threshold=threshold,
                             refractory=refractory,
                             reset=reset,
                             events=events,
                             method=method,
                             dt=dt,
                             clock=clock,
                             order=order,
                             namespace=namespace,
                             dtype=dtype,
                             name=name)
        # Parameters and intermediate variables for solving the cable equations
        # Note that some of these variables could have meaningful physical
        # units (e.g. _v_star is in volt, _I0_all is in amp/meter**2 etc.) but
        # since these variables should never be used in user code, we don't
        # assign them any units
        self.variables.add_arrays(
            [
                '_ab_star0',
                '_ab_star1',
                '_ab_star2',
                '_a_minus0',
                '_a_minus1',
                '_a_minus2',
                '_a_plus0',
                '_a_plus1',
                '_a_plus2',
                '_b_plus',
                '_b_minus',
                '_v_star',
                '_u_plus',
                '_u_minus',
                '_v_previous',
                # The following three are for solving the
                # three tridiag systems in parallel
                '_c1',
                '_c2',
                '_c3',
                # The following two are only necessary for
                # C code where we cannot deal with scalars
                # and arrays interchangeably:
                '_I0_all',
                '_gtot_all'
            ],
            size=self.N,
            read_only=True)

        self.Cm = Cm
        self.Ri = Ri
        # These explict assignments will load the morphology values from disk
        # in standalone mode
        self.distance_ = self.flat_morphology.distance
        self.length_ = self.flat_morphology.length
        self.area_ = self.flat_morphology.area
        self.diameter_ = self.flat_morphology.diameter
        self.r_length_1_ = self.flat_morphology.r_length_1
        self.r_length_2_ = self.flat_morphology.r_length_2
        if self.flat_morphology.has_coordinates:
            self.x_ = self.flat_morphology.x
            self.y_ = self.flat_morphology.y
            self.z_ = self.flat_morphology.z

        # Performs numerical integration step
        self.add_attribute('diffusion_state_updater')
        self.diffusion_state_updater = SpatialStateUpdater(self,
                                                           method,
                                                           clock=self.clock,
                                                           order=order)

        # Update v after the gating variables to obtain consistent Ic and Im
        self.diffusion_state_updater.order = 1

        # Creation of contained_objects that do the work
        self.contained_objects.extend([self.diffusion_state_updater])

        if len(constant_over_dt):
            self.subexpression_updater = SubexpressionUpdater(
                self, constant_over_dt)
            self.contained_objects.append(self.subexpression_updater)
def simulate(IXmean):

    common_params = {    # Parameters common to all neurons.
        'C': 100.*b2.pfarad,
        'tau_m': 10.*b2.ms,
        'EL': -60.*b2.mV,
        'DeltaT': 2.*b2.mV,
        'Vreset': -65.,  # *b2.mV
        'VTmean': -50.*b2.mV
    }

    common_params['gL'] = common_params['C'] / common_params['tau_m']

    E_cell_params = dict(common_params, **{'Ncells': num_E_cells,
                                           'IXmean': IXmean,  # 30
                                           'IXsd': 20*b2.pA})

    eqs = Equations(
        """
        Im = IX + 
            gL * (EL - vm) + 
            gL * DeltaT * exp((vm - VT) / DeltaT) : amp
        VT : volt
        IX : amp
        dvm/dt = Im / C : volt
        """
    )

    E_cells = b2.NeuronGroup(E_cell_params['Ncells'],
                             model=eqs,
                             method=integration_method,
                             threshold="vm > 0.*mV",
                             reset="vm={}*mV".format(E_cell_params['Vreset']),
                             refractory="vm > 0.*mV",
                             namespace=common_params)
    # Initialise random parameters.
    E_cells.VT = E_cell_params['VTmean']
    E_cells.IX = E_cell_params['IXmean']
    E_cells.vm = - 60 * b2.mV

    spike_monitor_E = b2.SpikeMonitor(E_cells)

    state_monitor_E = None
    if record_voltages:
        state_monitor_E = b2.StateMonitor(E_cells,
                                          "vm",
                                          record=True,
                                          dt=dt)

    net = b2.Network(E_cells)
    if record_voltages:
        net.add(state_monitor_E)
    net.add(spike_monitor_E)

    print('Simulation running...')
    start_time = time.time()
    b2.run(sim_duration*b2.ms)
    duration = time.time() - start_time
    print('Simulation time:', duration, 'seconds')

    return spike_monitor_E, state_monitor_E
Example #10
0
    def __init__(self,
                 N,
                 model,
                 method=('linear', 'euler', 'milstein'),
                 threshold=None,
                 reset=None,
                 refractory=False,
                 namespace=None,
                 dtype=None,
                 dt=None,
                 clock=None,
                 order=0,
                 name='neurongroup*',
                 codeobj_class=None):
        Group.__init__(self,
                       dt=dt,
                       clock=clock,
                       when='start',
                       order=order,
                       name=name)

        self.codeobj_class = codeobj_class

        try:
            self._N = N = int(N)
        except ValueError:
            if isinstance(N, str):
                raise TypeError(
                    "First NeuronGroup argument should be size, not equations."
                )
            raise
        if N < 1:
            raise ValueError("NeuronGroup size should be at least 1, was " +
                             str(N))

        self.start = 0
        self.stop = self._N

        ##### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Check flags
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('unless refractory', ),
            PARAMETER: ('constant', 'shared', 'linked'),
            SUBEXPRESSION: ('shared', )
        })

        # add refractoriness
        if refractory is not False:
            model = add_refractoriness(model)
        self.equations = model
        uses_refractoriness = len(model) and any([
            'unless refractory' in eq.flags
            for eq in model.itervalues() if eq.type == DIFFERENTIAL_EQUATION
        ])
        self._linked_variables = set()
        logger.debug("Creating NeuronGroup of size {self._N}, "
                     "equations {self.equations}.".format(self=self))

        if namespace is None:
            namespace = {}
        #: The group-specific namespace
        self.namespace = namespace

        # Setup variables
        self._create_variables(dtype)

        # All of the following will be created in before_run

        #: The threshold condition
        self.threshold = threshold

        #: The reset statement(s)
        self.reset = reset

        #: The refractory condition or timespan
        self._refractory = refractory
        if uses_refractoriness and refractory is False:
            logger.warn(
                'Model equations use the "unless refractory" flag but '
                'no refractory keyword was given.', 'no_refractory')

        #: The state update method selected by the user
        self.method_choice = method

        #: Performs thresholding step, sets the value of `spikes`
        self.thresholder = None
        if self.threshold is not None:
            self.thresholder = Thresholder(self)

        #: Resets neurons which have spiked (`spikes`)
        self.resetter = None
        if self.reset is not None:
            self.resetter = Resetter(self)

        # We try to run a before_run already now. This might fail because of an
        # incomplete namespace but if the namespace is already complete we
        # can spot unit errors in the equation already here.
        try:
            self.before_run(None)
        except KeyError:
            pass

        #: Performs numerical integration step
        self.state_updater = StateUpdater(self, method)

        # Creation of contained_objects that do the work
        self.contained_objects.append(self.state_updater)
        if self.thresholder is not None:
            self.contained_objects.append(self.thresholder)
        if self.resetter is not None:
            self.contained_objects.append(self.resetter)

        if refractory is not False:
            # Set the refractoriness information
            self.variables['lastspike'].set_value(-np.inf * second)
            self.variables['not_refractory'].set_value(True)

        # Activate name attribute access
        self._enable_group_attributes()
def simulate():

    common_params = {  # Parameters common to all neurons.
        'C': 100 * b2.pfarad,
        'tau_m': 10 * b2.ms,
        'EL': -60 * b2.mV,
        'DeltaT': 2 * b2.mV,
        'Vreset': -65,  # *b2.mV
        'VTmean': -50 * b2.mV,
        'VTsd': 2 * b2.mV
    }

    common_params['gL'] = common_params['C'] / common_params['tau_m']

    E_cell_params = dict(
        common_params, **{
            'Ncells': num_E_cells,
            'IXmean': 30 * b2.pA,
            'IXsd': 20 * b2.pA
        })

    eqs = Equations("""
        Im = IX + 
            gL * (EL - vm) + 
            gL * DeltaT * exp((vm - VT) / DeltaT) - 
            ge * (vm - Erev_e) - 
            gi * (vm - Erev_i) - 
            gx * (vm - Erev_x) : amp
        dgi/dt = (1*nsiemens-gi)/Tau_i - gi/Tau_i : siemens
        dgx/dt = (1*nsiemens-gx)/Tau_x - gx/Tau_x : siemens
        dge/dt = (1*nsiemens-ge)/Tau_e - ge/Tau_e : siemens
        VT : volt
        IX : amp
        dvm/dt = Im / C : volt
        """)

    param_E_syn = {
        "Erev_i": 0.0 * b2.mV,
        "Erev_x": 0.0 * b2.mV,
        "Erev_e": -80.0 * b2.mV,
        "Tau_i": 3.0 * b2.ms,
        "Tau_e": 4.0 * b2.ms,
        "Tau_x": 4.0 * b2.ms,
        "w_i": 0.6,  # *b2.nsiemens,  # Peak conductance
        # *b2.nsiemens,  # Peak conductance  (1 in paper)
        "w_x": 1.4,
        "w_e": 0.1,  # *b2.nsiemens,  # Peak conductance
        "p_i": 0.1,  # ./I_cell_params['Ncells'],  # ! 200
        "p_e": 0.05,  # /E_cell_params['Ncells'],  # ! 400
    }

    if state == "beta":
        param_E_syn['w_x'] = 0.55 * b2.nS
        param_E_syn['Tau_x'] = 12 * b2.ms
        param_E_syn['w_e'] = 0.05 * b2.nS
        param_E_syn['Tau_e'] = 12 * b2.ms
        param_E_syn['w_i'] = 0.1 * b2.nS
        param_E_syn['Tau_i'] = 15 * b2.ms

    E_cells = b2.NeuronGroup(E_cell_params['Ncells'],
                             model=eqs,
                             method=integration_method,
                             threshold="vm > 0.*mV",
                             reset="vm={}*mV".format(E_cell_params['Vreset']),
                             refractory="vm > 0.*mV",
                             namespace={
                                 **common_params,
                                 **param_E_syn,
                             })

    # Poisson_to_E = b2.PoissonGroup(
    #     E_cell_params['Ncells'], rates=input_rates())  # ! input_rates

    cEE = b2.Synapses(E_cells,
                      E_cells,
                      on_pre='ge+={}*nsiemens'.format(param_E_syn["w_e"]))
    # cEX = b2.Synapses(Poisson_to_E,
    #                   E_cells,
    #                   method=integration_method,
    #                   on_pre="gx += {}*nsiemens".format(param_E_syn["w_x"]))
    # cEX.connect(j='i')

    # Initialise random parameters.

    E_cells.VT = (randn(len(E_cells)) * E_cell_params['VTsd'] +
                  E_cell_params['VTmean'])
    E_cells.IX = (randn(len(E_cells)) * E_cell_params['IXsd'] +
                  E_cell_params['IXmean'])

    spike_monitor_E = b2.SpikeMonitor(E_cells)

    rate_monitor_E = b2.PopulationRateMonitor(E_cells)

    state_monitor_E = state_monitor_I = None
    if record_volrages:
        state_monitor_E = b2.StateMonitor(E_cells, "vm", record=True)
        state_monitor_I = b2.StateMonitor(I_cells, "vm", record=True)

    net = b2.Network(E_cells)
    if record_volrages:
        net.add(state_monitor_E)
    net.add(spike_monitor_E)
    net.add(rate_monitor_E)
    # net.add(cEX)
    # Randomise initial membrane potentials.
    E_cells.vm = randn(len(E_cells)) * 10 * b2.mV - 60 * b2.mV

    print('Simulation running...')

    start_time = time.time()
    b2.run(sim_duration * b2.ms)

    duration = time.time() - start_time
    print('Simulation time:', duration, 'seconds')
Example #12
0
def get_sensitivity_equations(group,
                              parameters,
                              namespace=None,
                              level=1,
                              optimize=True):
    """
    Get equations for sensitivity variables.

    Parameters
    ----------
    group : `NeuronGroup`
        The group of neurons that will be simulated.
    parameters : list of str
        Names of the parameters that are fit.
    namespace : dict, optional
        The namespace to use.
    level : `int`, optional
        How much farther to go down in the stack to find the namespace.
    optimize : bool, optional
        Whether to remove sensitivity variables from the equations that do
        not evolve if initialized to zero (e.g. ``dS_x_y/dt = -S_x_y/tau``
        would be removed). This avoids unnecessary computation but will fail
        in the rare case that such a sensitivity variable needs to be
        initialized to a non-zero value. Defaults to ``True``.

    Returns
    -------
    sensitivity_eqs : `Equations`
        The equations for the sensitivity variables.
    """
    if namespace is None:
        namespace = get_local_namespace(level)
        namespace.update(group.namespace)

    eqs = group.equations
    diff_eqs = eqs.get_substituted_expressions(group.variables)
    diff_eq_names = [name for name, _ in diff_eqs]

    system = sympy.Matrix(
        [str_to_sympy(diff_eq[1].code) for diff_eq in diff_eqs])
    J = system.jacobian([str_to_sympy(d) for d in diff_eq_names])

    sensitivity = []
    sensitivity_names = []
    for parameter in parameters:
        F = system.jacobian([str_to_sympy(parameter)])
        names = [
            str_to_sympy(f'S_{diff_eq_name}_{parameter}')
            for diff_eq_name in diff_eq_names
        ]
        sensitivity.append(J * sympy.Matrix(names) + F)
        sensitivity_names.append(names)

    new_eqs = []
    for names, sensitivity_eqs, param in zip(sensitivity_names, sensitivity,
                                             parameters):
        for name, eq, orig_var in zip(names, sensitivity_eqs, diff_eq_names):
            if param in namespace:
                unit = eqs[orig_var].dim / namespace[param].dim
            elif param in group.variables:
                unit = eqs[orig_var].dim / group.variables[param].dim
            else:
                raise AssertionError(
                    f'Parameter {param} neither in namespace nor variables')
            unit = repr(unit) if not unit.is_dimensionless else '1'
            if optimize:
                # Check if the equation stays at zero if initialized at zero
                zeroed = eq.subs(name, sympy.S.Zero)
                if zeroed == sympy.S.Zero:
                    # No need to include equation as differential equation
                    if unit == '1':
                        new_eqs.append(f'{sympy_to_str(name)} = 0 : {unit}')
                    else:
                        new_eqs.append(
                            f'{sympy_to_str(name)} = 0*{unit} : {unit}')
                    continue
            rhs = sympy_to_str(eq)
            if rhs == '0':  # avoid unit mismatch
                rhs = f'0*{unit}/second'
            new_eqs.append('d{lhs}/dt = {rhs} : {unit}'.format(
                lhs=sympy_to_str(name), rhs=rhs, unit=unit))
    new_eqs = Equations('\n'.join(new_eqs))
    return new_eqs
Example #13
0
    def __init__(self,
                 dt,
                 model,
                 input,
                 output,
                 input_var,
                 output_var,
                 n_samples,
                 threshold,
                 reset,
                 refractory,
                 method,
                 param_init,
                 use_units=True):
        """Initialize the fitter."""

        if dt is None:
            raise ValueError("dt-sampling frequency of the input must be set")

        if isinstance(model, str):
            model = Equations(model)
        if input_var not in model.identifiers:
            raise NameError("%s is not an identifier in the model" % input_var)

        self.dt = dt

        self.simulator = None

        self.parameter_names = model.parameter_names
        self.n_traces, n_steps = input.shape
        self.duration = n_steps * dt
        self.n_neurons = self.n_traces * n_samples

        self.n_samples = n_samples
        self.method = method
        self.threshold = threshold
        self.reset = reset
        self.refractory = refractory

        self.input = input
        self.output_var = output_var
        if output_var == 'spikes':
            self.output_dim = DIMENSIONLESS
        else:
            self.output_dim = model[output_var].dim
        self.model = model

        self.use_units = use_units

        input_dim = get_dimensions(input)
        input_dim = '1' if input_dim is DIMENSIONLESS else repr(input_dim)
        input_eqs = "{} = input_var(t, i % n_traces) : {}".format(
            input_var, input_dim)
        self.model += input_eqs

        input_traces = TimedArray(input.transpose(), dt=dt)
        self.input_traces = input_traces

        # initialization of attributes used later
        self._best_params = None
        self._best_error = None
        self.optimizer = None
        self.metric = None
        if not param_init:
            param_init = {}
        for param, val in param_init.items():
            if not (param in self.model.diff_eq_names
                    or param in self.model.parameter_names):
                raise ValueError("%s is not a model variable or a "
                                 "parameter in the model" % param)
        self.param_init = param_init
Example #14
0
    def __init__(self,
                 source,
                 target=None,
                 model=None,
                 pre=None,
                 post=None,
                 connect=False,
                 delay=None,
                 namespace=None,
                 dtype=None,
                 codeobj_class=None,
                 clock=None,
                 method=None,
                 name='synapses*'):

        BrianObject.__init__(self, when=clock, name=name)

        self.codeobj_class = codeobj_class

        self.source = weakref.proxy(source)
        if target is None:
            self.target = self.source
        else:
            self.target = weakref.proxy(target)

        ##### Prepare and validate equations
        if model is None:
            model = ''

        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Check flags
        model.check_flags({
            DIFFERENTIAL_EQUATION: ['event-driven', 'lumped'],
            STATIC_EQUATION: ['lumped'],
            PARAMETER: ['constant', 'lumped']
        })

        # Separate the equations into event-driven and continuously updated
        # equations
        event_driven = []
        continuous = []
        for single_equation in model.itervalues():
            if 'event-driven' in single_equation.flags:
                if 'lumped' in single_equation.flags:
                    raise ValueError(
                        ('Event-driven variable %s cannot be '
                         'a lumped variable.') % single_equation.varname)
                event_driven.append(single_equation)
            else:
                continuous.append(single_equation)
        # Add the lastupdate variable, used by event-driven equations
        continuous.append(SingleEquation(PARAMETER, 'lastupdate', second))

        if len(event_driven):
            self.event_driven = Equations(event_driven)
        else:
            self.event_driven = None

        self.equations = Equations(continuous)

        ##### Setup the memory
        self.arrays = self._allocate_memory(dtype=dtype)

        # Setup the namespace
        self._given_namespace = namespace
        self.namespace = create_namespace(namespace)

        self._queues = {}
        self._delays = {}

        self.item_mapping = SynapticItemMapping(self)
        self.indices = {
            '_idx': self.item_mapping,
            '_presynaptic_idx': self.item_mapping.synaptic_pre,
            '_postsynaptic_idx': self.item_mapping.synaptic_post
        }
        # Allow S.i instead of S.indices.i, etc.
        self.i = self.item_mapping.i
        self.j = self.item_mapping.j
        self.k = self.item_mapping.k

        # Setup variables
        self.variables = self._create_variables()

        #: List of names of all updaters, e.g. ['pre', 'post']
        self._updaters = []
        for prepost, argument in zip(('pre', 'post'), (pre, post)):
            if not argument:
                continue
            if isinstance(argument, basestring):
                self._add_updater(argument, prepost)
            elif isinstance(argument, collections.Mapping):
                for key, value in argument.iteritems():
                    if not isinstance(key, basestring):
                        err_msg = ('Keys for the "{}" argument'
                                   'have to be strings, got '
                                   '{} instead.').format(prepost, type(key))
                        raise TypeError(err_msg)
                    self._add_updater(value, prepost, objname=key)

        # If we have a pathway called "pre" (the most common use case), provide
        # direct access to its delay via a delay attribute (instead of having
        # to use pre.delay)
        if 'pre' in self._updaters:
            self.variables['delay'] = self.pre.variables['delay']

        if delay is not None:
            if isinstance(delay, Quantity):
                if not 'pre' in self._updaters:
                    raise ValueError(
                        ('Cannot set delay, no "pre" pathway exists.'
                         'Use a dictionary if you want to set the '
                         'delay for a pathway with a different name.'))
                delay = {'pre': delay}

            if not isinstance(delay, collections.Mapping):
                raise TypeError('Delay argument has to be a quantity or a '
                                'dictionary, is type %s instead.' %
                                type(delay))
            for pathway, pathway_delay in delay.iteritems():
                if not pathway in self._updaters:
                    raise ValueError(('Cannot set the delay for pathway '
                                      '"%s": unknown pathway.') % pathway)
                if not isinstance(pathway_delay, Quantity):
                    raise TypeError(('Cannot set the delay for pathway "%s": '
                                     'expected a quantity, got %s instead.') %
                                    (pathway, type(pathway_delay)))
                if pathway_delay.size != 1:
                    raise TypeError(('Cannot set the delay for pathway "%s": '
                                     'expected a scalar quantity, got a '
                                     'quantity with shape %s instead.') %
                                    str(pathway_delay.shape))
                fail_for_dimension_mismatch(pathway_delay, second,
                                            ('Delay has to be '
                                             'specified in units '
                                             'of seconds'))
                updater = getattr(self, pathway)
                self.item_mapping.unregister_variable(updater._delays)
                del updater._delays
                # For simplicity, store the delay as a one-element array
                # so that for example updater._delays[:] works.
                updater._delays = np.array([float(pathway_delay)])
                variable = ArrayVariable('delay',
                                         second,
                                         updater._delays,
                                         group_name=self.name,
                                         scalar=True)
                updater.variables['delay'] = variable
                if pathway == 'pre':
                    self.variables['delay'] = variable

        #: Performs numerical integration step
        self.state_updater = StateUpdater(self, method)
        self.contained_objects.append(self.state_updater)

        #: "Lumped variable" mechanism -- sum over all synapses of a
        #: postsynaptic target
        self.lumped_updaters = {}
        for single_equation in self.equations.itervalues():
            if 'lumped' in single_equation.flags:
                varname = single_equation.varname
                # For a lumped variable, we need an equivalent parameter in the
                # target group
                if not varname in self.target.variables:
                    raise ValueError(
                        ('The lumped variable %s needs a variable '
                         'of the same name in the target '
                         'group ') % single_equation.varname)
                fail_for_dimension_mismatch(self.variables[varname].unit,
                                            self.target.variables[varname],
                                            ('Lumped variables need to have '
                                             'the same units in Synapses '
                                             'and the target group'))
                # TODO: Add some more stringent check about the type of
                # variable in the target group
                updater = LumpedUpdater(varname, self, self.target)
                self.lumped_updaters[varname] = updater
                self.contained_objects.append(updater)

        # Do an initial connect, if requested
        if not isinstance(connect, (bool, basestring)):
            raise TypeError(
                ('"connect" keyword has to be a boolean value or a '
                 'string, is type %s instead.' % type(connect)))
        self._initial_connect = connect
        if not connect is False:
            self.connect(connect, level=1)

        # Activate name attribute access
        Group.__init__(self)
def simulate(IXmean=30. * b2.pA, p_rate=100 * b2.Hz):

    common_params = {  # Parameters common to all neurons.
        'C': 100 * b2.pfarad,
        'tau_m': 10 * b2.ms,
        'EL': -60 * b2.mV,
        'DeltaT': 2 * b2.mV,
        'Vreset': -65,  # *b2.mV
        'VTmean': -50 * b2.mV,
        'VTsd': 2 * b2.mV
    }

    common_params['gL'] = common_params['C'] / common_params['tau_m']

    E_cell_params = dict(
        common_params,
        **{
            'Ncells': num_E_cells,
            'IXmean': IXmean,  # 30
            'IXsd': 20 * b2.pA
        })

    eqs = Equations("""
        Im = IX + 
            gL * (EL - vm) + 
            gL * DeltaT * exp((vm - VT) / DeltaT) - 
            gx * (vm - Erev_x) : amp
        dgx/dt = -gx/Tau_x : siemens
        VT : volt
        IX : amp
        dvm/dt = Im / C : volt
        """)

    param_E_syn = {
        "Erev_x": 0.0 * b2.mV,
        "Tau_x": 4.0 * b2.ms,
        "w_x": 1.4,  # *b2.nsiemens,  # Peak conductance
    }

    if state == "beta":
        param_E_syn['w_x'] = 0.55 * b2.nS
        param_E_syn['Tau_x'] = 12 * b2.ms

    E_cells = b2.NeuronGroup(E_cell_params['Ncells'],
                             model=eqs,
                             method=integration_method,
                             threshold="vm > 0.*mV",
                             reset="vm={}*mV".format(E_cell_params['Vreset']),
                             refractory="vm > 0.*mV",
                             namespace={
                                 **common_params,
                                 **param_E_syn,
                             })
    Poisson_to_E = b2.PoissonGroup(E_cell_params['Ncells'], rates=p_rate)

    cEX = b2.Synapses(Poisson_to_E,
                      E_cells,
                      method=integration_method,
                      on_pre="gx += {}*nsiemens".format(param_E_syn["w_x"]))
    cEX.connect(j='i')

    # Initialise random parameters.

    E_cells.VT = E_cell_params['VTmean']
    E_cells.IX = E_cell_params['IXmean']

    spike_monitor_E = b2.SpikeMonitor(E_cells)

    state_monitor_E = None
    if record_voltages:
        state_monitor_E = b2.StateMonitor(E_cells, "vm", record=True, dt=dt0)

    net = b2.Network(E_cells)

    if record_voltages:
        net.add(state_monitor_E)

    net.add(spike_monitor_E)
    net.add(cEX)
    # Randomise initial membrane potentials.
    E_cells.vm = -60 * b2.mV

    print('Simulation running...')

    start_time = time.time()
    b2.run(sim_duration)
    duration = time.time() - start_time
    print('Simulation time:', duration, 'seconds')

    return spike_monitor_E, state_monitor_E
Example #16
0
    def __init__(self,
                 morphology=None,
                 model=None,
                 threshold=None,
                 refractory=False,
                 reset=None,
                 events=None,
                 threshold_location=None,
                 dt=None,
                 clock=None,
                 order=0,
                 Cm=0.9 * uF / cm**2,
                 Ri=150 * ohm * cm,
                 name='spatialneuron*',
                 dtype=None,
                 namespace=None,
                 method=('linear', 'exponential_euler', 'rk2', 'heun')):

        # #### Prepare and validate equations
        if isinstance(model, basestring):
            model = Equations(model)
        if not isinstance(model, Equations):
            raise TypeError(('model has to be a string or an Equations '
                             'object, is "%s" instead.') % type(model))

        # Insert the threshold mechanism at the specified location
        if threshold_location is not None:
            if hasattr(threshold_location,
                       '_indices'):  # assuming this is a method
                threshold_location = threshold_location._indices()
                # for now, only a single compartment allowed
                if len(threshold_location) == 1:
                    threshold_location = threshold_location[0]
                else:
                    raise AttributeError(('Threshold can only be applied on a '
                                          'single location'))
            threshold = '(' + threshold + ') and (i == ' + str(
                threshold_location) + ')'

        # Check flags (we have point currents)
        model.check_flags({
            DIFFERENTIAL_EQUATION: ('point current', ),
            PARAMETER: ('constant', 'shared', 'linked', 'point current'),
            SUBEXPRESSION: ('shared', 'point current')
        })

        # Add the membrane potential
        model += Equations('''
        v:volt # membrane potential
        ''')

        # Extract membrane equation
        if 'Im' in model:
            membrane_eq = model['Im']  # the membrane equation
        else:
            raise TypeError('The transmembrane current Im must be defined')

        # Insert point currents in the membrane equation
        for eq in model.itervalues():
            if 'point current' in eq.flags:
                fail_for_dimension_mismatch(
                    eq.unit, amp,
                    "Point current " + eq.varname + " should be in amp")
                eq.flags.remove('point current')
                membrane_eq.expr = Expression(
                    str(membrane_eq.expr.code) + '+' + eq.varname + '/area')

        ###### Process model equations (Im) to extract total conductance and the remaining current
        # Check conditional linearity with respect to v
        # Match to _A*v+_B
        var = sp.Symbol('v', real=True)
        wildcard = sp.Wild('_A', exclude=[var])
        constant_wildcard = sp.Wild('_B', exclude=[var])
        pattern = wildcard * var + constant_wildcard

        # Expand expressions in the membrane equation
        membrane_eq.type = DIFFERENTIAL_EQUATION
        for var, expr in model.get_substituted_expressions():
            if var == 'Im':
                Im_expr = expr
        membrane_eq.type = SUBEXPRESSION

        # Factor out the variable
        s_expr = sp.collect(str_to_sympy(Im_expr.code).expand(), var)
        matches = s_expr.match(pattern)

        if matches is None:
            raise TypeError, "The membrane current must be linear with respect to v"
        a, b = (matches[wildcard], matches[constant_wildcard])

        # Extracts the total conductance from Im, and the remaining current
        minusa_str, b_str = sympy_to_str(-a), sympy_to_str(b)
        # Add correct units if necessary
        if minusa_str == '0':
            minusa_str += '*siemens/meter**2'
        if b_str == '0':
            b_str += '*amp/meter**2'
        gtot_str = "gtot__private=" + minusa_str + ": siemens/meter**2"
        I0_str = "I0__private=" + b_str + ": amp/meter**2"
        model += Equations(gtot_str + "\n" + I0_str)

        # Insert morphology (store a copy)
        self.morphology = copy.deepcopy(morphology)

        # Flatten the morphology
        self.flat_morphology = FlatMorphology(morphology)

        # Equations for morphology
        # TODO: check whether Cm and Ri are already in the equations
        #       no: should be shared instead of constant
        #       yes: should be constant (check)
        eqs_constants = Equations("""
        length : meter (constant)
        distance : meter (constant)
        area : meter**2 (constant)
        volume : meter**3
        diameter : meter (constant)
        Cm : farad/meter**2 (constant)
        Ri : ohm*meter (constant, shared)
        r_length_1 : meter (constant)
        r_length_2 : meter (constant)
        time_constant = Cm/gtot__private : second
        space_constant = (2/pi)**(1.0/3.0) * (area/(1/r_length_1 + 1/r_length_2))**(1.0/6.0) /
                         (2*(Ri*gtot__private)**(1.0/2.0)) : meter
        """)
        if self.flat_morphology.has_coordinates:
            eqs_constants += Equations('''
            x : meter (constant)
            y : meter (constant)
            z : meter (constant)
            ''')

        NeuronGroup.__init__(self,
                             morphology.total_compartments,
                             model=model + eqs_constants,
                             threshold=threshold,
                             refractory=refractory,
                             reset=reset,
                             events=events,
                             method=method,
                             dt=dt,
                             clock=clock,
                             order=order,
                             namespace=namespace,
                             dtype=dtype,
                             name=name)
        # Parameters and intermediate variables for solving the cable equations
        # Note that some of these variables could have meaningful physical
        # units (e.g. _v_star is in volt, _I0_all is in amp/meter**2 etc.) but
        # since these variables should never be used in user code, we don't
        # assign them any units
        self.variables.add_arrays(
            [
                '_ab_star0',
                '_ab_star1',
                '_ab_star2',
                '_a_minus0',
                '_a_minus1',
                '_a_minus2',
                '_a_plus0',
                '_a_plus1',
                '_a_plus2',
                '_b_plus',
                '_b_minus',
                '_v_star',
                '_u_plus',
                '_u_minus',
                # The following three are for solving the
                # three tridiag systems in parallel
                '_c1',
                '_c2',
                '_c3',
                # The following two are only necessary for
                # C code where we cannot deal with scalars
                # and arrays interchangeably:
                '_I0_all',
                '_gtot_all'
            ],
            unit=1,
            size=self.N,
            read_only=True)

        self.Cm = Cm
        self.Ri = Ri
        # These explict assignments will load the morphology values from disk
        # in standalone mode
        self.distance_ = self.flat_morphology.distance
        self.length_ = self.flat_morphology.length
        self.area_ = self.flat_morphology.area
        self.diameter_ = self.flat_morphology.diameter
        self.r_length_1_ = self.flat_morphology.r_length_1
        self.r_length_2_ = self.flat_morphology.r_length_2
        if self.flat_morphology.has_coordinates:
            self.x_ = self.flat_morphology.x
            self.y_ = self.flat_morphology.y
            self.z_ = self.flat_morphology.z

        # Performs numerical integration step
        self.add_attribute('diffusion_state_updater')
        self.diffusion_state_updater = SpatialStateUpdater(self,
                                                           method,
                                                           clock=self.clock,
                                                           order=order)

        # Creation of contained_objects that do the work
        self.contained_objects.extend([self.diffusion_state_updater])