debug and add unittests 1

This commit is contained in:
a.ott
2020-02-27 09:28:34 +01:00
parent 234f58404e
commit 170ecab8f4
8 changed files with 426 additions and 120 deletions

View File

@@ -19,10 +19,10 @@ class LifacNoiseModel(AbstractModel):
"v_offset": 50,
"input_scaling": 1,
"delta_a": 0.4,
"tau_a": 40,
"tau_a": 0.04,
"a_zero": 0,
"noise_strength": 3,
"step_size": 0.01}
"step_size": 0.00005}
def __init__(self, params: dict = None):
super().__init__(params)
@@ -36,26 +36,31 @@ class LifacNoiseModel(AbstractModel):
def simulate(self, stimulus: AbstractStimulus, total_time_s):
self.stimulus = stimulus
output_voltage = []
adaption = []
time = np.arange(0, total_time_s, self.parameters["step_size"])
output_voltage = np.zeros(len(time), dtype='float64')
adaption = np.zeros(len(time), dtype='float64')
spiketimes = []
current_v = self.parameters["v_zero"]
current_a = self.parameters["a_zero"]
output_voltage[0] = current_v
adaption[0] = current_a
for time_point in np.arange(0, total_time_s*1000, self.parameters["step_size"]):
for i in range(1, len(time), 1):
time_point = time[i]
# rectified input:
stimulus_strength = fu.rectify(stimulus.value_at_time_in_ms(time_point))
stimulus_strength = fu.rectify(stimulus.value_at_time_in_s(time_point))
v_next = self._calculate_voltage_step(current_v, stimulus_strength - current_a)
a_next = self._calculate_adaption_step(current_a)
if v_next > self.parameters["threshold"]:
v_next = self.parameters["v_base"]
spiketimes.append(time_point/1000)
a_next += self.parameters["delta_a"] / (self.parameters["tau_a"] / 1000)
spiketimes.append(time_point)
a_next += self.parameters["delta_a"] / (self.parameters["tau_a"])
output_voltage.append(v_next)
adaption.append(a_next)
output_voltage[i] = v_next
adaption[i] = a_next
current_v = v_next
current_a = a_next
@@ -66,33 +71,6 @@ class LifacNoiseModel(AbstractModel):
return output_voltage, spiketimes
def simulate_fast(self, stimulus: AbstractStimulus, total_time_s, time_start=0):
v_zero = self.parameters["v_zero"]
a_zero = self.parameters["a_zero"]
step_size = self.parameters["step_size"]
threshold = self.parameters["threshold"]
v_base = self.parameters["v_base"]
delta_a = self.parameters["delta_a"]
tau_a = self.parameters["tau_a"]
v_offset = self.parameters["v_offset"]
mem_tau = self.parameters["mem_tau"]
noise_strength = self.parameters["noise_strength"]
stimulus_array = stimulus.as_array(time_start, total_time_s, step_size)
rectified_stimulus = rectify_stimulus_array(stimulus_array)
parameters = np.array([v_zero, a_zero, step_size, threshold, v_base, delta_a, tau_a, v_offset, mem_tau, noise_strength])
voltage_trace, adaption, spiketimes = simulate_fast(rectified_stimulus, total_time_s, parameters)
self.stimulus = stimulus
self.voltage_trace = voltage_trace
self.adaption_trace = adaption
self.spiketimes = spiketimes
return voltage_trace, spiketimes
def _calculate_voltage_step(self, current_v, input_v):
v_base = self.parameters["v_base"]
step_size = self.parameters["step_size"]
@@ -109,6 +87,31 @@ class LifacNoiseModel(AbstractModel):
step_size = self.parameters["step_size"]
return current_a + (step_size * (-current_a)) / self.parameters["tau_a"]
def simulate_fast(self, stimulus: AbstractStimulus, total_time_s, time_start=0):
v_zero = self.parameters["v_zero"]
a_zero = self.parameters["a_zero"]
step_size = self.parameters["step_size"]
threshold = self.parameters["threshold"]
v_base = self.parameters["v_base"]
delta_a = self.parameters["delta_a"]
tau_a = self.parameters["tau_a"]
v_offset = self.parameters["v_offset"]
mem_tau = self.parameters["mem_tau"]
noise_strength = self.parameters["noise_strength"]
rectified_stimulus = rectify_stimulus_array(stimulus.as_array(time_start, total_time_s, step_size))
parameters = np.array([v_zero, a_zero, step_size, threshold, v_base, delta_a, tau_a, v_offset, mem_tau, noise_strength, time_start])
voltage_trace, adaption, spiketimes = simulate_fast(rectified_stimulus, total_time_s, parameters)
self.stimulus = stimulus
self.voltage_trace = voltage_trace
self.adaption_trace = adaption
self.spiketimes = spiketimes
return voltage_trace, spiketimes
def min_stimulus_strength_to_spike(self):
return self.parameters["threshold"] - self.parameters["v_base"]
@@ -159,8 +162,8 @@ class LifacNoiseModel(AbstractModel):
"""
base_stimulus = SinusAmplitudeModulationStimulus(base_stimulus_freq, 0, 0)
_, spiketimes = self.simulate_fast(base_stimulus, 30)
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, 5)
time_x = 5
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, self.get_sampling_interval(), time_x)
relative_spiketimes = np.array([s % (1 / base_stimulus_freq) for s in spiketimes])
eod_durations = np.full((len(spiketimes)), 1 / base_stimulus_freq)
@@ -179,13 +182,10 @@ class LifacNoiseModel(AbstractModel):
f_infinities = []
for contrast in contrasts:
stimulus = SinusAmplitudeModulationStimulus(base_freq, contrast, modulation_frequency)
_, spiketimes = self.simulate_fast(stimulus, 0.5)
_, spiketimes = self.simulate_fast(stimulus, 1)
if len(spiketimes) < 2:
f_infinities.append(0)
else:
f_infinity = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, 0.4)
f_infinities.append(f_infinity)
f_infinity = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, self.get_sampling_interval(), 0.3)
f_infinities.append(f_infinity)
popt, pcov = curve_fit(fu.line, contrasts, f_infinities, maxfev=10000)
@@ -193,9 +193,58 @@ class LifacNoiseModel(AbstractModel):
return f_infinities, f_infinities_slope
def find_v_offset(self, goal_baseline_frequency, base_stimulus, threshold=10):
test_model = self.get_model_copy()
simulation_length = 5
v_search_step_size = 1000
current_v_offset = 0
current_freq = test_v_offset(test_model, current_v_offset, base_stimulus, simulation_length)
while current_freq < goal_baseline_frequency:
current_v_offset += v_search_step_size
current_freq = test_v_offset(test_model, current_v_offset, base_stimulus, simulation_length)
lower_bound = current_v_offset - v_search_step_size
upper_bound = current_v_offset
return binary_search_base_freq(test_model, base_stimulus, goal_baseline_frequency, simulation_length, lower_bound, upper_bound, threshold)
def binary_search_base_freq(model: LifacNoiseModel, base_stimulus, goal_frequency, simulation_length, lower_bound, upper_bound, threshold):
if threshold <= 0:
raise ValueError("binary_search_base_freq() - LifacNoiseModel: threshold is not allowed to be negative!")
while True:
middle = upper_bound - (upper_bound - lower_bound)/2
frequency = test_v_offset(model, middle, base_stimulus, simulation_length)
# print('{:.1f}, {:.1f}, {:.1f}, {:.1f} vs {:.1f} '.format(lower_bound, middle, upper_bound, frequency, goal_frequency))
if abs(frequency - goal_frequency) < threshold:
return middle
elif frequency < goal_frequency:
lower_bound = middle
elif frequency > goal_frequency:
upper_bound = middle
elif abs(upper_bound-lower_bound) < 0.01 * threshold:
return middle
else:
print('lower bound: {:.1f}, middle: {:.1f}, upper_bound: {:.1f}, frequency: {:.1f} vs goal: {:.1f} '.format(lower_bound, middle, upper_bound, frequency, goal_frequency))
raise ValueError("binary_search_base_freq() - LifacNoiseModel: Goal frequency might be nan?")
def test_v_offset(model: LifacNoiseModel, v_offset, base_stimulus, simulation_length):
model.set_variable("v_offset", v_offset)
_, spiketimes = model.simulate_fast(base_stimulus, simulation_length)
freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, 0.005, simulation_length/3)
return freq
@jit(nopython=True)
def rectify_stimulus_array(stimulus_array:np.ndarray):
def rectify_stimulus_array(stimulus_array: np.ndarray):
return np.array([x if x > 0 else 0 for x in stimulus_array])
@@ -212,8 +261,9 @@ def simulate_fast(rectified_stimulus_array, total_time_s, parameters: np.ndarray
v_offset = parameters[7]
mem_tau = parameters[8]
noise_strength = parameters[9]
time_start = parameters[10]
time = np.arange(0, total_time_s * 1000, step_size)
time = np.arange(time_start, total_time_s, step_size)
length = len(time)
output_voltage = np.zeros(length)
adaption = np.zeros(length)
@@ -223,7 +273,8 @@ def simulate_fast(rectified_stimulus_array, total_time_s, parameters: np.ndarray
output_voltage[0] = v_zero
adaption[0] = a_zero
for i in range(len(time)-1):
for i in range(1, len(time), 1):
noise_value = np.random.normal()
noise = noise_strength * noise_value / np.sqrt(step_size)
@@ -233,7 +284,7 @@ def simulate_fast(rectified_stimulus_array, total_time_s, parameters: np.ndarray
if output_voltage[i] > threshold:
output_voltage[i] = v_base
spiketimes.append(i*step_size)
adaption[i] += delta_a / (tau_a / 1000)
adaption[i] += delta_a / tau_a
return output_voltage, adaption, spiketimes