227 lines
9.0 KiB
Python
227 lines
9.0 KiB
Python
from models.LIFACnoise import LifacNoiseModel
|
|
from CellData import CellData, icelldata_of_dir
|
|
from FiCurve import FICurve
|
|
from AdaptionCurrent import Adaption
|
|
from stimuli.SinusAmplitudeModulation import SinusAmplitudeModulationStimulus
|
|
import helperFunctions as hF
|
|
import numpy as np
|
|
from scipy.optimize import curve_fit, minimize
|
|
import functions as fu
|
|
import time
|
|
import matplotlib.pyplot as plt
|
|
|
|
def main():
|
|
|
|
for celldata in icelldata_of_dir("./data/"):
|
|
start_time = time.time()
|
|
fitter = Fitter(celldata)
|
|
fmin, parameters = fitter.fit_model_to_data()
|
|
|
|
print(fmin)
|
|
print(parameters)
|
|
end_time = time.time()
|
|
|
|
print('Fitting of cell took function took {:.3f} s'.format((end_time - start_time)))
|
|
break
|
|
pass
|
|
|
|
|
|
class Fitter:
|
|
|
|
def __init__(self, data: CellData, step_size=None):
|
|
if step_size is not None:
|
|
self.model = LifacNoiseModel({"step_size": step_size})
|
|
else:
|
|
self.model = LifacNoiseModel({"step_size": 0.05})
|
|
self.data = data
|
|
self.fi_contrasts = []
|
|
self.eod_freq = 0
|
|
|
|
self.modulation_frequency = 10
|
|
self.sc_max_lag = 1
|
|
|
|
# expected values the model has to replicate
|
|
self.baseline_freq = 0
|
|
self.vector_strength = -1
|
|
self.serial_correlation = []
|
|
|
|
self.f_infinities = []
|
|
self.f_infinities_slope = 0
|
|
|
|
# fixed values needed to fit model
|
|
self.a_tau = 0
|
|
self.a_delta = 0
|
|
|
|
self.counter = 0
|
|
self.calculate_needed_values_from_data()
|
|
|
|
def calculate_needed_values_from_data(self):
|
|
self.eod_freq = self.data.get_eod_frequency()
|
|
|
|
self.baseline_freq = self.data.get_base_frequency()
|
|
self.vector_strength = self.data.get_vector_strength()
|
|
self.serial_correlation = self.data.get_serial_correlation(self.sc_max_lag)
|
|
|
|
fi_curve = FICurve(self.data, contrast=True)
|
|
self.fi_contrasts = fi_curve.stimulus_value
|
|
self.f_infinities = fi_curve.f_infinities
|
|
self.f_infinities_slope = fi_curve.get_f_infinity_slope()
|
|
|
|
f_zero_slope = fi_curve.get_fi_curve_slope_of_straight()
|
|
self.a_delta = f_zero_slope / self.f_infinities_slope
|
|
|
|
adaption = Adaption(self.data, fi_curve)
|
|
self.a_tau = adaption.get_tau_real()
|
|
|
|
# mem_tau, (threshold?), (v_offset), noise_strength, input_scaling
|
|
def cost_function(self, X, tau_a=10, delta_a=3, error_scaling=()):
|
|
# set model parameters to the given ones:
|
|
self.model.set_variable("mem_tau", X[0])
|
|
self.model.set_variable("noise_strength", X[1])
|
|
self.model.set_variable("input_scaling", X[2])
|
|
self.model.set_variable("tau_a", tau_a)
|
|
self.model.set_variable("delta_a", delta_a)
|
|
|
|
# minimize the difference in baseline_freq first by fitting v_offset
|
|
v_offset = self.__fit_v_offset_to_baseline_frequency__()
|
|
self.model.set_variable("v_offset", v_offset)
|
|
|
|
# only eod with amplitude 1 and no modulation
|
|
base_stimulus = SinusAmplitudeModulationStimulus(self.eod_freq, 0, 0)
|
|
_, spiketimes = self.model.simulate_fast(base_stimulus, 30)
|
|
|
|
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, 5)
|
|
# print("model:", baseline_freq, "data:", self.baseline_freq)
|
|
|
|
relative_spiketimes = np.array([s % (1/self.eod_freq) for s in spiketimes])
|
|
eod_durations = np.full((len(spiketimes)), 1/self.eod_freq)
|
|
vector_strength = hF.__vector_strength__(relative_spiketimes, eod_durations)
|
|
serial_correlation = hF.calculate_serial_correlation(np.array(spiketimes), self.sc_max_lag)
|
|
|
|
f_infinities = []
|
|
for contrast in self.fi_contrasts:
|
|
stimulus = SinusAmplitudeModulationStimulus(self.eod_freq, contrast, self.modulation_frequency)
|
|
_, spiketimes = self.model.simulate_fast(stimulus, 0.5)
|
|
|
|
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)
|
|
|
|
popt, pcov = curve_fit(fu.line, self.fi_contrasts, f_infinities, maxfev=10000)
|
|
|
|
f_infinities_slope = popt[0]
|
|
|
|
error_bf = abs((baseline_freq - self.baseline_freq) / self.baseline_freq)
|
|
error_vs = abs((vector_strength - self.vector_strength) / self.vector_strength)
|
|
error_sc = abs((serial_correlation[0] - self.serial_correlation[0]) / self.serial_correlation[0])
|
|
error_f_inf_slope = abs((f_infinities_slope - self.f_infinities_slope) / self.f_infinities_slope)
|
|
#print("vs:", vector_strength, self.vector_strength)
|
|
#print("sc", serial_correlation[0], self.serial_correlation[0])
|
|
#print("f slope:", f_infinities_slope, self.f_infinities_slope)
|
|
error_f_inf = 0
|
|
for i in range(len(f_infinities)):
|
|
error_f_inf += abs((f_infinities[i] - self.f_infinities[i]) / f_infinities[i])
|
|
|
|
error_f_inf = error_f_inf / len(f_infinities)
|
|
self.counter += 1
|
|
# print("mem_tau:", X[0], "noise:", X[0], "input_scaling:", X[2])
|
|
print("Cost function run times:", self.counter, "errors:", [error_bf, error_vs, error_sc, error_f_inf_slope, error_f_inf])
|
|
return error_bf + error_vs + error_sc + error_f_inf_slope + error_f_inf
|
|
|
|
def __fit_v_offset_to_baseline_frequency__(self):
|
|
test_model = self.model.get_model_copy()
|
|
voltage_step_size = 1000
|
|
simulation_time = 2
|
|
v_offset_start = 0
|
|
v_offset_current = v_offset_start
|
|
|
|
test_model.set_variable("v_offset", v_offset_current)
|
|
base_stimulus = SinusAmplitudeModulationStimulus(self.eod_freq, 0, 0)
|
|
_, spiketimes = test_model.simulate_fast(base_stimulus, simulation_time)
|
|
if len(spiketimes) < 5:
|
|
baseline_freq = 0
|
|
else:
|
|
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, simulation_time/2)
|
|
|
|
if baseline_freq < self.baseline_freq:
|
|
upwards = True
|
|
v_offset_current += voltage_step_size
|
|
else:
|
|
upwards = False
|
|
v_offset_current -= voltage_step_size
|
|
|
|
# search for a value below and above the baseline freq:
|
|
while True:
|
|
# print(self.counter, baseline_freq, self.baseline_freq, v_offset_current)
|
|
# self.counter += 1
|
|
test_model.set_variable("v_offset", v_offset_current)
|
|
base_stimulus = SinusAmplitudeModulationStimulus(self.eod_freq, 0, 0)
|
|
_, spiketimes = test_model.simulate_fast(base_stimulus, simulation_time)
|
|
|
|
if len(spiketimes) < 2:
|
|
baseline_freq = 0
|
|
else:
|
|
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, simulation_time/2)
|
|
|
|
if baseline_freq < self.baseline_freq and upwards:
|
|
v_offset_current += voltage_step_size
|
|
|
|
elif baseline_freq < self.baseline_freq and not upwards:
|
|
break
|
|
|
|
elif baseline_freq > self.baseline_freq and upwards:
|
|
break
|
|
|
|
elif baseline_freq > self.baseline_freq and not upwards:
|
|
v_offset_current -= voltage_step_size
|
|
|
|
elif baseline_freq == self.baseline_freq:
|
|
return v_offset_current
|
|
|
|
# found the edges use them to allow binary search:
|
|
if upwards:
|
|
lower_bound = v_offset_current - voltage_step_size
|
|
upper_bound = v_offset_current
|
|
else:
|
|
lower_bound = v_offset_current
|
|
upper_bound = v_offset_current + voltage_step_size
|
|
|
|
while True:
|
|
middle = lower_bound + (upper_bound - lower_bound)/2
|
|
# print(self.counter, "measured_freq:", baseline_freq, "wanted_freq:", self.baseline_freq, "current middle:", middle)
|
|
# self.counter += 1
|
|
test_model.set_variable("v_offset", middle)
|
|
base_stimulus = SinusAmplitudeModulationStimulus(self.eod_freq, 0, 0)
|
|
_, spiketimes = test_model.simulate_fast(base_stimulus, simulation_time)
|
|
|
|
if len(spiketimes) < 2:
|
|
baseline_freq = 0
|
|
else:
|
|
baseline_freq = hF.mean_freq_of_spiketimes_after_time_x(spiketimes, simulation_time/2)
|
|
|
|
if abs(baseline_freq - self.baseline_freq) < 1:
|
|
# print("close enough:", baseline_freq, self.baseline_freq, abs(baseline_freq - self.baseline_freq))
|
|
break
|
|
elif baseline_freq < self.baseline_freq:
|
|
lower_bound = middle
|
|
else:
|
|
upper_bound = middle
|
|
|
|
return middle
|
|
|
|
def fit_model_to_data(self):
|
|
x0 = np.array([20, 15, 75])
|
|
init_simplex = np.array([np.array([2, 1, 10]), np.array([40, 100, 140]), np.array([20, 50, 70]), np.array([150, 1, 200])])
|
|
fmin = minimize(fun=self.cost_function, x0=x0, args=(self.a_tau, self.a_delta), method="Nelder-Mead", options={"initial_simplex": init_simplex})
|
|
|
|
|
|
#fmin = minimize(fun=self.cost_function, x0=x0, args=(self.a_tau, self.a_delta), method="BFGS")
|
|
|
|
return fmin, self.model.get_parameters()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|