-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLUTsyn_example_functions.py
More file actions
370 lines (315 loc) · 14.2 KB
/
LUTsyn_example_functions.py
File metadata and controls
370 lines (315 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# LUTsyn_example_functions.py
# Code by Duy-Tan Jonathan Pham (duytanph@usc.edu)
# July 8, 2021
# This file contains supporting functions for LUTsyn_example_main.py
######################################################################################
# This software is Copyright © 2021 The University of Southern
# California. All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software
# and its documentation for educational, research and non-profit
# purposes, without fee, and without a written agreement is
# hereby granted, provided that the above copyright notice, this
# paragraph and the following three paragraphs appear in all copies.
#
# Permission to make commercial use of this software may be obtained by contacting:
# USC Stevens Center for Innovation
# University of Southern California
# 1150 S. Olive Street, Suite 2300
# Los Angeles, CA 90115, USA
# This software program and documentation are copyrighted by The
# University of Southern California. The software program and
# documentation are supplied "as is", without any accompanying
# services from USC. USC does not warrant that the operation of the
# program will be uninterrupted or error-free. The end-user understands
# that the program was developed for research purposes and is advised
# not to rely exclusively on the program for any reason.
# IN NO EVENT SHALL THE UNIVERSITY OF SOUTHERN CALIFORNIA BE
# LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
# OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE
# UNIVERSITY OF SOUTHERN CALIFORNIA HAS BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF SOUTHERN CALIFORNIA
# SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
# BASIS, AND THE UNIVERSITY OF SOUTHERN CALIFORNIA HAS NO OBLIGATIONS
# TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
######################################################################################
from neuron import h, numpy_element_ref
import numpy as np
import time as clock
###############################################################################################
# genSyn function instantiates a synapse mechanism (written in NMODL for NEURON)
# at a specified dendrite location.
#
# Inputs
# dend - NEURON section in which the synapse mechanism will be instantiated at
# synmodeltype - a string that specifies the type of synapse to instantiate
#
# Outputs
# syn - the NEURON mechanism that represents the newly created synapse
# synweight - the synaptic weight of the synapse (this is only used by the E2_AMPA mechanism
# NT - Neurotransmitter mechanism, which is ONLY used in series with the kinetic models
###############################################################################################
def genSyn(dend, synmodeltype):
NT = 0 # only used if using kinetic models
synweight = 1
### AMPA models ###
# double exponential synapse for AMPA
if synmodeltype == "E2_AMPA":
syn = h.Exp2Syn_v2(dend(0.5)) # create synapse
# set synapse parameters
syn.tau1 = 0.9
syn.tau2 = 4.4
synweight = 2.146142e-04 # scaling factor
# LUTsyn model for AMPA
elif synmodeltype == "LUTsyn_AMPA":
# load look-up table data structure into memory
gain_array_AMPA_E3 = np.load('./LUT_AMPA_kinetic_g_4th_M300ms_d1ms_NT0.1_DIFF_v3/LUT_AMPA_kinetic_g_4th_M300ms_d1ms_NT0.1_DIFF_v3.npy')
LUT_ptr = numpy_element_ref(gain_array_AMPA_E3,0) # create pointer to LUT
syn = h.LUTsyn_AMPA_4th_E3_dtc(dend(0.5)) # create synapse
# set synapse parameters
syn.basis_gain = 0.00021258154267359922
h.setpointer(LUT_ptr, 'gain_array', syn) # link LUT pointer to LUTsyn mechanism (NOTE: this syntax might not
# work in versions of NEURON later than 7.6.7)
syn.scalar = 1
syn.tc1 = 1.05
syn.tc2 = 3.8
syn.tc3 = 16.0
syn.wtc2 = 0.963468100127
syn.wtc3 = 1 - syn.wtc2
syn.factor = 2.2071045693250277
# first order parameters
syn.o1_tc1 = 1.05
syn.o1_tc2 = 3.8
syn.o1_tc3 = 16.0
syn.factor1 = 2.2071045693250277
# second order parameters
syn.o2_tc1 = 1.05
syn.o2_tc2 = 3.95
syn.o2_tc3 = 19.0
syn.factor2 = 2.150761928070988
# third order parameters
syn.o3_tc1 = 1.1
syn.o3_tc2 = 3.85
syn.o3_tc3 = 18.0
syn.factor3 = 2.2546969539333945
# fourth order parameters
syn.o4_tc1 = 1.05
syn.o4_tc2 = 4.2
syn.o4_tc3 = 19.5
syn.factor4 = 2.0721351225715905
elif synmodeltype == "Kinetic_AMPA":
# Note: kinetic model has 2 mechanisms in series with each other: NT diffusion and kinetic state model
NT = h.NTDiffusion(dend(0.5)) # neurotransmitter mechanism
NT.Radius = 0.060
NT.k = 1.32
syn = h.AMPA16v8_noNC(dend(0.5)) # create synapse
syn.nbAMPAR = 5
h.setpointer(NT._ref_NTConcentration, 'Glu', syn)
### NMDA models ###
elif synmodeltype == "E3_NMDA":
syn = h.E3_NMDA_v2(dend(0.5)) # create synapse
# set synapse parameters
syn.tau1 = 20.3877436154
syn.tau2 = 26.6830234133
syn.tau3 = 158.729359569
syn.wtau2 = 0.963468100127
syn.wtau3 = 1 - syn.wtau2
syn.factor = 8.670516899305603
syn.scalar = 2.4594359743378894e-05
elif synmodeltype == "LUTsyn_NMDA":
# load look-up table data structure into memory
gain_array_NMDA_E3 = np.load('./LUT_NMDA_kinetic_OSP_5th_M1000ms_d5ms_NT0.1_DIFF_v3/LUT_NMDA_kinetic_OSP_5th_M1000ms_d5ms_NT0.1_DIFF_v3.npy')
LUT_ptr = numpy_element_ref(gain_array_NMDA_E3,0) # create pointer to LUT
syn = h.LUTsyn_NMDA_5th_E3_dtc(dend(0.5)) # create synapse
# set synapse parameters
syn.basis_gain = 2.4594359743378894e-05
h.setpointer(LUT_ptr, 'gain_array', syn) # link LUT pointer to LUTsyn mechanism (NOTE: this syntax might not
# work in versions of NEURON later than 7.6.7)
syn.scalar = 1
syn.gran = 5
syn.tc1 = 18
syn.tc2 = 23
syn.tc3 = 148
syn.wtc2 = 0.963468100127
syn.wtc3 = 1 - syn.wtc2
syn.factor = 9.336247512713125
# first order parameters
syn.o1_tc1 = 18
syn.o1_tc2 = 23
syn.o1_tc3 = 148
syn.factor1 = 9.336247512713125
# second order parameters
syn.o2_tc1 = 17
syn.o2_tc2 = 20
syn.o2_tc3 = 140
syn.factor2 = 12.845340591656415
# third order parameters
syn.o3_tc1 = 18
syn.o3_tc2 = 21
syn.o3_tc3 = 144
syn.factor3 = 13.378037918262585
# fourth order parameters
syn.o4_tc1 = 18
syn.o4_tc2 = 22
syn.o4_tc3 = 168
syn.factor4 = 10.878447743899189
# fifth order parameters
syn.o5_tc1 = 18
syn.o5_tc2 = 21
syn.o5_tc3 = 140
syn.factor5 = 13.402875584141828
elif synmodeltype == "Kinetic_NMDA":
# Note: kinetic model has 2 mechanisms in series with each other: NT diffusion and kinetic state model
NT = h.NTDiffusion(dend(0.5)) # neurotransmitter mechanism
NT.Radius = 0.060
NT.k = 1.32
syn = h.NMDA_v6_3_opt(dend(0.5)) # create synapse
syn.nbNMDAR = 1.235
h.setpointer(NT._ref_NTConcentration, 'Glu', syn)
return syn, synweight, NT
###############################################################################################
# genStim function returns a NEURON Vector that contains the pulse times of a
# Poisson process with a specified mean firing rate, refractory period, and simulated time
#
# Inputs
# freq - specifies the mean input firing rate of stimulation (in Hz)
# tau_AHP - the time constant of afterhyperpolarization (AHP) measured in seconds
# which characterizes the refractory period
# tstop - specifies the total time length of the stimulation
#
# Outputs
# event_times - NEURON h.Vector() that contains the pulse stimulation time series
###############################################################################################
def genStim(freq,tau_AHP,tstop):
# Create Random Number Generator
ranGen = np.random
ranGen.seed(0)
# create pulse stimulus pattern as a poisson process
event_times = h.Vector()
if freq > 0:
tres = 0.00025 # seconds
refract = -1e9
time = 0
while time < tstop:
roll = ranGen.uniform(0,1)
refract = 1-np.exp(-(time-refract)/tau_AHP)
if (roll <= freq*tres*refract):
event_times.append(time*1000)
refract = time+tres
time += tres
else:
event_times.append(tstop+10)
return event_times
###############################################################################################
# nrn_sim function executes a NEURON simulation with specified stimulus parameters, receptor,
# and allows user to specify which synapse model to run
#
# Inputs
# stim_params - dictionary containing stimulation parameters such as simulation time, input
# firing rate, and refractory period
# receptor - string that indicates what receptor is being simulated at the synapse
# "AMPA" or "NMDA"
# synmodeltype - string that specifies which synapse model to simulate
# possible values: "E2_AMPA", "LUTsyn_AMPA", "Kinetic_AMPA", "E3_NMDA", "LUTsyn_NMDA",
# "Kinetic_NMDA"
#
# Outputs
# outputs - dictionary containing multiple traces of interest such as membrane voltage, EPSC,
# and receptor conductance
###############################################################################################
def nrn_sim(stim_params,receptor,synmodeltype):
# Set up NEURON simulation
h.load_file("stdrun.hoc")
h.tstop = stim_params['sim_time']
h.dt = 0.1 # timesteps
h.steps_per_ms = 1.0/h.dt
h.load_file('negative_init.hoc') # allow simulation to reach resting state
h.v_init = -73
h.celsius = 35
h.stdinit()
# create test NEURON cell ("ball and stick")
# note that this ball and stick cell is a simplification and is not representative
# of the cell compartments reported in the LUTsyn paper (Pham, 2021)
soma = h.Section(name='soma')
soma.diam = 15 # microns
dendrite = h.Section(name='dendrite')
dendrite.diam = 5
dendrite.L = 3000
dendrite.connect(soma(1))
# create synapse mechanism
syn, synweight,NT = genSyn(dendrite, synmodeltype)
# create stimulation pattern
sim_time = stim_params['sim_time'] # ms
freq = stim_params['freq'] # mean input firing rate (Hz)
tau_AHP = stim_params['tau_AHP'] # seconds
tstop = sim_time / 1000. # seconds
event_times = genStim(freq, tau_AHP, tstop) # generate input pulse train using Poisson process
input_stim = h.VecStim() # turn pulse train into a NEURON stimulation vector
input_stim.play(event_times)
# Create NetCon between synapse and input stimulation
# (if using kinetic model, then input connects to NTDiffusion Mechanism, not synapse)
if synmodeltype == 'Kinetic_AMPA' or synmodeltype == 'Kinetic_NMDA':
nc = h.NetCon(input_stim,NT)
else:
nc = h.NetCon(input_stim,syn)
nc.weight[0] = synweight
nc.delay = 1
# Set up NEURON recording vectors
t = h.Vector() # time vector
v = h.Vector() # membrane voltage
g = h.Vector() # receptor conductance
i = h.Vector() # receptor mediated EPSC
t.record(h._ref_t)
v.record(syn._ref_v1)
g.record(syn._ref_g)
i.record(syn._ref_i)
# if simulating NMDA receptor, record open-state probability too
if receptor == "NMDA":
osp = h.Vector()
if synmodeltype == 'Kinetic_NMDA':
osp.record(syn._ref_open_total)
else:
osp.record(syn._ref_open)
# Execute NEURON simulation (print out runtimes)
print('Simulating: ')
print(' Receptor : ', receptor)
print(' Syn model : ', synmodeltype)
wall_t0 = clock.time()
h.run() # RUN NEURON simulation
wall_t1 = clock.time()
runtime = wall_t1 - wall_t0
print(' Run Time : ', runtime)
# create a dictionary that will return all useful information about the simulation
outputs = {}
# return output values as a dictionary
outputs['runtime'] = runtime
outputs['i'] = np.array(i)
outputs['t'] = np.array(t)
outputs['g'] = np.array(g)
outputs['v'] = np.array(v)
if receptor == 'NMDA':
outputs['osp'] = np.array(osp)
return outputs
###############################################################################################
# calc_NRMSE function calculates the Normalized Root Square Error between two time series of
# equal length
#
# Inputs
# ref - the reference time series. Both time series get normalized with respect to the maximum
# value of this time series.
# test - the time series that is being evaluated and assigned an error value
#
# Outputs
# rms_error_val - the NRMSE value between the two time series with ref used as reference
###############################################################################################
def calc_NRMSE(ref, test):
data_max_val = np.max(np.absolute(ref))
data_norm_val = np.divide(ref.T, data_max_val)
estimate_norm_val = test / data_max_val
squared_difference_val = np.power(np.subtract(data_norm_val, estimate_norm_val), 2)
sum_data_norm_sq_val = np.sum(np.power(data_norm_val, 2))
rms_error_val = np.sqrt(sum(squared_difference_val) / sum_data_norm_sq_val)
return rms_error_val