def create_equations(self): if self.timer is not None: self.model += b2.Equations(f""" dtimer/dt = {self.timer:f} : second """) # This timer seems a bit odd: it increases more slowly than the regular # simulation time, and is only used in the threshold code, to prevent spikes. # It effectively increase the refractory time affecting spikes (but not dv/dt) # by a factor of 10 (to biologically unrealistic values). # TODO: should investigate effect of removing extended refractory period self.model = b2.Equations(str(self.model), v_rest="v_rest_e", tau="tau_e", v_eqm_synI="v_eqm_synI_e") if self.const_theta: self.model += b2.Equations("theta : volt") else: self.model += b2.Equations("dtheta/dt = -theta / tc_theta : volt") self.threshold = "v > (theta - theta_init + v_thresh_e)" if self.timer is not None: self.threshold = f"({self.threshold}) and (timer > refrac_e)" self.refractory = "refrac_e" self.reset = "v = v_reset_e" if self.timer is not None: self.reset += "; timer = 0*ms" if not self.const_theta: self.reset += "; theta += theta_plus_e"
def load_mod(modfile,bp): nakdic = json.load(open(modfile)) fundic = dict([(j,k.split(":")[0]) for j,k in nakdic["fun"].items()]) pardic = dict([(j,k) for j,k in nakdic["par"].items()]) bifpar = [(k,pardic.pop(k)) for k in bp] sdelist = [[j,] + k.split(':') for j,k in nakdic['aux_odes'].items()] sdelist = [(i,":".join((str(S(j).subs(fundic).subs(fundic).subs(pardic)),k))) for i,j,k in sdelist] sdelist+= [('v', str(sympy.solve(nakdic['current_balance_eq'],'dv/dt')[0].subs(nakdic['currents']).subs(fundic).subs(fundic).subs(pardic))+":volt")] sde = brian2.Equations("d{}/dt = {}".format(*sdelist[0])) for i,j in sdelist[1:]: sde += brian2.Equations("d{}/dt = {}".format(i,j)) return sde
def create_base_equations(self): """v : membrane potential of each neuron - in the absence of synaptic currents (`I_syn?`), this decays exponentially to the resting potential (`v_rest_e`) with time constant `tau_leak_e` I_synE: current due to excitatory synapses - this is proportional to the conductance of the excitatory synapses (`ge`) and the difference between the membrane potential (`v`) and the equilibrium potential of the excitatory synapses (v_eqm_e) - as implemented this implicitly includes a constant factor of the membrane resistance I_synI: current due to inhibitory synapses ge : conductance of excitatory synapses - this decays with time constant `tau_ge` gi : conductance of inhibitory synapses - this decays with time constant `tau_gi` v_eqm_synI_e: equilibrium potentials of inhibitory synapses in an excitatory neuron v_eqm_synI_i: equilibrium potentials of inhibitory synapses in an inhibitory neuron tau_e: time constant for membrane potential in an excitatory neuron tau_i: time constant for membrane potential in an inhibitory neuron tau_ge: conductance time constant for an excitatory synapse tau_gi: conductance time constant for an inhibitory synapse """ self.model = b2.Equations(""" dv/dt = ((v_rest - v) + (I_synE + I_synI) / nS) / tau : volt (unless refractory) I_synE = ge * nS * (v_eqm_synE - v) : amp I_synI = gi * nS * clip(v_eqm_synI - v, -100 * volt, 0 * mV) : amp dge/dt = -ge / tau_ge : 1 dgi/dt = -gi / tau_gi : 1 """)
def get_membrane_equation(self, return_string=False): membrane_equation = Template(EquationHelper.membrane_eq_template) all_membrane_model_strings = dict(self.neuron_model_strings) all_membrane_model_strings.update(self.synaptic_excinh_model_strings) if self.custom_strings is not None: all_membrane_model_strings.update(self.custom_strings) try: membrane_equation = membrane_equation.substitute(all_membrane_model_strings) except KeyError: print('Undefined key in membrane equation') membrane_equation = str(membrane_equation) eq_lines = membrane_equation.splitlines() eq_lines = [line.strip() + '\n' for line in eq_lines if len(line.strip()) > 0] final_membrane_equation = ''.join(eq_lines) if return_string is True: return final_membrane_equation else: substitutables = {k: k + '_' + self.compartment for k in self.comp_specific_vars} compartment_eq = b2.Equations(final_membrane_equation, **substitutables) return compartment_eq
def __init__(self, tau_i, v_r, sigma, tau_sigma, beta_sigma): # self.equ = b.Equations('v = v1 + v2 : volt') # self.equ += b.Equations('dv1/dt = -v1/tau_i : volt') # self.equ += b.Equations('dv2/dt = -v2/tau_i : volt') self.equ = b.Equations('dv/dt = -v/tau_i : volt') self.threshold = 'rand() < sigma(v, dt)' self.reset = 'v = v_r'
def main4(): # Incorporation of refractory period bs.start_scope() tau = 10 * bs.ms # the (unless refractory) is necessary # refer to the documentation for more detail eqs = ''' dv/dt = (1-v)/tau : 1 (unless refractory) ''' equation = bs.Equations(eqs) # conditions for spiking models threshold = 'v>0.8' reset = 'v = -0.8' refractory = 5 * bs.ms G = bs.NeuronGroup(1, eqs, threshold=threshold, reset=reset, method='exact', refractory=refractory) state_monitor = bs.StateMonitor(G, 'v', record=0) spike_monitor = bs.SpikeMonitor(G) bs.run(50 * bs.ms) plt.plot(state_monitor.t / bs.ms, state_monitor.v[0]) plt.xlabel('Time (ms)') plt.ylabel('v') plt.show()
class Izhikevich(cells.Izhikevich): __doc__ = cells.Izhikevich.__doc__ translations = build_translations( ('a', 'a', lambda **p: p["a"] * (1 / ms), lambda **p: p["a"] / (1 / ms)), ('b', 'b', lambda **p: p["b"] * (1 / ms), lambda **p: p["b"] / (1 / ms)), ('c', 'v_reset', lambda **p: p["c"] * mV, lambda **p: p["v_reset"] / mV), ('d', 'd', lambda **p: p["d"] * (mV / ms), lambda **p: p["d"] / (mV / ms)), ('i_offset', 'i_offset', lambda **p: p["i_offset"] * nA, lambda **p: p["i_offset"] / nA)) ### dv/dt = (0.04/ms/mV)*v*v ->>>> (0.04/ms/mV)*v**2 eqs = brian2.Equations(''' dv/dt = (0.04/ms/mV)*v*v + (5/ms)*v + 140*mV/ms - u + (i_offset + i_inj)/pF : volt (unless refractory) du/dt = a*(b*v-u) : volt/second (unless refractory) a : 1/second b : 1/second v_reset : volt d : volt/second i_offset : amp i_inj : amp ''') post_synaptic_variables = {'excitatory': 'v', 'inhibitory': 'v'} state_variable_translations = build_translations( ('v', 'v', lambda p: p * mV, lambda p: p / mV), ('u', 'u', lambda p: p * (mV / ms), lambda p: p / (mV / ms))) brian2_model = IzhikevichNeuronGroup
def run_brian_sim(stim, dt, init_values, param_dict, method='exact'): # Model specification eqs = brian2.Equations("") eqs += brian2.Equations( "dV/dt = 1 / C * (Ie(t) + I_0 + I_1 - G * (V - El)) : volt (unless refractory)" ) eqs += brian2.Equations("dI_0/dt = -k_0 * I_0 : amp (unless refractory)") eqs += brian2.Equations("dI_1/dt = -k_1 * I_1 : amp (unless refractory)") reset = "" reset = "\n".join([reset, "V = V_reset"]) reset = "\n".join( [reset, "I_0 = R_0 * I_0 * exp(-k_0 * (t_ref - dt)) + A_0"]) reset = "\n".join( [reset, "I_1 = R_1 * I_1 * exp(-k_1 * (t_ref - dt)) + A_1"]) threshold = "V > Th_inf" refractory = param_dict['t_ref'] Ie = brian2.TimedArray(stim, dt=dt) nrn = brian2.NeuronGroup(1, eqs, method=method, reset=reset, threshold=threshold, refractory=refractory, namespace=param_dict) nrn.V = init_values['V'] * brian2.units.volt nrn.I_0 = init_values['I_0'] * brian2.units.amp nrn.I_1 = init_values['I_1'] * brian2.units.amp monvars = [ 'V', 'I_0', 'I_1', ] mon = brian2.StateMonitor(nrn, monvars, record=True) num_step = len(stim) brian2.defaultclock.dt = dt brian2.run(num_step * dt) return ( mon.t / brian2.units.second, mon.V[0] / brian2.units.volt, mon.I_0[0] / brian2.units.amp, mon.I_1[0] / brian2.units.amp, )
def get_neuron_equations(self): """ Returns the membrane equations in a form that prints out nicely in Jupyter Notebook :return: b2.Equations object """ s = self.get_membrane_equation(return_string=True) return b2.Equations(s)
def simulate_Thl_cell_fig3(par, par_sim): num = par_sim['num'] i_sensory_motor = par_sim['I_sm'] # b2.set_device('cpp_standalone') b2.start_scope() eqs = b2.Equations(''' minfthl = 1/(1+exp(-(vt-thtmthl*mV)/(sigmthl*mV))): 1 hinfthl = 1/(1+exp((vt-thththl*mV)/(sighthl*mV))): 1 pinfthl = 1/(1+exp(-(vt-thtpthl*mV)/(sigpthl*mV))) :1 rinfthl = 1/(1+exp((vt-thtrthl*mV)/(sigrthl*mV))) :1 ahthl = ah0thl*exp(-(vt-thtahthl*mV)/(sigahthl*mV)) :1 bhthl = bh0thl/(1+exp(-(vt-thtbhthl*mV)/(sigbhthl*mV))) :1 tauhthl = 1/(ahthl+bhthl) *ms :second taurthl = taur0thl+taur1thl*exp(-(vt-thtrtauthl*mV)/(sigrtauthl*mV)) :second ilthl=glthl*(vt-vlthl):amp inathl=gnathl*minfthl*minfthl*minfthl*hthl*(vt-vnathl) :amp ikthl=gkthl*((0.75*(1-hthl))**4)*(vt-vkthl) :amp itthl=gtthl*pinfthl*pinfthl*rthl*(vt-vtthl) :amp iextthl:amp tmp_thl1 = sin(2*pi*(t-dsmthl)/tmsmthl) :1 tmp_thl2 = sin(2*pi*(t-dsmthl+wsmthl)/tmsmthl) :1 ym_thl1=1/(1+exp(-tmp_thl1/sigym)):1 ym_thl2=1/(1+exp(-tmp_thl2/sigym)):1 ithl_sm=imsmthl*ym_thl1*(1-ym_thl2) :amp membrane_Ithl = -(ilthl+inathl+ikthl+itthl)+iextthl+ithl_sm:amp drthl/dt=phirthl*(rinfthl-rthl)/taurthl :1 dhthl/dt=phihthl*(hinfthl-hthl)/tauhthl :1 dvt/dt = membrane_Ithl/cmthl : volt ''') neuron = b2.NeuronGroup( num, eqs, method=par_sim['integration_method'], dt=par_sim['dt'], threshold='vt>-55*mV', refractory='vt>-55*mV', namespace=par, ) neuron.vt = par['v0'] neuron.hthl = "hinfthl" neuron.rthl = "rinfthl" neuron.iextthl = par['iext'] state_monitor = b2.StateMonitor(neuron, ["vt", "ithl_sm"], record=True) net = b2.Network(neuron) net.add(state_monitor) net.run(par_sim['simulation_time']) return state_monitor
def get_compartment_equations(self, compartment_name): membrane_eq = self.get_membrane_equation(return_string=True) substitutables = { key: key + '_' + compartment_name for key in self.compartment_vars_and_consts } compartment_eq = b2.Equations(membrane_eq, **substitutables) return compartment_eq
def create_equations(self): self.model = b2.Equations(str(self.model), v_rest="v_rest_i", tau="tau_i", v_eqm_synI="v_eqm_synI_i") self.threshold = "v > v_thresh_i" self.refractory = "refrac_i" self.reset = "v = v_reset_i"
def run_brian_sim(stim, dt, init_values, param_dict, method='exact'): # Model specification eqs = brian2.Equations("") eqs += brian2.Equations( "dV/dt = 1 / C * (Ie(t) - G * (V - El)) : volt (unless refractory)") eqs += brian2.Equations( "dTh_s/dt = -b_s * Th_s : volt (unless refractory)") reset = "" reset = "\n".join([reset, "V = a_r * V + b_r"]) reset = "\n".join([reset, "Th_s = Th_s + a_s"]) threshold = "V > Th_inf + Th_s" refractory = param_dict['t_ref'] Ie = brian2.TimedArray(stim, dt=dt) nrn = brian2.NeuronGroup(1, eqs, method=method, reset=reset, threshold=threshold, refractory=refractory, namespace=param_dict) nrn.V = init_values['V'] * brian2.units.volt nrn.Th_s = init_values['Th_s'] * brian2.units.volt monvars = [ 'V', 'Th_s', ] mon = brian2.StateMonitor(nrn, monvars, record=True) num_step = len(stim) brian2.defaultclock.dt = dt brian2.run(num_step * dt) return ( mon.t / brian2.units.second, mon.V[0] / brian2.units.volt, mon.Th_s[0] / brian2.units.volt, )
def get_membrane_equation(self, substitute_ad_hoc=None, return_string=True): """ Compiles the membrane equation from the template for use in Brian2. This should be the only function where the template equation is used. :param substitute_ad_hoc: :param return_string: :return: """ # Get the generic template neuron_eqs_template = Template(self.membrane_eq_template) # Do ad hoc substitutions, overriding any definitions in full_model_defns if substitute_ad_hoc is not None: neuron_eqs_template2 = Template( neuron_eqs_template.safe_substitute(substitute_ad_hoc)) else: neuron_eqs_template2 = neuron_eqs_template # Make substitutions in full_model_defns neuron_eqs_template2 = Template( neuron_eqs_template2.safe_substitute(self.full_model_defns)) # Deal with extra placeholders in the eq template nullify_placeholders_dict = { k: '' for k in PointNeuron.all_template_placeholders } neuron_eqs_template_wo_placeholders = neuron_eqs_template2.substitute( nullify_placeholders_dict) # Stringify the equations neuron_eqs_string = str(neuron_eqs_template_wo_placeholders) eq_lines = neuron_eqs_string.splitlines() eq_lines = [ line.strip() + '\n' for line in eq_lines if len(line.strip()) > 0 ] model_membrane_equation = ''.join(eq_lines) if return_string is True: return model_membrane_equation else: #substitutables = {k: k+'_'+self.compartment for k in self.comp_specific_vars} #compartment_eq = b2.Equations(self.model_membrane_equation, **substitutables) return b2.Equations(model_membrane_equation)
def get_compartment_equations(self, compartment_name): """ Compiles the membrane equation and adds compartment name to all compartment-specific variables :param string compartment_name: name of the compartment :return: string """ membrane_eq = self.get_membrane_equation(return_string=True) substitutables = { key: key + '_' + compartment_name for key in self.compartment_vars_and_consts } compartment_eq = b2.Equations(membrane_eq, **substitutables) return compartment_eq
def get_membrane_equation(self, substitute_ad_hoc=None, return_string=True): """ Compiles the membrane equation from the template for use in Brian2. This should be the only function where the template equation is used. :param dict substitute_ad_hoc: dictionary of temporary values to use in the equation template :param bool return_string: If True, returns equations as a string. Otherwise, returns a b2.Equations object. :return: string (or b2.Equations object) """ # Get the generic template neuron_eqs_template = Template(self.membrane_eq_template) # Do ad hoc substitutions, overriding any definitions in full_model_defns if substitute_ad_hoc is not None: neuron_eqs_template2 = Template( neuron_eqs_template.safe_substitute(substitute_ad_hoc)) else: neuron_eqs_template2 = neuron_eqs_template # Make substitutions in full_model_defns neuron_eqs_template2 = Template( neuron_eqs_template2.safe_substitute(self.full_model_defns)) # Deal with extra placeholders in the eq template nullify_placeholders_dict = { k: '' for k in PointNeuron.all_template_placeholders } neuron_eqs_template_wo_placeholders = neuron_eqs_template2.substitute( nullify_placeholders_dict) # Stringify the equations neuron_eqs_string = str(neuron_eqs_template_wo_placeholders) eq_lines = neuron_eqs_string.splitlines() eq_lines = [ line.strip() + '\n' for line in eq_lines if len(line.strip()) > 0 ] model_membrane_equation = ''.join(eq_lines) if return_string is True: return model_membrane_equation else: return b2.Equations(model_membrane_equation)
from ...timeseries import TSContinuous, TSEvent from ..layer import Layer from rockpool.utilities import TimedArray as TAShift from typing import Optional, Union, Tuple, List # - Type alias for array-like objects ArrayLike = Union[np.ndarray, List, Tuple] # - Configure exports __all__ = ["FFExpSynBrian", "eqSynapseExp"] # - Equations for an exponential synapse eqSynapseExp = b2.Equations( """ dI_syn/dt = (-I_syn + I_inp(t, i)) / tau_s : amp # Synaptic current tau_s : second # Synapse time constant """ ) ## - FFExpSynBrian - Class: define an exponential synapse layer (spiking input) class FFExpSynBrian(Layer): """ *DEPRECATED* Define an exponential synapse layer (spiking input), with a Brian2 backend """ ## - Constructor def __init__( self, weights: Union[np.ndarray, int] = None, dt: float = 0.1 * ms, noise_std: float = 0 * mV,
class HH_cond_exp(cells.HH_cond_exp): __doc__ = cells.HH_cond_exp.__doc__ translations = build_translations( ('gbar_Na', 'gbar_Na', lambda **p: p["gbar_Na"] * uS, lambda **p: p["gbar_Na"] / uS), ('gbar_K', 'gbar_K', lambda **p: p["gbar_K"] * uS, lambda **p: p["gbar_K"] / uS), ('g_leak', 'g_leak', lambda **p: p["g_leak"] * uS, lambda **p: p["g_leak"] / uS), ('cm', 'c_m', lambda **p: p["cm"] * nF, lambda **p: p["c_m"] / nF), ('v_offset', 'v_offset', lambda **p: p["v_offset"] * mV, lambda **p: p["v_offset"] / mV), ('e_rev_Na', 'e_rev_Na', lambda **p: p["e_rev_Na"] * mV, lambda **p: p["e_rev_Na"] / mV), ('e_rev_K', 'e_rev_K', lambda **p: p["e_rev_K"] * mV, lambda **p: p["e_rev_K"] / mV), ('e_rev_leak', 'e_rev_leak', lambda **p: p["e_rev_leak"] * mV, lambda **p: p["e_rev_leak"] / mV), ('e_rev_E', 'e_rev_e', lambda **p: p["e_rev_E"] * mV, lambda **p: p["e_rev_e"] / mV), ('e_rev_I', 'e_rev_i', lambda **p: p["e_rev_I"] * mV, lambda **p: p["e_rev_i"] / mV), ('tau_syn_E', 'tau_syn_e', lambda **p: p["tau_syn_E"] * ms, lambda **p: p["tau_syn_e"] / ms), ('tau_syn_I', 'tau_syn_i', lambda **p: p["tau_syn_I"] * ms, lambda **p: p["tau_syn_i"] / ms), ('i_offset', 'i_offset', lambda **p: p["i_offset"] * nA, lambda **p: p["i_offset"] / nA)) eqs = brian2.Equations(''' dv/dt = (g_leak*(e_rev_leak-v) - gbar_Na*(m*m*m)*h*(v-e_rev_Na) - gbar_K*(n*n*n*n)*(v-e_rev_K) + i_syn + i_offset + i_inj)/c_m : volt dm/dt = (alpham*(1-m)-betam*m) : 1 dn/dt = (alphan*(1-n)-betan*n) : 1 dh/dt = (alphah*(1-h)-betah*h) : 1 alpham = (0.32/mV)*(13*mV-v+v_offset)/(exp((13*mV-v+v_offset)/(4*mV))-1.)/ms : Hz betam = (0.28/mV)*(v-v_offset-40*mV)/(exp((v-v_offset-40*mV)/(5*mV))-1)/ms : Hz alphah = 0.128*exp((17*mV-v+v_offset)/(18*mV))/ms : Hz betah = 4./(1+exp((40*mV-v+v_offset)/(5*mV)))/ms : Hz alphan = (0.032/mV)*(15*mV-v+v_offset)/(exp((15*mV-v+v_offset)/(5*mV))-1.)/ms : Hz betan = .5*exp((10*mV-v+v_offset)/(40*mV))/ms : Hz e_rev_Na : volt e_rev_K : volt e_rev_leak : volt gbar_Na : siemens gbar_K : siemens g_leak : siemens v_offset : volt c_m : farad i_offset : amp i_inj : amp ''') + conductance_based_exponential_synapses recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh', 'm', 'n', 'h'] post_synaptic_variables = {'excitatory': 'ge', 'inhibitory': 'gi'} state_variable_translations = build_translations( ('v', 'v', lambda p: p * mV, lambda p: p / mV), ('gsyn_exc', 'ge', lambda p: p * uS, lambda p: p / uS), ('gsyn_inh', 'gi', lambda p: p * uS, lambda p: p / uS), ('h', 'h', lambda p: p * 1, lambda p: p * 1), ('m', 'm', lambda p: p * 1, lambda p: p * 1), ('n', 'n', lambda p: p * 1, lambda p: p * 1)) brian2_model = BiophysicalNeuronGroup
from copy import deepcopy import brian2 from brian2 import mV, ms, nF, nA, uS, Hz, nS from pyNN.standardmodels import cells, build_translations from ..cells import (ThresholdNeuronGroup, SpikeGeneratorGroup, PoissonGroup, BiophysicalNeuronGroup, AdaptiveNeuronGroup, AdaptiveNeuronGroup2, IzhikevichNeuronGroup) import logging logger = logging.getLogger("PyNN") leaky_iaf = brian2.Equations(''' dv/dt = (v_rest-v)/tau_m + (i_syn + i_offset + i_inj)/c_m : volt (unless refractory) tau_m : second c_m : farad v_rest : volt i_offset : amp i_inj : amp ''') # give v_thresh a different name adexp_iaf = brian2.Equations(''' dv/dt = (delta_T*gL*exp((-v_thresh + v)/delta_T) + I + gL*(v_rest - v) - w )/ c_m : volt (unless refractory) dw/dt = (a*(v-v_rest) - w)/tau_w : amp gL = c_m / tau_m : siemens I = i_syn + i_inj + i_offset : amp a : siemens tau_m : second tau_w : second c_m : farad v_rest : volt v_spike : volt
def make_neuron_equations(self): # Topology here is: (POST) basal -> soma -> a0 -> a1 -> ... (PRE) # Multiple different indexes: # - C, g_leak are indexed 0...N from basal...last apical # - Apical dendrite compartments are indexed 0...(N-2) # - Ra is indexed 0...(N-1) from basal...last apical # In legacy code, vm of soma is vm, not vm_soma. To keep things separate & clear, however, # we will replace vm_soma->vm only in the very end # For convenience R_axial = self.neuron_parameters['Ra'] C = self.neuron_parameters['C'] g_leak = self.neuron_parameters['g_leak'] # Dendritic current templates I_dendr_basal = 'I_dendr = gapre*(vmpre-vmself) : amp' I_dendr_2way = 'I_dendr = gapre*(vmpre-vmself) + gapost*(vmpost-vmself) : amp' I_dendr_last_apical = 'I_dendr = gapost*(vmpost-vmself) : amp' # Basal compartment temp_eq = b2.Equations(I_dendr_basal, I_dendr='I_dendr_basal', vmself='vm_basal', vmpre='vm_soma', gapre=1 / (R_axial[0])) self.basal_compartment.add_external_current('I_dendr_basal', str(temp_eq)) eq_basal = self.basal_compartment.get_compartment_equations('basal') eq_basal = b2.Equations(str(eq_basal), C=C[0], g_leak=g_leak[0]) # The soma temp_eq = b2.Equations(I_dendr_2way, I_dendr='I_dendr_soma', vmself='vm_soma', vmpre='vm_a0', vmpost='vm_basal', gapre=1 / (R_axial[1]), gapost=1 / (R_axial[0])) self.soma_compartment.add_external_current('I_dendr_soma', str(temp_eq)) eq_soma = self.soma_compartment.get_compartment_equations('soma') eq_soma = b2.Equations(str(eq_soma), C=C[1], g_leak=g_leak[1]) # Finally, go through all apical compartments but not the last one n_apical = self.n_apical eq_apicals = [] post_compartment = 'soma' for i in range(n_apical-1): temp_eq = b2.Equations(I_dendr_2way, I_dendr='I_dendr_a%d'%i, vmself='vm_a%d'%i, vmpre='vm_a%d'%(i+1), vmpost='vm_'+post_compartment, gapre=1 / (R_axial[i+2]), gapost=1 / (R_axial[i+1])) self.apical_compartments[i].add_external_current('I_dendr_a%d'%i, str(temp_eq)) eq_apical = self.apical_compartments[i].get_compartment_equations('a%d'%i) eq_apical = b2.Equations(str(eq_apical), C=C[i+2], g_leak=g_leak[i+2]) eq_apicals.append(eq_apical) post_compartment = 'a%d'%i # The last apical compartment last_ix = n_apical - 1 if n_apical == 1: post_compartment = 'soma' else: post_compartment = 'a%d' % (last_ix - 1) vm_post = 'vm_'+post_compartment temp_eq = b2.Equations(I_dendr_last_apical, I_dendr='I_dendr_a%d'%last_ix, vmself='vm_a%d' % last_ix, vmpost=vm_post, gapost=1 / (R_axial[-1])) # R_axial[-1] can be wrong but we keep it in this legacy version self.apical_compartments[last_ix].add_external_current('I_dendr_a%d' % last_ix, str(temp_eq)) eq_apical = self.apical_compartments[last_ix].get_compartment_equations('a%d' % last_ix) eq_apical = b2.Equations(str(eq_apical), C=C[last_ix + 2], g_leak=g_leak[last_ix + 2]) eq_apicals.append(eq_apical) # Combine all the equations into one & replace vm_soma -> vm final_eq = eq_basal+eq_soma for i in range(n_apical): final_eq += eq_apicals[i] self.final_eqs = b2.Equations(str(final_eq), vm_soma='vm')
def find_ini_brian(): import pylab, brian2, brianutils, os, json, sympy, scipy, sys, \ datetime, shelve, autoutils, contextlib from sympy import S units= dict(brian2.units.__dict__.items() + brian2.units.allunits.__dict__.items() + brian2.__dict__.items()) unitlist=["mV","ms","cm2","uF","psiemens","um2","msiemens","cm"] baseunits=[(k,float(eval(k,units).base)) for k in unitlist] p = {"simfile" : "sim_DNap_2015-12-14_16:55:14.603625.shv", "modfile" : "cfg/wangBuzsaki_brian.json", #This is our model "contfile" : "dat/cont_WB.json", #This is what brian produces and hands to auto "workdir" : os.getenv("HOME") + "/Uni/CNS/3.Semester/schreiberlab/excitationblock/model0/final", "dt" : "0.05*ms", "bifpar" : { "I" : ["0.0* uA/cm2"], "Cm" : ["1.0*uF/cm2","1.1*uF/cm2","1.4*uF/cm2","1.41*uF/cm2","1.42*uF/cm2"] } } os.chdir(p["workdir"]) #Now, we define how to load in the model in a nice pythonic way and sort them #in a way that brian will understand them. def load_mod(modfile,bp): nakdic = json.load(open(modfile)) fundic = dict([(j,k.split(":")[0]) for j,k in nakdic["fun"].items()]) pardic = dict([(j,k) for j,k in nakdic["par"].items()]) bifpar = [(k,pardic.pop(k)) for k in bp] sdelist = [[j,] + k.split(':') for j,k in nakdic['aux_odes'].items()] sdelist = [(i,":".join((str(S(j).subs(fundic).subs(fundic).subs(pardic)),k))) for i,j,k in sdelist] sdelist+= [('v', str(sympy.solve(nakdic['current_balance_eq'],'dv/dt')[0].subs(nakdic['currents']).subs(fundic).subs(fundic).subs(pardic))+":volt")] sde = brian2.Equations("d{}/dt = {}".format(*sdelist[0])) for i,j in sdelist[1:]: sde += brian2.Equations("d{}/dt = {}".format(i,j)) return sde ## FIND INITIAL STEADY STATE ## sde = load_mod(p["modfile"],p['bifpar'].keys()) ode = brianutils.sde2ode(sde) diffuterms=dict([(k[3:],(S(j).coeff(k)**2).subs(baseunits)) for i,j in sde.eq_expressions for k in sde.stochastic_variables if S(j).coeff(k)!=0]) ## ADD PAR ## for j,k in p["bifpar"].items(): ode += brian2.Equations("{} : {}".format(j,repr(eval(k[0],units).dim))) brian2.defaultclock.dt = eval(p["dt"], units) G = brian2.NeuronGroup(1, model=ode, method="rk4", threshold='not_refractory and (v>5*mV)', refractory='v>-40*mV') # PAR INIT # for j,k in p["bifpar"].items(): setattr(G,j,eval(k[0],units)) # STATE INIT # G.v= eval("-66 * mV", units) states = brian2.StateMonitor(G, ode.eq_names, record=True) spikes = brian2.SpikeMonitor(G) net = brian2.Network(G,states,spikes) duration = eval("500 * ms",units) net.run(duration) autobifpar = dict([(i,float(eval(j[0],units))) for i,j in p['bifpar'].items()]) ## CREATE ADJOINT LINEAR SYSTEM ## baseunits2 = [('mV', 1), ('ms', 1), ('cm2', 1), ('uF', 1), ('psiemens', 1), ('um2', 1), ('msiemens', 1), ('cm', 1)] varrhs = [(i,sympy.S(j).subs(baseunits2)) for i,j in ode.eq_expressions] # varrhs_2 = [(i,sympy.S(j).subs(baseunits)) # for i,j in ode.eq_expressions] varrhs.sort(cmp=lambda x,y:cmp(x[0],y[0]),reverse=True) var,rhs = zip(*varrhs); advar = sympy.S(["ad{}".format(k) for k in var]) J = [[S(i).diff(j) for j in var] for i in rhs] J = [[j.subs(baseunits) for j in k] for k in J] adlinsys = [str(k) for k in (sympy.S("lam")*sympy.eye(len(advar))-sympy.Matrix(J).T)*sympy.Matrix(advar)] prcnorm=str((sympy.Matrix(sympy.S(advar)).T*sympy.Matrix(sympy.S(rhs)))[0,0] - sympy.S("dotZF/period")) # Ipar = eval("1.2 * uA/cm2", units) # LC spikecriterion = [str(S(k).subs([(i,"{}_left".format(i)) for i in var])) for j,k in zip(var,rhs) if j=="v"] #Hier kommt das Pythondings von Jan-Hendrik zum Einsatz! if "A" in autobifpar: autobifpar.pop("A") unames,pnames= autoutils.writeFP('tm_new', bifpar=autobifpar, rhs=rhs, var=var, bc=['{0}_left-{0}_right'.format(v) for v in var] + spikecriterion, ic=[]) inivals = ([float(getattr(states,j)[0][-1]) for j in var]) #convert first value (V) from V to mV. inivals[0] *= 1000 return unames, pnames, inivals, autobifpar
def create_equations(self): self.model = b2.Equations("w : 1") self.pre_eqn = "g{}_post += w".format(self.pre_conn_type) self.post_eqn = ""
def create_stdp_equations(self): if self.stdp_rule == "original": # original code from Diehl & Cooke (2015) repository # An implementation of the nearest-spike minimal triplet # model of Pfister & Gerstner (2006) # NOTES: # * the sign of the weight pre-synaptic weight update # appears to be wrong compared to PG06 self.model += b2.Equations(""" dpre/dt = -pre/(tc_pre_ee) : 1 (event-driven) dpost1/dt = -post1/(tc_post_1_ee) : 1 (event-driven) dpost2/dt = -post2/(tc_post_2_ee) : 1 (event-driven) """) self.pre_eqn += """ pre = 1. w = clip(w + nu_ee_pre * post1, 0, wmax_ee) """ self.post_eqn += """ w = clip(w + nu_ee_post * pre * post2, 0, wmax_ee) post1 = 1. post2 = 1. """ elif self.stdp_rule == "minimal-triplet": # Minimal (visual cortex) model of Pfister & Gerstner (2006) # Mapping of notation to PG06 and DC15: # pre = r_1 = pre # tc_pre = \tau_+ = tc_pre_ee # post1 = o_1 = post1 # post2 = o_2 = post2 # tc_post1 = \tau_- = tc_post_1_ee # tc_post2 = \tau_y = tc_post_2_ee # nu_pair_pre = A^-_2 = nu_ee_pre # nu_triple_post = A^+_3 = nu_ee_post self.model += b2.Equations(""" dpre/dt = -pre / tc_pre : 1 (event-driven) dpost1/dt = -post1 / tc_post1 : 1 (event-driven) dpost2/dt = -post2 / tc_post2 : 1 (event-driven) """) self.pre_eqn += """ w = clip(w - post1 * nu_pair_pre, 0, wmax) pre = 1.0 """ self.post_eqn += """ w = clip(w + pre * nu_triple_post * post2, 0, wmax) post1 = 1.0 post2 = 1.0 """ elif self.stdp_rule == "full-triplet": # Full model of Pfister & Gerstner (2006) # Mapping of notation to PG06 and DC15: # pre1 = r_1 = pre # pre2 = r_2 (neglected in minimal model) # tc_pre1 = \tau_+ = tc_pre_ee # tc_pre2 = \tau_x (neglected in minimal model) # post1 = o_1 = post1 # post2 = o_2 = post2 # tc_post1 = \tau_- = tc_post_1_ee # tc_post2 = \tau_y = tc_post_2_ee # nu_pair_pre = A^-_2 = nu_ee_pre # nu_triple_pre = A^-_3 (neglected in minimal model) # nu_pair_post = A^+_2 (neglected in minimal model) # nu_triple_post = A^+_3 = nu_ee_post self.model += b2.Equations(""" dpre1/dt = -pre1 / tc_pre1 : 1 (event-driven) dpre2/dt = -pre2 / tc_pre2 : 1 (event-driven) dpost1/dt = -post1 / tc_post1 : 1 (event-driven) dpost2/dt = -post2 / tc_post2 : 1 (event-driven) """) self.pre_eqn += """ w = clip(w - post1 * (nu_pair_pre + nu_triple_pre * pre2), 0, wmax) pre1 = 1.0 pre2 = 1.0 """ self.post_eqn += """ w = clip(w + pre1 * (nu_pair_post + nu_triple_post * post2), 0, wmax) post1 = 1.0 post2 = 1.0 """ if self.stdp_rule == "powerlaw": # inferred code from Diehl & Cooke (2015) self.model += b2.Equations(""" dpre/dt = -pre/ tc_pre : 1 (event-driven) """) self.pre_eqn += """ pre = pre + 1.0 """ self.post_eqn += """ w = clip(w + nu * (pre - tar) * (wmax - w)**mu, 0, wmax) """ if self.stdp_rule == "exponential": # inferred code from Diehl & Cooke (2015) self.model += b2.Equations(""" dpre/dt = -pre/ tc_pre : 1 (event-driven) """) self.pre_eqn += """ pre = pre + 1.0 """ self.post_eqn += """ w = clip(w + nu * (pre * exp(-beta * w) - tar * exp(-beta * (wmax - w))), 0, wmax) """ if self.stdp_rule == "symmetric": # inferred code from Diehl & Cooke (2015) self.model += b2.Equations(""" dpre/dt = -pre/ tc_pre : 1 (event-driven) dpost/dt = -post / tc_post : 1 (event-driven) """) self.pre_eqn += """ w = clip(w - nu_pre * pre * w**mu, 0, wmax) pre = pre + 1.0 """ self.post_eqn += """ w = clip(w + nu_post * (pre - tar) * (wmax - w)**mu, 0, wmax) post = post + 1.0 """ if self.stdp_rule == "clopath2010": # TODO: try Clopath et al. 2010 rule # not in DC15, but would be nice to try this sometime: # spike-timing dependent plasticity perhaps more bio-physically # mediated by the post-synaptic membrane voltage pass
def Simulate(g, inp, seed=1337): """Simulation of the Brunel network with relative strength between IPSP and EPSP g and external input parameter inp. Inp=1 is the minimum amount of external stimulation to observe sustained activity in the full network (12 500 neurons). """ br.set_device('cpp_standalone') br.prefs.codegen.target = 'weave' # NETWORK INITIALIZATION np.random.seed(seed) # set seed for reproducibility of simulations random.seed(seed) # set seed for reproducibility of simulations # ============================================================================= # PARAMETERS # Simulation parameters simdt = 0.01 * br.ms simtime = sim_time * br.second br.defaultclock.dt = simdt # Brian's default sim time step dt = br.defaultclock.dt / br.second # scaling parameter to the true on of Brunel 2000 N_true = 12500 delay = syn_delay * br.ms # Synaptic delay J = Vm_jump_after_EPSP * downscale * br.mV # jump of membrane potential after EPSP # Network numbers parameters N = int(N_true / downscale) # number of Neuron C = int(connectivity * N) # number of connexions per neuron sparseness = C / float(N) f_exc = 0.8 # fraction of exitatory synapses Ce = int(C * f_exc) # number of exitatory connexions NE = int(N * f_exc) # Number of excitatory cells NI = N - NE # Number of inhibitory cells CE_true = int(N_true * connectivity * f_exc) # Neurons parameters tau = 20.0 * br.ms # membrane time constant mu_0 = 0. * br.mV # offset to the membrane Vr = mu_0 + 10.0 * br.mV # potential of reset after spiking theta = mu_0 + 20.0 * br.mV # Spiking threshold taurefr = 2. * br.ms # time constant of refractory period # Synapse parameters g = float(g) # relative strength between IPSP and EPSP # parameter of external population Inp = float(inp) # coefficient multiplying nu_theta nu_theta = theta / (Ce * J * tau ) # minimal required input frequency for firing # ============================================================================= # INITIALIZE NEURONS GROUP # Main group eqs_neurons = br.Equations(''' dV/dt = (-V + mu_0)/tau : volt (unless refractory) ''') Group=br.NeuronGroup(N, model=eqs_neurons,\ threshold='V>=theta', reset='V=Vr', \ refractory=taurefr, method='euler') Group.V = np.linspace(mu_0 / br.mV - 20, theta / br.mV, N) * br.mV # external group # ============================================================================= # SYNAPSES DEFINITION AND CONNEXIONS # The implementation of connections come from ExcInhNet_Ostojic2014_Brunel2000_brian2.py # written by Aditya Gilra to connect the synapses. # Main group synapses sparseness_e = Ce / float(NE) sparseness_i = (1 - f_exc) * C / float(NI) con = br.Synapses(Group, Group, 'w:volt', on_pre='V_post += w', method='euler') # Connections from some Exc/Inh neurons to each neuron random.seed(seed) # set seed for reproducibility of simulations conn_i = [] conn_j = [] for j in range(0, N): # sample Ce number of neuron indices out of NE neurons preIdxsE = random.sample(range(NE), Ce) # sample Ci=C-excC number of neuron indices out of inhibitory neurons preIdxsI = random.sample(range(NE, N), C - Ce) # connect these presynaptically to i-th post-synaptic neuron # choose the synapses object based on whether post-syn nrn is exc or inh conn_i += preIdxsE conn_j += [j] * Ce conn_i += preIdxsI conn_j += [j] * (C - Ce) con.connect(i=conn_i, j=conn_j) con.delay = delay con.w['i<NE'] = J con.w['i>=NE'] = -g * J # # save connectivity matrix of the network for post-analysis purpose # # safe to comment if you are not interested in this information # struct = np.zeros((N,N)) # for i,j in zip(conn_i, conn_j): # struct[i,j] = 1 # utils.save('brunel_struct_seed={}.npy'.format(seed), struct) # EXTERNAL POPULATION #correct external population firing rate for downscaling if g > 4: approx_rate = ((Inp - 1) * nu_theta) / (g * 0.25 - 1) / N rate_ext_0 = Ce * Inp * nu_theta rate_balance = Ce * ((1/downscale) - 1) *\ (Inp * nu_theta + approx_rate*(1 + 0.25*g**2)) /\ (1 + g**2) rate_corrected = (rate_ext_0 + rate_balance) / Ce else: rate_corrected = Inp * nu_theta print(Inp * nu_theta, rate_corrected) PI = br.PoissonInput(Group, 'V', N=Ce, rate=rate_corrected, weight=J) # ============================================================================= # SIMULATION #np.random.seed(seed) #random.seed(seed) # Setting up monitors M = br.SpikeMonitor(Group) LFP = br.PopulationRateMonitor(Group) br.run(simtime, report='text') # saving population firing rate and spike trains fr = [ LFP.t / br.ms, LFP.smooth_rate(window='flat', width=0.5 * br.ms) / br.Hz ] sp = M.spike_trains() br.device.reinit() return sp, fr
sde = brian2.Equations("d{}/dt = {}".format(*sdelist[0])) for i, j in sdelist[1:]: sde += brian2.Equations("d{}/dt = {}".format(i, j)) return sde ## FIND INITIAL STEADY STATE ## sde = load_mod(p["modfile"], p['bifpar'].keys()) ode = brianutils.sde2ode(sde) diffuterms = dict([(k[3:], (S(j).coeff(k)**2).subs(baseunits)) for i, j in sde.eq_expressions for k in sde.stochastic_variables if S(j).coeff(k) != 0]) ## ADD PAR ## for j, k in p["bifpar"].items(): ode += brian2.Equations("{} : {}".format(j, repr(eval(k[0], units).dim))) brian2.defaultclock.dt = eval(p["dt"], units) G = brian2.NeuronGroup(1, model=ode, method="rk4", threshold='not_refractory and (v>5*mV)', refractory='v>-40*mV') # PAR INIT # for j, k in p["bifpar"].items(): setattr(G, j, eval(k[0], units)) # STATE INIT # G.v = eval("-66 * mV", units)