'''
Sensory circuit
@author: Klaus Wimmer
wimmer.klaus@googlemail.com
'''
from brian import *
import random as pyrandom
import numpy
from numpy.random import randn as randn
def make_sensory_circuit():
'''
Creates the spiking network representing the sensory circuit.
returns:
groups, connections, subgroups
groups and connections have to be added to the "Network" in order to run the simulation.
subgroups is used for establishing connections between the sensory and integration circuit; do not add subgroups to the "Network"
'''
# -----------------------------------------------------------------------------------------------
# Model parameters for the sensory circuit
# -----------------------------------------------------------------------------------------------
# Populations
N_E = 1600 # Total number of excitatory neurons
N_E1 = int(0.5 * N_E) # Size of excitatory population E1
N_E2 = N_E1 # Size of excitatory population E2
N_I = 400 # Size of inhibitory population I
N_X = 1000 # Size of external population X
N_X1 = int(0.5 * N_X) # Size of external population X1
N_X2 = int(0.5 * N_X) # Size of external population X2
# Connectivity - local recurrent connections
p = 0.2 # Connection probability for (EE, EI, IE, II)
w_p = 1.3 # Relative synaptic strength of connections within populations E1 and E2
w_m = 2.0 - w_p # Relative synaptic strength of connections across populations E1 and E2
gEE = 0.7589 * nS # Weight of excitatory to excitatory synapses
gEI = 1.5179 * nS # Weight of excitatory to inhibitory synapses
gIE = 12.6491 * nS # Weight of inhibitory to excitatory synapses
gII = gIE # Weight of inhibitory to inhibitory synapses
dE = (0.5 * ms, 1.5 * ms) # Range of uniformly distributed transmission delays of excitatory connections
dI = (0.1 * ms, 0.9 * ms) # Range of uniformly distributed transmission delays of inhibitory connections
# Connectivity - external connections
p_x = 0.32 # Connection probability for external connections
alpha_x = 0.0
gextE = 1.7076 * nS # Weight of external to excitatory synapses
gextI = 1.7076 * nS # Weight of external to inhibitory synapses
dXE = (0.5 * ms, 1.5 * ms) # Range of uniformly distributed transmission delays of external connections
# Neuron model
CmE = 0.25 * nF # Membrane capacitance of excitatory neurons
CmI = 0.25 * nF # Membrane capacitance of inhibitory neurons
gLeakE = 16.7 * nS # Leak conductance of excitatory neurons
gLeakI = 16.7 * nS # Leak conductance of inhibitory neurons
Vl = -70.0 * mV # Resting potential
Vt = -50.0 * mV # Spiking threshold
Vr = -60.0 * mV # Reset potential
tau_refE = 2.0 * ms # Absolute refractory period of excitatory neurons
tau_refI = 1.0 * ms # Absolute refractory period of inhibitory neurons
nu_ext = 12.5 * Hz # Firing rate of external Poisson neurons
# Synapse model
VrevE = 0 * mV # Reversal potential of excitatory synapses
VrevI = -80 * mV # Reversal potential of inhibitory synapses
tau_decay = 5.0 * ms # Decay constants of AMPA-type and GABA-type conductances
tau_rise = 1.0 * ms # Rise constant of AMPA- and GABA-type conductances
# Inputs
# -----------------------------------------------------------------------------------------------
# Set up the model
# -----------------------------------------------------------------------------------------------
# Neuron equations
eqs = '''
dV/dt = (-gea*(V-VrevE) - gi*(V-VrevI) - (V-Vl)) * (1.0/tau) + I/Cm: volt
dgea/dt = (xe-gea)*(1.0/tau_decay) : 1
dxe/dt = -xe*(1.0/tau_rise) : 1
dgi/dt = (xi-gi)*(1.0/tau_decay) : 1
dxi/dt = -xi*(1.0/tau_rise) : 1
I : nA
tau : second
Cm : nF
'''
# Set up the sensory circuit
sensoryE = NeuronGroup(N_E, model=eqs, threshold=Vt, reset=Vr, refractory=tau_refE)
sensoryI = NeuronGroup(N_I, model=eqs, threshold=Vt, reset=Vr, refractory=tau_refI)
sensoryE.tau = CmE / gLeakE
sensoryE.Cm = CmE
sensoryI.tau = CmI / gLeakI
sensoryI.Cm = CmI
sensoryE.I = 0.0
sensoryI.I = 0.0
sensoryE1 = sensoryE.subgroup(N_E1)
sensoryE2 = sensoryE.subgroup(N_E2)
# Connections involving AMPA synapses
C_SE_SE = Connection(sensoryE, sensoryE, 'xe', delay=True, max_delay=1.5 * ms)
C_SE_SE.connect_random(sensoryE1, sensoryE1, sparseness=p, weight=lambda:w_p * gEE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dE)
C_SE_SE.connect_random(sensoryE2, sensoryE2, sparseness=p, weight=lambda:w_p * gEE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dE)
C_SE_SE.connect_random(sensoryE1, sensoryE2, sparseness=p, weight=lambda:w_m * gEE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dE)
C_SE_SE.connect_random(sensoryE2, sensoryE1, sparseness=p, weight=lambda:w_m * gEE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dE)
C_SE_SI = Connection(sensoryE, sensoryI, 'xe', delay=True, max_delay=1.5 * ms)
C_SE_SI.connect_random(sparseness=p, weight=lambda:gEI/gLeakI * max(0.0, 1.0 + 0.5 * randn()), delay=dE)
# Connections involving GABA synapses
C_SI_SE = Connection(sensoryI, sensoryE, 'xi', delay=True, max_delay=0.9 * ms)
C_SI_SE.connect_random(sparseness=p, weight=lambda:gIE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dI)
C_SI_SI = Connection(sensoryI, sensoryI, 'xi', delay=True, max_delay=0.9 * ms)
C_SI_SI.connect_random(sparseness=p, weight=lambda:gII/gLeakI * max(0.0, 1.0 + 0.5 * randn()), delay=dI)
# External inputs
external = PoissonGroup(N_X, rates = nu_ext) # unspecific Poisson input
external1 = external.subgroup(N_X1)
external2 = external.subgroup(N_X2)
# Connect external inputs
C_SX_SE = Connection(external, sensoryE, 'xe', delay=True, max_delay=1.5 * ms)
C_SX_SE.connect_random(external1, sensoryE1, sparseness=p_x * (1.0 + alpha_x), weight=lambda:gextE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dXE)
C_SX_SE.connect_random(external2, sensoryE2, sparseness=p_x * (1.0 + alpha_x), weight=lambda:gextE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dXE)
C_SX_SE.connect_random(external1, sensoryE2, sparseness=p_x * (1.0 - alpha_x), weight=lambda:gextE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dXE)
C_SX_SE.connect_random(external2, sensoryE1, sparseness=p_x * (1.0 - alpha_x), weight=lambda:gextE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dXE)
C_SX_SI = Connection(external, sensoryI, 'xe', delay=True, max_delay=1.5 * ms)
C_SX_SI.connect_random(sparseness=p_x, weight=lambda:gextI/gLeakI * max(0.0, 1.0 + 0.5 * randn()), delay=dXE)
# Return the sensory circuit
groups = {'SE': sensoryE, 'SI': sensoryI, 'SX': external}
subgroups = {'SE1': sensoryE1, 'SE2': sensoryE2}
connections = {'C_SX_SE': C_SX_SE, 'C_SX_SI': C_SX_SI,
'C_SE_SE': C_SE_SE, 'C_SE_SI': C_SE_SI, 'C_SI_SE': C_SI_SE, 'C_SI_SI': C_SI_SI}
return groups, connections, subgroups