Synapses¶
Growth based synapses are possible when fronts are derived from the SynFront subclass. A Synapse
can be purely structural but can also be used as an input signal. The use of synapses is extensively demonstrated in the Synapses notebook.
Making synapses¶
A synapse can be made between any two non-migrating fronts that are not more than 5 µm apart. In making the synapse the user defines which front is presynaptic, the other is postsynaptic. At present, there can be only one synapse per front.
To make a synapse use the SynFront.add_synapse
method with a known other_front. The weight determines whether it is excitatory (positive float) or inhibitory (negative float):
def manage_front(self,constellation):
...
# make excitatory synapse from presynaptic asynfront1 to postsynaptic other_front1
asynfront1.add_synapse(constellation,other_front1,1.)
# make inhibitory synapse from postsynaptic asynfront2 to presynaptic other_front2
asynfront2.add_synapse(constellation,other_front2,-1.,presynaptic=False)
...
The presence of a synapse can be detected with the self.has_synapse()
method and its properties by self.get_synapse(constellation)
, self.is_presynaptic(constellation)
or self.is_postsynaptic(constellation)
:
def manage_front(self,constellation):
...
if self.has_synapse():
synapse = self.get_synapse(constellation)
if self.is_presynaptic():
print (self,"is presynaptic to",constellation.front_by_id(synapse.post_syn))
else:
print (self,"has postsynaptic to",constellation.front_by_id(synapse.pre_syn))
...
Note that synapses store the identity of the presynaptic (pre_syn attribute) and postsynaptic (pos_syn attribute) fronts as ID
.
Using syn_input¶
Each postsynaptic SynFront
will update its its syn_input before the start of each cycle and this can be used as an input signal in manage_front
. Note that the synaptic input is an average over the entire previous cycle.
The sign of syn_input is determined by whether the synapse is excitatory (positive weight) or inhibitory (negative weight):
def manage_front(self,constellation):
...
if self.has_synapse():
synapse = self.get_synapse(constellation)
if synapse.weight > 0.:
print (self,"has an excitatory synapse")
elif synapse.weight < 0.:
print (self,"has an inhibitory synapse")
...
The value of syn_input combines presynaptic properties, firing_rate and CV_ISI, with synaptic weight. In the absence of stochasticity (CV_ISI == 0.
) it reflects an average over time: syn_input = firing_rate * weight
. If CV_ISI > 0.
syn_input is stochastic and drawn from a normal distribution with mean syn_input computed as shown before. The presynaptic firing_rate and CV_ISI are set for the Neurons.
The weight of the synapse can be changed to simulate synaptic plasticity:
def manage_front(self,constellation):
...
if self.is_postsynaptic():
synapse = self.get_synapse(constellation)
synapse.set_weight(constellation,5.)
...
By correlating presynaptic firing rate with postsynaptic responses correlation based synaptic plasticity rules can be implemented. Note, however, that these operate on a slow developmental time scale, it is not possible to simulate spike-timing dependent plasticity in NeuroDevSim!
Note that only the initial value of weight is automatically stored in the database, to store updated values of weight admin.attrib_to_db
should be used as described in Storing additional attributes. Similarly, admin.attrib_to_db
can be used to store syn_input values.