def _event_builder(self, events): """ From events yield lems.OnCondition objects """ # loop ober the events of the group for event in events: event_out = lems.EventOut(event) # output (e.g. spike) # get threshold condition and add it to on_cond on_cond_code = events[event]['threshold']['code'] on_cond = lems.OnCondition(renderer.render_expr(on_cond_code)) on_cond.add_action(event_out) # if event is not in model ports add it if event not in self._component_type.event_ports: self._component_type.add( lems.EventPort(name=event, direction='out')) # check and add reset equations if 'reset' in events[event]: reset_code = events[event]['reset']['code'] # loop over multiple reset codes for single_reset_code in re.split(';|\n', reset_code): single_reset_eq = _equation_separator(single_reset_code) on_cond.add_action( lems.StateAssignment(single_reset_eq[0], single_reset_eq[1])) # check event is spike spike_flag = False if event == 'spike': spike_flag = True yield (spike_flag, on_cond)
def _event_builder(self, events, event_codes): """ From *events* and *event_codes* yields lems.OnCondition objects """ for ev in events: event_out = lems.EventOut(ev) # output (e.g. spike) oc = lems.OnCondition(renderer.render_expr(events[ev])) oc.add_action(event_out) # if event is not in model ports we should add it if ev not in self._component_type.event_ports: self._component_type.add(lems.EventPort(name=ev, direction='out')) if ev in event_codes: for ec in re.split(';|\n', event_codes[ev]): event_eq = _equation_separator(ec) oc.add_action(lems.StateAssignment(event_eq[0], event_eq[1])) spike_flag = False if ev == SPIKE: spike_flag = True yield (spike_flag, oc)
def build_lems_for_model(src): model = lems.Model() model.add(lems.Dimension('time', t=1)) # model.add(lems.Dimension('au')) # primary element of the model is a mass model component mass = lems.ComponentType(src.name, extends="baseCellMembPot") model.add(mass) ######### Adding v is required to ease mapping to NEURON... mass.dynamics.add(lems.StateVariable(name="v", dimension="voltage", exposure="v")) mass.add(lems.Attachments(name="synapses",type_="basePointCurrentDL")) for input in src.input: mass.dynamics.add(lems.DerivedVariable(name=input, dimension='none', exposure=input, select='synapses[*]/I', reduce='add')) mass.add(lems.Exposure(input, 'none')) for key, val in src.const.items(): mass.add(lems.Parameter(key, 'none')) # TODO units mass.add(lems.Constant(name="MSEC", dimension="time", value="1ms")) mass.add(lems.Constant(name="PI", dimension="none", value="3.14159265359")) states = [] der_vars = [] # for key in src.param: # mass.add(lems.Parameter(key, 'au')) # TODO units for key, val in src.auxex: val = val.replace('**', '^') mass.dynamics.add(lems.DerivedVariable(key, value=val)) for key in src.obsrv: name_dv = key.replace('(','_').replace(')','').replace(' - ','_min_') mass.dynamics.add(lems.DerivedVariable(name_dv, value=key, exposure=name_dv)) mass.add(lems.Exposure(name_dv, 'none')) for src_svar in src.state_space: name = src_svar.name ddt = src_svar.drift.replace('**', '^') mass.dynamics.add(lems.StateVariable(name, 'none', name)) mass.dynamics.add(lems.TimeDerivative(name, '(%s)/MSEC'%ddt)) mass.add(lems.Exposure(name, 'none')) ''' On condition is not need on the model but NeuroML requires its definition --> <OnCondition test="r .lt. 0"> <EventOut port="spike"/> </OnCondition>''' oc = lems.OnCondition(test='v .gt. 0') oc.actions.append(lems.EventOut(port='spike')) mass.dynamics.add(oc) return model
def add_neurongroup(self, neurongrp, index_neurongrp, initializers): """ Add NeuronGroup to self._model If number of elements is 1 it adds component of that type, if it's bigger, the network is created by calling: `make_multiinstantiate`. Parameters ---------- neurongrp : dict Standard dictionary representation of NeuronGroup object index_neurongrp : int Index of neurongroup in the network initializers : list List of initializers defined in the network """ # get name of the neurongrp component_name = neurongrp['name'] self._model_namespace["neuronname"] = component_name # add BASE_CELL component self._component_type = lems.ComponentType(component_name, extends=BASE_CELL) # get identifiers attached to neurongrp and create special_properties dict identifiers = [] special_properties = {} if 'identifiers' in neurongrp: identifiers = neurongrp['identifiers'] for initializer in initializers: if 'identifiers' in initializer: identifiers.update(initializer['identifiers']) for identifier in identifiers.keys(): special_properties.update({identifier: None}) # add the identifers as properties of the component for param in self._determine_properties(identifiers): self._component_type.add(param) # common things for every neuron definition # TODO: Is this same for custom events too? self._component_type.add(lems.EventPort(name='spike', direction='out')) # dynamics of the network dynamics = lems.Dynamics() # get neurongrp equations equations = neurongrp['equations'] # loop over the variables initializer_vars = [] for initializer in initializers: if initializer['source'] == neurongrp['name']: initializer_vars.append(initializer['variable']) for var in equations.keys(): # determine the dimension dimension = _determine_dimension(equations[var]['unit']) # add to all_params_unit self._all_params_unit[var] = dimension # identify diff eqns to add Exposure if equations[var]['type'] == 'differential equation': state_var = lems.StateVariable(var, dimension=dimension, exposure=var) self._component_type.add( lems.Exposure(var, dimension=dimension)) dynamics.add_state_variable(state_var) else: if var in initializer_vars and 'i' in initializer['value']: self._component_type.add(lems.Property(var, dimension)) special_properties[var] = initializer['value'] continue state_var = lems.StateVariable(var, dimension=dimension) dynamics.add_state_variable(state_var) # what happens at initialization onstart = lems.OnStart() # loop over the initializers for var in equations.keys(): if var in (NOT_REFRACTORY, LAST_SPIKE): continue # check the initializer is connected to this neurongrp if var not in initializer_vars: continue if var in special_properties: continue for initializer in initializers: if initializer['variable'] == var and initializer[ 'source'] == neurongrp['name']: init_value = initializer['value'] if type(init_value) != str: value = brian_unit_to_lems(init_value) else: value = renderer.render_expr(str(init_value)) # add to onstart onstart.add(lems.StateAssignment(var, value)) dynamics.add(onstart) # check whether refractoriness is defined if ('events' in neurongrp and 'spike' in neurongrp['events'] and 'refractory' in neurongrp['events']['spike']): # if refractoriness, we create separate regimes for integrating integr_regime = lems.Regime(INTEGRATING, dynamics, True) # True -> initial regime # check spike event # NOTE: isn't refractory only for spike events? for spike_flag, on_cond in self._event_builder( neurongrp['events']): if spike_flag: # if spike occured we make transition to refractory regime on_cond.add_action(lems.Transition(REFRACTORY)) integr_regime.add_event_handler(on_cond) # add refractory regime refrac_regime = lems.Regime(REFRACTORY, dynamics) # make lastspike variable and initialize it refrac_regime.add_state_variable( lems.StateVariable(LAST_SPIKE, dimension='time')) oe = lems.OnEntry() oe.add(lems.StateAssignment(LAST_SPIKE, 't')) refrac_regime.add(oe) # after refractory time we make transition to integrating regime refractory_code = neurongrp['events']['spike']['refractory'] if not _equation_separator(str(refractory_code)): # if there is no specific variable given, we assume # that this is time condition ref_oc = lems.OnCondition('t .gt. ( {0} + {1} )'.format( LAST_SPIKE, brian_unit_to_lems(refractory_code))) else: ref_oc = lems.OnCondition( renderer.render_expr(refractory_code)) ref_trans = lems.Transition(INTEGRATING) ref_oc.add_action(ref_trans) refrac_regime.add_event_handler(ref_oc) # identify variables with differential equation for var in neurongrp['equations']: if neurongrp['equations'][var][ 'type'] == 'differential equation': diff_var = neurongrp['equations'][var] # get the expression td = lems.TimeDerivative( var, renderer.render_expr(diff_var['expr'])) # check flags for UNLESS_REFRACTORY TODO: is this available in 'flags' key? if 'flags' in diff_var: # if unless refratory we add only do integration regime if UNLESS_REFRACTORY in diff_var['flags']: integr_regime.add_time_derivative(td) continue # add time derivative to both regimes integr_regime.add_time_derivative(td) refrac_regime.add_time_derivative(td) # add the regimes to dynamics dynamics.add_regime(integr_regime) dynamics.add_regime(refrac_regime) else: # adding events directly to dynamics for spike_flag, on_cond in self._event_builder( neurongrp['events']): dynamics.add_event_handler(on_cond) # get variables with diff eqns for var in neurongrp['equations']: if neurongrp['equations'][var][ 'type'] == 'differential equation': diff_var = neurongrp['equations'][var] td = lems.TimeDerivative( var, renderer.render_expr(diff_var['expr'])) # add to dynamics dynamics.add_time_derivative(td) # add dynamics to _component_type self._component_type.dynamics = dynamics # add _component_type to _model self._model.add_component_type(self._component_type) # get identifiers paramdict = dict() for ident_name, ident_value in neurongrp['identifiers'].items(): paramdict[ident_name] = self._unit_lems_validator(ident_value) # if more than one neuron use multiinstantiate if neurongrp['N'] == 1: self._model.add( lems.Component("n{}".format(index_neurongrp), component_name, **paramdict)) else: self.make_multiinstantiate(special_properties, component_name, paramdict, neurongrp['N'])
def add_neurongroup(self, obj, idx_of_ng, namespace, initializers): """ Adds NeuronGroup object *obj* to self._model. If number of elements is 1 it adds component of that type, if it's bigger, the network is created by calling: `make_multiinstantiate`. Parameters ---------- obj : brian2.NeuronGroup NeuronGroup object to parse idx_of_ng : int index of neurongroup namespace : dict dictionary with all neccassary variables definition initializers : dict initial values for all model variables """ if hasattr(obj, "namespace") and not obj.namespace: obj.namespace = namespace self._nr_of_neurons = obj.N # maybe not the most robust solution ct_name = "neuron{}".format(idx_of_ng+1) self._model_namespace["neuronname"] = ct_name self._component_type = lems.ComponentType(ct_name, extends=BASE_CELL) # adding parameters special_properties = {} for key in obj.namespace.keys(): special_properties[key] = None for param in self._determine_properties(obj.namespace): self._component_type.add(param) # common things for every neuron definition self._component_type.add(lems.EventPort(name='spike', direction='out')) # dynamics of the network dynamics = lems.Dynamics() ng_equations = obj.user_equations for var in ng_equations: if ng_equations[var].type == DIFFERENTIAL_EQUATION: dim_ = _determine_dimension(ng_equations[var].unit) sv_ = lems.StateVariable(var, dimension=dim_, exposure=var) self._all_params_unit[var] = dim_ dynamics.add_state_variable(sv_) self._component_type.add(lems.Exposure(var, dimension=dim_)) elif ng_equations[var].type in (PARAMETER, SUBEXPRESSION): if var == NOT_REFRACTORY: continue dim_ = _determine_dimension(ng_equations[var].unit) self._all_params_unit[var] = dim_ # all initializers contatining iterator need to be assigned # as a property # i is default iterator in Brian2 if var in initializers and "i" in get_identifiers(str(initializers[var])): self._component_type.add(lems.Property(var, dim_)) special_properties[var] = initializers[var] continue sv_ = lems.StateVariable(var, dimension=dim_) dynamics.add_state_variable(sv_) # what happens at initialization onstart = lems.OnStart() for var in obj.equations.names: if var in (NOT_REFRACTORY, LAST_SPIKE): continue if var not in initializers: continue if var in special_properties: continue init_value = initializers[var] if type(init_value) != str: init_value = brian_unit_to_lems(init_value) else: init_value = renderer.render_expr(str(init_value)) onstart.add(lems.StateAssignment(var, init_value)) dynamics.add(onstart) if obj._refractory: # if refractoriness, we create separate regimes # - for integrating integr_regime = lems.Regime(INTEGRATING, dynamics, True) # True -> initial regime for spike_flag, oc in self._event_builder(obj.events, obj.event_codes): if spike_flag: # if spike occured we make transition to refractory regime oc.add_action(lems.Transition(REFRACTORY)) integr_regime.add_event_handler(oc) # - for refractory refrac_regime = lems.Regime(REFRACTORY, dynamics) # we make lastspike variable and initialize it refrac_regime.add_state_variable(lems.StateVariable(LAST_SPIKE, dimension='time')) oe = lems.OnEntry() oe.add(lems.StateAssignment(LAST_SPIKE, 't')) refrac_regime.add(oe) # after time spiecified in _refractory we make transition # to integrating regime if not _equation_separator(str(obj._refractory)): # if there is no specific variable given, we assume # that this is time condition ref_oc = lems.OnCondition('t .gt. ( {0} + {1} )'.format(LAST_SPIKE, brian_unit_to_lems(obj._refractory))) else: ref_oc = lems.OnCondition(renderer.render_expr(obj._refractory)) ref_trans = lems.Transition(INTEGRATING) ref_oc.add_action(ref_trans) refrac_regime.add_event_handler(ref_oc) for var in obj.user_equations.diff_eq_names: td = lems.TimeDerivative(var, renderer.render_expr(str(ng_equations[var].expr))) # if unless refratory we add only do integration regime if UNLESS_REFRACTORY in ng_equations[var].flags: integr_regime.add_time_derivative(td) continue integr_regime.add_time_derivative(td) refrac_regime.add_time_derivative(td) dynamics.add_regime(integr_regime) dynamics.add_regime(refrac_regime) else: # here we add events directly to dynamics for spike_flag, oc in self._event_builder(obj.events, obj.event_codes): dynamics.add_event_handler(oc) for var in obj.user_equations.diff_eq_names: td = lems.TimeDerivative(var, renderer.render_expr(str(ng_equations[var].expr))) dynamics.add_time_derivative(td) self._component_type.dynamics = dynamics # making componenttype is done so we add it to the model self._model.add_component_type(self._component_type) obj.namespace.pop("init", None) # kick out init # adding component to the model paramdict = dict() for param in obj.namespace: paramdict[param] = self._unit_lems_validator(obj.namespace[param]) if obj.N == 1: self._model.add(lems.Component("n{}".format(idx_of_ng), ct_name, **paramdict)) else: self.make_multiinstantiate(special_properties, ct_name, paramdict)