diff --git a/docs/source/eFeatures.rst b/docs/source/eFeatures.rst index 67bac2fc..521b0f1b 100644 --- a/docs/source/eFeatures.rst +++ b/docs/source/eFeatures.rst @@ -18,8 +18,19 @@ Spike event features .. image:: _static/figures/inv_ISI.png -`LibV1`_ : time_to_first_spike -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : peak_time +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The times of the maxima of the peaks + +- **Required features**: peak_indices +- **Units**: ms +- **Pseudocode**: :: + + peak_time = time[peak_indices] + +`SpikeEvent`_ : time_to_first_spike +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time from the start of the stimulus to the maximum of the first peak @@ -30,8 +41,8 @@ Time from the start of the stimulus to the maximum of the first peak time_to_first_spike = peaktime[0] - stimstart -`LibV5`_ : time_to_second_spike -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_second_spike +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time from the start of the stimulus to the maximum of the second peak @@ -42,8 +53,8 @@ Time from the start of the stimulus to the maximum of the second peak time_to_second_spike = peaktime[1] - stimstart -`LibV5`_ : inv_time_to_first_spike -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : inv_time_to_first_spike +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1.0 over time to first spike (times 1000 to convert it to Hz); returns 0 when no spike @@ -69,8 +80,8 @@ The interspike intervals (i.e. time intervals) between adjacent peaks. isi_values = numpy.diff(peak_time)[1:] -`LibV1`_ : doublet_ISI -~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : doublet_ISI +~~~~~~~~~~~~~~~~~~~~~~~~~~~ The time interval between the first two peaks @@ -81,8 +92,8 @@ The time interval between the first two peaks doublet_ISI = peak_time[1] - peak_time[0] -`LibV5`_ : all_ISI_values -~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : all_ISI_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The interspike intervals, i.e., the time intervals between adjacent peaks. @@ -146,8 +157,8 @@ Computes all inverse spike interval values. all_isi_values_vec = numpy.diff(peak_time) inv_isi_values = 1000.0 / all_isi_values_vec -`LibV5`_ : time_to_last_spike -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_last_spike +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ time from stimulus start to last spike @@ -165,7 +176,7 @@ time from stimulus start to last spike number of spikes in the trace, including outside of stimulus interval -- **Required features**: LibV1:peak_indices +- **Required features**: peak_indices - **Units**: constant - **Pseudocode**: :: @@ -179,7 +190,7 @@ number of spikes in the trace, including outside of stimulus interval number of spikes inside the stimulus interval -- **Required features**: LibV1:peak_time +- **Required features**: peak_time - **Units**: constant - **Pseudocode**: :: @@ -189,12 +200,12 @@ number of spikes inside the stimulus interval **Note**: "spike_count_stimint" is the new name for the feature "Spikecount_stimint". "Spikecount_stimint", while still available, will be removed in the future. -`LibV5`_ : number_initial_spikes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : number_initial_spikes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ number of spikes at the beginning of the stimulus -- **Required features**: LibV1:peak_time +- **Required features**: peak_time - **Required parameters**: initial_perc (default=0.1) - **Units**: constant - **Pseudocode**: :: @@ -204,12 +215,12 @@ number of spikes at the beginning of the stimulus (peak_time >= stimstart) & \ (peak_time <= stimstart + initial_length))) -`LibV1`_ : mean_frequency -~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : mean_frequency +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The mean frequency of the firing rate -- **Required features**: stim_start, stim_end, LibV1:peak_time +- **Required features**: stim_start, stim_end, peak_time - **Units**: Hz - **Pseudocode**: :: @@ -218,8 +229,8 @@ The mean frequency of the firing rate last_spike_time = peak_time[peak_time < stim_end][-1] mean_frequency = 1000 * spikecount / (last_spike_time - stim_start) -`LibV5`_ : ISI_semilog_slope -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Python efeature`_ : ISI_semilog_slope +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The slope of a linear fit to a semilog plot of the ISI values. @@ -304,8 +315,8 @@ The first ISI can be taken into account if ignore_first_ISI is set to 0. irregularity_index = numpy.mean(numpy.absolute(ISI_values[1:] - ISI_values[:-1])) -`LibV1`_ : adaptation_index -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : adaptation_index +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Normalized average difference of two consecutive ISIs, skipping the first ISIs @@ -333,8 +344,8 @@ The adaptation index is zero for a constant firing rate and bigger than zero for adaptation_index = numpy.mean(ISI_sum / ISI_sub) -`LibV1`_ : adaptation_index_2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : adaptation_index_2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Normalized average difference of two consecutive ISIs, starting at the second ISI @@ -392,8 +403,8 @@ then the spikes are not considered to be part of any burst return burst_mean_freq -`LibV5`_ : strict_burst_mean_freq -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : strict_burst_mean_freq +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The mean frequency during a burst for each burst @@ -468,8 +479,8 @@ Starting 5 ms after that peak take the voltage average until 5 ms before the fir interburst_voltage.append(numpy.mean(voltage[start_idx:end_idx + 1])) -`LibV5`_ : strict_interburst_voltage -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : strict_interburst_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The voltage average in between two bursts @@ -498,8 +509,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto interburst_voltage.append(numpy.mean(v[start_idx:end_idx + 1])) -`LibV5`_ : interburst_min_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : interburst_min_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The minimum voltage between the end of a burst and the next spike. @@ -519,8 +530,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto ) for i in burst_end_indices if i + 1 < len(peak_indices) ] -`LibV5`_ : postburst_min_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : postburst_min_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The minimum voltage after the end of a burst. @@ -551,8 +562,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto v[peak_indices[burst_end_indices[-1]]:] )) -`LibV5`_ : postburst_slow_ahp_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : postburst_slow_ahp_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The slow AHP voltage after the end of a burst. @@ -581,8 +592,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto else: postburst_slow_ahp.append(numpy.min(v[i_start:])) -`LibV5`_ : time_to_interburst_min -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_interburst_min +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The time between the last spike of a burst and the minimum between that spike and the next. @@ -603,8 +614,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto for i in burst_end_indices if i + 1 < len(peak_indices) ] -`LibV5`_ : time_to_postburst_slow_ahp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_postburst_slow_ahp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The time between the last spike of a burst and the slow ahp afterwards. @@ -623,8 +634,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto time_to_postburst_slow_ahp_py = t[postburst_slow_ahp_indices] - peak_time[burst_end_indices] -`LibV5`_ : postburst_fast_ahp_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : postburst_fast_ahp_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The fast AHP voltage after the end of a burst. @@ -659,8 +670,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto return postburst_fahp -`LibV5`_ : postburst_adp_peak_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : postburst_adp_peak_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The small ADP peak after the fast AHP after the end of a burst. @@ -686,8 +697,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto return None return adp_peak_values -`LibV5`_ : time_to_postburst_fast_ahp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_postburst_fast_ahp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time to the fast AHP after the end of a burst. @@ -703,8 +714,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto [t[fahpi] - peak_time[burst_endi[i]] for i, fahpi in enumerate(postburst_fahpi)] -`LibV5`_ : time_to_postburst_adp_peak -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : time_to_postburst_adp_peak +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time to the small ADP peak after the fast AHP after the end of a burst. @@ -734,8 +745,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto return time_to_postburst_adp_peaks -`LibV5`_ : interburst_15percent_values, interburst_20percent_values, interburst_25percent_values, interburst_30percent_values, interburst_40percent_values, interburst_60percent_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : interburst_15percent_values, interburst_20percent_values, interburst_25percent_values, interburst_30percent_values, interburst_40percent_values, interburst_60percent_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Voltage value after a given percentage (15%, 20%, 25%, 30%, 40% or 60%) of the interburst duration after the fast AHP. @@ -757,8 +768,8 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto index_at_XXpercent = numpy.argwhere(t >= time_at_XXpercent)[0][0] interburst_XXpercent_values.append(v[index_at_XXpercent]) -`LibV5`_ : interburst_duration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeEvent`_ : interburst_duration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Duration between the last spike of each burst and the next spike. @@ -783,7 +794,7 @@ The burst detection can be fine-tuned by changing the setting strict_burst_facto Length of the second isi over the median of the rest of the isis. The first isi is not taken into account, because it could bias the feature. -See LibV1: ISI_values feature for more details. +See ISI_values feature for more details. If ignore_first_ISI is set to 0, then signle burst ratio becomes the length of the first isi over the median of the rest of the isis. @@ -803,7 +814,7 @@ The first spike is ignored by default. This can be changed by setting ignore_fir The burst detection can be fine-tuned by changing the setting strict_burst_factor. Defalt value is 2.0. -- **Required features**: LibV5: burst_begin_indices, LibV5: burst_end_indices +- **Required features**: burst_begin_indices, burst_end_indices - **Units**: constant - **Pseudocode**: :: @@ -861,46 +872,35 @@ Spike shape features .. image:: _static/figures/AP_Amplitude.png -`LibV1`_ : peak_time -~~~~~~~~~~~~~~~~~~~~ - -The times of the maxima of the peaks - -- **Required features**: LibV5:peak_indices -- **Units**: ms -- **Pseudocode**: :: - - peak_time = time[peak_indices] - -`LibV1`_ : peak_voltage -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : peak_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The voltages at the maxima of the peaks -- **Required features**: LibV5:peak_indices +- **Required features**: peak_indices - **Units**: mV - **Pseudocode**: :: peak_voltage = voltage[peak_indices] -`LibV1`_ : AP_height -~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_height +~~~~~~~~~~~~~~~~~~~~~~~~~ Same as peak_voltage: The voltages at the maxima of the peaks -- **Required features**: LibV1:peak_voltage +- **Required features**: peak_voltage - **Units**: mV - **Pseudocode**: :: AP_height = peak_voltage -`LibV1`_ : AP_amplitude, AP1_amp, AP2_amp, APlast_amp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_amplitude, AP1_amp, AP2_amp, APlast_amp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The relative height of the action potential from spike onset -- **Required features**: LibV5:AP_begin_indices, LibV1:peak_voltage (mV) +- **Required features**: AP_begin_indices, peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: @@ -909,124 +909,124 @@ The relative height of the action potential from spike onset AP2_amp = AP_amplitude[1] APlast_amp = AP_amplitude[-1] -`LibV5`_ : mean_AP_amplitude -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : mean_AP_amplitude +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The mean of all of the action potential amplitudes -- **Required features**: LibV1:AP_amplitude (mV) +- **Required features**: AP_amplitude (mV) - **Units**: mV - **Pseudocode**: :: mean_AP_amplitude = numpy.mean(AP_amplitude) -`LibV2`_ : AP_Amplitude_change -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_Amplitude_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the amplitudes of the second and the first action potential divided by the amplitude of the first action potential -- **Required features**: LibV1:AP_amplitude +- **Required features**: AP_amplitude - **Units**: constant - **Pseudocode**: :: AP_amplitude_change = (AP_amplitude[1:] - AP_amplitude[0]) / AP_amplitude[0] -`LibV5`_ : AP_amplitude_from_voltagebase -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_amplitude_from_voltagebase +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The relative height of the action potential from voltage base -- **Required features**: LibV5:voltage_base, LibV1:peak_voltage (mV) +- **Required features**: voltage_base, peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: AP_amplitude_from_voltagebase = peak_voltage - voltage_base -`LibV5`_ : AP1_peak, AP2_peak -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP1_peak, AP2_peak +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The peak voltage of the first and second action potentials -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: AP1_peak = peak_voltage[0] AP2_peak = peak_voltage[1] -`LibV5`_ : AP2_AP1_diff -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP2_AP1_diff +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference amplitude of the second to first spike -- **Required features**: LibV1:AP_amplitude (mV) +- **Required features**: AP_amplitude (mV) - **Units**: mV - **Pseudocode**: :: AP2_AP1_diff = AP_amplitude[1] - AP_amplitude[0] -`LibV5`_ : AP2_AP1_peak_diff -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP2_AP1_peak_diff +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference peak voltage of the second to first spike -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: AP2_AP1_diff = peak_voltage[1] - peak_voltage[0] -`LibV2`_ : amp_drop_first_second -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : amp_drop_first_second +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the amplitude of the first and the second peak -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: amp_drop_first_second = peak_voltage[0] - peak_voltage[1] -`LibV2`_ : amp_drop_first_last -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : amp_drop_first_last +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the amplitude of the first and the last peak -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: amp_drop_first_last = peak_voltage[0] - peak_voltage[-1] -`LibV2`_ : amp_drop_second_last -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : amp_drop_second_last +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the amplitude of the second and the last peak -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: amp_drop_second_last = peak_voltage[1] - peak_voltage[-1] -`LibV2`_ : max_amp_difference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : max_amp_difference +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Maximum difference of the height of two subsequent peaks -- **Required features**: LibV1:peak_voltage (mV) +- **Required features**: peak_voltage (mV) - **Units**: mV - **Pseudocode**: :: max_amp_difference = numpy.max(peak_voltage[:-1] - peak_voltage[1:]) -`LibV1`_ : AP_amplitude_diff -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_amplitude_diff +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the amplitude of two subsequent peaks -- **Required features**: LibV1:AP_amplitude (mV) +- **Required features**: AP_amplitude (mV) - **Units**: mV - **Pseudocode**: :: @@ -1034,107 +1034,107 @@ Difference of the amplitude of two subsequent peaks .. image:: _static/figures/AHP.png -`LibV5`_ : min_AHP_values -~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : min_AHP_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Absolute voltage values at the first after-hyperpolarization. -- **Required features**: LibV5:min_AHP_indices +- **Required features**: min_AHP_indices - **Units**: mV -`LibV5`_ : AHP_depth_abs -~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth_abs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Absolute voltage values at the first after-hyperpolarization. Is the same as min_AHP_values -- **Required features**: LibV5:min_AHP_values (mV) +- **Required features**: min_AHP_values (mV) - **Units**: mV -`LibV1`_ : AHP_depth_abs_slow -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth_abs_slow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Absolute voltage values at the first after-hyperpolarization starting a given number of ms (default: 5) after the peak -- **Required features**: LibV1:peak_indices +- **Required features**: peak_indices - **Units**: mV -`LibV1`_ : AHP_depth_slow -~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth_slow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Relative voltage values at the first after-hyperpolarization starting a given number of ms (default: 5) after the peak -- **Required features**: LibV5:voltage_base (mV), LibV1:AHP_depth_abs_slow (mV) +- **Required features**: voltage_base (mV), AHP_depth_abs_slow (mV) - **Units**: mV - **Pseudocode**: :: AHP_depth_slow = AHP_depth_abs_slow[:] - voltage_base -`LibV1`_ : AHP_slow_time -~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_slow_time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time difference between slow AHP (see AHP_depth_abs_slow) and peak, divided by interspike interval -- **Required features**: LibV1:AHP_depth_abs_slow +- **Required features**: AHP_depth_abs_slow - **Units**: constant -`LibV1`_ : AHP_depth -~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth +~~~~~~~~~~~~~~~~~~~~~~~~~ Relative voltage values at the first after-hyperpolarization -- **Required features**: LibV5:voltage_base (mV), LibV5:min_AHP_values (mV) +- **Required features**: voltage_base (mV), min_AHP_values (mV) - **Units**: mV - **Pseudocode**: :: min_AHP_values = first_min_element(voltage, peak_indices) AHP_depth = min_AHP_values[:] - voltage_base -`LibV1`_ : AHP_depth_diff -~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth_diff +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of subsequent relative voltage values at the first after-hyperpolarization -- **Required features**: LibV1:AHP_depth (mV) +- **Required features**: AHP_depth (mV) - **Units**: mV - **Pseudocode**: :: AHP_depth_diff = AHP_depth[1:] - AHP_depth[:-1] -`LibV2`_ : fast_AHP -~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : fast_AHP +~~~~~~~~~~~~~~~~~~~~~~~~ Voltage value of the action potential onset relative to the subsequent AHP Ignores the last spike -- **Required features**: LibV5:AP_begin_indices, LibV5:min_AHP_values +- **Required features**: AP_begin_indices, min_AHP_values - **Units**: mV - **Pseudocode**: :: fast_AHP = voltage[AP_begin_indices[:-1]] - voltage[min_AHP_indices[:-1]] -`LibV2`_ : fast_AHP_change -~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : fast_AHP_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the fast AHP of the second and the first action potential divided by the fast AHP of the first action potential -- **Required features**: LibV2:fast_AHP +- **Required features**: fast_AHP - **Units**: constant - **Pseudocode**: :: fast_AHP_change = (fast_AHP[1:] - fast_AHP[0]) / fast_AHP[0] -`LibV5`_ : AHP_depth_from_peak, AHP1_depth_from_peak, AHP2_depth_from_peak -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_depth_from_peak, AHP1_depth_from_peak, AHP2_depth_from_peak +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Voltage difference between AP peaks and first AHP depths -- **Required features**: LibV1:peak_indices, LibV5:min_AHP_indices +- **Required features**: peak_indices, min_AHP_indices - **Units**: mV - **Pseudocode**: :: @@ -1142,26 +1142,26 @@ Voltage difference between AP peaks and first AHP depths AHP1_depth_from_peak = AHP_depth_from_peak[0] AHP2_depth_from_peak = AHP_depth_from_peak[1] -`LibV5`_ : AHP_time_from_peak -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AHP_time_from_peak +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time between AP peaks and first AHP depths -- **Required features**: LibV1:peak_indices, LibV5:min_AHP_values (mV) +- **Required features**: peak_indices, min_AHP_values (mV) - **Units**: ms - **Pseudocode**: :: min_AHP_indices = first_min_element(voltage, peak_indices) AHP_time_from_peak = t[min_AHP_indices[:]] - t[peak_indices[i]] -`LibV5`_ : ADP_peak_values -~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : ADP_peak_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Absolute voltage values of the small afterdepolarization peak strict_stiminterval should be set to True for this feature to behave as expected. -- **Required features**: LibV5:min_AHP_indices, LibV5:min_between_peaks_indices +- **Required features**: min_AHP_indices, min_between_peaks_indices - **Units**: mV - **Pseudocode**: :: @@ -1169,26 +1169,26 @@ strict_stiminterval should be set to True for this feature to behave as expected [numpy.max(v[i:j + 1]) for (i, j) in zip(min_AHP_indices, min_v_indices)] ) -`LibV5`_ : ADP_peak_amplitude -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : ADP_peak_amplitude +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Amplitude of the small afterdepolarization peak with respect to the fast AHP voltage strict_stiminterval should be set to True for this feature to behave as expected. -- **Required features**: LibV5:min_AHP_values, LibV5:ADP_peak_values +- **Required features**: min_AHP_values, ADP_peak_values - **Units**: mV - **Pseudocode**: :: adp_peak_amplitude = adp_peak_values - min_AHP_values -`LibV3`_ : depolarized_base -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : depolarized_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mean voltage between consecutive spikes (from the end of one spike to the beginning of the next one) -- **Required features**: LibV5:AP_end_indices, LibV5:AP_begin_indices +- **Required features**: AP_end_indices, AP_begin_indices - **Units**: mV - **Pseudocode**: :: @@ -1198,12 +1198,12 @@ Mean voltage between consecutive spikes ): depolarized_base.append(numpy.mean(voltage[start_idx:end_idx])) -`LibV5`_ : min_voltage_between_spikes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : min_voltage_between_spikes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Minimal voltage between consecutive spikes -- **Required features**: LibV5:peak_indices +- **Required features**: peak_indices - **Units**: mV - **Pseudocode**: :: @@ -1211,8 +1211,8 @@ Minimal voltage between consecutive spikes for peak1, peak2 in zip(peak_indices[:-1], peak_indices[1:]): min_voltage_between_spikes.append(numpy.min(voltage[peak1:peak2])) -`LibV5`_ : min_between_peaks_values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : min_between_peaks_values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Minimal voltage between consecutive spikes @@ -1221,7 +1221,7 @@ if strict stiminterval is True, and minimum between last spike and last voltage if strict stiminterval is False -- **Required features**: LibV5:min_between_peaks_indices +- **Required features**: min_between_peaks_indices - **Units**: mV - **Pseudocode**: :: @@ -1231,12 +1231,12 @@ if strict stiminterval is False .. image:: _static/figures/AP_duration_half_width.png -`LibV2`_ : AP_duration_half_width -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_duration_half_width +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Width of spike at half spike amplitude, with spike onset as described in LibV5: AP_begin_time +Width of spike at half spike amplitude, with spike onset as described in AP_begin_time -- **Required features**: LibV2: AP_rise_indices, LibV2: AP_fall_indices +- **Required features**: AP_rise_indices, AP_fall_indices - **Units**: ms - **Pseudocode**: :: @@ -1244,13 +1244,13 @@ Width of spike at half spike amplitude, with spike onset as described in LibV5: AP_fall_indices = index_after_peak((v(peak_indices) - v(AP_begin_indices)) / 2) AP_duration_half_width = t(AP_fall_indices) - t(AP_rise_indices) -`LibV2`_ : AP_duration_half_width_change -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_duration_half_width_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the FWHM of the second and the first action potential divided by the FWHM of the first action potential -- **Required features**: LibV2: AP_duration_half_width +- **Required features**: AP_duration_half_width - **Units**: constant - **Pseudocode**: :: @@ -1258,14 +1258,14 @@ divided by the FWHM of the first action potential AP_duration_half_width[1:] - AP_duration_half_width[0] ) / AP_duration_half_width[0] -`LibV1`_ : AP_width -~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_width +~~~~~~~~~~~~~~~~~~~~~~~~ Width of spike at threshold, bounded by minimum AHP Can use strict_stiminterval compute only for data in stimulus interval. -- **Required features**: LibV1: peak_indices, LibV5: min_AHP_indices, threshold +- **Required features**: peak_indices, min_AHP_indices, threshold - **Units**: ms - **Pseudocode**: :: @@ -1276,36 +1276,36 @@ Can use strict_stiminterval compute only for data in stimulus interval. offset_time[i] = t[numpy.where(v[onset_index:min_AHP_indices[i+1]] < threshold)[0]] AP_width[i] = t(offset_time[i]) - t(onset_time[i]) -`LibV2`_ : AP_duration -~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_duration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ Duration of an action potential from onset to offset -- **Required features**: LibV5:AP_begin_indices, LibV5:AP_end_indices +- **Required features**: AP_begin_indices, AP_end_indices - **Units**: ms - **Pseudocode**: :: AP_duration = time[AP_end_indices] - time[AP_begin_indices] -`LibV2`_ : AP_duration_change -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_duration_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the durations of the second and the first action potential divided by the duration of the first action potential -- **Required features**: LibV2:AP_duration +- **Required features**: AP_duration - **Units**: constant - **Pseudocode**: :: AP_duration_change = (AP_duration[1:] - AP_duration[0]) / AP_duration[0] -`LibV5`_ : AP_width_between_threshold -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_width_between_threshold +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Width of spike at threshold, bounded by minimum between peaks Can use strict_stiminterval to not use minimum after stimulus end. -- **Required features**: LibV1: peak_indices, LibV5: min_between_peaks_indices, threshold +- **Required features**: peak_indices, min_between_peaks_indices, threshold - **Units**: ms - **Pseudocode**: :: @@ -1316,13 +1316,13 @@ Can use strict_stiminterval to not use minimum after stimulus end. offset_time[i] = t[numpy.where(v[onset_index:min_between_peaks_indices[i+1]] < threshold)[0]] AP_width[i] = t(offset_time[i]) - t(onset_time[i]) -`LibV5`_ : spike_half_width, AP1_width, AP2_width, APlast_width -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : spike_half_width, AP1_width, AP2_width, APlast_width +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Width of spike at half spike amplitude, with the spike amplitude taken as the difference between the minimum between two peaks and the next peak -- **Required features**: LibV5: peak_indices, LibV5: min_AHP_indices +- **Required features**: peak_indices, min_AHP_indices - **Units**: ms - **Pseudocode**: :: @@ -1347,13 +1347,13 @@ with the spike amplitude taken as the difference between the minimum between two APlast_width = spike_half_width[-1] -`LibV1`_ : spike_width2 -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : spike_width2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Width of spike at half spike amplitude, with the spike onset taken as the maximum of the second derivative of the voltage in the range between the minimum between two peaks and the next peak -- **Required features**: LibV5: peak_indices, LibV5: min_AHP_indices +- **Required features**: peak_indices, min_AHP_indices - **Units**: ms - **Pseudocode**: :: @@ -1376,12 +1376,12 @@ the minimum between two peaks and the next peak spike_width2[i] = t[fall_idx] + t_dev_fall - t[rise_idx] - t_dev_rise -`LibV5`_ : AP_begin_width, AP1_begin_width, AP2_begin_width -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_begin_width, AP1_begin_width, AP2_begin_width +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Width of spike at spike start -- **Required features**: LibV5: min_AHP_indices, LibV5: AP_begin_indices +- **Required features**: min_AHP_indices, AP_begin_indices - **Units**: ms - **Pseudocode**: :: @@ -1393,23 +1393,23 @@ Width of spike at spike start AP1_begin_width = AP_begin_width[0] AP2_begin_width = AP_begin_width[1] -`LibV5`_ : AP2_AP1_begin_width_diff -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP2_AP1_begin_width_diff +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference width of the second to first spike -- **Required features**: LibV5: AP_begin_width +- **Required features**: AP_begin_width - **Units**: ms - **Pseudocode**: :: AP2_AP1_begin_width_diff = AP_begin_width[1] - AP_begin_width[0] -`LibV5`_ : AP_begin_voltage, AP1_begin_voltage, AP2_begin_voltage -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_begin_voltage, AP1_begin_voltage, AP2_begin_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Voltage at spike start -- **Required features**: LibV5: AP_begin_indices +- **Required features**: AP_begin_indices - **Units**: mV - **Pseudocode**: :: @@ -1417,23 +1417,23 @@ Voltage at spike start AP1_begin_voltage = AP_begin_voltage[0] AP2_begin_voltage = AP_begin_voltage[1] -`LibV5`_ : AP_begin_time -~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_begin_time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time at spike start. Spike start is defined as where the first derivative of the voltage trace is higher than 10 V/s , for at least 5 points -- **Required features**: LibV5: AP_begin_indices +- **Required features**: AP_begin_indices - **Units**: ms - **Pseudocode**: :: AP_begin_time = t[AP_begin_indices] -`LibV5`_ : AP_peak_upstroke -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_peak_upstroke +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Maximum of rise rate of spike -- **Required features**: LibV5: AP_begin_indices, LibV5: peak_indices +- **Required features**: AP_begin_indices, peak_indices - **Units**: V/s - **Pseudocode**: :: @@ -1442,12 +1442,12 @@ Maximum of rise rate of spike ap_peak_upstroke.append(numpy.max(dvdt[apbi:pi])) -`LibV5`_ : AP_peak_downstroke -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_peak_downstroke +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Minimum of fall rate from spike -- **Required features**: LibV5: min_AHP_indices, LibV5: peak_indices +- **Required features**: min_AHP_indices, peak_indices - **Units**: V/s - **Pseudocode**: :: @@ -1455,13 +1455,13 @@ Minimum of fall rate from spike for ahpi, pi in zip(min_ahp_indices, peak_indices): ap_peak_downstroke.append(numpy.min(dvdt[pi:ahpi])) -`LibV2`_ : AP_rise_time -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_rise_time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time between the AP threshold and the peak, given a window (default: from 0% to 100% of the AP amplitude) -- **Required features**: LibV5: AP_begin_indices, LibV5: peak_indices, LibV1: AP_amplitude +- **Required features**: AP_begin_indices, peak_indices, AP_amplitude - **Units**: ms - **Pseudocode**: :: @@ -1483,23 +1483,23 @@ Time between the AP threshold and the peak, given a window rise_times.append(time[new_end_indice] - time[new_begin_indice]) -`LibV2`_ : AP_fall_time -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_fall_time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time from action potential maximum to the offset -- **Required features**: LibV5: AP_end_indices, LibV5: peak_indices +- **Required features**: AP_end_indices, peak_indices - **Units**: ms - **Pseudocode**: :: AP_fall_time = time[AP_end_indices] - time[peak_indices] -`LibV2`_ : AP_rise_rate -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_rise_rate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Voltage change rate during the rising phase of the action potential -- **Required features**: LibV5: AP_begin_indices, LibV5: peak_indices +- **Required features**: AP_begin_indices, peak_indices - **Units**: V/s - **Pseudocode**: :: @@ -1507,12 +1507,12 @@ Voltage change rate during the rising phase of the action potential time[peak_indices] - time[AP_begin_indices] ) -`LibV2`_ : AP_fall_rate -~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_fall_rate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Voltage change rate during the falling phase of the action potential -- **Required features**: LibV5: AP_end_indices, LibV5: peak_indices +- **Required features**: AP_end_indices, peak_indices - **Units**: V/s - **Pseudocode**: :: @@ -1520,38 +1520,38 @@ Voltage change rate during the falling phase of the action potential time[AP_end_indices] - time[peak_indices] ) -`LibV2`_ : AP_rise_rate_change -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_rise_rate_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the rise rates of the second and the first action potential divided by the rise rate of the first action potential -- **Required features**: LibV2: AP_rise_rate_change +- **Required features**: AP_rise_rate_change - **Units**: constant - **Pseudocode**: :: AP_rise_rate_change = (AP_rise_rate[1:] - AP_rise_rate[0]) / AP_rise_rate[0] -`LibV2`_ : AP_fall_rate_change -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_fall_rate_change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference of the fall rates of the second and the first action potential divided by the fall rate of the first action potential -- **Required features**: LibV2: AP_fall_rate_change +- **Required features**: AP_fall_rate_change - **Units**: constant - **Pseudocode**: :: AP_fall_rate_change = (AP_fall_rate[1:] - AP_fall_rate[0]) / AP_fall_rate[0] -`LibV5`_ : AP_phaseslope -~~~~~~~~~~~~~~~~~~~~~~~~ +`SpikeShape`_ : AP_phaseslope +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Slope of the V, dVdt phasespace plot at the beginning of every spike (at the point where the derivative crosses the DerivativeThreshold) -- **Required features**: LibV5:AP_begin_indices +- **Required features**: AP_begin_indices - **Parameters**: AP_phaseslope_range - **Units**: 1/(ms) - **Pseudocode**: :: @@ -1582,7 +1582,7 @@ The end of the initial burst is detected when the ISIs frequency gets lower than Then the sahp is searched for the interval between initburst_sahp_start (in ms) after the last spike of the burst, and initburst_sahp_end (in ms) after the last spike of the burst. -- **Required features**: LibV1: peak_time +- **Required features**: peak_time - **Parameters**: initburst_freq_threshold (default=50), initburst_sahp_start (default=5), initburst_sahp_end (default=100) - **Units**: mV @@ -1591,7 +1591,7 @@ and initburst_sahp_end (in ms) after the last spike of the burst. Slow AHP voltage from steady_state_voltage_stimend after initial burst -- **Required features**: LibV5: steady_state_voltage_stimend, initburst_sahp +- **Required features**: steady_state_voltage_stimend, initburst_sahp - **Units**: mV - **Pseudocode**: :: @@ -1602,7 +1602,7 @@ Slow AHP voltage from steady_state_voltage_stimend after initial burst Slow AHP voltage from voltage base after initial burst -- **Required features**: LibV5: voltage_base, initburst_sahp +- **Required features**: voltage_base, initburst_sahp - **Units**: mV - **Pseudocode**: :: @@ -1614,8 +1614,8 @@ Subthreshold features .. image:: _static/figures/voltage_features.png -`LibV5`_ : steady_state_voltage_stimend -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : steady_state_voltage_stimend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The average voltage during the last 10% of the stimulus duration. @@ -1628,8 +1628,8 @@ The average voltage during the last 10% of the stimulus duration. end_time = stim_end steady_state_voltage_stimend = numpy.mean(voltage[numpy.where((t < end_time) & (t >= begin_time))]) -`LibV2`_ : steady_state_hyper -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : steady_state_hyper +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Steady state voltage during hyperpolarization for 30 data points (after interpolation) @@ -1640,8 +1640,8 @@ Steady state voltage during hyperpolarization for 30 data points (after interpol stim_end_idx = numpy.argwhere(time >= stim_end)[0][0] steady_state_hyper = numpy.mean(voltage[stim_end_idx - 35:stim_end_idx - 5]) -`LibV1`_ : steady_state_voltage -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : steady_state_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The average voltage after the stimulus @@ -1652,8 +1652,8 @@ The average voltage after the stimulus steady_state_voltage = numpy.mean(voltage[numpy.where((t <= max(t)) & (t > stim_end))]) -`LibV5`_ : voltage_base -~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : voltage_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The average voltage during the last 10% of time before the stimulus. @@ -1666,8 +1666,8 @@ The average voltage during the last 10% of time before the stimulus. (t >= voltage_base_start_perc * stim_start) & (t <= voltage_base_end_perc * stim_start))]) -`LibV5`_ : current_base -~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : current_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The average current during the last 10% of time before the stimulus. @@ -1684,8 +1684,8 @@ The average current during the last 10% of time before the stimulus. elif current_base_mode == "median": current_base = numpy.median(current_slice) -`LibV1`_ : time_constant -~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : time_constant +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The membrane time constant @@ -1774,8 +1774,8 @@ Yield the time constant of that decay. time_constant = -1. / slope -`LibV5`_ : decay_time_constant_after_stim -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : decay_time_constant_after_stim +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The decay time constant of the voltage right after the stimulus @@ -1795,8 +1795,8 @@ The decay time constant of the voltage right after the stimulus decay_time_constant_after_stim = -1. / slope -`LibV5`_ : multiple_decay_time_constant_after_stim -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : multiple_decay_time_constant_after_stim +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When multiple stimuli are applied, this function returns a list of decay time constants each computed on the voltage right after each stimulus. @@ -1818,8 +1818,8 @@ Each is a list containing the start and end times of each stimulus present in th decay_time_constant_after_stim(stim_start, stim_end) ) -`LibV5`_ : sag_time_constant -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : sag_time_constant +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The decay time constant of the exponential voltage decay from the bottom of the sag to the steady-state. @@ -1854,8 +1854,8 @@ The golden search algorithm is not used, since the data is expected to be noisy .. image:: _static/figures/sag.png -`LibV5`_ : sag_amplitude -~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : sag_amplitude +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The difference between the minimal voltage and the steady state at stimend @@ -1870,8 +1870,8 @@ The difference between the minimal voltage and the steady state at stimend sag_amplitude = None -`LibV5`_ : sag_ratio1 -~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : sag_ratio1 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ratio between the sag amplitude and the maximal sag extend from voltage base @@ -1885,8 +1885,8 @@ The ratio between the sag amplitude and the maximal sag extend from voltage base else: sag_ratio1 = None -`LibV5`_ : sag_ratio2 -~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : sag_ratio2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ratio between the maximal extends of sag from steady state and voltage base @@ -1900,8 +1900,8 @@ The ratio between the maximal extends of sag from steady state and voltage base else: sag_ratio2 = None -`LibV1`_ : ohmic_input_resistance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : ohmic_input_resistance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ratio between the voltage deflection and stimulus current @@ -1912,8 +1912,8 @@ The ratio between the voltage deflection and stimulus current ohmic_input_resistance = voltage_deflection / stimulus_current -`LibV5`_ : ohmic_input_resistance_vb_ssse -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : ohmic_input_resistance_vb_ssse +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ratio between the voltage deflection (between voltage base and steady-state voltage at stimend) and stimulus current @@ -1924,8 +1924,8 @@ The ratio between the voltage deflection (between voltage base and steady-state ohmic_input_resistance_vb_ssse = voltage_deflection_vb_ssse / stimulus_current -`LibV5`_ : voltage_deflection_vb_ssse -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : voltage_deflection_vb_ssse +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The voltage deflection between voltage base and steady-state voltage at stimend @@ -1939,8 +1939,8 @@ the average voltage during the last 10% of the stimulus duration. voltage_deflection_vb_ssse = steady_state_voltage_stimend - voltage_base -`LibV1`_ : voltage_deflection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : voltage_deflection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The voltage deflection between voltage base and steady-state voltage at stimend @@ -1958,8 +1958,8 @@ before the end of the stimulus duration. steady_state_voltage_stimend = numpy.mean(V[stim_end_idx-10:stim_end_idx-5]) voltage_deflection = steady_state_voltage_stimend - voltage_base -`LibV5`_ : voltage_deflection_begin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : voltage_deflection_begin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The voltage deflection between voltage base and steady-state voltage soon after stimulation start @@ -1978,8 +1978,8 @@ the average voltage taken from 5% to 15% of the stimulus duration. steady_state_voltage_stimend = numpy.mean(V[condition]) voltage_deflection = steady_state_voltage_stimend - voltage_base -`LibV5`_ : voltage_after_stim -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : voltage_after_stim +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The mean voltage after the stimulus in (stim_end + 25%*end_period, stim_end + 75%*end_period) @@ -1993,8 +1993,8 @@ The mean voltage after the stimulus in condition = numpy.all((tstart < t, t < tend), axis=0) voltage_after_stim = numpy.mean(V[condition]) -`LibV1`_ : minimum_voltage -~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : minimum_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The minimum of the voltage during the stimulus @@ -2004,8 +2004,8 @@ The minimum of the voltage during the stimulus minimum_voltage = min(voltage[numpy.where((t >= stim_start) & (t <= stim_end))]) -`LibV1`_ : maximum_voltage -~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : maximum_voltage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The maximum of the voltage during the stimulus @@ -2015,8 +2015,8 @@ The maximum of the voltage during the stimulus maximum_voltage = max(voltage[numpy.where((t >= stim_start) & (t <= stim_end))]) -`LibV5`_ : maximum_voltage_from_voltagebase -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Subthreshold`_ : maximum_voltage_from_voltagebase +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Difference between maximum voltage during stimulus and voltage base @@ -2034,12 +2034,12 @@ Requested eFeatures Cpp features ------------ -LibV1 : AHP_depth_last -~~~~~~~~~~~~~~~~~~~~~~ +AHP_depth_last +~~~~~~~~~~~~~~ Relative voltage values at the last after-hyperpolarization -- **Required features**: LibV5:voltage_base (mV), LibV5:last_AHP_values (mV) +- **Required features**: voltage_base (mV), last_AHP_values (mV) - **Units**: mV - **Pseudocode**: :: @@ -2047,12 +2047,12 @@ Relative voltage values at the last after-hyperpolarization AHP_depth = last_AHP_values[:] - voltage_base -LibV5 : AHP_time_from_peak_last -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +AHP_time_from_peak_last +~~~~~~~~~~~~~~~~~~~~~~~ Time between AP peaks and last AHP depths -- **Required features**: LibV1:peak_indices, LibV5:min_AHP_values (mV) +- **Required features**: peak_indices, min_AHP_values (mV) - **Units**: ms - **Pseudocode**: :: @@ -2060,58 +2060,58 @@ Time between AP peaks and last AHP depths AHP_time_from_peak_last = t[last_AHP_indices[:]] - t[peak_indices[i]] -LibV5 : steady_state_voltage_stimend_from_voltage_base -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +steady_state_voltage_stimend_from_voltage_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The average voltage during the last 90% of the stimulus duration realtive to voltage_base -- **Required features**: LibV5: steady_state_voltage_stimend (mV), LibV5: voltage_base (mV) +- **Required features**: steady_state_voltage_stimend (mV), voltage_base (mV) - **Units**: mV - **Pseudocode**: :: steady_state_voltage_stimend_from_voltage_base = steady_state_voltage_stimend - voltage_base -LibV5 : min_duringstim_from_voltage_base -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +min_duringstim_from_voltage_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The minimum voltage during stimulus -- **Required features**: LibV5: min_duringstim (mV), LibV5: voltage_base (mV) +- **Required features**: min_duringstim (mV), voltage_base (mV) - **Units**: mV - **Pseudocode**: :: min_duringstim_from_voltage_base = minimum_voltage - voltage_base -LibV5 : max_duringstim_from_voltage_base -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +max_duringstim_from_voltage_base +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The minimum voltage during stimulus -- **Required features**: LibV5: max_duringstim (mV), LibV5: voltage_base (mV) +- **Required features**: max_duringstim (mV), voltage_base (mV) - **Units**: mV - **Pseudocode**: :: max_duringstim_from_voltage_base = maximum_voltage - voltage_base -LibV5 : diff_max_duringstim -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +diff_max_duringstim +~~~~~~~~~~~~~~~~~~~ Difference between maximum and steady state during stimulation -- **Required features**: LibV5: max_duringstim (mV), LibV5: steady_state_voltage_stimend (mV) +- **Required features**: max_duringstim (mV), steady_state_voltage_stimend (mV) - **Units**: mV - **Pseudocode**: :: diff_max_duringstim: max_duringstim - steady_state_voltage_stimend -LibV5 : diff_min_duringstim -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +diff_min_duringstim +~~~~~~~~~~~~~~~~~~~ Difference between minimum and steady state during stimulation -- **Required features**: LibV5: min_duringstim (mV), LibV5: steady_state_voltage_stimend (mV) +- **Required features**: min_duringstim (mV), steady_state_voltage_stimend (mV) - **Units**: mV - **Pseudocode**: :: @@ -2130,7 +2130,7 @@ A depolarization block is detected when the voltage stays higher than the mean o A hyperpolarization block is detected when, after stimulus start, the voltage stays below -75 mV for longer than 50 ms. -- **Required features**: LibV5: AP_begin_voltage +- **Required features**: AP_begin_voltage - **Units**: constant `Python efeature`_ : impedance @@ -2140,7 +2140,7 @@ Computes the impedance given a ZAP current input and its voltage response. It will return the frequency at which the impedance is maximal, in the range (0, impedance_max_freq] Hz, with impedance_max_freq being a setting with 50.0 as a default value. -- **Required features**: current, spike_count, LibV5:voltage_base, LibV5:current_base +- **Required features**: current, spike_count, voltage_base, current_base - **Units**: Hz - **Pseudocode**: :: @@ -2164,8 +2164,7 @@ with impedance_max_freq being a setting with 50.0 as a default value. -.. _LibV1: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/LibV1.cpp -.. _LibV2: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/LibV2.cpp -.. _LibV3: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/LibV3.cpp -.. _LibV5: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/LibV5.cpp +.. _SpikeEvent: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/SpikeEvent.cpp +.. _SpikeShape: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/SpikeShape.cpp +.. _Subthreshold: https://github.com/BlueBrain/eFEL/blob/master/efel/cppcore/Subthreshold.cpp .. _Python efeature: https://github.com/BlueBrain/eFEL/blob/master/efel/pyfeatures/pyfeatures.py diff --git a/efel/DependencyV5.txt b/efel/DependencyV5.txt index fe43da29..d7ec6ab3 100644 --- a/efel/DependencyV5.txt +++ b/efel/DependencyV5.txt @@ -1,140 +1,140 @@ -LibV1:interpolate -LibV5:peak_indices #LibV1:interpolate -LibV1:doublet_ISI #LibV1:peak_time #LibV1:interpolate -LibV1:peak_voltage #LibV5:peak_indices #LibV1:interpolate -LibV1:mean_frequency #LibV1:peak_time #LibV1:interpolate -LibV1:peak_time #LibV5:peak_indices #LibV1:interpolate -LibV1:time_to_first_spike #LibV1:peak_time #LibV1:interpolate -LibV1:adaptation_index #LibV1:peak_time #LibV1:interpolate -LibV1:adaptation_index2 #LibV1:peak_time #LibV1:interpolate -LibV1:spike_width2 #LibV5:min_AHP_indices #LibV1:interpolate -LibV1:AP_width #LibV5:peak_indices #LibV5:min_AHP_indices #LibV1:interpolate -LibV1:AP_height #LibV1:peak_voltage #LibV1:interpolate -LibV1:AP_amplitude #LibV5:AP_begin_indices #LibV1:peak_voltage #LibV1:peak_time #LibV1:interpolate -LibV1:AHP_depth_abs_slow #LibV5:peak_indices #LibV1:interpolate -LibV1:AHP_slow_time #LibV1:AHP_depth_abs_slow #LibV1:interpolate -LibV1:time_constant #LibV1:interpolate -LibV1:voltage_deflection #LibV1:interpolate -LibV5:voltage_deflection_vb_ssse #LibV5:voltage_base #LibV5:steady_state_voltage_stimend #LibV1:interpolate -LibV1:ohmic_input_resistance #LibV1:voltage_deflection #LibV1:interpolate -LibV5:ohmic_input_resistance_vb_ssse #LibV5:voltage_deflection_vb_ssse #LibV1:interpolate -LibV1:maximum_voltage #LibV1:interpolate -LibV1:minimum_voltage #LibV1:interpolate -LibV1:steady_state_voltage #LibV1:interpolate -LibV3:depolarized_base #LibV5:AP_end_indices #LibV5:AP_begin_indices #LibV1:interpolate -LibV1:AHP_depth #LibV5:voltage_base #LibV5:min_AHP_values #LibV1:interpolate -LibV1:AHP_depth_slow #LibV5:voltage_base #LibV1:AHP_depth_abs_slow #LibV1:interpolate -LibV2:AP_rise_indices #LibV5:peak_indices #LibV5:AP_begin_indices #LibV1:interpolate -LibV5:AP_end_indices #LibV5:peak_indices #LibV1:interpolate -LibV2:AP_fall_indices #LibV5:peak_indices #LibV5:AP_begin_indices #LibV5:AP_end_indices #LibV1:interpolate -LibV2:AP_duration #LibV5:AP_begin_indices #LibV5:AP_end_indices #LibV1:interpolate -LibV2:AP_duration_half_width #LibV2:AP_rise_indices #LibV2:AP_fall_indices #LibV1:interpolate -LibV2:AP_rise_time #LibV5:AP_begin_indices #LibV5:peak_indices #LibV1:interpolate -LibV2:AP_fall_time #LibV5:peak_indices #LibV5:AP_end_indices #LibV1:interpolate -LibV2:AP_rise_rate #LibV5:AP_begin_indices #LibV5:peak_indices #LibV1:interpolate -LibV2:AP_fall_rate #LibV5:peak_indices #LibV5:AP_end_indices #LibV1:interpolate -LibV2:fast_AHP #LibV5:AP_begin_indices #LibV5:min_AHP_indices #LibV1:interpolate -LibV2:AP_amplitude_change #LibV1:AP_amplitude #LibV1:interpolate -LibV2:AP_duration_change #LibV2:AP_duration #LibV1:interpolate -LibV2:AP_rise_rate_change #LibV2:AP_rise_rate #LibV1:interpolate -LibV2:AP_fall_rate_change #LibV2:AP_fall_rate #LibV1:interpolate -LibV2:fast_AHP_change #LibV2:fast_AHP #LibV1:interpolate -LibV2:AP_duration_half_width_change #LibV2:AP_duration_half_width #LibV1:interpolate -LibV2:steady_state_hyper #LibV1:interpolate -LibV2:amp_drop_first_second #LibV1:peak_voltage #LibV1:interpolate -LibV2:amp_drop_first_last #LibV1:peak_voltage #LibV1:interpolate -LibV2:amp_drop_second_last #LibV1:peak_voltage #LibV1:interpolate -LibV2:max_amp_difference #LibV1:peak_voltage #LibV1:interpolate -LibV1:AP_amplitude_diff #LibV1:AP_amplitude #LibV1:interpolate -LibV1:AHP_depth_diff #LibV1:AHP_depth #LibV1:interpolate -LibV5:min_AHP_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:min_AHP_values #LibV5:min_AHP_indices #LibV1:interpolate -LibV5:number_initial_spikes #LibV1:peak_time #LibV1:interpolate -LibV5:AP1_amp #LibV1:AP_amplitude #LibV1:interpolate -LibV5:APlast_amp #LibV1:AP_amplitude #LibV1:interpolate -LibV5:AP2_amp #LibV1:AP_amplitude #LibV1:interpolate -LibV5:AP1_peak #LibV1:peak_voltage #LibV1:interpolate -LibV5:AP2_peak #LibV1:peak_voltage #LibV1:interpolate -LibV5:AP2_AP1_diff #LibV1:AP_amplitude #LibV1:interpolate -LibV5:AP2_AP1_peak_diff #LibV1:peak_voltage #LibV1:interpolate -LibV5:AP1_width #LibV5:spike_half_width #LibV1:interpolate -LibV5:AP2_width #LibV5:spike_half_width #LibV1:interpolate -LibV5:APlast_width #LibV5:spike_half_width #LibV1:interpolate -LibV5:AHP_depth_from_peak #LibV5:peak_indices #LibV5:min_AHP_indices #LibV1:interpolate -LibV5:AHP_time_from_peak #LibV5:peak_indices #LibV5:min_AHP_indices #LibV1:interpolate -LibV5:AHP1_depth_from_peak #LibV5:AHP_depth_from_peak #LibV1:interpolate -LibV5:AHP2_depth_from_peak #LibV5:AHP_depth_from_peak #LibV1:interpolate -LibV5:time_to_second_spike #LibV1:peak_time #LibV1:interpolate -LibV5:time_to_last_spike #LibV1:peak_time #LibV1:interpolate -LibV5:inv_time_to_first_spike #LibV1:time_to_first_spike #LibV1:interpolate -LibV5:spike_half_width #LibV5:min_AHP_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:AP_begin_indices #LibV5:min_AHP_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:AHP_depth_abs #LibV5:min_AHP_values #LibV1:interpolate -LibV5:AP_begin_width #LibV5:min_AHP_indices #LibV5:AP_begin_indices #LibV1:interpolate -LibV5:AP_begin_voltage #LibV5:AP_begin_indices #LibV1:interpolate -LibV5:AP_begin_time #LibV5:AP_begin_indices #LibV1:interpolate -LibV5:AP1_begin_voltage #LibV5:AP_begin_voltage #LibV1:interpolate -LibV5:AP2_begin_voltage #LibV5:AP_begin_voltage #LibV1:interpolate -LibV5:AP1_begin_width #LibV5:AP_begin_width #LibV1:interpolate -LibV5:AP2_begin_width #LibV5:AP_begin_width #LibV1:interpolate -LibV5:voltage_deflection_begin #LibV1:interpolate -LibV5:is_not_stuck #LibV1:peak_time #LibV1:interpolate -LibV5:mean_AP_amplitude #LibV1:AP_amplitude #LibV1:interpolate -LibV5:voltage_after_stim #LibV1:interpolate -LibV5:AP2_AP1_begin_width_diff #LibV5:AP_begin_width #LibV1:interpolate -LibV5:AP_phaseslope #LibV5:AP_begin_indices #LibV1:interpolate -LibV5:all_ISI_values #LibV1:peak_time #LibV1:interpolate -LibV5:AP_amplitude_from_voltagebase #LibV5:voltage_base #LibV1:peak_voltage #LibV1:interpolate -LibV5:min_voltage_between_spikes #LibV5:peak_indices #LibV1:interpolate -LibV5:voltage #LibV1:interpolate -LibV5:current #LibV1:interpolate -LibV5:time #LibV1:interpolate -LibV5:steady_state_voltage_stimend #LibV1:interpolate -LibV5:voltage_base #LibV1:interpolate -LibV5:current_base #LibV1:interpolate -LibV5:decay_time_constant_after_stim #LibV1:interpolate -LibV5:sag_time_constant #LibV1:minimum_voltage #LibV5:steady_state_voltage_stimend #LibV5:sag_amplitude #LibV1:interpolate -LibV5:multiple_decay_time_constant_after_stim #LibV5:decay_time_constant_after_stim #LibV1:interpolate -LibV5:maximum_voltage_from_voltagebase #LibV5:voltage_base #LibV1:maximum_voltage #LibV1:interpolate -LibV5:sag_amplitude #LibV1:minimum_voltage #LibV5:steady_state_voltage_stimend #LibV5:voltage_deflection_vb_ssse #LibV1:interpolate -LibV5:sag_ratio1 #LibV1:minimum_voltage #LibV5:sag_amplitude #LibV5:voltage_base #LibV1:interpolate -LibV5:sag_ratio2 #LibV1:minimum_voltage #LibV5:steady_state_voltage_stimend #LibV5:voltage_base #LibV1:interpolate -LibV5:AP_peak_upstroke #LibV5:AP_begin_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:AP_peak_downstroke #LibV5:min_AHP_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:min_between_peaks_indices #LibV5:peak_indices #LibV1:interpolate -LibV5:min_between_peaks_values #LibV5:min_between_peaks_indices #LibV1:interpolate -LibV5:AP_width_between_threshold #LibV5:min_between_peaks_indices #LibV1:interpolate -LibV5:burst_begin_indices #LibV5:all_ISI_values #LibV1:interpolate -LibV5:burst_end_indices #LibV5:burst_begin_indices #LibV1:interpolate -LibV5:strict_burst_mean_freq #LibV1:peak_time #LibV5:burst_begin_indices #LibV5:burst_end_indices #LibV1:interpolate -LibV5:strict_interburst_voltage #LibV5:peak_indices #LibV5:burst_begin_indices #LibV1:interpolate -LibV5:ADP_peak_indices #LibV5:min_AHP_indices #LibV5:min_between_peaks_indices #LibV1:interpolate -LibV5:ADP_peak_values #LibV5:ADP_peak_indices #LibV1:interpolate -LibV5:ADP_peak_amplitude #LibV5:ADP_peak_values #LibV5:min_AHP_values #LibV1:interpolate -LibV5:interburst_min_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV1:interpolate -LibV5:interburst_min_values #LibV5:interburst_min_indices #LibV1:interpolate -LibV5:postburst_min_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV1:interpolate -LibV5:postburst_min_values #LibV5:postburst_min_indices #LibV1: interpolate -LibV5:postburst_slow_ahp_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV1:interpolate -LibV5:postburst_slow_ahp_values #LibV5:postburst_slow_ahp_indices #LibV1:interpolate -LibV5:time_to_interburst_min #LibV5:interburst_min_indices #LibV1:peak_time #LibV5:burst_end_indices #LibV1:interpolate -LibV5:time_to_postburst_slow_ahp #LibV5:postburst_slow_ahp_indices #LibV1:peak_time #LibV5:burst_end_indices #LibV1:interpolate -LibV5:postburst_fast_ahp_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV1:interpolate -LibV5:postburst_fast_ahp_values #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:postburst_adp_peak_indices #LibV5:postburst_fast_ahp_indices #LibV5:postburst_slow_ahp_indices #LibV1:interpolate -LibV5:postburst_adp_peak_values #LibV5:postburst_adp_peak_indices #LibV1:interpolate -LibV5:time_to_postburst_fast_ahp #LibV5:postburst_fast_ahp_indices #LibV1:peak_time #LibV5:burst_end_indices #LibV1:interpolate -LibV5:time_to_postburst_adp_peak #LibV5:postburst_adp_peak_indices #LibV1:peak_time #LibV5:burst_end_indices #LibV1:interpolate -LibV5:interburst_15percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_15percent_values #LibV5:interburst_15percent_indices #LibV1:interpolate -LibV5:interburst_20percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_20percent_values #LibV5:interburst_20percent_indices #LibV1:interpolate -LibV5:interburst_25percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_25percent_values #LibV5:interburst_25percent_indices #LibV1:interpolate -LibV5:interburst_30percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_30percent_values #LibV5:interburst_30percent_indices #LibV1:interpolate -LibV5:interburst_40percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_40percent_values #LibV5:interburst_40percent_indices #LibV1:interpolate -LibV5:interburst_60percent_indices #LibV5:peak_indices #LibV5:burst_end_indices #LibV5:postburst_fast_ahp_indices #LibV1:interpolate -LibV5:interburst_60percent_values #LibV5:interburst_60percent_indices #LibV1:interpolate -LibV5:interburst_duration #LibV1:peak_time #LibV5:burst_end_indices #LibV1:interpolate +BasicFeatures:interpolate +SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeEvent:doublet_ISI #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeShape:peak_voltage #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeEvent:mean_frequency #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:peak_time #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeEvent:time_to_first_spike #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:adaptation_index #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:adaptation_index2 #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeShape:spike_width2 #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeShape:AP_width #SpikeEvent:peak_indices #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeShape:AP_height #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:AP_amplitude #SpikeShape:AP_begin_indices #SpikeShape:peak_voltage #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeShape:AHP_depth_abs_slow #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AHP_slow_time #SpikeShape:AHP_depth_abs_slow #BasicFeatures:interpolate +Subthreshold:time_constant #BasicFeatures:interpolate +Subthreshold:voltage_deflection #BasicFeatures:interpolate +Subthreshold:voltage_deflection_vb_ssse #Subthreshold:voltage_base #Subthreshold:steady_state_voltage_stimend #BasicFeatures:interpolate +Subthreshold:ohmic_input_resistance #Subthreshold:voltage_deflection #BasicFeatures:interpolate +Subthreshold:ohmic_input_resistance_vb_ssse #Subthreshold:voltage_deflection_vb_ssse #BasicFeatures:interpolate +Subthreshold:maximum_voltage #BasicFeatures:interpolate +Subthreshold:minimum_voltage #BasicFeatures:interpolate +Subthreshold:steady_state_voltage #BasicFeatures:interpolate +SpikeShape:depolarized_base #SpikeShape:AP_end_indices #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeShape:AHP_depth #Subthreshold:voltage_base #SpikeShape:min_AHP_values #BasicFeatures:interpolate +SpikeShape:AHP_depth_slow #Subthreshold:voltage_base #SpikeShape:AHP_depth_abs_slow #BasicFeatures:interpolate +SpikeShape:AP_rise_indices #SpikeEvent:peak_indices #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeShape:AP_end_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AP_fall_indices #SpikeEvent:peak_indices #SpikeShape:AP_begin_indices #SpikeShape:AP_end_indices #BasicFeatures:interpolate +SpikeShape:AP_duration #SpikeShape:AP_begin_indices #SpikeShape:AP_end_indices #BasicFeatures:interpolate +SpikeShape:AP_duration_half_width #SpikeShape:AP_rise_indices #SpikeShape:AP_fall_indices #BasicFeatures:interpolate +SpikeShape:AP_rise_time #SpikeShape:AP_begin_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AP_fall_time #SpikeEvent:peak_indices #SpikeShape:AP_end_indices #BasicFeatures:interpolate +SpikeShape:AP_rise_rate #SpikeShape:AP_begin_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AP_fall_rate #SpikeEvent:peak_indices #SpikeShape:AP_end_indices #BasicFeatures:interpolate +SpikeShape:fast_AHP #SpikeShape:AP_begin_indices #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeShape:AP_amplitude_change #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:AP_duration_change #SpikeShape:AP_duration #BasicFeatures:interpolate +SpikeShape:AP_rise_rate_change #SpikeShape:AP_rise_rate #BasicFeatures:interpolate +SpikeShape:AP_fall_rate_change #SpikeShape:AP_fall_rate #BasicFeatures:interpolate +SpikeShape:fast_AHP_change #SpikeShape:fast_AHP #BasicFeatures:interpolate +SpikeShape:AP_duration_half_width_change #SpikeShape:AP_duration_half_width #BasicFeatures:interpolate +Subthreshold:steady_state_hyper #BasicFeatures:interpolate +SpikeShape:amp_drop_first_second #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:amp_drop_first_last #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:amp_drop_second_last #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:max_amp_difference #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:AP_amplitude_diff #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:AHP_depth_diff #SpikeShape:AHP_depth #BasicFeatures:interpolate +SpikeShape:min_AHP_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:min_AHP_values #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeEvent:number_initial_spikes #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeShape:AP1_amp #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:APlast_amp #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:AP2_amp #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:AP1_peak #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:AP2_peak #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:AP2_AP1_diff #SpikeShape:AP_amplitude #BasicFeatures:interpolate +SpikeShape:AP2_AP1_peak_diff #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:AP1_width #SpikeShape:spike_half_width #BasicFeatures:interpolate +SpikeShape:AP2_width #SpikeShape:spike_half_width #BasicFeatures:interpolate +SpikeShape:APlast_width #SpikeShape:spike_half_width #BasicFeatures:interpolate +SpikeShape:AHP_depth_from_peak #SpikeEvent:peak_indices #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeShape:AHP_time_from_peak #SpikeEvent:peak_indices #SpikeShape:min_AHP_indices #BasicFeatures:interpolate +SpikeShape:AHP1_depth_from_peak #SpikeShape:AHP_depth_from_peak #BasicFeatures:interpolate +SpikeShape:AHP2_depth_from_peak #SpikeShape:AHP_depth_from_peak #BasicFeatures:interpolate +SpikeEvent:time_to_second_spike #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:time_to_last_spike #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:inv_time_to_first_spike #SpikeEvent:time_to_first_spike #BasicFeatures:interpolate +SpikeShape:spike_half_width #SpikeShape:min_AHP_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AP_begin_indices #SpikeShape:min_AHP_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AHP_depth_abs #SpikeShape:min_AHP_values #BasicFeatures:interpolate +SpikeShape:AP_begin_width #SpikeShape:min_AHP_indices #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeShape:AP_begin_voltage #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeShape:AP_begin_time #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeShape:AP1_begin_voltage #SpikeShape:AP_begin_voltage #BasicFeatures:interpolate +SpikeShape:AP2_begin_voltage #SpikeShape:AP_begin_voltage #BasicFeatures:interpolate +SpikeShape:AP1_begin_width #SpikeShape:AP_begin_width #BasicFeatures:interpolate +SpikeShape:AP2_begin_width #SpikeShape:AP_begin_width #BasicFeatures:interpolate +Subthreshold:voltage_deflection_begin #BasicFeatures:interpolate +SpikeEvent:is_not_stuck #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeShape:mean_AP_amplitude #SpikeShape:AP_amplitude #BasicFeatures:interpolate +Subthreshold:voltage_after_stim #BasicFeatures:interpolate +SpikeShape:AP2_AP1_begin_width_diff #SpikeShape:AP_begin_width #BasicFeatures:interpolate +SpikeShape:AP_phaseslope #SpikeShape:AP_begin_indices #BasicFeatures:interpolate +SpikeEvent:all_ISI_values #SpikeEvent:peak_time #BasicFeatures:interpolate +SpikeEvent:AP_amplitude_from_voltagebase #Subthreshold:voltage_base #SpikeShape:peak_voltage #BasicFeatures:interpolate +SpikeShape:min_voltage_between_spikes #SpikeEvent:peak_indices #BasicFeatures:interpolate +BasicFeatures:voltage #BasicFeatures:interpolate +BasicFeatures:current #BasicFeatures:interpolate +BasicFeatures:time #BasicFeatures:interpolate +Subthreshold:steady_state_voltage_stimend #BasicFeatures:interpolate +Subthreshold:voltage_base #BasicFeatures:interpolate +Subthreshold:current_base #BasicFeatures:interpolate +Subthreshold:decay_time_constant_after_stim #BasicFeatures:interpolate +Subthreshold:sag_time_constant #Subthreshold:minimum_voltage #Subthreshold:steady_state_voltage_stimend #Subthreshold:sag_amplitude #BasicFeatures:interpolate +Subthreshold:multiple_decay_time_constant_after_stim #Subthreshold:decay_time_constant_after_stim #BasicFeatures:interpolate +Subthreshold:maximum_voltage_from_voltagebase #Subthreshold:voltage_base #Subthreshold:maximum_voltage #BasicFeatures:interpolate +Subthreshold:sag_amplitude #Subthreshold:minimum_voltage #Subthreshold:steady_state_voltage_stimend #Subthreshold:voltage_deflection_vb_ssse #BasicFeatures:interpolate +Subthreshold:sag_ratio1 #Subthreshold:minimum_voltage #Subthreshold:sag_amplitude #Subthreshold:voltage_base #BasicFeatures:interpolate +Subthreshold:sag_ratio2 #Subthreshold:minimum_voltage #Subthreshold:steady_state_voltage_stimend #Subthreshold:voltage_base #BasicFeatures:interpolate +SpikeShape:AP_peak_upstroke #SpikeShape:AP_begin_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:AP_peak_downstroke #SpikeShape:min_AHP_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:min_between_peaks_indices #SpikeEvent:peak_indices #BasicFeatures:interpolate +SpikeShape:min_between_peaks_values #SpikeShape:min_between_peaks_indices #BasicFeatures:interpolate +SpikeShape:AP_width_between_threshold #SpikeShape:min_between_peaks_indices #BasicFeatures:interpolate +SpikeEvent:burst_begin_indices #SpikeEvent:all_ISI_values #BasicFeatures:interpolate +SpikeEvent:burst_end_indices #SpikeEvent:burst_begin_indices #BasicFeatures:interpolate +SpikeEvent:strict_burst_mean_freq #SpikeEvent:peak_time #SpikeEvent:burst_begin_indices #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:strict_interburst_voltage #SpikeEvent:peak_indices #SpikeEvent:burst_begin_indices #BasicFeatures:interpolate +SpikeShape:ADP_peak_indices #SpikeShape:min_AHP_indices #SpikeShape:min_between_peaks_indices #BasicFeatures:interpolate +SpikeShape:ADP_peak_values #SpikeShape:ADP_peak_indices #BasicFeatures:interpolate +SpikeShape:ADP_peak_amplitude #SpikeShape:ADP_peak_values #SpikeShape:min_AHP_values #BasicFeatures:interpolate +SpikeEvent:interburst_min_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:interburst_min_values #SpikeEvent:interburst_min_indices #BasicFeatures:interpolate +SpikeEvent:postburst_min_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:postburst_min_values #SpikeEvent:postburst_min_indices #BasicFeatures:interpolate +SpikeEvent:postburst_slow_ahp_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:postburst_slow_ahp_values #SpikeEvent:postburst_slow_ahp_indices #BasicFeatures:interpolate +SpikeEvent:time_to_interburst_min #SpikeEvent:interburst_min_indices #SpikeEvent:peak_time #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:time_to_postburst_slow_ahp #SpikeEvent:postburst_slow_ahp_indices #SpikeEvent:peak_time #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:postburst_fast_ahp_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:postburst_fast_ahp_values #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:postburst_adp_peak_indices #SpikeEvent:postburst_fast_ahp_indices #SpikeEvent:postburst_slow_ahp_indices #BasicFeatures:interpolate +SpikeEvent:postburst_adp_peak_values #SpikeEvent:postburst_adp_peak_indices #BasicFeatures:interpolate +SpikeEvent:time_to_postburst_fast_ahp #SpikeEvent:postburst_fast_ahp_indices #SpikeEvent:peak_time #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:time_to_postburst_adp_peak #SpikeEvent:postburst_adp_peak_indices #SpikeEvent:peak_time #SpikeEvent:burst_end_indices #BasicFeatures:interpolate +SpikeEvent:interburst_15percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_15percent_values #SpikeEvent:interburst_15percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_20percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_20percent_values #SpikeEvent:interburst_20percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_25percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_25percent_values #SpikeEvent:interburst_25percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_30percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_30percent_values #SpikeEvent:interburst_30percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_40percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_40percent_values #SpikeEvent:interburst_40percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_60percent_indices #SpikeEvent:peak_indices #SpikeEvent:burst_end_indices #SpikeEvent:postburst_fast_ahp_indices #BasicFeatures:interpolate +SpikeEvent:interburst_60percent_values #SpikeEvent:interburst_60percent_indices #BasicFeatures:interpolate +SpikeEvent:interburst_duration #SpikeEvent:peak_time #SpikeEvent:burst_end_indices #BasicFeatures:interpolate diff --git a/efel/cppcore/BasicFeatures.cpp b/efel/cppcore/BasicFeatures.cpp new file mode 100644 index 00000000..ad185c67 --- /dev/null +++ b/efel/cppcore/BasicFeatures.cpp @@ -0,0 +1,75 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +int BasicFeatures::interpolate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + vector V, T, VIntrpol, TIntrpol, InterpStepVec; + T = getFeature(DoubleFeatureData, "T"); + // interp_step is a stimulus independent parameter + int retVal = getParam(DoubleFeatureData, "interp_step", InterpStepVec); + double InterpStep = (retVal <= 0) ? 0.1 : InterpStepVec[0]; + + try // interpolate V if it's available + { + V = getFeature(DoubleFeatureData, "V"); + LinearInterpolation(InterpStep, T, V, TIntrpol, VIntrpol); + setVec(DoubleFeatureData, StringData, "V", VIntrpol); + setVec(DoubleFeatureData, StringData, "T", TIntrpol); + } catch (...) { + return -1; // interpolation failed + } + + // also interpolate current if present + vector I, IIntrpol, TIntrpolI; + try { + I = getFeature(DoubleFeatureData, "I"); + LinearInterpolation(InterpStep, T, I, TIntrpolI, IIntrpol); + setVec(DoubleFeatureData, StringData, "I", IIntrpol); + setVec(DoubleFeatureData, StringData, "T", TIntrpol); + } catch (...) { + } // pass, it is optional + return 1; +} + +// return (possibly interpolate) voltage trace +int BasicFeatures::voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& v = getFeature(DoubleFeatureData, "V"); + setVec(DoubleFeatureData, StringData, "voltage", v); + return v.size(); +} + +// return (possibly interpolate) current trace +int BasicFeatures::current(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& i = getFeature(DoubleFeatureData, "I"); + setVec(DoubleFeatureData, StringData, "current", i); + return i.size(); +} + +// return (possibly interpolate) time trace +int BasicFeatures::time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, mapStr2Str& StringData) { + const vector& t = getFeature(DoubleFeatureData, "T"); + setVec(DoubleFeatureData, StringData, "time", t); + return t.size(); +} diff --git a/efel/cppcore/BasicFeatures.h b/efel/cppcore/BasicFeatures.h new file mode 100644 index 00000000..474ad513 --- /dev/null +++ b/efel/cppcore/BasicFeatures.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "mapoperations.h" +#include "Utils.h" + +#include +#include + +using std::vector; + +namespace BasicFeatures { +int interpolate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int current(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +} \ No newline at end of file diff --git a/efel/cppcore/CMakeLists.txt b/efel/cppcore/CMakeLists.txt index 81ae4be5..9a022263 100644 --- a/efel/cppcore/CMakeLists.txt +++ b/efel/cppcore/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2015, EPFL/Blue Brain Project +# Copyright (c) 2015-2024, EPFL/Blue Brain Project # # This file is part of eFEL # @@ -24,7 +24,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_BUILD_TYPE Debug) -set(FEATURESRCS Utils.cpp LibV1.cpp LibV2.cpp LibV3.cpp LibV5.cpp +set(FEATURESRCS Utils.cpp BasicFeatures.cpp SpikeEvent.cpp SpikeShape.cpp Subthreshold.cpp FillFptrTable.cpp DependencyTree.cpp cfeature.cpp mapoperations.cpp) @@ -39,7 +39,7 @@ install(TARGETS efelStatic ARCHIVE DESTINATION lib) add_library(efel SHARED ${FEATURESRCS}) install(TARGETS efel LIBRARY DESTINATION lib) -install(FILES cfeature.h FillFptrTable.h LibV1.h LibV2.h LibV3.h - LibV5.h mapoperations.h Utils.h DependencyTree.h eFELLogger.h EfelExceptions.h +install(FILES cfeature.h FillFptrTable.h BasicFeatures.h SpikeEvent.h SpikeShape.h + Subthreshold.h mapoperations.h Utils.h DependencyTree.h eFELLogger.h EfelExceptions.h types.h DESTINATION include) diff --git a/efel/cppcore/FillFptrTable.cpp b/efel/cppcore/FillFptrTable.cpp index 333b4713..86a1fd8f 100644 --- a/efel/cppcore/FillFptrTable.cpp +++ b/efel/cppcore/FillFptrTable.cpp @@ -18,242 +18,203 @@ #include "FillFptrTable.h" int FillFptrTable() { - //****************** for FptrTableV1 ***************************** - FptrTableV1["interpolate"] = &LibV1::interpolate; - FptrTableV1["peak_voltage"] = &LibV1::peak_voltage; - FptrTableV1["mean_frequency"] = &LibV1::firing_rate; - FptrTableV1["peak_time"] = &LibV1::peak_time; - FptrTableV1["time_to_first_spike"] = &LibV1::first_spike_time; - FptrTableV1["adaptation_index"] = &LibV1::adaptation_index; - FptrTableV1["spike_width2"] = &LibV1::spike_width2; - // passive properties - FptrTableV1["time_constant"] = &LibV1::time_constant; - FptrTableV1["voltage_deflection"] = &LibV1::voltage_deflection; - FptrTableV1["ohmic_input_resistance"] = &LibV1::ohmic_input_resistance; - FptrTableV1["maximum_voltage"] = &LibV1::maximum_voltage; - FptrTableV1["minimum_voltage"] = &LibV1::minimum_voltage; - FptrTableV1["steady_state_voltage"] = &LibV1::steady_state_voltage; - - FptrTableV1["AP_height"] = &LibV1::AP_height; - FptrTableV1["AP_amplitude"] = &LibV1::AP_amplitude; - FptrTableV1["AP_width"] = &LibV1::AP_width; - FptrTableV1["doublet_ISI"] = &LibV1::doublet_ISI; - FptrTableV1["adaptation_index2"] = &LibV1::adaptation_index2; - FptrTableV1["AHP_depth_abs_slow"] = &LibV1::AHP_depth_abs_slow; - FptrTableV1["AHP_slow_time"] = &LibV1::AHP_slow_time; - FptrTableV1["AHP_depth"] = &LibV1::AHP_depth; - FptrTableV1["AHP_depth_slow"] = &LibV1::AHP_depth_slow; - FptrTableV1["AP_amplitude_diff"] = &LibV1::AP_amplitude_diff; - FptrTableV1["AHP_depth_diff"] = &LibV1::AHP_depth_diff; - - //****************** for FptrTableV1 ***************************** - - //****************** for FptrTableV2 ***************************** - /* - FptrTableV2["AHP_min_indices"] = &LibV2::AHP_min_indices; - FptrTableV2["AHP_values"] = &LibV2::AHP_values; - FptrTableV2["burst_vector"] = &LibV2::burst_vector; - */ - - // AP parameter - FptrTableV2["AP_rise_indices"] = &LibV2::AP_rise_indices; - FptrTableV2["AP_fall_indices"] = &LibV2::AP_fall_indices; - // eFeatures - FptrTableV2["AP_duration"] = &LibV2::AP_duration; - FptrTableV2["AP_rise_time"] = &LibV2::AP_rise_time; - FptrTableV2["AP_fall_time"] = &LibV2::AP_fall_time; - FptrTableV2["AP_rise_rate"] = &LibV2::AP_rise_rate; - FptrTableV2["AP_fall_rate"] = &LibV2::AP_fall_rate; - FptrTableV2["fast_AHP"] = &LibV2::fast_AHP; - FptrTableV2["AP_amplitude_change"] = &LibV2::AP_amplitude_change; - FptrTableV2["AP_duration_change"] = &LibV2::AP_duration_change; - FptrTableV2["AP_rise_rate_change"] = &LibV2::AP_rise_rate_change; - FptrTableV2["AP_fall_rate_change"] = &LibV2::AP_fall_rate_change; - FptrTableV2["fast_AHP_change"] = &LibV2::fast_AHP_change; - FptrTableV2["AP_duration_half_width"] = &LibV2::AP_duration_half_width; - FptrTableV2["AP_duration_half_width_change"] = - &LibV2::AP_duration_half_width_change; - FptrTableV2["steady_state_hyper"] = &LibV2::steady_state_hyper; - FptrTableV2[string("amp_drop_first_second")] = &LibV2::amp_drop_first_second; - FptrTableV2[string("amp_drop_first_last")] = &LibV2::amp_drop_first_last; - FptrTableV2[string("amp_drop_second_last")] = &LibV2::amp_drop_second_last; - FptrTableV2[string("max_amp_difference")] = &LibV2::max_amp_difference; - // end of feature definition - //****************** end of FptrTableV2 ***************************** - - //****************** FptrTableV3 ***************************** - // eFeatures - FptrTableV3["depolarized_base"] = &LibV3::depolarized_base; - - //****************** end of FptrTableV3 ***************************** - - //****************** FptrTableV5 ***************************** - FptrTableV5["time_to_second_spike"] = &LibV5::time_to_second_spike; - FptrTableV5["time_to_last_spike"] = &LibV5::time_to_last_spike; - FptrTableV5["inv_time_to_first_spike"] = &LibV5::inv_time_to_first_spike; - FptrTableV5["min_AHP_indices"] = &LibV5::min_AHP_indices; - FptrTableV5["min_AHP_values"] = &LibV5::min_AHP_values; - FptrTableV5["AHP_depth_abs"] = &LibV5::AHP_depth_abs; - FptrTableV5["spike_half_width"] = &LibV5::spike_width1; - FptrTableV5["AP_begin_indices"] = &LibV5::AP_begin_indices; - FptrTableV5["AP_end_indices"] = &LibV5::AP_end_indices; - FptrTableV5["number_initial_spikes"] = &LibV5::number_initial_spikes; - FptrTableV5["AP1_amp"] = &LibV5::AP1_amp; - FptrTableV5["APlast_amp"] = &LibV5::APlast_amp; - FptrTableV5["AP1_peak"] = &LibV5::AP1_peak; - FptrTableV5["AP1_width"] = &LibV5::AP1_width; - FptrTableV5["AP2_amp"] = &LibV5::AP2_amp; - FptrTableV5["AP2_peak"] = &LibV5::AP2_peak; - FptrTableV5["AP2_AP1_diff"] = &LibV5::AP2_AP1_diff; - FptrTableV5["AP2_AP1_peak_diff"] = &LibV5::AP2_AP1_peak_diff; - FptrTableV5["AP2_width"] = &LibV5::AP2_width; - FptrTableV5["APlast_width"] = &LibV5::APlast_width; - FptrTableV5["AHP_depth_from_peak"] = &LibV5::AHP_depth_from_peak; - FptrTableV5["AHP_time_from_peak"] = &LibV5::AHP_time_from_peak; - FptrTableV5["AHP1_depth_from_peak"] = &LibV5::AHP1_depth_from_peak; - FptrTableV5["AHP2_depth_from_peak"] = &LibV5::AHP2_depth_from_peak; - FptrTableV5["AP_begin_width"] = &LibV5::AP_begin_width; - FptrTableV5["AP_begin_time"] = &LibV5::AP_begin_time; - FptrTableV5["AP_begin_voltage"] = &LibV5::AP_begin_voltage; - - FptrTableV5["AP1_begin_width"] = &LibV5::AP1_begin_width; - FptrTableV5["AP2_begin_width"] = &LibV5::AP2_begin_width; - - FptrTableV5["AP1_begin_voltage"] = &LibV5::AP1_begin_voltage; - FptrTableV5["AP2_begin_voltage"] = &LibV5::AP2_begin_voltage; - - FptrTableV5["voltage_deflection_begin"] = &LibV5::voltage_deflection_begin; - FptrTableV5["is_not_stuck"] = &LibV5::is_not_stuck; - FptrTableV5["mean_AP_amplitude"] = &LibV5::mean_AP_amplitude; - FptrTableV5["voltage_after_stim"] = &LibV5::voltage_after_stim; - - FptrTableV5["AP2_AP1_begin_width_diff"] = &LibV5::AP2_AP1_begin_width_diff; - - FptrTableV5["AP_phaseslope"] = &LibV5::AP_phaseslope; - - FptrTableV5["all_ISI_values"] = &LibV5::all_ISI_values; - - FptrTableV5["AP_amplitude_from_voltagebase"] = - &LibV5::AP_amplitude_from_voltagebase; - FptrTableV5["min_voltage_between_spikes"] = - &LibV5::min_voltage_between_spikes; - FptrTableV5["voltage"] = &LibV5::voltage; - FptrTableV5["current"] = &LibV5::current; - FptrTableV5["time"] = &LibV5::time; - FptrTableV5["steady_state_voltage_stimend"] = - &LibV5::steady_state_voltage_stimend; - FptrTableV5["voltage_base"] = &LibV5::voltage_base; - FptrTableV5["current_base"] = &LibV5::current_base; - FptrTableV5["decay_time_constant_after_stim"] = - &LibV5::decay_time_constant_after_stim; - FptrTableV5["multiple_decay_time_constant_after_stim"] = - &LibV5::multiple_decay_time_constant_after_stim; - FptrTableV5["sag_time_constant"] = &LibV5::sag_time_constant; - - FptrTableV5["ohmic_input_resistance_vb_ssse"] = - &LibV5::ohmic_input_resistance_vb_ssse; - FptrTableV5["voltage_deflection_vb_ssse"] = - &LibV5::voltage_deflection_vb_ssse; - FptrTableV5["maximum_voltage_from_voltagebase"] = - &LibV5::maximum_voltage_from_voltagebase; - - FptrTableV5["peak_indices"] = &LibV5::peak_indices; - FptrTableV5["sag_amplitude"] = &LibV5::sag_amplitude; - FptrTableV5["sag_ratio1"] = &LibV5::sag_ratio1; - FptrTableV5["sag_ratio2"] = &LibV5::sag_ratio2; - FptrTableV5["AP_peak_upstroke"] = &LibV5::AP_peak_upstroke; - FptrTableV5["AP_peak_downstroke"] = &LibV5::AP_peak_downstroke; - FptrTableV5["min_between_peaks_indices"] = &LibV5::min_between_peaks_indices; - FptrTableV5["min_between_peaks_values"] = &LibV5::min_between_peaks_values; - FptrTableV5["AP_width_between_threshold"] = - &LibV5::AP_width_between_threshold; - - FptrTableV5["burst_begin_indices"] = &LibV5::burst_begin_indices; - FptrTableV5["burst_end_indices"] = &LibV5::burst_end_indices; - FptrTableV5["strict_burst_mean_freq"] = &LibV5::strict_burst_mean_freq; - FptrTableV5["strict_interburst_voltage"] = &LibV5::strict_interburst_voltage; - - FptrTableV5["ADP_peak_indices"] = &LibV5::ADP_peak_indices; - FptrTableV5["ADP_peak_values"] = &LibV5::ADP_peak_values; - FptrTableV5["ADP_peak_amplitude"] = &LibV5::ADP_peak_amplitude; - - FptrTableV5["interburst_min_indices"] = &LibV5::interburst_min_indices; - FptrTableV5["interburst_min_values"] = &LibV5::interburst_min_values; - FptrTableV5["postburst_min_indices"] = &LibV5::postburst_min_indices; - FptrTableV5["postburst_min_values"] = &LibV5::postburst_min_values; - FptrTableV5["postburst_slow_ahp_indices"] = &LibV5::postburst_slow_ahp_indices; - FptrTableV5["postburst_slow_ahp_values"] = &LibV5::postburst_slow_ahp_values; - FptrTableV5["time_to_interburst_min"] = &LibV5::time_to_interburst_min; - FptrTableV5["time_to_postburst_slow_ahp"] = &LibV5::time_to_postburst_slow_ahp; - FptrTableV5["postburst_fast_ahp_indices"] = &LibV5::postburst_fast_ahp_indices; - FptrTableV5["postburst_fast_ahp_values"] = &LibV5::postburst_fast_ahp_values; - FptrTableV5["postburst_adp_peak_indices"] = &LibV5::postburst_adp_peak_indices; - FptrTableV5["postburst_adp_peak_values"] = &LibV5::postburst_adp_peak_values; - FptrTableV5["time_to_postburst_fast_ahp"] = &LibV5::time_to_postburst_fast_ahp; - FptrTableV5["time_to_postburst_adp_peak"] = &LibV5::time_to_postburst_adp_peak; - FptrTableV5["interburst_15percent_indices"] = [](mapStr2intVec& intData, + //****************** for FptrTableBF ***************************** + FptrTableBF["interpolate"] = &BasicFeatures::interpolate; + FptrTableBF["voltage"] = &BasicFeatures::voltage; + FptrTableBF["current"] = &BasicFeatures::current; + FptrTableBF["time"] = &BasicFeatures::time; + + //****************** for FptrTableSE ***************************** + + FptrTableSE["peak_indices"] = &SpikeEvent::peak_indices; + FptrTableSE["peak_time"] = &SpikeEvent::peak_time; + FptrTableSE["time_to_first_spike"] = &SpikeEvent::first_spike_time; + FptrTableSE["time_to_second_spike"] = &SpikeEvent::time_to_second_spike; + FptrTableSE["inv_time_to_first_spike"] = &SpikeEvent::inv_time_to_first_spike; + FptrTableSE["doublet_ISI"] = &SpikeEvent::doublet_ISI; + FptrTableSE["all_ISI_values"] = &SpikeEvent::all_ISI_values; + FptrTableSE["time_to_last_spike"] = &SpikeEvent::time_to_last_spike; + FptrTableSE["number_initial_spikes"] = &SpikeEvent::number_initial_spikes; + FptrTableSE["mean_frequency"] = &SpikeEvent::firing_rate; + FptrTableSE["adaptation_index"] = &SpikeEvent::adaptation_index; + FptrTableSE["adaptation_index2"] = &SpikeEvent::adaptation_index2; + FptrTableSE["burst_begin_indices"] = &SpikeEvent::burst_begin_indices; + FptrTableSE["burst_end_indices"] = &SpikeEvent::burst_end_indices; + FptrTableSE["strict_burst_mean_freq"] = &SpikeEvent::strict_burst_mean_freq; + FptrTableSE["strict_interburst_voltage"] = &SpikeEvent::strict_interburst_voltage; + FptrTableSE["interburst_min_indices"] = &SpikeEvent::interburst_min_indices; + FptrTableSE["interburst_min_values"] = &SpikeEvent::interburst_min_values; + FptrTableSE["postburst_min_indices"] = &SpikeEvent::postburst_min_indices; + FptrTableSE["postburst_min_values"] = &SpikeEvent::postburst_min_values; + FptrTableSE["time_to_interburst_min"] = &SpikeEvent::time_to_interburst_min; + FptrTableSE["postburst_slow_ahp_indices"] = &SpikeEvent::postburst_slow_ahp_indices; + FptrTableSE["postburst_slow_ahp_values"] = &SpikeEvent::postburst_slow_ahp_values; + FptrTableSE["time_to_postburst_slow_ahp"] = &SpikeEvent::time_to_postburst_slow_ahp; + FptrTableSE["postburst_fast_ahp_indices"] = &SpikeEvent::postburst_fast_ahp_indices; + FptrTableSE["postburst_fast_ahp_values"] = &SpikeEvent::postburst_fast_ahp_values; + FptrTableSE["postburst_adp_peak_indices"] = &SpikeEvent::postburst_adp_peak_indices; + FptrTableSE["postburst_adp_peak_values"] = &SpikeEvent::postburst_adp_peak_values; + FptrTableSE["time_to_postburst_fast_ahp"] = &SpikeEvent::time_to_postburst_fast_ahp; + FptrTableSE["time_to_postburst_adp_peak"] = &SpikeEvent::time_to_postburst_adp_peak; + FptrTableSE["interburst_15percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 15); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 15); }; - FptrTableV5["interburst_15percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_15percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 15); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 15); }; - FptrTableV5["interburst_20percent_indices"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_20percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 20); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 20); }; - FptrTableV5["interburst_20percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_20percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 20); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 20); }; - FptrTableV5["interburst_25percent_indices"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_25percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 25); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 25); }; - FptrTableV5["interburst_25percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_25percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 25); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 25); }; - FptrTableV5["interburst_30percent_indices"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_30percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 30); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 30); }; - FptrTableV5["interburst_30percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_30percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 30); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 30); }; - FptrTableV5["interburst_40percent_indices"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_40percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 40); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 40); }; - FptrTableV5["interburst_40percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_40percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 40); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 40); }; - FptrTableV5["interburst_60percent_indices"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_60percent_indices"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 60); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 60); }; - FptrTableV5["interburst_60percent_values"] = [](mapStr2intVec& intData, + FptrTableSE["interburst_60percent_values"] = [](mapStr2intVec& intData, mapStr2doubleVec& doubleData, mapStr2Str& strData) { - return LibV5::interburst_XXpercent_indices(intData, doubleData, strData, 60); + return SpikeEvent::interburst_XXpercent_indices(intData, doubleData, strData, 60); }; - FptrTableV5["interburst_duration"] = &LibV5::interburst_duration; - - //****************** end of FptrTableV5 ***************************** + FptrTableSE["interburst_duration"] = &SpikeEvent::interburst_duration; + FptrTableSE["peak_inis_not_stuckdices"] = &SpikeEvent::is_not_stuck; + //****************** FptrTableSS ***************************** + // eFeatures + FptrTableSS["peak_voltage"] = &SpikeShape::peak_voltage; + FptrTableSS["AP_height"] = &SpikeShape::AP_height; + FptrTableSS["AP_amplitude"] = &SpikeShape::AP_amplitude; + FptrTableSS["AP1_amp"] = &SpikeShape::AP1_amp; + FptrTableSS["AP2_amp"] = &SpikeShape::AP2_amp; + FptrTableSS["mean_AP_amplitude"] = &SpikeShape::mean_AP_amplitude; + FptrTableSS["APlast_amp"] = &SpikeShape::APlast_amp; + FptrTableSS["AP_amplitude_change"] = &SpikeShape::AP_amplitude_change; + FptrTableSS["AP_amplitude_from_voltagebase"] = &SpikeShape::AP_amplitude_from_voltagebase; + FptrTableSS["AP1_peak"] = &SpikeShape::AP1_peak; + FptrTableSS["AP2_peak"] = &SpikeShape::AP2_peak; + FptrTableSS["AP2_AP1_diff"] = &SpikeShape::AP2_AP1_diff; + FptrTableSS["AP2_AP1_peak_diff"] = &SpikeShape::AP2_AP1_peak_diff; + FptrTableSS["amp_drop_first_second"] = &SpikeShape::amp_drop_first_second; + FptrTableSS["amp_drop_first_last"] = &SpikeShape::amp_drop_first_last; + FptrTableSS["amp_drop_second_last"] = &SpikeShape::amp_drop_second_last; + FptrTableSS["max_amp_difference"] = &SpikeShape::max_amp_difference; + FptrTableSS["AP_amplitude_diff"] = &SpikeShape::AP_amplitude_diff; + FptrTableSS["min_AHP_indices"] = &SpikeShape::min_AHP_indices; + FptrTableSS["min_AHP_values"] = &SpikeShape::min_AHP_values; + FptrTableSS["AHP_depth_abs"] = &SpikeShape::AHP_depth_abs; + FptrTableSS["AHP_depth_abs_slow"] = &SpikeShape::AHP_depth_abs_slow; + FptrTableSS["AHP_slow_time"] = &SpikeShape::AHP_slow_time; + FptrTableSS["AHP_depth_slow"] = &SpikeShape::AHP_depth_slow; + FptrTableSS["AHP_depth"] = &SpikeShape::AHP_depth; + FptrTableSS["AHP_depth_diff"] = &SpikeShape::AHP_depth_diff; + FptrTableSS["fast_AHP"] = &SpikeShape::fast_AHP; + FptrTableSS["fast_AHP_change"] = &SpikeShape::fast_AHP_change; + FptrTableSS["AHP_depth_from_peak"] = &SpikeShape::AHP_depth_from_peak; + FptrTableSS["AHP1_depth_from_peak"] = &SpikeShape::AHP1_depth_from_peak; + FptrTableSS["AHP2_depth_from_peak"] = &SpikeShape::AHP2_depth_from_peak; + FptrTableSS["AHP_time_from_peak"] = &SpikeShape::AHP_time_from_peak; + FptrTableSS["ADP_peak_indices"] = &SpikeShape::ADP_peak_indices; + FptrTableSS["ADP_peak_values"] = &SpikeShape::ADP_peak_values; + FptrTableSS["ADP_peak_amplitude"] = &SpikeShape::ADP_peak_amplitude; + FptrTableSS["depolarized_base"] = &SpikeShape::depolarized_base; + FptrTableSS["min_voltage_between_spikes"] = &SpikeShape::min_voltage_between_spikes; + FptrTableSS["min_between_peaks_indices"] = &SpikeShape::min_between_peaks_indices; + FptrTableSS["min_between_peaks_values"] = &SpikeShape::min_between_peaks_values; + FptrTableSS["AP_duration_half_width"] = &SpikeShape::AP_duration_half_width; + FptrTableSS["AP_duration_half_width_change"] = &SpikeShape::AP_duration_half_width_change; + FptrTableSS["AP_width"] = &SpikeShape::AP_width; + FptrTableSS["AP_duration"] = &SpikeShape::AP_duration; + FptrTableSS["AP_duration_change"] = &SpikeShape::AP_duration_change; + FptrTableSS["AP_width_between_threshold"] = &SpikeShape::AP_width_between_threshold; + FptrTableSS["spike_width1"] = &SpikeShape::spike_width1; + FptrTableSS["AP1_width"] = &SpikeShape::AP1_width; + FptrTableSS["AP2_width"] = &SpikeShape::AP2_width; + FptrTableSS["APlast_width"] = &SpikeShape::APlast_width; + FptrTableSS["spike_width2"] = &SpikeShape::spike_width2; + FptrTableSS["AP_begin_width"] = &SpikeShape::AP_begin_width; + FptrTableSS["AP1_begin_width"] = &SpikeShape::AP1_begin_width; + FptrTableSS["AP2_begin_width"] = &SpikeShape::AP2_begin_width; + FptrTableSS["AP2_AP1_begin_width_diff"] = &SpikeShape::AP2_AP1_begin_width_diff; + FptrTableSS["AP_begin_indices"] = &SpikeShape::AP_begin_indices; + FptrTableSS["AP_end_indices"] = &SpikeShape::AP_end_indices; + FptrTableSS["AP_begin_voltage"] = &SpikeShape::AP_begin_voltage; + FptrTableSS["AP1_begin_voltage"] = &SpikeShape::AP1_begin_voltage; + FptrTableSS["AP2_begin_voltage"] = &SpikeShape::AP2_begin_voltage; + FptrTableSS["AP_begin_time"] = &SpikeShape::AP_begin_time; + FptrTableSS["AP_peak_upstroke"] = &SpikeShape::AP_peak_upstroke; + FptrTableSS["AP_peak_downstroke"] = &SpikeShape::AP_peak_downstroke; + FptrTableSS["AP_rise_indices"] = &SpikeShape::AP_rise_indices; + FptrTableSS["AP_fall_indices"] = &SpikeShape::AP_fall_indices; + FptrTableSS["AP_rise_time"] = &SpikeShape::AP_rise_time; + FptrTableSS["AP_fall_time"] = &SpikeShape::AP_fall_time; + FptrTableSS["AP_rise_rate"] = &SpikeShape::AP_rise_rate; + FptrTableSS["AP_fall_rate"] = &SpikeShape::AP_fall_rate; + FptrTableSS["AP_rise_rate_change"] = &SpikeShape::AP_rise_rate_change; + FptrTableSS["AP_fall_rate_change"] = &SpikeShape::AP_fall_rate_change; + FptrTableSS["AP_phaseslope"] = &SpikeShape::AP_phaseslope; + + //****************** FptrTableST ***************************** + FptrTableST["steady_state_voltage_stimend"] = &Subthreshold::steady_state_voltage_stimend; + FptrTableST["steady_state_hyper"] = &Subthreshold::steady_state_hyper; + FptrTableST["steady_state_voltage"] = &Subthreshold::steady_state_voltage; + FptrTableST["voltage_base"] = &Subthreshold::voltage_base; + FptrTableST["current_base"] = &Subthreshold::current_base; + FptrTableST["time_constant"] = &Subthreshold::time_constant; + FptrTableST["decay_time_constant_after_stim"] = &Subthreshold::decay_time_constant_after_stim; + FptrTableST["multiple_decay_time_constant_after_stim"] = &Subthreshold::multiple_decay_time_constant_after_stim; + FptrTableST["sag_time_constant"] = &Subthreshold::sag_time_constant; + FptrTableST["sag_amplitude"] = &Subthreshold::sag_amplitude; + FptrTableST["sag_ratio1"] = &Subthreshold::sag_ratio1; + FptrTableST["sag_ratio2"] = &Subthreshold::sag_ratio2; + FptrTableST["ohmic_input_resistance"] = &Subthreshold::ohmic_input_resistance; + FptrTableST["ohmic_input_resistance_vb_ssse"] = &Subthreshold::ohmic_input_resistance_vb_ssse; + FptrTableST["voltage_deflection_vb_ssse"] = &Subthreshold::voltage_deflection_vb_ssse; + FptrTableST["voltage_deflection"] = &Subthreshold::voltage_deflection; + FptrTableST["voltage_deflection_begin"] = &Subthreshold::voltage_deflection_begin; + FptrTableST["voltage_after_stim"] = &Subthreshold::voltage_after_stim; + FptrTableST["maximum_voltage"] = &Subthreshold::maximum_voltage; + FptrTableST["minimum_voltage"] = &Subthreshold::minimum_voltage; + FptrTableST["maximum_voltage_from_voltagebase"] = &Subthreshold::maximum_voltage_from_voltagebase; + return 1; } diff --git a/efel/cppcore/FillFptrTable.h b/efel/cppcore/FillFptrTable.h index 7817c07a..3c8ee74a 100644 --- a/efel/cppcore/FillFptrTable.h +++ b/efel/cppcore/FillFptrTable.h @@ -21,15 +21,15 @@ #include "types.h" -#include "LibV1.h" -#include "LibV2.h" -#include "LibV3.h" -#include "LibV5.h" +#include "BasicFeatures.h" +#include "SpikeEvent.h" +#include "SpikeShape.h" +#include "Subthreshold.h" -extern feature2function FptrTableV1; -extern feature2function FptrTableV2; -extern feature2function FptrTableV3; -extern feature2function FptrTableV5; +extern feature2function FptrTableBF; +extern feature2function FptrTableSE; +extern feature2function FptrTableSS; +extern feature2function FptrTableST; int FillFptrTable(); #endif diff --git a/efel/cppcore/LibV1.cpp b/efel/cppcore/LibV1.cpp index 579b1fa4..8f6194a8 100644 --- a/efel/cppcore/LibV1.cpp +++ b/efel/cppcore/LibV1.cpp @@ -32,11 +32,6 @@ #include "EfelExceptions.h" -using std::distance; -using std::find_if; -using std::list; -using std::max_element; -using std::min_element; template std::string to_string(const T& value) { @@ -45,990 +40,7 @@ std::string to_string(const T& value) { return oss.str(); } -int LibV1::interpolate(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - vector V, T, VIntrpol, TIntrpol, InterpStepVec; - T = getFeature(DoubleFeatureData, "T"); - // interp_step is a stimulus independent parameter - int retVal = getParam(DoubleFeatureData, "interp_step", InterpStepVec); - double InterpStep = (retVal <= 0) ? 0.1 : InterpStepVec[0]; - try // interpolate V if it's available - { - V = getFeature(DoubleFeatureData, "V"); - LinearInterpolation(InterpStep, T, V, TIntrpol, VIntrpol); - setVec(DoubleFeatureData, StringData, "V", VIntrpol); - setVec(DoubleFeatureData, StringData, "T", TIntrpol); - } catch (...) { - return -1; // interpolation failed - } - // also interpolate current if present - vector I, IIntrpol, TIntrpolI; - try { - I = getFeature(DoubleFeatureData, "I"); - LinearInterpolation(InterpStep, T, I, TIntrpolI, IIntrpol); - setVec(DoubleFeatureData, StringData, "I", IIntrpol); - setVec(DoubleFeatureData, StringData, "T", TIntrpol); - } catch (...) { - } // pass, it is optional - return 1; -} - - -int LibV1::peak_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); - vector peakV; - for (const auto& index : intFeatures.at("peak_indices")) { - peakV.push_back(doubleFeatures.at("V")[index]); - } - setVec(DoubleFeatureData, StringData, "peak_voltage", peakV); - return peakV.size(); -} - -int LibV1::firing_rate(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time", "stim_start", "stim_end"}); - double lastAPTime = 0.; - int nCount = 0; - for (const auto& time : doubleFeatures.at("peak_time")) { - if ((time >= doubleFeatures.at("stim_start")[0]) && - (time <= doubleFeatures.at("stim_end")[0])) { - lastAPTime = time; - nCount++; - } - } - if (lastAPTime == doubleFeatures.at("stim_start")[0]) - throw FeatureComputationError("Prevent divide by zero."); - vector firing_rate; - firing_rate.push_back(nCount * 1000 / - (lastAPTime - doubleFeatures.at("stim_start")[0])); - setVec(DoubleFeatureData, StringData, "mean_frequency", firing_rate); - return firing_rate.size(); -} - -int LibV1::peak_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); - vector pvTime; - for (const auto& index : intFeatures.at("peak_indices")) { - pvTime.push_back(doubleFeatures.at("T")[index]); - } - setVec(DoubleFeatureData, StringData, "peak_time", pvTime); - return pvTime.size(); -} - -// time from stimulus start to first threshold crossing -int LibV1::first_spike_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); - if (doubleFeatures.at("peak_time").size() < 1) - throw FeatureComputationError("One spike required for time_to_first_spike."); - vector first_spike; - first_spike.push_back(doubleFeatures.at("peak_time")[0] - - doubleFeatures.at("stim_start")[0]); - setVec(DoubleFeatureData, StringData, "time_to_first_spike", first_spike); - return first_spike.size(); -} - -int LibV1::AP_height(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_voltage"}); - setVec(DoubleFeatureData, StringData, "AP_height", - doubleFeatures.at("peak_voltage")); - return doubleFeatures.at("peak_voltage").size(); -} - -// spike amplitude: peak_voltage - v[AP_begin_indices] -int LibV1::AP_amplitude(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, - {"V", "stim_start", "stim_end", "peak_voltage", "peak_time"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); - if (doubleFeatures.at("peak_voltage").size() != doubleFeatures.at("peak_time").size()) - throw FeatureComputationError("AP_amplitude: Not the same amount of peak_time and peak_voltage entries"); - vector peakvoltage_duringstim; - for (size_t i = 0; i < doubleFeatures.at("peak_time").size(); i++) { - if (doubleFeatures.at("peak_time")[i] >= - doubleFeatures.at("stim_start")[0] && - doubleFeatures.at("peak_time")[i] <= doubleFeatures.at("stim_end")[0]) { - peakvoltage_duringstim.push_back(doubleFeatures.at("peak_voltage")[i]); - } - } - if (peakvoltage_duringstim.size() > intFeatures.at("AP_begin_indices").size()) - throw FeatureComputationError("AP_amplitude: More peak_voltage entries during the stimulus than AP_begin_indices entries"); - vector apamplitude; - apamplitude.resize(peakvoltage_duringstim.size()); - for (size_t i = 0; i < apamplitude.size(); i++) { - apamplitude[i] = - peakvoltage_duringstim[i] - - doubleFeatures.at("V")[intFeatures.at("AP_begin_indices")[i]]; - } - setVec(DoubleFeatureData, StringData, "AP_amplitude", apamplitude); - return apamplitude.size(); -} - -// *** AHP_depth_abs_slow *** -// same as AHP_depth_abs but the minimum search starts -// 5 ms (or custom duration) after the spike, -// first ISI is ignored -static int __AHP_depth_abs_slow_indices(const vector& t, - const vector& v, - const vector& peakindices, - double sahp_start, - vector& adas_indices) { - adas_indices.resize(peakindices.size() - 2); - for (size_t i = 0; i < adas_indices.size(); i++) { - // start 5 ms (or custom duration) after last spike - double t_start = t[peakindices[i + 1]] + sahp_start; - adas_indices[i] = distance( - v.begin(), - min_element(v.begin() + distance(t.begin(), - find_if(t.begin() + peakindices[i + 1], - t.begin() + peakindices[i + 2], - [t_start](double val) { - return val >= t_start; - })), - v.begin() + peakindices[i + 2])); - } - return adas_indices.size(); -} - -int LibV1::AHP_depth_abs_slow(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "sahp_start"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); - if (intFeatures.at("peak_indices").size() < 3) - throw FeatureComputationError("At least 3 spikes needed for AHP_depth_abs_slow and AHP_slow_time."); - double sahp_start = (doubleFeatures.at("sahp_start").empty()) - ? 5 - : doubleFeatures.at("sahp_start")[0]; - vector adas_indices; - int retval = __AHP_depth_abs_slow_indices( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), sahp_start, adas_indices); - vector ahpdepthabsslow(adas_indices.size()); - vector ahpslowtime(adas_indices.size()); - for (size_t i = 0; i < adas_indices.size(); i++) { - ahpdepthabsslow[i] = doubleFeatures.at("V")[adas_indices[i]]; - ahpslowtime[i] = - (doubleFeatures.at("T")[adas_indices[i]] - - doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 1]]) / - (doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 2]] - - doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 1]]); - } - if (retval >= 0) { - setVec(DoubleFeatureData, StringData, "AHP_depth_abs_slow", - ahpdepthabsslow); - setVec(DoubleFeatureData, StringData, "AHP_slow_time", ahpslowtime); - } - return retval; -} -// end of AHP_depth_abs_slow - -int LibV1::AHP_slow_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return -1; -} - -static int __adaptation_index(double spikeSkipf, int maxnSpike, - double StimStart, double StimEnd, double Offset, - const vector& peakVTime, - vector& adaptation_index) { - list SpikeTime; - vector ISI; - // Select spike time between given time scale (stim_start and stim_end ) - // consider Offset also if it is given as input - for (size_t i = 0; i < peakVTime.size(); i++) { - if ((peakVTime[i] >= (StimStart - Offset)) && - (peakVTime[i] <= (StimEnd + Offset))) { - SpikeTime.push_back(peakVTime[i]); - } - } - // Remove n spikes given by spike_skipf or max_spike_skip - int spikeToRemove = (int)((SpikeTime.size() * spikeSkipf) + 0.5); - // spike To remove is minimum of spike_skipf or max_spike_skip - if (maxnSpike < spikeToRemove) { - spikeToRemove = maxnSpike; - } - - // Remove spikeToRemove spike from SpikeTime list - for (int i = 0; i < spikeToRemove; ++i) { - SpikeTime.pop_front(); - } - - // Adaptation index can not be calculated if nAPVec <4 or no of ISI is <3 - if (SpikeTime.size() < 4) - throw FeatureComputationError("Minimum 4 spikes needed for feature [adaptation_index]."); - - // Generate ISI vector - list::iterator lstItr = SpikeTime.begin(); - double lastValue = *lstItr; - for (lstItr++; lstItr != SpikeTime.end(); lstItr++) { - ISI.push_back(*lstItr - lastValue); - lastValue = *lstItr; - } - - // get addition and subtraction of ISIs - double ISISum, ISISub, ADI; - ADI = ISISum = ISISub = 0; - for (size_t i = 1; i < ISI.size(); i++) { - ISISum = ISI[i] + ISI[i - 1]; - ISISub = ISI[i] - ISI[i - 1]; - ADI = ADI + (ISISub / ISISum); - } - ADI = ADI / (ISI.size() - 1); - adaptation_index.clear(); - adaptation_index.push_back(ADI); - return 1; -} - -int LibV1::adaptation_index(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, - {"peak_time", "stim_start", "stim_end", "spike_skipf"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"max_spike_skip"}); - - if (doubleFeatures.at("spike_skipf")[0] < 0 || - doubleFeatures.at("spike_skipf")[0] >= 1) { - throw FeatureComputationError("spike_skipf should lie between [0 1)."); - } - vector OffSetVec; - double Offset; - int retval = getParam(DoubleFeatureData, "offset", OffSetVec); - if (retval < 0) { - Offset = 0; // Keep old behavior, set Offset to 0 if offset is not found - } else { - Offset = OffSetVec[0]; // Use the first element of OffSetVec if found - } - - vector adaptation_index; - int retVal = __adaptation_index( - doubleFeatures.at("spike_skipf")[0], intFeatures.at("max_spike_skip")[0], - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], - Offset, doubleFeatures.at("peak_time"), adaptation_index); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "adaptation_index", adaptation_index); - } - return retVal; -} - -// *** adaptation_index2 *** -// as adaptation_index, but start at the second ISI instead of the round(N * -// spikeskipf) -static int __adaptation_index2(double StimStart, double StimEnd, double Offset, - const vector& peakVTime, - vector& adaptation_index) { - list SpikeTime; - vector ISI; - // Select spike time between given time scale (stim_start and stim_end ) - // considet Offset also if it is given as input - for (size_t i = 0; i < peakVTime.size(); i++) { - if ((peakVTime[i] >= (StimStart - Offset)) && - (peakVTime[i] <= (StimEnd + Offset))) { - SpikeTime.push_back(peakVTime[i]); - } - } - - if (SpikeTime.size() < 4) { - throw FeatureComputationError("At least 4 spikes within stimulus interval needed for adaptation_index2."); - } - // start at second ISI: - SpikeTime.pop_front(); - - // Generate ISI vector - list::iterator lstItr = SpikeTime.begin(); - double lastValue = *lstItr; - for (++lstItr; lstItr != SpikeTime.end(); ++lstItr) { - ISI.push_back(*lstItr - lastValue); - lastValue = *lstItr; - } - - // get addition and subtraction of ISIs - double ISISum, ISISub, ADI; - ADI = ISISum = ISISub = 0; - for (size_t i = 1; i < ISI.size(); i++) { - ISISum = ISI[i] + ISI[i - 1]; - ISISub = ISI[i] - ISI[i - 1]; - ADI = ADI + (ISISub / ISISum); - } - ADI = ADI / (ISI.size() - 1); - adaptation_index.clear(); - adaptation_index.push_back(ADI); - return 1; -} - -int LibV1::adaptation_index2(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time", "stim_start", "stim_end"}); - vector OffSetVec; - double Offset; - int retval = getParam(DoubleFeatureData, "offset", OffSetVec); - if (retval < 0) - Offset = 0; - else - Offset = OffSetVec[0]; - - if (doubleFeatures.at("peak_time").size() < 4) { - throw FeatureComputationError("At least 4 spikes needed for adaptation_index2."); - } - - vector adaptationindex2; - retval = __adaptation_index2( - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], - Offset, doubleFeatures.at("peak_time"), adaptationindex2); - - if (retval >= 0) { - setVec(DoubleFeatureData, StringData, "adaptation_index2", - adaptationindex2); - } - return retval; -} - -// end of adaptation_index2 - -// To find spike width using Central difference derivative vec1[i] = -// ((vec[i+1]+vec[i-1])/2)/dx and half width is between -// MinAHP and APThreshold -static int __spike_width2(const vector& t, const vector& V, - const vector& PeakIndex, - const vector& minAHPIndex, - vector& spike_width2) { - vector v, dv1, dv2; - double dx = t[1] - t[0]; - double VoltThreshold, VoltMax, HalfV, T0, V0, V1, fraction, TStart, TEnd; - for (size_t i = 0; i < minAHPIndex.size() && i < PeakIndex.size() - 1; i++) { - v.clear(); - dv1.clear(); - dv2.clear(); - - for (int j = minAHPIndex[i]; j <= PeakIndex[i + 1]; j++) { - if (j < 0) { - throw FeatureComputationError("Invalid index"); - } - v.push_back(V[j]); - } - - getCentralDifferenceDerivative(dx, v, dv1); - getCentralDifferenceDerivative(dx, dv1, dv2); - double dMax = dv2[0]; - - size_t index = 0; - for (size_t j = 1; j < dv2.size(); ++j) { - if (dMax <= dv2[j]) { - dMax = dv2[j]; - index = j; - } - } - - // Take voltage at point where double derivative is maximum - index += minAHPIndex[i]; - VoltThreshold = V[index]; - VoltMax = V[PeakIndex[i + 1]]; - HalfV = (VoltMax + VoltThreshold) / 2; - - // Find voltage where it crosses HalfV in the rising phase of action - // potential - for (size_t j = 0; j < v.size(); ++j) { - if (v[j] > HalfV) { // point is found where v is crossing HalfV - index = minAHPIndex[i] + j; - break; - } - } - - // index is the index where v crossed HalfV now use linear interpolation to - // find actual time at HalfV - T0 = t[index - 1]; - V0 = V[index - 1]; - V1 = V[index]; - fraction = (HalfV - V0) / (V1 - V0); - TStart = T0 + (fraction * dx); - - // Find voltage where it crosses HalfV in the falling phase of the action - // potential - for (size_t j = PeakIndex[i + 1]; j < V.size(); j++) { - if (V[j] < HalfV) { - index = j; - break; - } - } - - if (index == V.size()) { - throw FeatureComputationError("Falling phase of last spike is missing."); - } - - T0 = t[index - 1]; - V0 = V[index - 1]; - V1 = V[index]; - fraction = (HalfV - V0) / (V1 - V0); - TEnd = T0 + (fraction * dx); - - spike_width2.push_back(TEnd - TStart); - } - - return spike_width2.size(); -} - -int LibV1::spike_width2(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); - if (intFeatures.at("peak_indices").size() <= 1) { - throw FeatureComputationError("More than one spike is needed for spikewidth2 calculation."); - } - vector spike_width2; - int retVal = __spike_width2(doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), - intFeatures.at("min_AHP_indices"), spike_width2); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "spike_width2", spike_width2); - } - return retVal; -} - -// passive properties implementation -// -// *** timeconstant *** -// requires hyperpolarizing stimulus -// -// the exponential fit works iteratively -// -static int __time_constant(const vector& v, const vector& t, - double stimStart, double stimEnd, - vector& tc) { - // value of the derivative near the minimum - double min_derivative = 5e-3; - // minimal required length of the decay (indices) - size_t min_length = 10; - // minimal required time length in ms - int t_length = 70; - size_t stimstartindex; - for (stimstartindex = 0; t[stimstartindex] < stimStart; stimstartindex++) - ; - // increment stimstartindex to skip a possible transient - stimstartindex += 10; - // int stimendindex; - // for(stimendindex = 0; t[stimendindex] < stimEnd; stimendindex++) ; - // int stimmiddleindex = (stimstartindex + stimendindex) / 2; - double mid_stim = (stimStart + stimEnd) / 2.; - auto it_mid_stim = - find_if(t.begin() + stimstartindex, t.end(), - [mid_stim](double val) { return val >= mid_stim; }); - int stimmiddleindex = distance(t.begin(), it_mid_stim); - - if (stimstartindex >= v.size() || stimmiddleindex < 0 || - static_cast(stimmiddleindex) >= v.size()) { - return -1; - } - vector part_v(&v[stimstartindex], &v[stimmiddleindex]); - - vector part_t(&t[stimstartindex], &t[stimmiddleindex]); - vector dv; - vector dt; - vector dvdt(part_t.size()); - // calculate |dV/dt12 - // getCentralDifferenceDerivative(1.,part_v,dv); - // getCentralDifferenceDerivative(1.,part_t,dt); - getfivepointstencilderivative(part_v, dv); - getfivepointstencilderivative(part_t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - // find start of the decay - int i_start = 0; - while (find_if(dvdt.begin() + i_start, dvdt.begin() + i_start + 5, - [min_derivative](double val) { - return val > -min_derivative; - }) != dvdt.begin() + i_start + 5) { - if (dvdt.begin() + i_start + 5 == dvdt.end()) { - throw FeatureComputationError("Could not find the decay."); - } - i_start++; - } - // find the flat - // bool foundflat = false; - int i_flat; - for (i_flat = i_start; - t[i_flat + stimstartindex] < t[stimmiddleindex] - t_length; i_flat++) { - if (dvdt[i_flat] + min_derivative > 0.) { - double sum = 0.; - int length = 0; - for (int i_mean = 0; - t[i_flat + i_mean + stimstartindex] - t[i_flat + stimstartindex] < - t_length; - i_mean++) { - sum += dvdt[i_flat + i_mean]; - length++; - } - double mean = sum / (double)length; - if (mean + min_derivative > 0.) { - // foundflat = true; - break; - } - } - } - // if(!foundflat) { - // GErrorStr += "\nCould not locate plateau within range.\n"; - // return -1; - //} - // containing the flat: - vector dvdt_decay(dvdt.begin() + i_start, dvdt.begin() + i_flat); - vector t_decay(part_t.begin() + i_start, part_t.begin() + i_flat); - vector v_decay(part_v.begin() + i_start, part_v.begin() + i_flat); - if (dvdt_decay.size() < min_length) { - throw FeatureComputationError("Trace fall time too short."); - } - - // fit to exponential - // - vector log_v(dvdt_decay.size(), 0.); - - // golden section search algorithm - const double PHI = 1.618033988; - vector x(3, .0); - // time_constant is searched in between 0 and 1000 ms - x[2] = min_derivative * 1000.; - x[1] = (x[0] * PHI + x[2]) / (1. + PHI); - // calculate residuals at x[1] - for (size_t i = 0; i < log_v.size(); i++) { - log_v[i] = log(v_decay[i] - v_decay.back() + x[1]); - } - - linear_fit_result fit; - fit = slope_straight_line_fit(t_decay, log_v); - double residuum = fit.normalized_std; - bool right = true; - double newx; - while (x[2] - x[0] > .01) { - // calculate new x value according to the golden section - if (right) { - newx = (x[1] * PHI + x[2]) / (1. + PHI); - } else { - newx = (x[0] + PHI * x[1]) / (1. + PHI); - } - // calculate residuals at newx - for (size_t i = 0; i < log_v.size(); i++) { - log_v[i] = log(v_decay[i] - v_decay.back() + newx); - } - fit = slope_straight_line_fit(t_decay, log_v); - - if (fit.normalized_std < residuum) { - if (right) { - x[0] = x[1]; - x[1] = newx; - } else { - x[2] = x[1]; - x[1] = newx; - } - residuum = fit.normalized_std; - } else { - if (right) { - x[2] = newx; - } else { - x[0] = newx; - } - right = !right; - } - } - tc.push_back(-1. / fit.slope); - return 1; -} -int LibV1::time_constant(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - vector tc; - int retVal = __time_constant(doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0], tc); - if (retVal >= 0) { - setVec(DoubleFeatureData, StringData, "time_constant", tc); - } - return retVal; -} - -// *** voltage deflection *** - -static int __voltage_deflection(const vector& v, - const vector& t, double stimStart, - double stimEnd, vector& vd) { - const size_t window_size = 5; - - size_t stimendindex = 0; - double base = 0.; - int base_size = 0; - for (size_t i = 0; i < t.size(); i++) { - if (t[i] < stimStart) { - base += v[i]; - base_size++; - } - if (t[i] > stimEnd) { - stimendindex = (int)i; - break; - } - } - if (base_size == 0) return -1; - base /= base_size; - double wind_mean = 0.; - if (!(stimendindex >= 2 * window_size && v.size() > 0 && - stimendindex > window_size && stimendindex - window_size < v.size())) { - return -1; - } - for (size_t i = stimendindex - 2 * window_size; - i < stimendindex - window_size; i++) { - wind_mean += v[i]; - } - wind_mean /= window_size; - vd.push_back(wind_mean - base); - return 1; -} - -int LibV1::voltage_deflection(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - vector vd; - int retVal = __voltage_deflection( - doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], vd); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "voltage_deflection", vd); - } - return retVal; -} - -// *** ohmic input resistance *** - -static int __ohmic_input_resistance(double voltage_deflection, - double stimulus_current, - vector& oir) { - if (stimulus_current == 0) - throw FeatureComputationError("Stimulus current is zero which will result in division by zero."); - oir.push_back(voltage_deflection / stimulus_current); - return 1; -} - -int LibV1::ohmic_input_resistance(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"voltage_deflection", "stimulus_current"}); - vector oir; - int retVal = - __ohmic_input_resistance(doubleFeatures.at("voltage_deflection")[0], - doubleFeatures.at("stimulus_current")[0], oir); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "ohmic_input_resistance", oir); - } - return retVal; -} - -static int __maxmin_voltage(const vector& v, const vector& t, - double stimStart, double stimEnd, - vector& maxV, vector& minV) { - if (stimStart > t[t.size() - 1]) - throw FeatureComputationError("Stimulus start larger than max time in trace"); - - if (stimEnd > t[t.size() - 1]) stimEnd = t.back(); - - size_t stimstartindex = 0; - while (t[stimstartindex] < stimStart && stimstartindex <= t.size()) - stimstartindex++; - - if (stimstartindex >= t.size()) { - throw FeatureComputationError("Stimulus start index not found"); - } - - size_t stimendindex = 0; - while (t[stimendindex] < stimEnd && stimstartindex <= t.size()) - stimendindex++; - - if (stimendindex >= t.size()) { - throw FeatureComputationError("Stimulus end index not found"); - } - - maxV.push_back(*max_element(&v[stimstartindex], &v[stimendindex])); - minV.push_back(*min_element(&v[stimstartindex], &v[stimendindex])); - - return 1; -} - -int LibV1::maximum_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - vector maxV, minV; - int retVal = __maxmin_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0], maxV, minV); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "maximum_voltage", maxV); - } - return retVal; -} - -// *** maximum voltage *** -// -int LibV1::minimum_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - vector maxV, minV; - int retVal = __maxmin_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0], maxV, minV); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "minimum_voltage", minV); - } - return retVal; -} - -// *** steady state voltage *** -static int __steady_state_voltage(const vector& v, - const vector& t, double stimEnd, - vector& ssv) { - int mean_size = 0; - double mean = 0; - for (int i = t.size() - 1; t[i] > stimEnd; i--) { - mean += v[i]; - mean_size++; - } - mean /= mean_size; - ssv.push_back(mean); - return 1; -} - -int LibV1::steady_state_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_end"}); - if (doubleFeatures.at("stim_end").size() != 1) return -1; - - vector ssv; - int retVal = - __steady_state_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_end")[0], ssv); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "steady_state_voltage", ssv); - } - return retVal; -} - - -// *** AP_width *** -// -// spike width calculation according to threshold value for the first spike -// unfortunately spike width means the width of the spike on onset not at half -// maximum -static int __AP_width(const vector& t, const vector& v, - double stimstart, double stimend, double threshold, - const vector& peakindices, - const vector& minahpindices, - const bool strict_stiminterval, vector& apwidth) { - // printf("\n Inside AP_width...\n"); - // printf("\nStimStart = %f , thereshold = %f ", stimstart, threshold); - vector indices; - if (strict_stiminterval) { - int start_index = distance( - t.begin(), find_if(t.begin(), t.end(), - [stimstart](double x) { return x >= stimstart; })); - int end_index = distance( - t.begin(), find_if(t.begin(), t.end(), - [stimend](double x) { return x >= stimend; })); - indices.push_back(start_index); - for (size_t i = 0; i < minahpindices.size(); i++) { - if (start_index < minahpindices[i] && minahpindices[i] < end_index) { - indices.push_back(minahpindices[i]); - } - } - } else { - indices.push_back(0); - for (size_t i = 0; i < minahpindices.size(); i++) { - indices.push_back(minahpindices[i]); - } - } - for (size_t i = 0; i < indices.size() - 1; i++) { - /* - // FWHM (not used): - // half maximum - double v_hm = (v[peakindices[i]] + threshold) / 2.; - // half maximum indices - int hm_index1 = distance(v.begin(), find_if(v.begin() + indices[i], - v.begin() + indices[i + 1], bind2nd(greater_equal(), v_hm))); - int hm_index2 = distance(v.begin(), find_if(v.begin() + peakindices[i], - v.begin() + indices[i + 1], bind2nd(less_equal(), v_hm))); - apwidth.push_back(t[hm_index2] - t[hm_index1]); - */ - auto onset_index = distance( - v.begin(), find_if(v.begin() + indices[i], v.begin() + indices[i + 1], - [threshold](double x) { return x >= threshold; })); - // int end_index = distance(v.begin(), find_if(v.begin() + peakindices[i], - // v.begin() + indices[i + 1], bind2nd(less_equal(), threshold))); - auto end_index = distance( - v.begin(), find_if(v.begin() + onset_index, v.begin() + indices[i + 1], - [threshold](double x) { return x <= threshold; })); - apwidth.push_back(t[end_index] - t[onset_index]); - } - return apwidth.size(); -} - -int LibV1::AP_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"T", "V", "Threshold", "stim_start", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, - {"peak_indices", "min_AHP_indices", "strict_stiminterval"}); - bool strict_stiminterval = - intFeatures.at("strict_stiminterval").size() > 0 - ? bool(intFeatures.at("strict_stiminterval")[0]) - : false; - vector apwidth; - int retval = __AP_width( - doubleFeatures.at("T"), doubleFeatures.at("V"), - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], - doubleFeatures.at("Threshold")[0], intFeatures.at("peak_indices"), - intFeatures.at("min_AHP_indices"), strict_stiminterval, apwidth); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_width", apwidth); - } - return retval; -} // end of AP_width - -// *** doublet_ISI *** -// value of the first ISI -int LibV1::doublet_ISI(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_time"}); - if (doubleFeatures.at("peak_time").size() < 2) { - throw FeatureComputationError("Need at least two spikes for doublet_ISI."); - } - vector doubletisi( - 1, doubleFeatures.at("peak_time")[1] - doubleFeatures.at("peak_time")[0]); - setVec(DoubleFeatureData, StringData, "doublet_ISI", doubletisi); - return doubleFeatures.at("peak_time").size(); -} - -// *** AHP_depth_slow *** -static int __AHP_depth_slow(const vector& voltagebase, - const vector& minahpvalues, - vector& ahpdepth) { - for (size_t i = 0; i < minahpvalues.size(); i++) { - ahpdepth.push_back(minahpvalues[i] - voltagebase[0]); - } - return ahpdepth.size(); -} - -int LibV1::AHP_depth_slow(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"voltage_base", "AHP_depth_abs_slow"}); - vector ahpdepthslow; - int retval = - __AHP_depth_slow(doubleFeatures.at("voltage_base"), - doubleFeatures.at("AHP_depth_abs_slow"), ahpdepthslow); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AHP_depth_slow", ahpdepthslow); - } - return retval; -} - -// *** AHP_depth *** -static int __AHP_depth(const vector& voltagebase, - const vector& minahpvalues, - vector& ahpdepth) { - for (size_t i = 0; i < minahpvalues.size(); i++) { - ahpdepth.push_back(minahpvalues[i] - voltagebase[0]); - } - return ahpdepth.size(); -} -int LibV1::AHP_depth(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"voltage_base", "min_AHP_values"}); - vector ahpdepth; - int retval = __AHP_depth(doubleFeatures.at("voltage_base"), - doubleFeatures.at("min_AHP_values"), ahpdepth); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AHP_depth", ahpdepth); - } - return retval; -} - -// *** AP_amplitude_diff based on AP_amplitude_change but not normalized *** -static int __AP_amplitude_diff(const vector& apamplitude, - vector& apamplitudediff) { - if (apamplitude.size() <= 1) return -1; - apamplitudediff.resize(apamplitude.size() - 1); - for (size_t i = 0; i < apamplitudediff.size(); i++) { - apamplitudediff[i] = (apamplitude[i + 1] - apamplitude[i]); - } - return apamplitudediff.size(); -} - -int LibV1::AP_amplitude_diff(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"AP_amplitude"}); - vector apamplitudediff; - int retval = - __AP_amplitude_diff(doubleFeatures.at("AP_amplitude"), apamplitudediff); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_amplitude_diff", apamplitudediff); - } - return retval; -} - -// *** AHP_depth_diff, returns AHP_depth[i+1] - AHP_depth[i] *** -static int __AHP_depth_diff(const vector& ahpdepth, - vector& ahpdepthdiff) { - if (ahpdepth.size() <= 1) return -1; - ahpdepthdiff.resize(ahpdepth.size() - 1); - for (size_t i = 0; i < ahpdepthdiff.size(); i++) { - ahpdepthdiff[i] = (ahpdepth[i + 1] - ahpdepth[i]); - } - return ahpdepthdiff.size(); -} -int LibV1::AHP_depth_diff(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"AHP_depth"}); - vector ahpdepthdiff; - int retval = __AHP_depth_diff(doubleFeatures.at("AHP_depth"), ahpdepthdiff); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AHP_depth_diff", ahpdepthdiff); - } - return retval; -} // end of feature definition diff --git a/efel/cppcore/LibV2.cpp b/efel/cppcore/LibV2.cpp index ee5f1e9a..17a28d39 100644 --- a/efel/cppcore/LibV2.cpp +++ b/efel/cppcore/LibV2.cpp @@ -32,689 +32,10 @@ using std::transform; // AP parameters // -// *** AP rise indices *** -// -static int __AP_rise_indices(const vector& v, const vector& apbi, - const vector& pi, vector& apri) { - apri.resize(std::min(apbi.size(), pi.size())); - for (size_t i = 0; i < apri.size(); i++) { - double halfheight = (v[pi[i]] + v[apbi[i]]) / 2.; - vector vpeak; - if (pi[i] < apbi[i]) { - // For some reason the peak and begin indices are out of sync - // Peak should always be later than begin index - return -1; - } - vpeak.resize(pi[i] - apbi[i]); - transform(v.begin() + apbi[i], v.begin() + pi[i], vpeak.begin(), - [halfheight](double val) { return fabs(val - halfheight); }); - apri[i] = distance(vpeak.begin(), min_element(vpeak.begin(), vpeak.end())) + - apbi[i]; - } - return apri.size(); -} -int LibV2::AP_rise_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); - vector apriseindices; - int retval = __AP_rise_indices(doubleFeatures.at("V"), - intFeatures.at("AP_begin_indices"), - intFeatures.at("peak_indices"), apriseindices); - if (retval > 0) { - setVec(IntFeatureData, StringData, "AP_rise_indices", apriseindices); - } - return retval; -} - -// *** AP fall indices *** -// -static int __AP_fall_indices(const vector& v, const vector& apbi, - const vector& apei, const vector& pi, - vector& apfi) { - apfi.resize(std::min(apbi.size(), pi.size())); - for (size_t i = 0; i < apfi.size(); i++) { - if (pi[i] >= v.size() || apbi[i] >= v.size() || apei[i] >= v.size() || pi[i] > apei[i]) { - continue; - } - double halfheight = (v[pi[i]] + v[apbi[i]]) / 2.; - vector vpeak(&v[pi[i]], &v[apei[i]]); - transform(vpeak.begin(), vpeak.end(), vpeak.begin(), - [halfheight](double val) { return fabs(val - halfheight); }); - apfi[i] = distance(vpeak.begin(), min_element(vpeak.begin(), vpeak.end())) + - pi[i]; - } - return apfi.size(); -} -int LibV2::AP_fall_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = getFeatures( - IntFeatureData, {"AP_begin_indices", "AP_end_indices", "peak_indices"}); - vector apfallindices; - int retval = __AP_fall_indices(doubleFeatures.at("V"), - intFeatures.at("AP_begin_indices"), - intFeatures.at("AP_end_indices"), - intFeatures.at("peak_indices"), apfallindices); - if (retval > 0) { - setVec(IntFeatureData, StringData, "AP_fall_indices", apfallindices); - } - return retval; -} - -// eFeatures -// *** AP_duration according to E7 and E15 *** -static int __AP_duration(const vector& t, - const vector& apbeginindices, - const vector& endindices, - vector& apduration) { - apduration.resize(std::min(apbeginindices.size(), endindices.size())); - for (size_t i = 0; i < apduration.size(); i++) { - // printf("%d, %d, %d\n", t.size(), apbeginindices.size(), - // endindices.size()) - apduration[i] = t[endindices[i]] - t[apbeginindices[i]]; - } - return apduration.size(); -} -int LibV2::AP_duration(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "AP_end_indices"}); - - vector apduration; - int retval = - __AP_duration(doubleFeatures.at("T"), intFeatures.at("AP_begin_indices"), - intFeatures.at("AP_end_indices"), apduration); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_duration", apduration); - } - return retval; -} - -// *** AP_duration_half_width according to E8 and E16 *** -static int __AP_duration_half_width(const vector& t, - const vector& apriseindices, - const vector& apfallindices, - vector& apdurationhalfwidth) { - apdurationhalfwidth.resize(apriseindices.size()); - for (size_t i = 0; i < apdurationhalfwidth.size(); i++) { - apdurationhalfwidth[i] = t[apfallindices[i]] - t[apriseindices[i]]; - } - return apdurationhalfwidth.size(); -} -int LibV2::AP_duration_half_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Fetching all required features in one go. - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_rise_indices", "AP_fall_indices"}); - - vector apdurationhalfwidth; - int retval = __AP_duration_half_width( - doubleFeatures.at("T"), intFeatures.at("AP_rise_indices"), - intFeatures.at("AP_fall_indices"), apdurationhalfwidth); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_duration_half_width", - apdurationhalfwidth); - } - return retval; -} - -// *** AP_rise_time according to E9 and E17 *** -static int __AP_rise_time(const vector& t, const vector& v, - const vector& apbeginindices, - const vector& peakindices, - double beginperc, double endperc, - vector& aprisetime) { - aprisetime.resize(std::min(apbeginindices.size(), peakindices.size())); - // Make sure that we do not use peaks starting before the 1st AP_begin_index - // Because AP_begin_indices only takes into account peaks after stimstart - vector newpeakindices; - if (apbeginindices.size() > 0) { - newpeakindices = peaks_after_stim_start(apbeginindices[0], peakindices); - } - double begin_v; - double end_v; - double begin_indice; - double end_indice; - double apamplitude; - for (size_t i = 0; i < aprisetime.size(); i++) { - // do not use AP_amplitude feature because it does not take into account - // peaks after stim_end - apamplitude = v[newpeakindices[i]] - v[apbeginindices[i]]; - begin_v = v[apbeginindices[i]] + beginperc * apamplitude; - end_v = v[apbeginindices[i]] + endperc * apamplitude; - - // Get begin indice - size_t j = apbeginindices[i]; - // change slightly begin_v for almost equal case - // truncature error can change begin_v even when beginperc == 0.0 - while (j < newpeakindices[i] && v[j] < begin_v - 0.0000000000001){ - j++; - } - begin_indice = j; - - // Get end indice - j = newpeakindices[i]; - // change slightly end_v for almost equal case - // truncature error can change end_v even when beginperc == 0.0 - while (j > apbeginindices[i] && v[j] > end_v + 0.0000000000001) { - j--; - } - end_indice = j; - - aprisetime[i] = t[end_indice] - t[begin_indice]; - } - return aprisetime.size(); -} -int LibV2::AP_rise_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Fetching all required features in one go. - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, - {"T", "V", "rise_start_perc", "rise_end_perc"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); - vector aprisetime; - int retval = __AP_rise_time( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("AP_begin_indices"), intFeatures.at("peak_indices"), - doubleFeatures.at("rise_start_perc").empty() - ? 0.0 - : doubleFeatures.at("rise_start_perc").front(), - doubleFeatures.at("rise_end_perc").empty() - ? 1.0 - : doubleFeatures.at("rise_end_perc").front(), - aprisetime); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_rise_time", aprisetime); - } - return retval; -} - -// *** AP_fall_time according to E10 and E18 *** -static int __AP_fall_time(const vector& t, - const double stimstart, - const vector& peakindices, - const vector& apendindices, - vector& apfalltime) { - apfalltime.resize(std::min(peakindices.size(), apendindices.size())); - // Make sure that we do not use peaks starting before stim start - // Because AP_end_indices only takes into account peaks after stim start - vector newpeakindices = peaks_after_stim_start(stimstart, peakindices, t); - - for (size_t i = 0; i < apfalltime.size(); i++) { - apfalltime[i] = t[apendindices[i]] - t[newpeakindices[i]]; - } - return apfalltime.size(); -} -int LibV2::AP_fall_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "stim_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "AP_end_indices"}); - - const vector& t = doubleFeatures.at("T"); - const double stim_start = doubleFeatures.at("stim_start")[0]; - const vector& peakindices = intFeatures.at("peak_indices"); - const vector& apendindices = intFeatures.at("AP_end_indices"); - - vector apfalltime; - int retval = __AP_fall_time(t, stim_start, peakindices, apendindices, apfalltime); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_fall_time", apfalltime); - } - return retval; -} - -// *** AP_rise_rate according to E11 and E19 *** -static int __AP_rise_rate(const vector& t, const vector& v, - const vector& apbeginindices, - const vector& peakindices, - vector& apriserate) { - apriserate.resize(std::min(peakindices.size(), apbeginindices.size())); - vector newpeakindices; - if (apbeginindices.size() > 0) { - newpeakindices = peaks_after_stim_start(apbeginindices[0], peakindices); - } - for (size_t i = 0; i < apriserate.size(); i++) { - apriserate[i] = (v[newpeakindices[i]] - v[apbeginindices[i]]) / - (t[newpeakindices[i]] - t[apbeginindices[i]]); - } - return apriserate.size(); -} -int LibV2::AP_rise_rate(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); - - const vector& t = doubleFeatures.at("T"); - const vector& v = doubleFeatures.at("V"); - const vector& apbeginindices = intFeatures.at("AP_begin_indices"); - const vector& peakindices = intFeatures.at("peak_indices"); - - vector apriserate; - int retval = __AP_rise_rate(t, v, apbeginindices, peakindices, apriserate); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_rise_rate", apriserate); - } - return retval; -} -// end of AP_rise_rate - -// *** AP_fall_rate according to E12 and E20 *** -static int __AP_fall_rate(const vector& t, const vector& v, - const double stimstart, - const vector& peakindices, - const vector& apendindices, - vector& apfallrate) { - apfallrate.resize(std::min(apendindices.size(), peakindices.size())); - vector newpeakindices = peaks_after_stim_start(stimstart, peakindices, t); - - for (size_t i = 0; i < apfallrate.size(); i++) { - apfallrate[i] = (v[apendindices[i]] - v[newpeakindices[i]]) / - (t[apendindices[i]] - t[newpeakindices[i]]); - } - return apfallrate.size(); -} -int LibV2::AP_fall_rate(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V", "stim_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "AP_end_indices"}); - - const vector& t = doubleFeatures.at("T"); - const vector& v = doubleFeatures.at("V"); - const double stim_start = doubleFeatures.at("stim_start")[0]; - const vector& peakindices = intFeatures.at("peak_indices"); - const vector& apendindices = intFeatures.at("AP_end_indices"); - - vector apfallrate; - int retval = __AP_fall_rate(t, v, stim_start, peakindices, apendindices, apfallrate); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_fall_rate", apfallrate); - } - return retval; -} - -// *** fast_AHP according to E13 and E21 *** -static int __fast_AHP(const vector& v, - const vector& apbeginindices, - const vector& minahpindices, - vector& fastahp) { - if (apbeginindices.size() < 1) { - return -1; - } - fastahp.resize(apbeginindices.size() - 1); - for (size_t i = 0; i < fastahp.size(); i++) { - fastahp[i] = v[apbeginindices[i]] - v[minahpindices[i]]; - } - return fastahp.size(); -} -int LibV2::fast_AHP(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "min_AHP_indices"}); - - const vector& v = doubleFeatures.at("V"); - const vector& apbeginindices = intFeatures.at("AP_begin_indices"); - const vector& minahpindices = intFeatures.at("min_AHP_indices"); - - vector fastahp; - int retval = __fast_AHP(v, apbeginindices, minahpindices, fastahp); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "fast_AHP", fastahp); - } - return retval; -} -// *** AP_amplitude_change according to E22 *** -static int __AP_amplitude_change(const vector& apamplitude, - vector& apamplitudechange) { - if (apamplitude.size() < 1) { - return -1; - } - apamplitudechange.resize(apamplitude.size() - 1); - for (size_t i = 0; i < apamplitudechange.size(); i++) { - apamplitudechange[i] = - (apamplitude[i + 1] - apamplitude[0]) / apamplitude[0]; - } - return apamplitudechange.size(); -} -int LibV2::AP_amplitude_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"AP_amplitude"}); - const vector& apamplitude = features.at("AP_amplitude"); - - vector apamplitudechange; - int retval = __AP_amplitude_change(apamplitude, apamplitudechange); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_amplitude_change", - apamplitudechange); - } - return retval; -} - -// *** AP_duration_change according to E23 *** -static int __AP_duration_change(const vector& apduration, - vector& apdurationchange) { - if (apduration.size() < 1) { - return -1; - } - apdurationchange.resize(apduration.size() - 1); - for (size_t i = 0; i < apdurationchange.size(); i++) { - apdurationchange[i] = (apduration[i + 1] - apduration[0]) / apduration[0]; - } - return apdurationchange.size(); -} -int LibV2::AP_duration_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"AP_duration"}); - const vector& apduration = features.at("AP_duration"); - - vector apdurationchange; - int retval = __AP_duration_change(apduration, apdurationchange); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_duration_change", - apdurationchange); - } - return retval; -} -// end of AP_duration_change - -// *** AP_duration_half_width_change according to E24 *** -static int __AP_duration_half_width_change( - const vector& apdurationhalfwidth, - vector& apdurationhalfwidthchange) { - if (apdurationhalfwidth.size() < 1) { - return -1; - } - apdurationhalfwidthchange.resize(apdurationhalfwidth.size() - 1); - for (size_t i = 0; i < apdurationhalfwidthchange.size(); i++) { - apdurationhalfwidthchange[i] = - (apdurationhalfwidth[i + 1] - apdurationhalfwidth[0]) / - apdurationhalfwidth[0]; - } - return apdurationhalfwidthchange.size(); -} -int LibV2::AP_duration_half_width_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = - getFeatures(DoubleFeatureData, {"AP_duration_half_width"}); - const vector& apdurationhalfwidth = - features.at("AP_duration_half_width"); - vector apdurationhalfwidthchange; - int retval = __AP_duration_half_width_change(apdurationhalfwidth, - apdurationhalfwidthchange); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_duration_half_width_change", - apdurationhalfwidthchange); - } - return retval; -} -// end of AP_duration_half_width_change - -// *** AP_rise_rate_change according to E25 *** -static int __AP_rise_rate_change(const vector& apriserate, - vector& apriseratechange) { - if (apriserate.size() < 1) { - return -1; - } - apriseratechange.resize(apriserate.size() - 1); - - for (size_t i = 0; i < apriseratechange.size(); i++) { - apriseratechange[i] = (apriserate[i + 1] - apriserate[0]) / apriserate[0]; - } - return apriseratechange.size(); -} -int LibV2::AP_rise_rate_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"AP_rise_rate"}); - const vector& apriserate = features.at("AP_rise_rate"); - vector apriseratechange; - int retval = __AP_rise_rate_change(apriserate, apriseratechange); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_rise_rate_change", - apriseratechange); - } - return retval; -} -// end of AP_rise_rate_change - -// *** AP_fall_rate_change according to E26 *** -static int __AP_fall_rate_change(const vector& apfallrate, - vector& apfallratechange) { - if (apfallrate.size() < 1) { - return -1; - } - apfallratechange.resize(apfallrate.size() - 1); - for (size_t i = 0; i < apfallratechange.size(); i++) { - apfallratechange[i] = (apfallrate[i + 1] - apfallrate[0]) / apfallrate[0]; - } - return apfallratechange.size(); -} -int LibV2::AP_fall_rate_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"AP_fall_rate"}); - const vector& apfallrate = features.at("AP_fall_rate"); - vector apfallratechange; - int retval = __AP_fall_rate_change(apfallrate, apfallratechange); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_fall_rate_change", - apfallratechange); - } - return retval; -} - -// *** fast_AHP_change according to E27 *** -static int __fast_AHP_change(const vector& fastahp, - vector& fastahpchange) { - if (fastahp.size() < 1) { - return -1; - } - fastahpchange.resize(fastahp.size() - 1); - for (size_t i = 0; i < fastahpchange.size(); i++) { - fastahpchange[i] = (fastahp[i + 1] - fastahp[0]) / fastahp[0]; - } - return fastahpchange.size(); -} -int LibV2::fast_AHP_change(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"fast_AHP"}); - const vector& fastahp = features.at("fast_AHP"); - vector fastahpchange; - int retval = __fast_AHP_change(fastahp, fastahpchange); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "fast_AHP_change", fastahpchange); - } - return retval; -} // end of fast_AHP_change -// *** amp_drop_first_second *** -static int __amp_drop_first_second(const vector& peakvoltage, - vector& ampdropfirstsecond) { - ampdropfirstsecond.push_back(peakvoltage[0] - peakvoltage[1]); - return ampdropfirstsecond.size(); -} -int LibV2::amp_drop_first_second(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"peak_voltage"}); - const vector peakvoltage = features.at("peak_voltage"); - - if (peakvoltage.size() < 2) { - throw FeatureComputationError("At least 2 spikes needed for calculation of amp_drop_first_second."); - } - vector ampdropfirstsecond; - int retval = __amp_drop_first_second(peakvoltage, ampdropfirstsecond); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "amp_drop_first_second", - ampdropfirstsecond); - } - return retval; -} - -// *** amp_drop_first_last *** -static int __amp_drop_first_last(const vector& peakvoltage, - vector& ampdropfirstlast) { - ampdropfirstlast.push_back(peakvoltage[0] - peakvoltage.back()); - return ampdropfirstlast.size(); -} -int LibV2::amp_drop_first_last(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& peakVoltageFeature = - getFeatures(DoubleFeatureData, {"peak_voltage"}); - const vector& peakvoltage = peakVoltageFeature.at("peak_voltage"); - - if (peakvoltage.size() < 2) { - throw FeatureComputationError("At least 2 spikes needed for calculation of amp_drop_first_last."); - } - vector ampdropfirstlast; - int retval = __amp_drop_first_last(peakvoltage, ampdropfirstlast); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "amp_drop_first_last", - ampdropfirstlast); - } - return retval; -} -// end of amp_drop_first_last - -// *** amp_drop_second_last *** -static int __amp_drop_second_last(const vector& peakvoltage, - vector& ampdropsecondlast) { - ampdropsecondlast.push_back(peakvoltage[1] - peakvoltage.back()); - return ampdropsecondlast.size(); -} -int LibV2::amp_drop_second_last(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& peakVoltageFeatures = - getFeatures(DoubleFeatureData, {"peak_voltage"}); - const vector& peakvoltage = peakVoltageFeatures.at("peak_voltage"); - // Ensure there are at least 3 spikes for calculation - if (peakvoltage.size() < 3) { - throw FeatureComputationError("At least 3 spikes needed for calculation of amp_drop_second_last."); - } - vector ampdropsecondlast; - int retval = __amp_drop_second_last(peakvoltage, ampdropsecondlast); - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "amp_drop_second_last", - ampdropsecondlast); - } - return retval; -} - -// *** max_amp_difference *** -static int __max_amp_difference(const vector& peakvoltage, - vector& maxampdifference) { - vector diff_peak_voltage; - if (peakvoltage.size() < 1) { - return -1; - } - diff_peak_voltage.resize(peakvoltage.size() - 1); - for (size_t i = 0; i < diff_peak_voltage.size(); i++) { - diff_peak_voltage[i] = peakvoltage[i] - peakvoltage[i + 1]; - } - maxampdifference.push_back( - *max_element(diff_peak_voltage.begin(), diff_peak_voltage.end())); - return maxampdifference.size(); -} - -int LibV2::max_amp_difference(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& features = getFeatures(DoubleFeatureData, {"peak_voltage"}); - - // Ensure there are at least 2 spikes for calculation - if (features.at("peak_voltage").size() < 2) { - throw FeatureComputationError("At least 2 spikes needed for calculation of max_amp_difference."); - } - vector maxampdifference; - int retval = - __max_amp_difference(features.at("peak_voltage"), maxampdifference); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "max_amp_difference", - maxampdifference); - } - return retval; -} - -// steady state of the voltage response during hyperpolarizing stimulus, -// elementary feature for E29 -// *** steady_state_hyper -static int __steady_state_hyper(const vector& v, - const vector& t, double stimend, - vector& steady_state_hyper) { - // Find the iterator pointing to the first time value greater than or equal to - // stimend - auto it_stimend = find_if( - t.begin(), t.end(), [stimend](double t_val) { return t_val >= stimend; }); - - // Calculate the index, ensuring you account for the offset of -5 - int i_end = distance(t.begin(), it_stimend) - 5; - - const int offset = 30; - if (i_end < 0 || i_end < offset) { - return -1; - } - - size_t i_begin = static_cast(i_end - offset); - - double sum = 0.; - - for (size_t i = i_begin; i < static_cast(i_end); i++) { - sum += v[i]; - } - - double mean = sum / (i_end - i_begin); - steady_state_hyper.push_back(mean); - return 1; -} - -int LibV2::steady_state_hyper(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required features at once - const auto& features = getFeatures(DoubleFeatureData, {"V", "T", "stim_end"}); - - vector steady_state_hyper; - int retval = - __steady_state_hyper(features.at("V"), features.at("T"), - features.at("stim_end").front(), steady_state_hyper); - - if (retval > 0) { - setVec(DoubleFeatureData, StringData, "steady_state_hyper", - steady_state_hyper); - } - return retval; -} // // end of feature definition diff --git a/efel/cppcore/LibV3.cpp b/efel/cppcore/LibV3.cpp index 9af5a602..e17df087 100644 --- a/efel/cppcore/LibV3.cpp +++ b/efel/cppcore/LibV3.cpp @@ -28,57 +28,3 @@ using std::find_if; using std::list; using std::min_element; -static int __depolarized_base(const vector& t, const vector& v, - double stimstart, double stimend, - const vector& apbi, - const vector& apendi, - vector& dep_base) { - int i, n, k, startIndex, endIndex, nPt; - double baseValue; - // to make sure it access minimum index of both length - n = std::min(apendi.size(), apbi.size()); - - if (n > 2) { - dep_base.clear(); - for (i = 0; i < n - 1; i++) { - nPt = 0; - baseValue = 0; - startIndex = apendi[i]; - endIndex = apbi[i + 1]; - for (k = startIndex; k < endIndex; k++) { - if (k >= 0 && k < v.size()) { - baseValue += v[k]; - ++nPt; - } - } - if (nPt > 0) { - baseValue /= nPt; - dep_base.push_back(baseValue); - } - } - return dep_base.size(); - } - return -1; -} - -int LibV3::depolarized_base(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_end_indices", "AP_begin_indices"}); - - vector dep_base; - int retVal = __depolarized_base( - doubleFeatures.at("T"), doubleFeatures.at("V"), - doubleFeatures.at("stim_start").front(), - doubleFeatures.at("stim_end").front(), intFeatures.at("AP_begin_indices"), - intFeatures.at("AP_end_indices"), dep_base); - - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "depolarized_base", dep_base); - } - return retVal; -} diff --git a/efel/cppcore/LibV5.cpp b/efel/cppcore/LibV5.cpp index 55a80a51..ec57e8fb 100644 --- a/efel/cppcore/LibV5.cpp +++ b/efel/cppcore/LibV5.cpp @@ -34,2765 +34,4 @@ using std::distance; using std::find_if; -// time from stimulus start to second threshold crossing -int LibV5::time_to_second_spike(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); - const auto& peaktime = doubleFeatures.at("peak_time"); - const auto& stimstart = doubleFeatures.at("stim_start"); - if (peaktime.size() < 2) - throw FeatureComputationError("Two spikes required for time_to_second_spike."); - vector second_spike = {peaktime[1] - stimstart[0]}; - setVec(DoubleFeatureData, StringData, "time_to_second_spike", second_spike); - return 1; -} - -// time from stimulus start to last spike -int LibV5::time_to_last_spike(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); - const auto& peaktime = doubleFeatures.at("peak_time"); - const auto& stimstart = doubleFeatures.at("stim_start"); - - vector last_spike = {peaktime.back() - stimstart[0]}; - - setVec(DoubleFeatureData, StringData, "time_to_last_spike", last_spike); - return 1; -} - -// 1.0 over time to first spike (in Hz); returns 0 when no spike -int LibV5::inv_time_to_first_spike(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - vector time_to_first_spike_vec = - getFeature(DoubleFeatureData, "time_to_first_spike"); - vector inv_time_to_first_spike_vec; - - double inv_time_to_first_spike = 1000.0 / time_to_first_spike_vec[0]; - inv_time_to_first_spike_vec.push_back(inv_time_to_first_spike); - - setVec(DoubleFeatureData, StringData, "inv_time_to_first_spike", - inv_time_to_first_spike_vec); - return 1; -} - -static int __min_AHP_indices(const vector& t, const vector& v, - const vector& peak_indices, - const double stim_start, const double stim_end, - const bool strict_stiminterval, - vector& min_ahp_indices, - vector& min_ahp_values) { - vector peak_indices_plus = peak_indices; - unsigned end_index = 0; - - if (strict_stiminterval) { - end_index = distance(t.begin(), - find_if(t.begin(), t.end(), [stim_end](double t_val) { - return t_val >= stim_end; - })); - } else { - end_index = distance(t.begin(), t.end()); - } - - size_t ahpindex = 0; - - peak_indices_plus.push_back(end_index); - - for (size_t i = 0; i < peak_indices_plus.size() - 1; i++) { - ahpindex = distance( - v.begin(), first_min_element(v.begin() + peak_indices_plus[i], - v.begin() + peak_indices_plus[i + 1])); - - if (ahpindex != end_index - 1) { - min_ahp_indices.push_back(ahpindex); - - EFEL_ASSERT(ahpindex < v.size(), - "AHP index falls outside of voltage array"); - min_ahp_values.push_back(v[ahpindex]); - } - } - - return min_ahp_indices.size(); -} - -// min_AHP_indices -// find the first minimum between two spikes, -// and the first minimum between the last spike and the time the stimulus ends -int LibV5::min_AHP_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - int retVal; - // Retrieve all required double features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); - - vector min_ahp_indices; - vector min_ahp_values; - double stim_start = doubleFeatures.at("stim_start")[0]; - double stim_end = doubleFeatures.at("stim_end")[0]; - // Get strict_stiminterval - vector strict_stiminterval_vec; - bool strict_stiminterval; - retVal = - getParam(IntFeatureData, "strict_stiminterval", strict_stiminterval_vec); - if (retVal <= 0) { - strict_stiminterval = false; - } else { - strict_stiminterval = bool(strict_stiminterval_vec[0]); - } - - retVal = - __min_AHP_indices(doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), stim_start, stim_end, - strict_stiminterval, min_ahp_indices, min_ahp_values); - - if (retVal == 0) return -1; - if (retVal > 0) { - setVec(IntFeatureData, StringData, "min_AHP_indices", min_ahp_indices); - setVec(DoubleFeatureData, StringData, "min_AHP_values", min_ahp_values); - } - return retVal; -} - -int LibV5::min_AHP_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return -1; -} - -// Difference with LibV1 is that this function doesn't return -1 if there are no -// min_AHP_values -int LibV5::AHP_depth_abs(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& vAHP = getFeature(DoubleFeatureData, "min_AHP_values"); - setVec(DoubleFeatureData, StringData, "AHP_depth_abs", vAHP); - return vAHP.size(); -} - -// spike half width -// for spike amplitude = v_peak - v_AHP -static int __spike_width1(const vector& t, const vector& v, - const vector& peak_indices, - const vector& min_ahp_indices, double stim_start, - vector& spike_width1) { - int start_index = distance( - t.begin(), find_if(t.begin(), t.end(), [stim_start](double t_val) { - return t_val >= stim_start; - })); - vector min_ahp_indices_plus(min_ahp_indices.size() + 1, start_index); - copy(min_ahp_indices.begin(), min_ahp_indices.end(), - min_ahp_indices_plus.begin() + 1); - for (size_t i = 1; i < min_ahp_indices_plus.size(); i++) { - double v_half = (v[peak_indices[i - 1]] + v[min_ahp_indices_plus[i]]) / 2.; - // interpolate this one time step where the voltage is close to v_half in - // the rising and in the falling edge - double v_dev; - double delta_v; - double t_dev_rise; - double t_dev_fall; - double delta_t; - int rise_index = distance( - v.begin(), find_if(v.begin() + min_ahp_indices_plus[i - 1], - v.begin() + peak_indices[i - 1], - [v_half](double v_val) { return v_val >= v_half; })); - v_dev = v_half - v[rise_index]; - delta_v = v[rise_index] - v[rise_index - 1]; - delta_t = t[rise_index] - t[rise_index - 1]; - t_dev_rise = delta_t * v_dev / delta_v; - int fall_index = distance( - v.begin(), find_if(v.begin() + peak_indices[i - 1], - v.begin() + min_ahp_indices_plus[i], - [v_half](double v_val) { return v_val <= v_half; })); - v_dev = v_half - v[fall_index]; - delta_v = v[fall_index] - v[fall_index - 1]; - delta_t = t[fall_index] - t[fall_index - 1]; - t_dev_fall = delta_t * v_dev / delta_v; - spike_width1.push_back(t[fall_index] - t_dev_rise - t[rise_index] + - t_dev_fall); - } - return spike_width1.size(); -} - -int LibV5::spike_width1(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); - - vector spike_width1; - // Calculate spike width - int retVal = __spike_width1(doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), - intFeatures.at("min_AHP_indices"), - doubleFeatures.at("stim_start")[0], spike_width1); - - if (retVal >= 0) { - setVec(DoubleFeatureData, StringData, "spike_half_width", spike_width1); - } - return retVal; -} - -// -// *** AP begin indices *** -// -static int __AP_begin_indices(const vector& t, const vector& v, - double stimstart, double stimend, - const vector& pi, const vector& ahpi, - vector& apbi, double dTh, - int derivative_window) { - const double derivativethreshold = dTh; - vector dvdt(v.size()); - vector dv; - vector dt; - getCentralDifferenceDerivative(1., v, dv); - getCentralDifferenceDerivative(1., t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - - // restrict to time interval where stimulus is applied - vector minima, peak_indices; - auto stimbeginindex = - distance(t.begin(), find_if(t.begin(), t.end(), [stimstart](double time) { - return time >= stimstart; - })); - - if (stimbeginindex > 1) { - // to avoid skipping AP_begin when it is exactly at stimbeginindex - // also because of float precision and interpolation, sometimes - // stimbeginindex can be slightly above 'real' stim begin index - minima.push_back(stimbeginindex - 2); - } else if (stimbeginindex == 1) { - minima.push_back(stimbeginindex - 1); - } else { - minima.push_back(stimbeginindex); - } - for (size_t i = 0; i < ahpi.size(); i++) { - if (ahpi[i] > stimbeginindex) { - minima.push_back(ahpi[i]); - } - // if(t[ahpi[i]] > stimend) { - // break; - //} - } - for (size_t i = 0; i < pi.size(); i++) { - if (pi[i] > stimbeginindex) { - peak_indices.push_back(pi[i]); - } - } - int endindex = distance(t.begin(), t.end()); - if (minima.size() < peak_indices.size()) - throw FeatureComputationError("More peaks than min_AHP in AP_begin_indices."); - - // printf("Found %d minima\n", minima.size()); - for (size_t i = 0; i < peak_indices.size(); i++) { - // assure that the width of the slope is bigger than 4 - int newbegin = peak_indices[i]; - int begin = minima[i]; - int width = derivative_window; - bool skip = false; - - // Detect where the derivate crosses derivativethreshold, and make sure - // this happens in a window of 'width' sampling point - do { - begin = distance(dvdt.begin(), - find_if( - // use reverse iterator to get last occurence - // and avoid false positive long before the spike - dvdt.rbegin() + v.size() - newbegin, - dvdt.rbegin() + v.size() - minima[i], - [derivativethreshold](double val) { - return val <= derivativethreshold; - }) - .base()); - // cover edge case to avoid going out of index in the while condition - if (begin > endindex - width) { - begin = endindex - width; - } - // printf("%d %d\n", newbegin, minima[i+1]); - if (begin == minima[i]) { - // printf("Skipping %d %d\n", newbegin, minima[i+1]); - // could not find a spike in between these minima - skip = true; - break; - } - newbegin = begin - 1; - } while (find_if(dvdt.begin() + begin, dvdt.begin() + begin + width, - [derivativethreshold](double val) { - return val < derivativethreshold; - }) != dvdt.begin() + begin + width); - if (skip) { - continue; - } - apbi.push_back(begin); - } - return apbi.size(); -} - -int LibV5::AP_begin_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); - - vector apbi; - - // Get DerivativeThreshold - vector dTh; - int retVal = getParam(DoubleFeatureData, "DerivativeThreshold", dTh); - if (retVal <= 0) { - // derivative at peak start according to eCode specification 10mV/ms - // according to Shaul 12mV/ms - dTh.push_back(12.0); - } - - // Get DerivativeWindow - vector derivative_window; - retVal = getParam(IntFeatureData, "DerivativeWindow", derivative_window); - if (retVal <= 0) - throw FeatureComputationError("DerivativeWindow not set."); - - // Calculate feature - retVal = __AP_begin_indices( - doubleFeatures.at("T"), doubleFeatures.at("V"), - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], - intFeatures.at("peak_indices"), intFeatures.at("min_AHP_indices"), apbi, - dTh[0], derivative_window[0]); - - // Save feature value - if (retVal >= 0) { - setVec(IntFeatureData, StringData, "AP_begin_indices", apbi); - } - return retVal; -} - -static int __AP_end_indices(const vector& t, const vector& v, - const double stimstart, const vector& pi, - vector& apei, double derivativethreshold) { - - vector dvdt(v.size()); - vector dv; - vector dt; - vector peak_indices; - int max_slope; - getCentralDifferenceDerivative(1., v, dv); - getCentralDifferenceDerivative(1., t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - - auto stimbeginindex = distance(t.begin(), - find_if(t.begin(), t.end(), - [stimstart](double time){ return time >= stimstart; })); - - for (size_t i = 0; i < pi.size(); i++) { - if (pi[i] > stimbeginindex) { - peak_indices.push_back(pi[i]); - } - } - peak_indices.push_back(v.size() - 1); - - for (size_t i = 0; i < peak_indices.size() - 1; i++) { - size_t start_index = peak_indices[i] + 1; - size_t end_index = peak_indices[i + 1]; - - if (start_index >= end_index || start_index >= dvdt.size() || end_index >= dvdt.size()) { - continue; - } - - auto min_element_it = std::min_element(dvdt.begin() + start_index, dvdt.begin() + end_index); - auto max_slope = std::distance(dvdt.begin(), min_element_it); - // assure that the width of the slope is bigger than 4 - auto threshold_it = std::find_if(dvdt.begin() + max_slope, dvdt.begin() + end_index, - [derivativethreshold](double x) { return x >= derivativethreshold; }); - - if (threshold_it != dvdt.begin() + end_index) { - apei.push_back(std::distance(dvdt.begin(), threshold_it)); - } - } - return apei.size(); -} - -int LibV5::AP_end_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& T = getFeature(DoubleFeatureData, "T"); - const auto& V = getFeature(DoubleFeatureData, "V"); - const auto& stim_start = getFeature(DoubleFeatureData, "stim_start"); - const auto& peak_indices = getFeature(IntFeatureData, "peak_indices"); - - vector dTh; - int retVal = getParam(DoubleFeatureData, "DownDerivativeThreshold", dTh); - double downDerivativeThreshold = (retVal <= 0) ? -12.0 : dTh[0]; - - vector AP_end_indices; - retVal = __AP_end_indices(T, V, stim_start[0], peak_indices, AP_end_indices, - downDerivativeThreshold); - if (retVal >= 0) { - setVec(IntFeatureData, StringData, "AP_end_indices", AP_end_indices); - } - return retVal; -} - -static int __number_initial_spikes(const vector& peak_times, - double stimstart, double stimend, - double initial_perc, - vector& number_initial_spikes) { - double initialLength = (stimend - stimstart) * initial_perc; - - int startIndex = - distance(peak_times.begin(), - find_if(peak_times.begin(), peak_times.end(), - [stimstart](double t) { return t >= stimstart; })); - int endIndex = distance(peak_times.begin(), - find_if(peak_times.begin(), peak_times.end(), - [stimstart, initialLength](double t) { - return t >= stimstart + initialLength; - })); - - number_initial_spikes.push_back(endIndex - startIndex); - - return 1; -} - -// Number of spikes in the initial_perc interval -int LibV5::number_initial_spikes(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, - {"peak_time", "initial_perc", "stim_start", "stim_end"}); - vector number_initial_spikes; - - const vector& peak_times = doubleFeatures.at("peak_time"); - const vector& initial_perc = doubleFeatures.at("initial_perc"); - const vector& stimstart = doubleFeatures.at("stim_start"); - const vector& stimend = doubleFeatures.at("stim_end"); - - if ((initial_perc[0] < 0) || (initial_perc[0] >= 1)) { - throw FeatureComputationError("initial_perc should lie between [0 1)."); - } - - int retVal = __number_initial_spikes(peak_times, stimstart[0], stimend[0], - initial_perc[0], number_initial_spikes); - if (retVal >= 0) { - setVec(IntFeatureData, StringData, "number_initial_spikes", - number_initial_spikes); - } - return retVal; -} - -// Amplitude of the first spike -int LibV5::AP1_amp(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_amplitudes = - getFeature(DoubleFeatureData, "AP_amplitude"); - vector AP1_amp; - AP1_amp.push_back(AP_amplitudes[0]); - setVec(DoubleFeatureData, StringData, "AP1_amp", AP1_amp); - return 1; -} - -// Amplitude of the first spike -int LibV5::APlast_amp(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_amplitudes = - getFeature(DoubleFeatureData, "AP_amplitude"); - vector APlast_amp; - APlast_amp.push_back(AP_amplitudes[AP_amplitudes.size() - 1]); - setVec(DoubleFeatureData, StringData, "APlast_amp", APlast_amp); - return 1; -} - -// Peak voltage of the first spike -int LibV5::AP1_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& peak_voltage = - getFeature(DoubleFeatureData, "peak_voltage"); - vector AP1_peak; - AP1_peak.push_back(peak_voltage[0]); - setVec(DoubleFeatureData, StringData, "AP1_peak", AP1_peak); - return 1; -} - -// Amplitude of the second spike -int LibV5::AP2_amp(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_amplitudes = - getFeature(DoubleFeatureData, "AP_amplitude"); - vector AP2_amp; - if (AP_amplitudes.size() < 2) { - throw FeatureComputationError( - "Size of AP_amplitude should be >= 2 for AP2_amp"); - } - AP2_amp.push_back(AP_amplitudes[1]); - setVec(DoubleFeatureData, StringData, "AP2_amp", AP2_amp); - return 1; -} - -// Peak voltage of the second spike -int LibV5::AP2_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& peak_voltage = - getFeature(DoubleFeatureData, "peak_voltage"); - vector AP2_peak; - if (peak_voltage.size() < 2) { - throw FeatureComputationError( - "Size of peak_voltage should be >= 2 for AP2_peak"); - } - AP2_peak.push_back(peak_voltage[1]); - setVec(DoubleFeatureData, StringData, "AP2_peak", AP2_peak); - return 1; -} - -// Difference amplitude of the second to first spike -int LibV5::AP2_AP1_diff(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_amplitudes = - getFeature(DoubleFeatureData, "AP_amplitude"); - vector AP2_AP1_diff; - if (AP_amplitudes.size() < 2) { - throw FeatureComputationError( - "Size of AP_amplitude should be >= 2 for AP2_AP1_diff"); - } - AP2_AP1_diff.push_back(AP_amplitudes[1] - AP_amplitudes[0]); - setVec(DoubleFeatureData, StringData, "AP2_AP1_diff", AP2_AP1_diff); - return 1; -} - -// Difference peak_amp of the second to first spike -int LibV5::AP2_AP1_peak_diff(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& peak_voltage = - getFeature(DoubleFeatureData, "peak_voltage"); - vector AP2_AP1_peak_diff; - if (peak_voltage.size() < 2) { - throw FeatureComputationError( - "Size of peak_voltage should be >= 2 for AP2_AP1_peak_diff"); - } - AP2_AP1_peak_diff.push_back(peak_voltage[1] - peak_voltage[0]); - setVec(DoubleFeatureData, StringData, "AP2_AP1_peak_diff", - AP2_AP1_peak_diff); - return 1; -} - -// Width of the first spike -int LibV5::AP1_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& spike_half_width = - getFeature(DoubleFeatureData, "spike_half_width"); - vector AP1_width; - AP1_width.push_back(spike_half_width[0]); - setVec(DoubleFeatureData, StringData, "AP1_width", AP1_width); - return 1; -} - -// Width of the second spike -int LibV5::AP2_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& spike_half_width = - getFeature(DoubleFeatureData, "spike_half_width"); - vector AP2_width; - if (spike_half_width.size() < 2) { - throw FeatureComputationError( - "Size of spike_half_width should be >= 2 for AP2_width"); - } - AP2_width.push_back(spike_half_width[1]); - setVec(DoubleFeatureData, StringData, "AP2_width", AP2_width); - return 1; -} - -// Width of the last spike -int LibV5::APlast_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& spike_half_width = - getFeature(DoubleFeatureData, "spike_half_width"); - vector APlast_width; - APlast_width.push_back(spike_half_width[spike_half_width.size() - 1]); - setVec(DoubleFeatureData, StringData, "APlast_width", APlast_width); - return 1; -} - -static int __AHP_time_from_peak(const vector& t, - const vector& peakIndices, - const vector& minAHPIndices, - vector& ahpTimeFromPeak) { - for (size_t i = 0; i < peakIndices.size() && i < minAHPIndices.size(); i++) { - ahpTimeFromPeak.push_back(t[minAHPIndices[i]] - t[peakIndices[i]]); - } - return ahpTimeFromPeak.size(); -} - -int LibV5::AHP_time_from_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); - vector ahpTimeFromPeak; - const vector& T = doubleFeatures.at("T"); - const vector& peakIndices = intFeatures.at("peak_indices"); - const vector& minAHPIndices = intFeatures.at("min_AHP_indices"); - int retVal = - __AHP_time_from_peak(T, peakIndices, minAHPIndices, ahpTimeFromPeak); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AHP_time_from_peak", - ahpTimeFromPeak); - } - return retVal; -} - -static int __AHP_depth_from_peak(const vector& v, - const vector& peakIndices, - const vector& minAHPIndices, - vector& ahpDepthFromPeak) { - if (peakIndices.size() < minAHPIndices.size()) return -1; - for (size_t i = 0; i < minAHPIndices.size(); i++) { - ahpDepthFromPeak.push_back(v[peakIndices[i]] - v[minAHPIndices[i]]); - } - return ahpDepthFromPeak.size(); -} - -int LibV5::AHP_depth_from_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); - vector ahpDepthFromPeak; - const vector& V = doubleFeatures.at("V"); - const vector& peakIndices = intFeatures.at("peak_indices"); - const vector& minAHPIndices = intFeatures.at("min_AHP_indices"); - int retVal = - __AHP_depth_from_peak(V, peakIndices, minAHPIndices, ahpDepthFromPeak); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AHP_depth_from_peak", - ahpDepthFromPeak); - } - return retVal; -} - -// AHP_depth_from_peak of first spike -int LibV5::AHP1_depth_from_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& ahpDepthFromPeak = - getFeature(DoubleFeatureData, "AHP_depth_from_peak"); - vector ahp1DepthFromPeak; - ahp1DepthFromPeak.push_back(ahpDepthFromPeak[0]); - setVec(DoubleFeatureData, StringData, "AHP1_depth_from_peak", - ahp1DepthFromPeak); - return 1; -} - -// AHP_depth_from_peak of second spike -int LibV5::AHP2_depth_from_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& ahpDepthFromPeak = - getFeature(DoubleFeatureData, "AHP_depth_from_peak"); - vector ahp2DepthFromPeak; - if (ahpDepthFromPeak.size() < 2) { - throw FeatureComputationError( - "Size of AHP_depth_from_peak should be >= 2 for AHP2_depth_from_peak"); - } - ahp2DepthFromPeak.push_back(ahpDepthFromPeak[1]); - setVec(DoubleFeatureData, StringData, "AHP2_depth_from_peak", - ahp2DepthFromPeak); - return 1; -} - -// spike width at spike start -static int __AP_begin_width(const vector& t, const vector& v, - double stimstart, - const vector& AP_begin_indices, - const vector& min_ahp_indices, - vector& AP_begin_width) { - // int start_index = distance(t.begin(), find_if(t.begin(), t.end(), - // std::bind2nd(std::greater_equal(), stim_start))); - /// vector min_ahp_indices_plus(min_ahp_indices.size() + 1, start_index); - // copy(min_ahp_indices.begin(), min_ahp_indices.end(), - // min_ahp_indices_plus.begin()); - - // keep only min_ahp_indices values that are after stim start - // because AP_begin_indices are all after stim start - // if not done, could cause cases where AP_begin_indices[i] > min_ahp_indices[i] - // leading to segmentation faults - auto it = find_if(t.begin(), t.end(), - [stimstart](double val) { return val >= stimstart; }); - int stimbeginindex = distance(t.begin(), it); - vector strict_min_ahp_indices; - for (size_t i = 0; i < min_ahp_indices.size(); i++) { - if (min_ahp_indices[i] > stimbeginindex) { - strict_min_ahp_indices.push_back(min_ahp_indices[i]); - } - } - - if (AP_begin_indices.size() < strict_min_ahp_indices.size()) return -1; - for (size_t i = 0; i < strict_min_ahp_indices.size(); i++) { - double v_start = v[AP_begin_indices[i]]; - // interpolate this one time step where the voltage is close to v_start in - // the falling edge - int rise_index = AP_begin_indices[i]; - int fall_index = distance( - v.begin(), - find_if(v.begin() + rise_index + 1, - v.begin() + strict_min_ahp_indices[i], - [v_start](const auto& val) { return val <= v_start; })); - // v_dev = v_start - v[fall_index]; - // delta_v = v[fall_index] - v[fall_index - 1]; - // delta_t = t[fall_index] - t[fall_index - 1]; - // t_dev_fall = delta_t * v_dev / delta_v; - // printf("%f %f\n",delta_v, t_dev_fall); - AP_begin_width.push_back(t[fall_index] - t[rise_index] /*+ t_dev_fall*/); - } - return AP_begin_width.size(); -} - -int LibV5::AP_begin_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"min_AHP_indices", "AP_begin_indices"}); - vector AP_begin_width; - const vector& V = doubleFeatures.at("V"); - const vector& t = doubleFeatures.at("T"); - const vector& stim_start = doubleFeatures.at("stim_start"); - const vector& min_AHP_indices = intFeatures.at("min_AHP_indices"); - const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); - int retVal = __AP_begin_width(t, V, stim_start[0], AP_begin_indices, - min_AHP_indices, AP_begin_width); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AP_begin_width", AP_begin_width); - } - return retVal; -} - -static int __AP_begin_time(const vector& t, const vector& v, - const vector& AP_begin_indices, - vector& AP_begin_time) { - for (size_t i = 0; i < AP_begin_indices.size(); i++) { - AP_begin_time.push_back(t[AP_begin_indices[i]]); - } - return AP_begin_time.size(); -} - -int LibV5::AP_begin_time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); - vector AP_begin_time; - const vector& V = doubleFeatures.at("V"); - const vector& t = doubleFeatures.at("T"); - const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); - int retVal = __AP_begin_time(t, V, AP_begin_indices, AP_begin_time); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AP_begin_time", AP_begin_time); - } - return retVal; -} - -static int __AP_begin_voltage(const vector& t, const vector& v, - const vector& AP_begin_indices, - vector& AP_begin_voltage) { - for (size_t i = 0; i < AP_begin_indices.size(); i++) { - AP_begin_voltage.push_back(v[AP_begin_indices[i]]); - } - return AP_begin_voltage.size(); -} - -int LibV5::AP_begin_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); - vector AP_begin_voltage; - const vector& V = doubleFeatures.at("V"); - const vector& t = doubleFeatures.at("T"); - const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); - int retVal = __AP_begin_voltage(t, V, AP_begin_indices, AP_begin_voltage); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AP_begin_voltage", AP_begin_voltage); - } - return retVal; -} - -int LibV5::AP1_begin_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_begin_voltage = - getFeature(DoubleFeatureData, "AP_begin_voltage"); - vector AP1_begin_voltage; - AP1_begin_voltage.push_back(AP_begin_voltage[0]); - setVec(DoubleFeatureData, StringData, "AP1_begin_voltage", AP1_begin_voltage); - return 1; -} - -int LibV5::AP2_begin_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_begin_voltage = - getFeature(DoubleFeatureData, "AP_begin_voltage"); - vector AP2_begin_voltage; - - if (AP_begin_voltage.size() < 2) { - throw FeatureComputationError("There are less than 2 spikes in the trace."); - } - AP2_begin_voltage.push_back(AP_begin_voltage[1]); - setVec(DoubleFeatureData, StringData, "AP2_begin_voltage", AP2_begin_voltage); - return 1; -} - -int LibV5::AP1_begin_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_begin_width = - getFeature(DoubleFeatureData, "AP_begin_width"); - vector AP1_begin_width; - AP1_begin_width.push_back(AP_begin_width[0]); - setVec(DoubleFeatureData, StringData, "AP1_begin_width", AP1_begin_width); - return 1; -} - -int LibV5::AP2_begin_width(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_begin_width = - getFeature(DoubleFeatureData, "AP_begin_width"); - vector AP2_begin_width; - if (AP_begin_width.size() < 2) { - throw FeatureComputationError("There are less than 2 spikes in the trace."); - } - AP2_begin_width.push_back(AP_begin_width[1]); - setVec(DoubleFeatureData, StringData, "AP2_begin_width", AP2_begin_width); - return 1; -} - -// Difference amplitude of the second to first spike -int LibV5::AP2_AP1_begin_width_diff(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_begin_widths = - getFeature(DoubleFeatureData, "AP_begin_width"); - vector AP2_AP1_begin_width_diff; - if (AP_begin_widths.size() < 2) { - throw FeatureComputationError("There are less than 2 spikes in the trace."); - } - AP2_AP1_begin_width_diff.push_back(AP_begin_widths[1] - AP_begin_widths[0]); - setVec(DoubleFeatureData, StringData, "AP2_AP1_begin_width_diff", - AP2_AP1_begin_width_diff); - return 1; -} - -static int __voltage_deflection_begin(const vector& v, - const vector& t, double stimStart, - double stimEnd, vector& vd) { - double deflection_range_percentage = 0.10; - double range_begin = - stimStart + (stimEnd - stimStart) * (deflection_range_percentage / 2); - double range_stop = - range_begin + (stimEnd - stimStart) * (deflection_range_percentage); - double base = 0.; - int base_size = 0; - for (size_t i = 0; i < t.size(); i++) { - if (t[i] < stimStart) { - base += v[i]; - base_size++; - } else { - break; - } - } - base /= base_size; - double volt = 0; - int volt_size = 0; - for (size_t i = 0; i < t.size(); i++) { - if (t[i] > range_stop) { - break; - } - if (t[i] > range_begin) { - volt += v[i]; - volt_size++; - } - } - volt /= volt_size; - - vd.push_back(volt - base); - return 1; -} - -int LibV5::voltage_deflection_begin(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& v = getFeature(DoubleFeatureData, "V"); - const vector& t = getFeature(DoubleFeatureData, "T"); - const vector& stimStart = getFeature(DoubleFeatureData, "stim_start"); - const vector& stimEnd = getFeature(DoubleFeatureData, "stim_end"); - vector vd; - int retVal = __voltage_deflection_begin(v, t, stimStart[0], stimEnd[0], vd); - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "voltage_deflection_begin", vd); - } - return retVal; -} - -// Check if a cell is transiently stuck (i.e. not firing any spikes) at the end -// of -// retval will be -1 if the cell get's stuck, retval will be 1 if the cell -// doesn't get stuck -int LibV5::is_not_stuck(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& peak_time = getFeature(DoubleFeatureData, "peak_time"); - const vector& stim_start = - getFeature(DoubleFeatureData, "stim_start"); - const vector& stim_end = getFeature(DoubleFeatureData, "stim_end"); - bool stuck = true; - for (const auto& pt : peak_time) { - if (pt > stim_end[0] * 0.5 && pt < stim_end[0]) { - stuck = false; - break; - } - } - if (!stuck) { - vector tc = {1}; - setVec(IntFeatureData, StringData, "is_not_stuck", tc); - return tc.size(); - } else { - return -1; - } -} - -// The mean voltage after the stimulus in (stim_end + 25%*end_period, stim_end + -// 75%*end_period) -int LibV5::voltage_after_stim(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& v = getFeature(DoubleFeatureData, "V"); - const vector& t = getFeature(DoubleFeatureData, "T"); - const vector& stimEnd = getFeature(DoubleFeatureData, "stim_end"); - double startTime = stimEnd[0] + (t.back() - stimEnd[0]) * .25; - double endTime = stimEnd[0] + (t.back() - stimEnd[0]) * .75; - int nCount = 0; - double vSum = 0; - - for (size_t i = 0; i < t.size(); i++) { - if (t[i] >= startTime) { - vSum += v[i]; - nCount++; - } - if (t[i] > endTime) break; - } - - if (nCount == 0) return -1; - - vector vRest = {vSum / nCount}; - setVec(DoubleFeatureData, StringData, "voltage_after_stim", vRest); - - return 1; -} - -int LibV5::mean_AP_amplitude(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& AP_amplitude = - getFeature(DoubleFeatureData, "AP_amplitude"); - double mean_amp = 0.0; - for (const auto& amplitude : AP_amplitude) { - mean_amp += amplitude; - } - - mean_amp /= AP_amplitude.size(); - vector mean_AP_amplitude = {mean_amp}; - - setVec(DoubleFeatureData, StringData, "mean_AP_amplitude", mean_AP_amplitude); - - return mean_AP_amplitude.size(); -} - -static int __AP_phaseslope(const vector& v, const vector& t, - double stimStart, double stimEnd, - vector& ap_phaseslopes, vector apbi, - double range) { - vector dvdt(v.size()); - vector dv; - vector dt; - int apbegin_index, range_max_index, range_min_index; - double ap_phaseslope; - getCentralDifferenceDerivative(1., v, dv); - getCentralDifferenceDerivative(1., t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - - for (size_t i = 0; i < apbi.size(); i++) { - apbegin_index = apbi[i]; - range_min_index = apbegin_index - int(range); - range_max_index = apbegin_index + int(range); - if (range_min_index < 0 || range_max_index < 0) return -1; - if (range_min_index > (int)t.size() || range_max_index > (int)t.size()) - return -1; - if (v[range_max_index] - v[range_min_index] == 0) return -1; - ap_phaseslope = (dvdt[range_max_index] - dvdt[range_min_index]) / - (v[range_max_index] - v[range_min_index]); - ap_phaseslopes.push_back(ap_phaseslope); - // printf("slope %f, mint %f, minv %f, mindvdt %f\n", ap_phaseslope, - // t[range_min_index], v[range_min_index], dvdt[range_min_index]); - // printf("slope %f, maxt %f, maxv %f, maxdvdt %f\n", ap_phaseslope, - // t[range_max_index], v[range_max_index], dvdt[range_max_index]); - } - - return ap_phaseslopes.size(); -} -/// Calculate the slope of the V, dVdt plot at the beginning of every spike -/// (at the point where the derivative crosses the DerivativeThreshold) -int LibV5::AP_phaseslope(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, - {"V", "T", "stim_start", "stim_end", "AP_phaseslope_range"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); - vector ap_phaseslopes; - int retVal = __AP_phaseslope(doubleFeatures.at("V"), doubleFeatures.at("T"), - doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0], ap_phaseslopes, - intFeatures.at("AP_begin_indices"), - doubleFeatures.at("AP_phaseslope_range")[0]); - - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "AP_phaseslope", ap_phaseslopes); - } - return retVal; -} - -int LibV5::all_ISI_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& peak_time = getFeature(DoubleFeatureData, "peak_time"); - if (peak_time.size() < 2) - throw FeatureComputationError("Two spikes required for calculation of all_ISI_values."); - - vector VecISI; - for (size_t i = 1; i < peak_time.size(); i++) { - VecISI.push_back(peak_time[i] - peak_time[i - 1]); - } - setVec(DoubleFeatureData, StringData, "all_ISI_values", VecISI); - return VecISI.size(); -} - -// spike amplitude: peak_voltage - voltage_base -int LibV5::AP_amplitude_from_voltagebase(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"voltage_base", "peak_voltage"}); - vector apamplitude; - for (const auto& peak : doubleFeatures.at("peak_voltage")) { - apamplitude.push_back(peak - doubleFeatures.at("voltage_base")[0]); - } - setVec(DoubleFeatureData, StringData, "AP_amplitude_from_voltagebase", - apamplitude); - return apamplitude.size(); -} - -// min_voltage_between_spikes: minimal voltage between consecutive spikes -int LibV5::min_voltage_between_spikes(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); - - if (intFeatures.at("peak_indices").size() < 2) { - throw FeatureComputationError( - "Size of peak_indices should be >= 2 for min_voltage_between_spikes"); - } - - vector min_voltage_between_spikes; - for (size_t i = 0; i < intFeatures.at("peak_indices").size() - 1; i++) { - min_voltage_between_spikes.push_back(*min_element( - doubleFeatures.at("V").begin() + intFeatures.at("peak_indices")[i], - doubleFeatures.at("V").begin() + - intFeatures.at("peak_indices")[i + 1])); - } - - setVec(DoubleFeatureData, StringData, "min_voltage_between_spikes", - min_voltage_between_spikes); - return min_voltage_between_spikes.size(); -} - -// return (possibly interpolate) voltage trace -int LibV5::voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& v = getFeature(DoubleFeatureData, "V"); - setVec(DoubleFeatureData, StringData, "voltage", v); - return v.size(); -} - -// return (possibly interpolate) current trace -int LibV5::current(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& i = getFeature(DoubleFeatureData, "I"); - setVec(DoubleFeatureData, StringData, "current", i); - return i.size(); -} - -// return (possibly interpolate) time trace -int LibV5::time(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, mapStr2Str& StringData) { - const vector& t = getFeature(DoubleFeatureData, "T"); - setVec(DoubleFeatureData, StringData, "time", t); - return t.size(); -} - -// *** The average voltage during the last 90% of the stimulus duration. *** -int LibV5::steady_state_voltage_stimend(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_end", "stim_start"}); - - const vector& voltages = doubleFeatures.at("V"); - const vector& times = doubleFeatures.at("T"); - const double stimStart = doubleFeatures.at("stim_start")[0]; - const double stimEnd = doubleFeatures.at("stim_end")[0]; - - double start_time = stimEnd - 0.1 * (stimEnd - stimStart); - auto start_it = find_if(times.begin(), times.end(), - [start_time](double x) { return x >= start_time; }); - auto stop_it = find_if(times.begin(), times.end(), - [stimEnd](double x) { return x >= stimEnd; }); - - size_t start_index = distance(times.begin(), start_it); - size_t stop_index = distance(times.begin(), stop_it); - - double mean = accumulate(voltages.begin() + start_index, - voltages.begin() + stop_index, 0.0); - size_t mean_size = stop_index - start_index; - - vector ssv; - if (mean_size > 0) { - mean /= mean_size; - ssv.push_back(mean); - setVec(DoubleFeatureData, StringData, "steady_state_voltage_stimend", ssv); - return 1; - } - return -1; -} - -int LibV5::voltage_base(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const vector& v = getFeature(DoubleFeatureData, "V"); - const vector& t = getFeature(DoubleFeatureData, "T"); - const vector& stimStart = getFeature(DoubleFeatureData, "stim_start"); - - // Retrieve percentage values or use defaults. - double vb_start_perc = 0.9; // Default value - double vb_end_perc = 1.0; // Default value - try { - auto vb_start_perc_vec = - getFeature(DoubleFeatureData, "voltage_base_start_perc"); - if (vb_start_perc_vec.size() == 1) vb_start_perc = vb_start_perc_vec[0]; - } catch (const EmptyFeatureError&) { - // If there's an error, use the default value. - } - - try { - auto vb_end_perc_vec = - getFeature(DoubleFeatureData, "voltage_base_end_perc"); - if (vb_end_perc_vec.size() == 1) vb_end_perc = vb_end_perc_vec[0]; - } catch (const EmptyFeatureError&) { - // If there's an error, use the default value. - } - - // Calculate start and end times based on stimStart and percentages. - double startTime = stimStart[0] * vb_start_perc; - double endTime = stimStart[0] * vb_end_perc; - - // Validate start and end times. - if (startTime >= endTime) - throw FeatureComputationError("voltage_base: startTime >= endTime"); - - const auto& precisionThreshold = - getFeature(DoubleFeatureData, "precision_threshold"); - - // Find index range for the time vector within the specified start and end - // times. - std::pair time_index = - get_time_index(t, startTime, endTime, precisionThreshold[0]); - - // Extract sub-vector of voltages based on calculated indices. - vector subVector(v.begin() + time_index.first, - v.begin() + time_index.second); - - // Determine computation mode and calculate voltage base. - std::string computation_mode; - - int retVal = getStrParam(StringData, "voltage_base_mode", computation_mode); - if (retVal < 0) return -1; - double vBase; - - // Perform computation based on the mode. - if (computation_mode == "mean") - vBase = vec_mean(subVector); - else if (computation_mode == "median") - vBase = vec_median(subVector); - else - throw FeatureComputationError("Undefined computational mode. Only mean and median are enabled."); - - vector vRest = {vBase}; - setVec(DoubleFeatureData, StringData, "voltage_base", vRest); - return 1; -} - -int LibV5::current_base(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"I", "T", "stim_start"}); - double cb_start_perc = 0.9; // Default value - double cb_end_perc = 1.0; // Default value - - try { - cb_start_perc = getFeature(DoubleFeatureData, "current_base_start_perc")[0]; - } catch (const std::runtime_error&) { - } // Use default value if not found or an error occurs - - try { - cb_end_perc = getFeature(DoubleFeatureData, "current_base_end_perc")[0]; - } catch (const std::runtime_error&) { - } // Use default value if not found or an error occurs - - double startTime = doubleFeatures.at("stim_start")[0] * cb_start_perc; - double endTime = doubleFeatures.at("stim_start")[0] * cb_end_perc; - - if (startTime >= endTime) - throw FeatureComputationError("current_base: startTime >= endTime"); - - vector precisionThreshold; - int retVal = - getParam(DoubleFeatureData, "precision_threshold", precisionThreshold); - if (retVal < 0) return -1; - - std::pair time_index = get_time_index( - doubleFeatures.at("T"), startTime, endTime, precisionThreshold[0]); - - vector subVector(doubleFeatures.at("I").begin() + time_index.first, - doubleFeatures.at("I").begin() + time_index.second); - - double iBase; - std::string computation_mode; - retVal = getStrParam(StringData, "current_base_mode", computation_mode); - if (retVal < 0) return -1; - if (computation_mode == "mean") - iBase = vec_mean(subVector); - else if (computation_mode == "median") - iBase = vec_median(subVector); - else - throw FeatureComputationError("Undefined computational mode. Only mean and median are enabled."); - - vector iRest{iBase}; - setVec(DoubleFeatureData, StringData, "current_base", iRest); - return 1; -} - -size_t get_index(const vector& times, double t) { - return distance(times.begin(), find_if(times.begin(), times.end(), - [t](double x) { return x >= t; })); -} - -double __decay_time_constant_after_stim(const vector& times, - const vector& voltage, - const double decay_start_after_stim, - const double decay_end_after_stim, - const double stimStart, - const double stimEnd) { - const size_t stimStartIdx = get_index(times, stimStart); - const size_t decayStartIdx = - get_index(times, stimEnd + decay_start_after_stim); - - const size_t decayEndIdx = get_index(times, stimEnd + decay_end_after_stim); - - const double reference = voltage[stimStartIdx]; - - vector decayValues(decayEndIdx - decayStartIdx); - vector decayTimes(decayEndIdx - decayStartIdx); - - for (size_t i = 0; i != decayValues.size(); ++i) { - const double u0 = std::abs(voltage[decayStartIdx + i] - reference); - - decayValues[i] = log(u0); - decayTimes[i] = times[decayStartIdx + i]; - } - - if (decayTimes.size() < 1 || decayValues.size() < 1) { - throw FeatureComputationError("No data points to calculate decay_time_constant_after_stim"); - } - linear_fit_result fit; - fit = slope_straight_line_fit(decayTimes, decayValues); - - const double tau = -1.0 / fit.slope; - return std::abs(tau); -} - -// *** Decay time constant measured during decay after the stimulus*** -int LibV5::decay_time_constant_after_stim(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); - - double decay_start_after_stim, decay_end_after_stim; - - try { - const auto& decayStartFeatures = - getFeatures(DoubleFeatureData, {"decay_start_after_stim"}); - decay_start_after_stim = decayStartFeatures.at("decay_start_after_stim")[0]; - } catch (const std::runtime_error&) { - decay_start_after_stim = 1.0; // Default value if not found - } - - try { - const auto& decayEndFeatures = - getFeatures(DoubleFeatureData, {"decay_end_after_stim"}); - decay_end_after_stim = decayEndFeatures.at("decay_end_after_stim")[0]; - } catch (const std::runtime_error&) { - decay_end_after_stim = 10.0; // Default value if not found - } - // Validate decay times - if (decay_start_after_stim >= decay_end_after_stim) - throw FeatureComputationError("Error decay_start_after_stim small larger than decay_end_after_stim"); - - // Perform calculation - const double val = __decay_time_constant_after_stim( - doubleFeatures.at("T"), doubleFeatures.at("V"), decay_start_after_stim, - decay_end_after_stim, doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0]); - - // Store the result - vector dtcas{val}; - setVec(DoubleFeatureData, StringData, "decay_time_constant_after_stim", - dtcas); - - return 1; -} - -// Calculate the time constants after each step for a stimuli containing several -// steps, as for example SpikeRec protocols -int LibV5::multiple_decay_time_constant_after_stim( - mapStr2intVec& IntFeatureData, mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"V", "T", "multi_stim_start", "multi_stim_end"}); - vector stimsEnd, stimsStart; - - stimsEnd = doubleFeatures.at("multi_stim_end"); - stimsStart = doubleFeatures.at("multi_stim_start"); - - // Attempt to get decay parameters, using defaults if not found or if not - // exactly one element - double decay_start_after_stim = 1.0; - double decay_end_after_stim = 10.0; - try { - decay_start_after_stim = getFeature(DoubleFeatureData, "decay_start_after_stim")[0]; - } catch (const std::runtime_error&) { - } // Use default value - try { - decay_end_after_stim = getFeature(DoubleFeatureData, "decay_end_after_stim")[0]; - } catch (const std::runtime_error&) { - } // Use default value - vector dtcas; - for (size_t i = 0; i < stimsStart.size(); i++) { - double ret_dtcas = __decay_time_constant_after_stim( - doubleFeatures.at("T"), doubleFeatures.at("V"), decay_start_after_stim, - decay_end_after_stim, stimsStart[i], stimsEnd[i]); - dtcas.push_back(ret_dtcas); - } - setVec(DoubleFeatureData, StringData, - "multiple_decay_time_constant_after_stim", dtcas); - return 1; -} - -// compute time constant for the decay from the sag to the steady_state_voltage -// noisy data is expected, so no golden section search is used -// because with noisy data, x>0 often gives a worse logarithmic fit -static int __sag_time_constant(const vector& times, - const vector& voltage, - const double minimum_voltage, - const double steady_state_v, - const double sag_amplitude, - const double stimStart, const double stimEnd, - vector& sagtc) { - // minimal required length of each decay (indices) - size_t min_length = 10; - - // get start index - const size_t decayStartIdx = distance( - voltage.begin(), - find_if(voltage.begin(), voltage.end(), - [minimum_voltage](double v) { return v <= minimum_voltage; })); - - // voltage at which 90% of the sag amplitude has decayed - double steady_state_90 = steady_state_v - sag_amplitude * 0.1; - // get end index - const size_t decayEndIdx = distance( - voltage.begin(), - find_if(voltage.begin() + decayStartIdx, voltage.end(), - [steady_state_90](double v) { return v >= steady_state_90; })); - - // voltage reference by which the voltage (i the decay interval) - // is going to be substracted - // there should be no '0' in (decay_v - v_reference), - // so no problem when the log is taken - double v_reference = voltage[decayEndIdx]; - - // decay interval - vector VInterval(&voltage[decayStartIdx], &voltage[decayEndIdx]); - vector TInterval(×[decayStartIdx], ×[decayEndIdx]); - - // compute time constant - vector decayValues(decayEndIdx - decayStartIdx); - for (size_t i = 0; i < VInterval.size(); ++i) { - const double u0 = std::abs(VInterval[i] - v_reference); - decayValues[i] = log(u0); - } - if (decayValues.size() < min_length) { - throw FeatureComputationError("Not enough data points to compute time constant."); - } - linear_fit_result fit; - fit = slope_straight_line_fit(TInterval, decayValues); - - // append tau - sagtc.push_back(std::abs(1.0 / fit.slope)); - - return 1; -} - -// *** Decay time constant measured from minimum voltage to steady-state -// voltage*** -int LibV5::sag_time_constant(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"V", "T", "stim_end", "stim_start", "minimum_voltage", - "steady_state_voltage_stimend", "sag_amplitude"}); - vector sagtc; - int retVal = __sag_time_constant( - doubleFeatures.at("T"), doubleFeatures.at("V"), - doubleFeatures.at("minimum_voltage")[0], - doubleFeatures.at("steady_state_voltage_stimend")[0], - doubleFeatures.at("sag_amplitude")[0], doubleFeatures.at("stim_start")[0], - doubleFeatures.at("stim_end")[0], sagtc); - - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "sag_time_constant", sagtc); - } - return retVal; -} - -/// *** Voltage deflection between voltage_base and steady_state_voltage_stimend -int LibV5::voltage_deflection_vb_ssse(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"voltage_base", "steady_state_voltage_stimend"}); - - vector voltage_deflection_vb_ssse; - voltage_deflection_vb_ssse.push_back( - doubleFeatures.at("steady_state_voltage_stimend")[0] - - doubleFeatures.at("voltage_base")[0]); - setVec(DoubleFeatureData, StringData, "voltage_deflection_vb_ssse", - voltage_deflection_vb_ssse); - return 1; -} - -// *** ohmic input resistance based on voltage_deflection_vb_ssse*** - -int LibV5::ohmic_input_resistance_vb_ssse(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"voltage_deflection_vb_ssse", "stimulus_current"}); - const double stimulus_current = doubleFeatures.at("stimulus_current")[0]; - if (stimulus_current == 0) - throw FeatureComputationError("Stimulus current is zero which will result in division by zero."); - vector ohmic_input_resistance_vb_ssse; - ohmic_input_resistance_vb_ssse.push_back( - doubleFeatures.at("voltage_deflection_vb_ssse")[0] / stimulus_current); - setVec(DoubleFeatureData, StringData, "ohmic_input_resistance_vb_ssse", - ohmic_input_resistance_vb_ssse); - - return 1; -} - -// *** Diff between maximum voltage during stimulus and voltage_base *** -int LibV5::maximum_voltage_from_voltagebase(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"maximum_voltage", "voltage_base"}); - - vector maximum_voltage_from_voltagebase; - maximum_voltage_from_voltagebase.push_back( - doubleFeatures.at("maximum_voltage")[0] - - doubleFeatures.at("voltage_base")[0]); - setVec(DoubleFeatureData, StringData, "maximum_voltage_from_voltagebase", - maximum_voltage_from_voltagebase); - return 1; -} - -static int __peak_indices(double threshold, const vector& V, - const vector& t, vector& PeakIndex, - bool strict_stiminterval, double stim_start, - double stim_end) { - vector upVec, dnVec; - double dtmp; - size_t itmp = 0; - bool itmp_set = false; - - for (size_t i = 1; i < V.size(); i++) { - if (V[i] > threshold && V[i - 1] < threshold) { - upVec.push_back(i); - } else if (V[i] < threshold && V[i - 1] > threshold) { - dnVec.push_back(i); - } - } - if (dnVec.size() == 0) - throw FeatureComputationError("Voltage never goes below or above threshold in spike detection."); - if (upVec.size() == 0) - throw FeatureComputationError("Voltage never goes above threshold in spike detection."); - - // case where voltage starts above threshold: remove 1st dnVec - while (dnVec.size() > 0 && dnVec[0] < upVec[0]) { - dnVec.erase(dnVec.begin()); - } - - if (upVec.size() > dnVec.size()) { - size_t size_diff = upVec.size() - dnVec.size(); - for (size_t i = 0; i < size_diff; i++) { - upVec.pop_back(); - } - } - - PeakIndex.clear(); - int j = 0; - for (size_t i = 0; i < upVec.size(); i++) { - dtmp = -1e9; - itmp = 0; - itmp_set = false; - EFEL_ASSERT(i < dnVec.size(), "dnVec array too small"); - for (j = upVec[i]; j <= dnVec[i]; j++) { - if (dtmp < V[j]) { - dtmp = V[j]; - itmp = j; - itmp_set = true; - } - } - if (itmp_set) { - if (strict_stiminterval) { - EFEL_ASSERT(itmp < t.size(), "peak_time falls outside of time array"); - if (t[itmp] >= stim_start && t[itmp] <= stim_end) { - PeakIndex.push_back(itmp); - } - } else { - PeakIndex.push_back(itmp); - } - } - } - return PeakIndex.size(); -} - -int LibV5::peak_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"V", "T", "Threshold", "stim_start", "stim_end"}); - bool strict_stiminterval; - try { - const auto& intFeatures = - getFeatures(IntFeatureData, {"strict_stiminterval"}); - strict_stiminterval = bool(intFeatures.at("strict_stiminterval")[0]); - } catch (const std::runtime_error& e) { - strict_stiminterval = false; - } - vector PeakIndex; - int retVal = __peak_indices( - doubleFeatures.at("Threshold")[0], doubleFeatures.at("V"), - doubleFeatures.at("T"), PeakIndex, strict_stiminterval, - doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0]); - - if (retVal > 0) { - setVec(IntFeatureData, StringData, "peak_indices", PeakIndex); - } - return retVal; -} - -int LibV5::sag_amplitude(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"steady_state_voltage_stimend", - "voltage_deflection_vb_ssse", "minimum_voltage"}); - - vector sag_amplitude; - if (doubleFeatures.at("voltage_deflection_vb_ssse")[0] <= 0) { - sag_amplitude.push_back( - doubleFeatures.at("steady_state_voltage_stimend")[0] - - doubleFeatures.at("minimum_voltage")[0]); - } else - throw FeatureComputationError("sag_amplitude: voltage_deflection is positive"); - - if (!sag_amplitude.empty()) { - setVec(DoubleFeatureData, StringData, "sag_amplitude", sag_amplitude); - } - return sag_amplitude.empty() ? -1 : 1; -} - -int LibV5::sag_ratio1(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double features at once - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, {"sag_amplitude", "voltage_base", "minimum_voltage"}); - - vector sag_ratio1; - if (doubleFeatures.at("minimum_voltage")[0] == doubleFeatures.at("voltage_base")[0]) - throw FeatureComputationError("voltage_base equals minimum_voltage"); - - sag_ratio1.push_back(doubleFeatures.at("sag_amplitude")[0] / - (doubleFeatures.at("voltage_base")[0] - - doubleFeatures.at("minimum_voltage")[0])); - - if (!sag_ratio1.empty()) { - setVec(DoubleFeatureData, StringData, "sag_ratio1", sag_ratio1); - } - return sag_ratio1.empty() ? -1 : 1; -} - -int LibV5::sag_ratio2(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double features at once - const auto& doubleFeatures = getFeatures( - DoubleFeatureData, - {"voltage_base", "minimum_voltage", "steady_state_voltage_stimend"}); - - vector sag_ratio2; - if (doubleFeatures.at("minimum_voltage")[0] == doubleFeatures.at("voltage_base")[0]) - throw FeatureComputationError("voltage_base equals minimum_voltage"); - - sag_ratio2.push_back( - (doubleFeatures.at("voltage_base")[0] - - doubleFeatures.at("steady_state_voltage_stimend")[0]) / - (doubleFeatures.at("voltage_base")[0] - - doubleFeatures.at("minimum_voltage")[0])); - - if (!sag_ratio2.empty()) { - setVec(DoubleFeatureData, StringData, "sag_ratio2", sag_ratio2); - } - return sag_ratio2.empty() ? -1 : 1; -} - -// -// *** Action potential peak upstroke *** -// -static int __AP_peak_upstroke(const vector& t, const vector& v, - const vector& pi, // peak indices - const vector& apbi, // AP begin indices - vector& pus) { // AP peak upstroke - vector dvdt(v.size()); - vector dv; - vector dt; - getCentralDifferenceDerivative(1., v, dv); - getCentralDifferenceDerivative(1., t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - - // Make sure that each value of pi is greater than its apbi counterpart - vector new_pi; - size_t j = 0; - for (size_t i = 0; i < apbi.size(); i++) { - while (j < pi.size() && pi[j] < apbi[i]) { - j++; - } - - if (j < pi.size() && pi[j] >= apbi[i]) { - new_pi.push_back(pi[j]); - j++; - } - } - - for (size_t i = 0; i < std::min(apbi.size(), new_pi.size()); i++) { - pus.push_back( - *std::max_element(dvdt.begin() + apbi[i], dvdt.begin() + new_pi[i])); - } - - return pus.size(); -} - -int LibV5::AP_peak_upstroke(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); - - vector pus; - int retVal = __AP_peak_upstroke( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), intFeatures.at("AP_begin_indices"), pus); - - if (retVal >= 0) { - setVec(DoubleFeatureData, StringData, "AP_peak_upstroke", pus); - } - return retVal; -} - -// -// *** Action potential peak downstroke *** -// -static int __AP_peak_downstroke(const vector& t, - const vector& v, - const vector& pi, // peak indices - const vector& ahpi, // min AHP indices - vector& pds) { // AP peak downstroke - vector dvdt(v.size()); - vector dv; - vector dt; - getCentralDifferenceDerivative(1., v, dv); - getCentralDifferenceDerivative(1., t, dt); - transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), - std::divides()); - - for (size_t i = 0; i < std::min(ahpi.size(), pi.size()); i++) { - pds.push_back( - *std::min_element(dvdt.begin() + pi[i], dvdt.begin() + ahpi[i])); - } - - return pds.size(); -} - -int LibV5::AP_peak_downstroke(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); - - vector pds; - int retVal = __AP_peak_downstroke( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), intFeatures.at("min_AHP_indices"), pds); - - if (retVal >= 0) { - setVec(DoubleFeatureData, StringData, "AP_peak_downstroke", pds); - } - return retVal; -} - -static int __min_between_peaks_indices( - const vector& t, const vector& v, - const vector& peak_indices, const double stim_start, - const double stim_end, const bool strict_stiminterval, - vector& min_btw_peaks_indices, vector& min_btw_peaks_values) { - vector peak_indices_plus = peak_indices; - unsigned end_index = 0; - - if (strict_stiminterval) { - end_index = distance(t.begin(), - find_if(t.begin(), t.end(), [stim_end](double t_val) { - return t_val >= stim_end; - })); - } else { - end_index = distance(t.begin(), t.end()); - } - - size_t minindex = 0; - - peak_indices_plus.push_back(end_index); - - for (size_t i = 0; i < peak_indices_plus.size() - 1; i++) { - minindex = distance(v.begin(), - std::min_element(v.begin() + peak_indices_plus[i], - v.begin() + peak_indices_plus[i + 1])); - - min_btw_peaks_indices.push_back(minindex); - - EFEL_ASSERT(minindex < v.size(), - "min index falls outside of voltage array"); - min_btw_peaks_values.push_back(v[minindex]); - } - - return min_btw_peaks_indices.size(); -} - -// min_between_peaks_indices -// find the minimum between two spikes, -// and the minimum between the last spike and the time the stimulus ends. -// This is different from min_AHP_indices, for traces that have broad peaks -// with local minima and maxima in it (e.g. dendritic AP) -int LibV5::min_between_peaks_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "strict_stiminterval"}); - - vector min_btw_peaks_indices; - vector min_btw_peaks_values; - int retVal = __min_between_peaks_indices( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), doubleFeatures.at("stim_start").front(), - doubleFeatures.at("stim_end").front(), - intFeatures.at("strict_stiminterval").empty() - ? false - : intFeatures.at("strict_stiminterval").front(), - min_btw_peaks_indices, min_btw_peaks_values); - - if (retVal > 0) { - setVec(IntFeatureData, StringData, "min_between_peaks_indices", - min_btw_peaks_indices); - setVec(DoubleFeatureData, StringData, "min_between_peaks_values", - min_btw_peaks_values); - } - return retVal; -} - -int LibV5::min_between_peaks_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -// AP_width_between_threshold -// spike width calculation according to threshold value. -// min_between_peaks_indices are used to split the different APs -// do not add width if threshold crossing has not been found -static int __AP_width_between_threshold( - const vector& t, const vector& v, double stimstart, - double threshold, const vector& min_between_peaks_indices, - vector& ap_width_threshold) { - vector indices(min_between_peaks_indices.size() + 1); - int start_index = distance( - t.begin(), find_if(t.begin(), t.end(), - [stimstart](double x) { return x >= stimstart; })); - indices[0] = start_index; - copy(min_between_peaks_indices.begin(), min_between_peaks_indices.end(), - indices.begin() + 1); - - for (size_t i = 0; i < indices.size() - 1; i++) { - int onset_index = distance( - v.begin(), find_if(v.begin() + indices[i], v.begin() + indices[i + 1], - [threshold](double x) { return x >= threshold; })); - int end_index = distance( - v.begin(), find_if(v.begin() + onset_index, v.begin() + indices[i + 1], - [threshold](double x) { return x <= threshold; })); - if (end_index != indices[i + 1]) { - ap_width_threshold.push_back(t[end_index] - t[onset_index]); - } - } - - return ap_width_threshold.size(); -} - -int LibV5::AP_width_between_threshold(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "Threshold", "stim_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"min_between_peaks_indices"}); - - vector ap_width_threshold; - int retval = __AP_width_between_threshold( - doubleFeatures.at("T"), doubleFeatures.at("V"), - doubleFeatures.at("stim_start").front(), - doubleFeatures.at("Threshold").front(), - intFeatures.at("min_between_peaks_indices"), ap_width_threshold); - if (retval == 0) - return -1; - else if (retval > 0) { - setVec(DoubleFeatureData, StringData, "AP_width_between_threshold", - ap_width_threshold); - } - return retval; -} - -// the recorded indices correspond to the peak indices -// does not skip the first ISI by default -static int __burst_indices(double burst_factor, int IgnoreFirstISI, - const vector ISI_values, - vector& burst_begin_indices, - vector& burst_end_indices) { - vector ISIpcopy; - vector::iterator it1, it2; - int n; - double dMedian; - bool in_burst; - int first_ISI = IgnoreFirstISI, count = IgnoreFirstISI; - - burst_begin_indices.push_back(first_ISI); - - for (size_t i = first_ISI + 1; i < (ISI_values.size()); i++) { - // get median - ISIpcopy.clear(); - for (size_t j = count; j < i; j++) ISIpcopy.push_back(ISI_values[j]); - sort(ISIpcopy.begin(), ISIpcopy.end()); - n = ISIpcopy.size(); - if ((n % 2) == 0) { - dMedian = - (ISIpcopy[int((n - 1) / 2)] + ISIpcopy[int((n - 1) / 2) + 1]) / 2; - } else { - dMedian = ISIpcopy[int(n / 2)]; - } - - in_burst = (burst_end_indices.size() == 0 || - burst_begin_indices.back() > burst_end_indices.back()); - - // look for end burst - if (in_burst && ISI_values[i] > (burst_factor * dMedian)) { - burst_end_indices.push_back(i); - count = i; - } - - if (ISI_values[i] < ISI_values[i - 1] / burst_factor) { - if (in_burst) { - burst_begin_indices.back() = i; - } else { - burst_begin_indices.push_back(i); - } - count = i; - } - } - - in_burst = (burst_end_indices.size() == 0 || - burst_begin_indices.back() > burst_end_indices.back()); - if (in_burst) { - burst_end_indices.push_back(ISI_values.size()); - } - - return burst_begin_indices.size(); -} - -int LibV5::burst_begin_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - int retVal; - vector burst_begin_indices, burst_end_indices, retIgnore; - vector ISI_values, tVec; - int IgnoreFirstISI; - double burst_factor = 0; - ISI_values = getFeature(DoubleFeatureData, "all_ISI_values"); - if (ISI_values.size() < 2) { - GErrorStr += - "\nError: At least than 3 spikes are needed for burst calculation.\n"; - return -1; - } - retVal = getParam(DoubleFeatureData, "strict_burst_factor", tVec); - if (retVal < 0) - burst_factor = 2; - else - burst_factor = tVec[0]; - - retVal = getParam(IntFeatureData, "ignore_first_ISI", retIgnore); - if ((retVal == 1) && (retIgnore.size() > 0) && (retIgnore[0] == 0)) - IgnoreFirstISI = 0; - else - IgnoreFirstISI = 1; - - retVal = __burst_indices(burst_factor, IgnoreFirstISI, ISI_values, - burst_begin_indices, burst_end_indices); - if (retVal >= 0) { - setVec(IntFeatureData, StringData, "burst_begin_indices", - burst_begin_indices); - setVec(IntFeatureData, StringData, "burst_end_indices", burst_end_indices); - } - return retVal; -} - -int LibV5::burst_end_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -static int __strict_burst_mean_freq(const vector& PVTime, - const vector& burst_begin_indices, - const vector& burst_end_indices, - vector& BurstMeanFreq) { - if (burst_begin_indices.size() == 0) return BurstMeanFreq.size(); - double span; - size_t i; - - for (i = 0; i < burst_begin_indices.size(); i++) { - if (burst_end_indices[i] - burst_begin_indices[i] > 0) { - span = PVTime[burst_end_indices[i]] - PVTime[burst_begin_indices[i]]; - BurstMeanFreq.push_back( - (burst_end_indices[i] - burst_begin_indices[i] + 1) * 1000 / span); - } - } - - return BurstMeanFreq.size(); -} - -int LibV5::strict_burst_mean_freq(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_time"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"burst_begin_indices", "burst_end_indices"}); - - vector BurstMeanFreq; - int retVal = __strict_burst_mean_freq( - doubleFeatures.at("peak_time"), intFeatures.at("burst_begin_indices"), - intFeatures.at("burst_end_indices"), BurstMeanFreq); - - if (retVal >= 0) { - setVec(DoubleFeatureData, StringData, "strict_burst_mean_freq", - BurstMeanFreq); - } - return retVal; -} - -static int __strict_interburst_voltage(const vector& burst_begin_indices, - const vector& PeakIndex, - const vector& T, - const vector& V, - vector& IBV) { - if (burst_begin_indices.size() < 1) return 0; - int j, pIndex, tsIndex, teIndex, cnt; - double tStart, tEnd, vTotal = 0; - for (size_t i = 1; i < burst_begin_indices.size(); i++) { - pIndex = burst_begin_indices[i] - 1; - tsIndex = PeakIndex[pIndex]; - tStart = T[tsIndex] + 5; // 5 millisecond after - pIndex = burst_begin_indices[i]; - teIndex = PeakIndex[pIndex]; - tEnd = T[teIndex] - 5; // 5 millisecond before - - for (j = tsIndex; j < teIndex; j++) { - if (T[j] > tStart) break; - } - tsIndex = --j; - - for (j = teIndex; j > tsIndex; j--) { - if (T[j] < tEnd) break; - } - teIndex = ++j; - vTotal = 0; - for (j = tsIndex, cnt = 1; j <= teIndex; j++, cnt++) vTotal = vTotal + V[j]; - if (cnt == 0) continue; - IBV.push_back(vTotal / (cnt - 1)); - } - return IBV.size(); -} - -int LibV5::strict_interburst_voltage(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_begin_indices"}); - vector IBV; - int retVal = __strict_interburst_voltage( - intFeatures.at("burst_begin_indices"), intFeatures.at("peak_indices"), - doubleFeatures.at("T"), doubleFeatures.at("V"), IBV); - - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "strict_interburst_voltage", IBV); - } - return retVal; -} - -// strict_stiminterval should be True when using this feature -static int __ADP_peak_indices(const vector& v, - const vector& min_AHP_indices, - const vector& min_between_peaks_indices, - vector& ADP_peak_indices, - vector& ADP_peak_values) { - if (min_AHP_indices.size() > min_between_peaks_indices.size()) { - throw FeatureComputationError("min_AHP_indices should not have less elements than min_between_peaks_indices"); - } - - unsigned adp_peak_index; - for (size_t i = 0; i < min_AHP_indices.size(); i++) { - adp_peak_index = max_element(v.begin() + min_AHP_indices[i], - v.begin() + min_between_peaks_indices[i]) - - v.begin(); - - ADP_peak_indices.push_back(adp_peak_index); - ADP_peak_values.push_back(v[adp_peak_index]); - } - - return ADP_peak_indices.size(); -} - -// strict_stiminterval should be True when using this feature -int LibV5::ADP_peak_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = getFeatures( - IntFeatureData, {"min_AHP_indices", "min_between_peaks_indices"}); - vector ADP_peak_indices; - vector ADP_peak_values; - int retVal = __ADP_peak_indices(doubleFeatures.at("V"), - intFeatures.at("min_AHP_indices"), - intFeatures.at("min_between_peaks_indices"), - ADP_peak_indices, ADP_peak_values); - if (retVal > 0) { - setVec(IntFeatureData, StringData, "ADP_peak_indices", ADP_peak_indices); - setVec(DoubleFeatureData, StringData, "ADP_peak_values", ADP_peak_values); - } - return retVal; -} - -// strict_stiminterval should be True when using this feature -int LibV5::ADP_peak_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -// strict_stiminterval should be True when using this feature -int LibV5::ADP_peak_amplitude(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"min_AHP_values", "ADP_peak_values"}); - vector ADP_peak_amplitude; - const vector& min_AHP_values = doubleFeatures.at("min_AHP_values"); - const vector& ADP_peak_values = doubleFeatures.at("ADP_peak_values"); - - if (min_AHP_values.size() != ADP_peak_values.size()) - throw FeatureComputationError("min_AHP_values and ADP_peak_values should have the same number of elements"); - for (size_t i = 0; i < ADP_peak_values.size(); i++) { - ADP_peak_amplitude.push_back(ADP_peak_values[i] - min_AHP_values[i]); - } - setVec(DoubleFeatureData, StringData, "ADP_peak_amplitude", - ADP_peak_amplitude); - return ADP_peak_amplitude.size(); -} - -static int __interburst_min_indices(const vector& v, - const vector& peak_indices, - const vector& burst_end_indices, - vector& interburst_min_indices, - vector& interburst_min_values) { - unsigned interburst_min_index; - for (size_t i = 0; i < burst_end_indices.size() && - burst_end_indices[i] + 1 < peak_indices.size(); - i++) { - interburst_min_index = - min_element(v.begin() + peak_indices[burst_end_indices[i]], - v.begin() + peak_indices[burst_end_indices[i] + 1]) - - v.begin(); - - interburst_min_indices.push_back(interburst_min_index); - interburst_min_values.push_back(v[interburst_min_index]); - } - return interburst_min_indices.size(); -} - -int LibV5::interburst_min_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); - vector interburst_min_indices; - vector interburst_min_values; - const vector& v = doubleFeatures.at("V"); - const vector& peak_indices = intFeatures.at("peak_indices"); - const vector& burst_end_indices = intFeatures.at("burst_end_indices"); - int retVal = - __interburst_min_indices(v, peak_indices, burst_end_indices, - interburst_min_indices, interburst_min_values); - if (retVal > 0) { - setVec(IntFeatureData, StringData, "interburst_min_indices", - interburst_min_indices); - setVec(DoubleFeatureData, StringData, "interburst_min_values", - interburst_min_values); - } - return retVal; -} - -int LibV5::interburst_min_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -static int __postburst_min_indices(const vector& t, - const vector& v, - const vector& peak_indices, - const vector& burst_end_indices, - vector& postburst_min_indices, - vector& postburst_min_values, - const double stim_end) { - unsigned postburst_min_index, stim_end_index, end_index; - stim_end_index = distance( - t.begin(), find_if(t.begin(), t.end(), - [stim_end](double x) { return x >= stim_end; })); - end_index = distance(t.begin(), t.end()); - for (size_t i = 0; i < burst_end_indices.size(); i++) { - if (burst_end_indices[i] + 1 < peak_indices.size()){ - postburst_min_index = min_element( - v.begin() + peak_indices[burst_end_indices[i]], - v.begin() + peak_indices[burst_end_indices[i] + 1] - ) - v.begin(); - } else if (peak_indices[burst_end_indices[i]] < stim_end_index){ - postburst_min_index = min_element( - v.begin() + peak_indices[burst_end_indices[i]], - v.begin() + stim_end_index - ) - v.begin(); - if (postburst_min_index == stim_end_index){ - continue; - } - } else { - postburst_min_index = min_element( - v.begin() + peak_indices[burst_end_indices[i]], - v.begin() + end_index - ) - v.begin(); - if (postburst_min_index == end_index){ - continue; - } - } - - postburst_min_indices.push_back(postburst_min_index); - postburst_min_values.push_back(v[postburst_min_index]); - } - - return postburst_min_indices.size(); -} - -int LibV5::postburst_min_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); - vector postburst_min_indices; - vector postburst_min_values; - double stim_end = doubleFeatures.at("stim_end").front(); - int retVal = __postburst_min_indices( - doubleFeatures.at("T"), doubleFeatures.at("V"), - intFeatures.at("peak_indices"), intFeatures.at("burst_end_indices"), - postburst_min_indices, postburst_min_values, stim_end); - if (retVal > 0) { - setVec(IntFeatureData, StringData, "postburst_min_indices", - postburst_min_indices); - setVec(DoubleFeatureData, StringData, "postburst_min_values", - postburst_min_values); - } - return retVal; -} - -int LibV5::postburst_min_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -static int __postburst_slow_ahp_indices(const vector& t, - const vector& v, - const vector& peak_indices, - const vector& burst_end_indices, - vector& postburst_slow_ahp_indices, - vector& postburst_slow_ahp_values, - const double stim_end, - const double sahp_start) { - unsigned postburst_slow_ahp_index, stim_end_index, end_index, t_start_index; - stim_end_index = - distance(t.begin(), - find_if(t.begin(), t.end(), - [stim_end](double x) { return x >= stim_end; })); - end_index = distance(t.begin(), t.end()); - for (size_t i = 0; i < burst_end_indices.size(); i++) { - double t_start = t[peak_indices[burst_end_indices[i]]] + sahp_start; - - if (burst_end_indices[i] + 1 < peak_indices.size()){ - t_start_index = find_if( - t.begin() + peak_indices[burst_end_indices[i]], - t.begin() + peak_indices[burst_end_indices[i] + 1], - [t_start](double x) { return x >= t_start; } - ) - t.begin(); - postburst_slow_ahp_index = min_element( - v.begin() + t_start_index, - v.begin() + peak_indices[burst_end_indices[i] + 1] - ) - v.begin(); - } else if (peak_indices[burst_end_indices[i]] < stim_end_index){ - t_start_index = find_if( - t.begin() + peak_indices[burst_end_indices[i]], - t.begin() + stim_end_index, - [t_start](double x) { return x >= t_start; } - ) - t.begin(); - if (t_start_index < stim_end_index){ - postburst_slow_ahp_index = min_element( - v.begin() + t_start_index, v.begin() + stim_end_index - ) - v.begin(); - } else { - // edge case: stim_end_index is 1 index after stim_end - continue; - } - } else { - t_start_index = find_if( - t.begin() + peak_indices[burst_end_indices[i]], - t.begin() + end_index, - [t_start](double x) { return x >= t_start; } - ) - t.begin(); - if (t_start_index < end_index){ - postburst_slow_ahp_index = min_element( - v.begin() + t_start_index, v.begin() + end_index - ) - v.begin(); - } else{ - // edge case: end_index is 1 index after end - continue; - } - } - - postburst_slow_ahp_indices.push_back(postburst_slow_ahp_index); - postburst_slow_ahp_values.push_back(v[postburst_slow_ahp_index]); - } - - return postburst_slow_ahp_indices.size(); -} - -int LibV5::postburst_slow_ahp_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_end", "sahp_start"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); - - vector postburst_slow_ahp_values; - vector postburst_slow_ahp_indices; - int retVal = __postburst_slow_ahp_indices( - doubleFeatures.at("T"), - doubleFeatures.at("V"), - intFeatures.at("peak_indices"), - intFeatures.at("burst_end_indices"), - postburst_slow_ahp_indices, - postburst_slow_ahp_values, - doubleFeatures.at("stim_end").front(), - // time after the spike in ms after which to start searching for minimum - doubleFeatures.at("sahp_start").empty() ? 5.0 : doubleFeatures.at("sahp_start").front() - ); - if (retVal >= 0) { - setVec(IntFeatureData, StringData, "postburst_slow_ahp_indices", - postburst_slow_ahp_indices); - setVec(DoubleFeatureData, StringData, "postburst_slow_ahp_values", - postburst_slow_ahp_values); - } - return retVal; -} - -int LibV5::postburst_slow_ahp_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -int LibV5::time_to_interburst_min(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - // Retrieve all required double and int features at once - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "peak_time"}); - const auto& intFeatures = getFeatures( - IntFeatureData, {"burst_end_indices", "interburst_min_indices"}); - - vector time_to_interburst_min; - const vector& time = doubleFeatures.at("T"); - const vector& peak_time = doubleFeatures.at("peak_time"); - const vector& burst_end_indices = intFeatures.at("burst_end_indices"); - const vector& interburst_min_indices = - intFeatures.at("interburst_min_indices"); - - if (burst_end_indices.size() < interburst_min_indices.size()) { - throw FeatureComputationError("burst_end_indices should not have less elements than interburst_min_indices"); - } - - for (size_t i = 0; i < interburst_min_indices.size(); i++) { - time_to_interburst_min.push_back(time[interburst_min_indices[i]] - - peak_time[burst_end_indices[i]]); - } - - setVec(DoubleFeatureData, StringData, "time_to_interburst_min", - time_to_interburst_min); - return time_to_interburst_min.size(); -} - -int LibV5::time_to_postburst_slow_ahp(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "peak_time"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"postburst_slow_ahp_indices", "burst_end_indices"}); - - vector time_to_postburst_slow_ahp; - const vector& time = doubleFeatures.at("T"); - const vector& peak_time = doubleFeatures.at("peak_time"); - const vector& burst_end_indices = intFeatures.at("burst_end_indices"); - const vector& postburst_slow_ahp_indices = intFeatures.at("postburst_slow_ahp_indices"); - - if (burst_end_indices.size() < postburst_slow_ahp_indices.size()){ - GErrorStr += - "\nburst_end_indices should not have less elements than postburst_slow_ahp_indices\n"; - return -1; - } - - for (size_t i = 0; i < postburst_slow_ahp_indices.size(); i++) { - time_to_postburst_slow_ahp.push_back(time[postburst_slow_ahp_indices[i]] - - peak_time[burst_end_indices[i]]); - } - setVec(DoubleFeatureData, StringData, "time_to_postburst_slow_ahp", - time_to_postburst_slow_ahp); - return (time_to_postburst_slow_ahp.size()); -} - -static int __postburst_fast_ahp_indices(const vector& t, const vector& v, - const vector& peak_indices, - const vector& burst_end_indices, - const double stim_end, - vector& postburst_fast_ahp_indices, - vector& postburst_fast_ahp_values) { - vector start_indices, end_indices; - for (size_t i = 0; i < burst_end_indices.size(); i++) { - start_indices.push_back(peak_indices[burst_end_indices[i]]); - if (burst_end_indices[i] + 1 < peak_indices.size()){ - end_indices.push_back(peak_indices[burst_end_indices[i] + 1]); - } - } - - unsigned end_index = 0; - if (t[start_indices.back()] < stim_end) { - end_index = - distance(t.begin(), - find_if(t.begin(), t.end(), - [stim_end](double x) { return x >= stim_end; })); - } else { - end_index = distance(t.begin(), t.end()); - } - - if (end_indices.size() < start_indices.size()){ - end_indices.push_back(end_index); - } - - size_t fahpindex = 0; - for (size_t i = 0; i < start_indices.size(); i++) { - // can use first_min_element because dv/dt is very steep before fash ahp - // and noise is very unlikely to make voltage go up before reaching fast ahp - fahpindex = distance( - v.begin(), first_min_element(v.begin() + start_indices[i], - v.begin() + end_indices[i])); - - if (fahpindex != end_index - 1) { - postburst_fast_ahp_indices.push_back(fahpindex); - - EFEL_ASSERT(fahpindex < v.size(), - "fast AHP index falls outside of voltage array"); - postburst_fast_ahp_values.push_back(v[fahpindex]); - } - } - - return postburst_fast_ahp_indices.size(); -} - -int LibV5::postburst_fast_ahp_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V", "stim_end"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); - - vector postburst_fast_ahp_indices; - vector postburst_fast_ahp_values; - int retVal = - __postburst_fast_ahp_indices( - doubleFeatures.at("T"), - doubleFeatures.at("V"), - intFeatures.at("peak_indices"), - intFeatures.at("burst_end_indices"), - doubleFeatures.at("stim_end").front(), - postburst_fast_ahp_indices, - postburst_fast_ahp_values - ); - - if (retVal > 0) { - setVec(IntFeatureData, StringData, "postburst_fast_ahp_indices", postburst_fast_ahp_indices); - setVec(DoubleFeatureData, StringData, "postburst_fast_ahp_values", - postburst_fast_ahp_values); - return postburst_fast_ahp_indices.size(); - } - return -1; -} - -int LibV5::postburst_fast_ahp_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -static int __postburst_adp_peak_indices(const vector& t, const vector& v, - const vector& postburst_fast_ahp_indices, - const vector& postburst_slow_ahp_indices, - vector& postburst_adp_peak_indices, - vector& postburst_adp_peak_values) { - - if (postburst_slow_ahp_indices.size() > postburst_fast_ahp_indices.size()){ - GErrorStr += - "\n postburst_slow_ahp should not have more elements than " - "postburst_fast_ahp for postburst_adp_peak_indices calculation.\n"; - return -1; - } - size_t adppeakindex = 0; - for (size_t i = 0; i < postburst_slow_ahp_indices.size(); i++) { - if (postburst_slow_ahp_indices[i] < postburst_fast_ahp_indices[i]){ - continue; - } - adppeakindex = distance( - v.begin(), max_element(v.begin() + postburst_fast_ahp_indices[i], - v.begin() + postburst_slow_ahp_indices[i])); - - if (adppeakindex < postburst_slow_ahp_indices[i] - 1) { - postburst_adp_peak_indices.push_back(adppeakindex); - - EFEL_ASSERT(adppeakindex < v.size(), - "ADP peak index falls outside of voltage array"); - postburst_adp_peak_values.push_back(v[adppeakindex]); - } - } - - return postburst_adp_peak_indices.size(); -} - -int LibV5::postburst_adp_peak_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"postburst_fast_ahp_indices", "postburst_slow_ahp_indices"}); - vector postburst_adp_peak_indices; - vector postburst_adp_peak_values; - int retVal = - __postburst_adp_peak_indices( - doubleFeatures.at("T"), - doubleFeatures.at("V"), - intFeatures.at("postburst_fast_ahp_indices"), - intFeatures.at("postburst_slow_ahp_indices"), - postburst_adp_peak_indices, - postburst_adp_peak_values - ); - - if (retVal > 0) { - setVec(IntFeatureData, StringData, "postburst_adp_peak_indices", postburst_adp_peak_indices); - setVec(DoubleFeatureData, StringData, "postburst_adp_peak_values", - postburst_adp_peak_values); - return retVal; - } - return -1; -} - -int LibV5::postburst_adp_peak_values(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - return 1; -} - -int LibV5::time_to_postburst_fast_ahp(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "peak_time"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"postburst_fast_ahp_indices", "burst_end_indices"}); - - vector time_to_postburst_fast_ahp; - const vector& time = doubleFeatures.at("T"); - const vector& peak_time = doubleFeatures.at("peak_time"); - const vector& burst_end_indices = intFeatures.at("burst_end_indices"); - const vector& postburst_fast_ahp_indices = intFeatures.at("postburst_fast_ahp_indices"); - - if (burst_end_indices.size() < postburst_fast_ahp_indices.size()){ - GErrorStr += - "\nburst_end_indices should not have less elements than postburst_fast_ahp_indices\n"; - return -1; - } - - for (size_t i = 0; i < postburst_fast_ahp_indices.size(); i++) { - time_to_postburst_fast_ahp.push_back(time[postburst_fast_ahp_indices[i]] - - peak_time[burst_end_indices[i]]); - } - setVec(DoubleFeatureData, StringData, "time_to_postburst_fast_ahp", - time_to_postburst_fast_ahp); - return (time_to_postburst_fast_ahp.size()); -} - -int LibV5::time_to_postburst_adp_peak(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "peak_time"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"postburst_adp_peak_indices", "burst_end_indices"}); - - vector time_to_postburst_adp_peak; - const vector& time = doubleFeatures.at("T"); - const vector& peak_time = doubleFeatures.at("peak_time"); - const vector& burst_end_indices = intFeatures.at("burst_end_indices"); - const vector& postburst_adp_peak_indices = intFeatures.at("postburst_adp_peak_indices"); - - if (burst_end_indices.size() < postburst_adp_peak_indices.size()){ - GErrorStr += - "\nburst_end_indices should not have less elements than postburst_adp_peak_indices\n"; - return -1; - } - - for (size_t i = 0; i < postburst_adp_peak_indices.size(); i++) { - // there are not always an adp peak after each burst - // so make sure that the burst and adp peak indices are consistent - size_t k = 0; - while (burst_end_indices[i] + k + 1 < peak_time.size() && - peak_time[burst_end_indices[i] + k + 1] < time[postburst_adp_peak_indices[i]]){ - k++; - } - time_to_postburst_adp_peak.push_back(time[postburst_adp_peak_indices[i]] - - peak_time[burst_end_indices[i] + k]); - } - setVec(DoubleFeatureData, StringData, "time_to_postburst_adp_peak", - time_to_postburst_adp_peak); - return (time_to_postburst_adp_peak.size()); -} - -// index and voltage value at a given percentage of the duration of the interburst after fast AHP -int __interburst_percent_indices(const vector& t, const vector& v, - const vector& postburst_fast_ahp_indices, - const vector& peak_indices, - const vector& burst_end_indices, - vector& interburst_percent_indices, - vector& interburst_percent_values, - // percentage should be a value between 0 and 1 - double fraction) { - - double time_interval, time_at_fraction; - size_t index_at_fraction; - for (size_t i = 0; i < postburst_fast_ahp_indices.size(); i++) { - if (i < burst_end_indices.size()){ - if (burst_end_indices[i] + 1 < peak_indices.size()){ - time_interval = t[peak_indices[burst_end_indices[i] + 1]] - t[postburst_fast_ahp_indices[i]]; - time_at_fraction = t[postburst_fast_ahp_indices[i]] + time_interval * fraction; - index_at_fraction = - distance(t.begin(), - find_if(t.begin(), t.end(), - [time_at_fraction](double x){ return x >= time_at_fraction; })); - interburst_percent_indices.push_back(index_at_fraction); - interburst_percent_values.push_back(v[index_at_fraction]); - } - } - } - return interburst_percent_indices.size(); -} - -int LibV5::interburst_XXpercent_indices(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData, int percent) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"T", "V"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices", "postburst_fast_ahp_indices"}); - - vector interburst_XXpercent_indices; - vector interburst_XXpercent_values; - char featureNameIndices[30], featureNameValues[30]; - sprintf(featureNameIndices, "interburst_%dpercent_indices", percent); - sprintf(featureNameValues, "interburst_%dpercent_values", percent); - - int retVal = - __interburst_percent_indices( - doubleFeatures.at("T"), - doubleFeatures.at("V"), - intFeatures.at("postburst_fast_ahp_indices"), - intFeatures.at("peak_indices"), - intFeatures.at("burst_end_indices"), - interburst_XXpercent_indices, - interburst_XXpercent_values, - percent / 100. - ); - - if (retVal > 0) { - setVec(IntFeatureData, StringData, featureNameIndices, interburst_XXpercent_indices); - setVec(DoubleFeatureData, StringData, featureNameValues, - interburst_XXpercent_values); - return interburst_XXpercent_indices.size(); - } - return -1; -} - -static int __interburst_duration(const vector& peak_time, - const vector& burst_end_indices, - vector& interburst_duration) { - - double duration; - for (size_t i = 0; i < burst_end_indices.size(); i++) { - if (burst_end_indices[i] + 1 < peak_time.size()){ - duration = peak_time[burst_end_indices[i] + 1] - peak_time[burst_end_indices[i]]; - interburst_duration.push_back(duration); - } - } - return interburst_duration.size(); -} - -int LibV5::interburst_duration(mapStr2intVec& IntFeatureData, - mapStr2doubleVec& DoubleFeatureData, - mapStr2Str& StringData) { - const auto& doubleFeatures = - getFeatures(DoubleFeatureData, {"peak_time"}); - const auto& intFeatures = - getFeatures(IntFeatureData, {"burst_end_indices"}); - - vector interburst_duration; - int retVal = - __interburst_duration( - doubleFeatures.at("peak_time"), intFeatures.at("burst_end_indices"), interburst_duration); - - if (retVal > 0) { - setVec(DoubleFeatureData, StringData, "interburst_duration", interburst_duration); - return interburst_duration.size(); - } - return -1; -} diff --git a/efel/cppcore/SpikeEvent.cpp b/efel/cppcore/SpikeEvent.cpp new file mode 100644 index 00000000..5b843009 --- /dev/null +++ b/efel/cppcore/SpikeEvent.cpp @@ -0,0 +1,1269 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EfelExceptions.h" + +using std::distance; +using std::find_if; +using std::list; +using std::max_element; +using std::min_element; + + +static int __peak_indices(double threshold, const vector& V, + const vector& t, vector& PeakIndex, + bool strict_stiminterval, double stim_start, + double stim_end) { + vector upVec, dnVec; + double dtmp; + size_t itmp = 0; + bool itmp_set = false; + + for (size_t i = 1; i < V.size(); i++) { + if (V[i] > threshold && V[i - 1] < threshold) { + upVec.push_back(i); + } else if (V[i] < threshold && V[i - 1] > threshold) { + dnVec.push_back(i); + } + } + if (dnVec.size() == 0) + throw FeatureComputationError("Voltage never goes below or above threshold in spike detection."); + if (upVec.size() == 0) + throw FeatureComputationError("Voltage never goes above threshold in spike detection."); + + // case where voltage starts above threshold: remove 1st dnVec + while (dnVec.size() > 0 && dnVec[0] < upVec[0]) { + dnVec.erase(dnVec.begin()); + } + + if (upVec.size() > dnVec.size()) { + size_t size_diff = upVec.size() - dnVec.size(); + for (size_t i = 0; i < size_diff; i++) { + upVec.pop_back(); + } + } + + PeakIndex.clear(); + int j = 0; + for (size_t i = 0; i < upVec.size(); i++) { + dtmp = -1e9; + itmp = 0; + itmp_set = false; + EFEL_ASSERT(i < dnVec.size(), "dnVec array too small"); + for (j = upVec[i]; j <= dnVec[i]; j++) { + if (dtmp < V[j]) { + dtmp = V[j]; + itmp = j; + itmp_set = true; + } + } + if (itmp_set) { + if (strict_stiminterval) { + EFEL_ASSERT(itmp < t.size(), "peak_time falls outside of time array"); + if (t[itmp] >= stim_start && t[itmp] <= stim_end) { + PeakIndex.push_back(itmp); + } + } else { + PeakIndex.push_back(itmp); + } + } + } + return PeakIndex.size(); +} + +int SpikeEvent::peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"V", "T", "Threshold", "stim_start", "stim_end"}); + bool strict_stiminterval; + try { + const auto& intFeatures = + getFeatures(IntFeatureData, {"strict_stiminterval"}); + strict_stiminterval = bool(intFeatures.at("strict_stiminterval")[0]); + } catch (const std::runtime_error& e) { + strict_stiminterval = false; + } + vector PeakIndex; + int retVal = __peak_indices( + doubleFeatures.at("Threshold")[0], doubleFeatures.at("V"), + doubleFeatures.at("T"), PeakIndex, strict_stiminterval, + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0]); + + if (retVal > 0) { + setVec(IntFeatureData, StringData, "peak_indices", PeakIndex); + } + return retVal; +} + +int SpikeEvent::peak_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); + vector pvTime; + for (const auto& index : intFeatures.at("peak_indices")) { + pvTime.push_back(doubleFeatures.at("T")[index]); + } + setVec(DoubleFeatureData, StringData, "peak_time", pvTime); + return pvTime.size(); +} + +// time from stimulus start to first threshold crossing +int SpikeEvent::first_spike_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); + if (doubleFeatures.at("peak_time").size() < 1) + throw FeatureComputationError("One spike required for time_to_first_spike."); + vector first_spike; + first_spike.push_back(doubleFeatures.at("peak_time")[0] - + doubleFeatures.at("stim_start")[0]); + setVec(DoubleFeatureData, StringData, "time_to_first_spike", first_spike); + return first_spike.size(); +} + +// time from stimulus start to second threshold crossing +int SpikeEvent::time_to_second_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); + const auto& peaktime = doubleFeatures.at("peak_time"); + const auto& stimstart = doubleFeatures.at("stim_start"); + if (peaktime.size() < 2) + throw FeatureComputationError("Two spikes required for time_to_second_spike."); + + vector second_spike = {peaktime[1] - stimstart[0]}; + setVec(DoubleFeatureData, StringData, "time_to_second_spike", second_spike); + return 1; +} + +// 1.0 over time to first spike (in Hz); returns 0 when no spike +int SpikeEvent::inv_time_to_first_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + vector time_to_first_spike_vec = + getFeature(DoubleFeatureData, "time_to_first_spike"); + vector inv_time_to_first_spike_vec; + + double inv_time_to_first_spike = 1000.0 / time_to_first_spike_vec[0]; + inv_time_to_first_spike_vec.push_back(inv_time_to_first_spike); + + setVec(DoubleFeatureData, StringData, "inv_time_to_first_spike", + inv_time_to_first_spike_vec); + return 1; +} + +// *** doublet_ISI *** +// value of the first ISI +int SpikeEvent::doublet_ISI(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_time"}); + if (doubleFeatures.at("peak_time").size() < 2) { + throw FeatureComputationError("Need at least two spikes for doublet_ISI."); + } + vector doubletisi( + 1, doubleFeatures.at("peak_time")[1] - doubleFeatures.at("peak_time")[0]); + setVec(DoubleFeatureData, StringData, "doublet_ISI", doubletisi); + return doubleFeatures.at("peak_time").size(); +} + +int SpikeEvent::all_ISI_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& peak_time = getFeature(DoubleFeatureData, "peak_time"); + if (peak_time.size() < 2) + throw FeatureComputationError("Two spikes required for calculation of all_ISI_values."); + + vector VecISI; + for (size_t i = 1; i < peak_time.size(); i++) { + VecISI.push_back(peak_time[i] - peak_time[i - 1]); + } + setVec(DoubleFeatureData, StringData, "all_ISI_values", VecISI); + return VecISI.size(); +} + +// time from stimulus start to last spike +int SpikeEvent::time_to_last_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time", "stim_start"}); + const auto& peaktime = doubleFeatures.at("peak_time"); + const auto& stimstart = doubleFeatures.at("stim_start"); + + vector last_spike = {peaktime.back() - stimstart[0]}; + + setVec(DoubleFeatureData, StringData, "time_to_last_spike", last_spike); + return 1; +} + +static int __number_initial_spikes(const vector& peak_times, + double stimstart, double stimend, + double initial_perc, + vector& number_initial_spikes) { + double initialLength = (stimend - stimstart) * initial_perc; + + int startIndex = + distance(peak_times.begin(), + find_if(peak_times.begin(), peak_times.end(), + [stimstart](double t) { return t >= stimstart; })); + int endIndex = distance(peak_times.begin(), + find_if(peak_times.begin(), peak_times.end(), + [stimstart, initialLength](double t) { + return t >= stimstart + initialLength; + })); + + number_initial_spikes.push_back(endIndex - startIndex); + + return 1; +} + +// Number of spikes in the initial_perc interval +int SpikeEvent::number_initial_spikes(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, + {"peak_time", "initial_perc", "stim_start", "stim_end"}); + vector number_initial_spikes; + + const vector& peak_times = doubleFeatures.at("peak_time"); + const vector& initial_perc = doubleFeatures.at("initial_perc"); + const vector& stimstart = doubleFeatures.at("stim_start"); + const vector& stimend = doubleFeatures.at("stim_end"); + + if ((initial_perc[0] < 0) || (initial_perc[0] >= 1)) { + throw FeatureComputationError("initial_perc should lie between [0 1)."); + } + + int retVal = __number_initial_spikes(peak_times, stimstart[0], stimend[0], + initial_perc[0], number_initial_spikes); + if (retVal >= 0) { + setVec(IntFeatureData, StringData, "number_initial_spikes", + number_initial_spikes); + } + return retVal; +} + +int SpikeEvent::firing_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time", "stim_start", "stim_end"}); + double lastAPTime = 0.; + int nCount = 0; + for (const auto& time : doubleFeatures.at("peak_time")) { + if ((time >= doubleFeatures.at("stim_start")[0]) && + (time <= doubleFeatures.at("stim_end")[0])) { + lastAPTime = time; + nCount++; + } + } + if (lastAPTime == doubleFeatures.at("stim_start")[0]) + throw FeatureComputationError("Prevent divide by zero."); + vector firing_rate; + firing_rate.push_back(nCount * 1000 / + (lastAPTime - doubleFeatures.at("stim_start")[0])); + setVec(DoubleFeatureData, StringData, "mean_frequency", firing_rate); + return firing_rate.size(); +} + +static int __adaptation_index(double spikeSkipf, int maxnSpike, + double StimStart, double StimEnd, double Offset, + const vector& peakVTime, + vector& adaptation_index) { + list SpikeTime; + vector ISI; + // Select spike time between given time scale (stim_start and stim_end ) + // consider Offset also if it is given as input + for (size_t i = 0; i < peakVTime.size(); i++) { + if ((peakVTime[i] >= (StimStart - Offset)) && + (peakVTime[i] <= (StimEnd + Offset))) { + SpikeTime.push_back(peakVTime[i]); + } + } + // Remove n spikes given by spike_skipf or max_spike_skip + int spikeToRemove = (int)((SpikeTime.size() * spikeSkipf) + 0.5); + // spike To remove is minimum of spike_skipf or max_spike_skip + if (maxnSpike < spikeToRemove) { + spikeToRemove = maxnSpike; + } + + // Remove spikeToRemove spike from SpikeTime list + for (int i = 0; i < spikeToRemove; ++i) { + SpikeTime.pop_front(); + } + + // Adaptation index can not be calculated if nAPVec <4 or no of ISI is <3 + if (SpikeTime.size() < 4) + throw FeatureComputationError("Minimum 4 spikes needed for feature [adaptation_index]."); + + // Generate ISI vector + list::iterator lstItr = SpikeTime.begin(); + double lastValue = *lstItr; + for (lstItr++; lstItr != SpikeTime.end(); lstItr++) { + ISI.push_back(*lstItr - lastValue); + lastValue = *lstItr; + } + + // get addition and subtraction of ISIs + double ISISum, ISISub, ADI; + ADI = ISISum = ISISub = 0; + for (size_t i = 1; i < ISI.size(); i++) { + ISISum = ISI[i] + ISI[i - 1]; + ISISub = ISI[i] - ISI[i - 1]; + ADI = ADI + (ISISub / ISISum); + } + ADI = ADI / (ISI.size() - 1); + adaptation_index.clear(); + adaptation_index.push_back(ADI); + return 1; +} + +int SpikeEvent::adaptation_index(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, + {"peak_time", "stim_start", "stim_end", "spike_skipf"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"max_spike_skip"}); + + if (doubleFeatures.at("spike_skipf")[0] < 0 || + doubleFeatures.at("spike_skipf")[0] >= 1) { + throw FeatureComputationError("spike_skipf should lie between [0 1)."); + } + vector OffSetVec; + double Offset; + int retval = getParam(DoubleFeatureData, "offset", OffSetVec); + if (retval < 0) { + Offset = 0; // Keep old behavior, set Offset to 0 if offset is not found + } else { + Offset = OffSetVec[0]; // Use the first element of OffSetVec if found + } + + vector adaptation_index; + int retVal = __adaptation_index( + doubleFeatures.at("spike_skipf")[0], intFeatures.at("max_spike_skip")[0], + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], + Offset, doubleFeatures.at("peak_time"), adaptation_index); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "adaptation_index", adaptation_index); + } + return retVal; +} + +// *** adaptation_index2 *** +// as adaptation_index, but start at the second ISI instead of the round(N * +// spikeskipf) +static int __adaptation_index2(double StimStart, double StimEnd, double Offset, + const vector& peakVTime, + vector& adaptation_index) { + list SpikeTime; + vector ISI; + // Select spike time between given time scale (stim_start and stim_end ) + // considet Offset also if it is given as input + for (size_t i = 0; i < peakVTime.size(); i++) { + if ((peakVTime[i] >= (StimStart - Offset)) && + (peakVTime[i] <= (StimEnd + Offset))) { + SpikeTime.push_back(peakVTime[i]); + } + } + + if (SpikeTime.size() < 4) { + throw FeatureComputationError("At least 4 spikes within stimulus interval needed for adaptation_index2."); + } + // start at second ISI: + SpikeTime.pop_front(); + + // Generate ISI vector + list::iterator lstItr = SpikeTime.begin(); + double lastValue = *lstItr; + for (++lstItr; lstItr != SpikeTime.end(); ++lstItr) { + ISI.push_back(*lstItr - lastValue); + lastValue = *lstItr; + } + + // get addition and subtraction of ISIs + double ISISum, ISISub, ADI; + ADI = ISISum = ISISub = 0; + for (size_t i = 1; i < ISI.size(); i++) { + ISISum = ISI[i] + ISI[i - 1]; + ISISub = ISI[i] - ISI[i - 1]; + ADI = ADI + (ISISub / ISISum); + } + ADI = ADI / (ISI.size() - 1); + adaptation_index.clear(); + adaptation_index.push_back(ADI); + return 1; +} + +int SpikeEvent::adaptation_index2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time", "stim_start", "stim_end"}); + vector OffSetVec; + double Offset; + int retval = getParam(DoubleFeatureData, "offset", OffSetVec); + if (retval < 0) + Offset = 0; + else + Offset = OffSetVec[0]; + + if (doubleFeatures.at("peak_time").size() < 4) { + throw FeatureComputationError("At least 4 spikes needed for adaptation_index2."); + } + + vector adaptationindex2; + retval = __adaptation_index2( + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], + Offset, doubleFeatures.at("peak_time"), adaptationindex2); + + if (retval >= 0) { + setVec(DoubleFeatureData, StringData, "adaptation_index2", + adaptationindex2); + } + return retval; +} + +// the recorded indices correspond to the peak indices +// does not skip the first ISI by default +static int __burst_indices(double burst_factor, int IgnoreFirstISI, + const vector ISI_values, + vector& burst_begin_indices, + vector& burst_end_indices) { + vector ISIpcopy; + vector::iterator it1, it2; + int n; + double dMedian; + bool in_burst; + int first_ISI = IgnoreFirstISI, count = IgnoreFirstISI; + + burst_begin_indices.push_back(first_ISI); + + for (size_t i = first_ISI + 1; i < (ISI_values.size()); i++) { + // get median + ISIpcopy.clear(); + for (size_t j = count; j < i; j++) ISIpcopy.push_back(ISI_values[j]); + sort(ISIpcopy.begin(), ISIpcopy.end()); + n = ISIpcopy.size(); + if ((n % 2) == 0) { + dMedian = + (ISIpcopy[int((n - 1) / 2)] + ISIpcopy[int((n - 1) / 2) + 1]) / 2; + } else { + dMedian = ISIpcopy[int(n / 2)]; + } + + in_burst = (burst_end_indices.size() == 0 || + burst_begin_indices.back() > burst_end_indices.back()); + + // look for end burst + if (in_burst && ISI_values[i] > (burst_factor * dMedian)) { + burst_end_indices.push_back(i); + count = i; + } + + if (ISI_values[i] < ISI_values[i - 1] / burst_factor) { + if (in_burst) { + burst_begin_indices.back() = i; + } else { + burst_begin_indices.push_back(i); + } + count = i; + } + } + + in_burst = (burst_end_indices.size() == 0 || + burst_begin_indices.back() > burst_end_indices.back()); + if (in_burst) { + burst_end_indices.push_back(ISI_values.size()); + } + + return burst_begin_indices.size(); +} + +int SpikeEvent::burst_begin_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + int retVal; + vector burst_begin_indices, burst_end_indices, retIgnore; + vector ISI_values, tVec; + int IgnoreFirstISI; + double burst_factor = 0; + ISI_values = getFeature(DoubleFeatureData, "all_ISI_values"); + if (ISI_values.size() < 2) { + GErrorStr += + "\nError: At least than 3 spikes are needed for burst calculation.\n"; + return -1; + } + retVal = getParam(DoubleFeatureData, "strict_burst_factor", tVec); + if (retVal < 0) + burst_factor = 2; + else + burst_factor = tVec[0]; + + retVal = getParam(IntFeatureData, "ignore_first_ISI", retIgnore); + if ((retVal == 1) && (retIgnore.size() > 0) && (retIgnore[0] == 0)) + IgnoreFirstISI = 0; + else + IgnoreFirstISI = 1; + + retVal = __burst_indices(burst_factor, IgnoreFirstISI, ISI_values, + burst_begin_indices, burst_end_indices); + if (retVal >= 0) { + setVec(IntFeatureData, StringData, "burst_begin_indices", + burst_begin_indices); + setVec(IntFeatureData, StringData, "burst_end_indices", burst_end_indices); + } + return retVal; +} + +int SpikeEvent::burst_end_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +static int __strict_burst_mean_freq(const vector& PVTime, + const vector& burst_begin_indices, + const vector& burst_end_indices, + vector& BurstMeanFreq) { + if (burst_begin_indices.size() == 0) return BurstMeanFreq.size(); + double span; + size_t i; + + for (i = 0; i < burst_begin_indices.size(); i++) { + if (burst_end_indices[i] - burst_begin_indices[i] > 0) { + span = PVTime[burst_end_indices[i]] - PVTime[burst_begin_indices[i]]; + BurstMeanFreq.push_back( + (burst_end_indices[i] - burst_begin_indices[i] + 1) * 1000 / span); + } + } + + return BurstMeanFreq.size(); +} + +int SpikeEvent::strict_burst_mean_freq(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_time"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"burst_begin_indices", "burst_end_indices"}); + + vector BurstMeanFreq; + int retVal = __strict_burst_mean_freq( + doubleFeatures.at("peak_time"), intFeatures.at("burst_begin_indices"), + intFeatures.at("burst_end_indices"), BurstMeanFreq); + + if (retVal >= 0) { + setVec(DoubleFeatureData, StringData, "strict_burst_mean_freq", + BurstMeanFreq); + } + return retVal; +} + +static int __strict_interburst_voltage(const vector& burst_begin_indices, + const vector& PeakIndex, + const vector& T, + const vector& V, + vector& IBV) { + if (burst_begin_indices.size() < 1) return 0; + int j, pIndex, tsIndex, teIndex, cnt; + double tStart, tEnd, vTotal = 0; + for (size_t i = 1; i < burst_begin_indices.size(); i++) { + pIndex = burst_begin_indices[i] - 1; + tsIndex = PeakIndex[pIndex]; + tStart = T[tsIndex] + 5; // 5 millisecond after + pIndex = burst_begin_indices[i]; + teIndex = PeakIndex[pIndex]; + tEnd = T[teIndex] - 5; // 5 millisecond before + + for (j = tsIndex; j < teIndex; j++) { + if (T[j] > tStart) break; + } + tsIndex = --j; + + for (j = teIndex; j > tsIndex; j--) { + if (T[j] < tEnd) break; + } + teIndex = ++j; + vTotal = 0; + for (j = tsIndex, cnt = 1; j <= teIndex; j++, cnt++) vTotal = vTotal + V[j]; + if (cnt == 0) continue; + IBV.push_back(vTotal / (cnt - 1)); + } + return IBV.size(); +} + +int SpikeEvent::strict_interburst_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_begin_indices"}); + vector IBV; + int retVal = __strict_interburst_voltage( + intFeatures.at("burst_begin_indices"), intFeatures.at("peak_indices"), + doubleFeatures.at("T"), doubleFeatures.at("V"), IBV); + + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "strict_interburst_voltage", IBV); + } + return retVal; +} + +static int __interburst_min_indices(const vector& v, + const vector& peak_indices, + const vector& burst_end_indices, + vector& interburst_min_indices, + vector& interburst_min_values) { + unsigned interburst_min_index; + for (size_t i = 0; i < burst_end_indices.size() && + burst_end_indices[i] + 1 < peak_indices.size(); + i++) { + interburst_min_index = + min_element(v.begin() + peak_indices[burst_end_indices[i]], + v.begin() + peak_indices[burst_end_indices[i] + 1]) - + v.begin(); + + interburst_min_indices.push_back(interburst_min_index); + interburst_min_values.push_back(v[interburst_min_index]); + } + return interburst_min_indices.size(); +} + +int SpikeEvent::interburst_min_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); + vector interburst_min_indices; + vector interburst_min_values; + const vector& v = doubleFeatures.at("V"); + const vector& peak_indices = intFeatures.at("peak_indices"); + const vector& burst_end_indices = intFeatures.at("burst_end_indices"); + int retVal = + __interburst_min_indices(v, peak_indices, burst_end_indices, + interburst_min_indices, interburst_min_values); + if (retVal > 0) { + setVec(IntFeatureData, StringData, "interburst_min_indices", + interburst_min_indices); + setVec(DoubleFeatureData, StringData, "interburst_min_values", + interburst_min_values); + } + return retVal; +} + +int SpikeEvent::interburst_min_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +static int __postburst_min_indices(const vector& t, + const vector& v, + const vector& peak_indices, + const vector& burst_end_indices, + vector& postburst_min_indices, + vector& postburst_min_values, + const double stim_end) { + unsigned postburst_min_index, stim_end_index, end_index; + stim_end_index = distance( + t.begin(), find_if(t.begin(), t.end(), + [stim_end](double x) { return x >= stim_end; })); + end_index = distance(t.begin(), t.end()); + for (size_t i = 0; i < burst_end_indices.size(); i++) { + if (burst_end_indices[i] + 1 < peak_indices.size()){ + postburst_min_index = min_element( + v.begin() + peak_indices[burst_end_indices[i]], + v.begin() + peak_indices[burst_end_indices[i] + 1] + ) - v.begin(); + } else if (peak_indices[burst_end_indices[i]] < stim_end_index){ + postburst_min_index = min_element( + v.begin() + peak_indices[burst_end_indices[i]], + v.begin() + stim_end_index + ) - v.begin(); + if (postburst_min_index == stim_end_index){ + continue; + } + } else { + postburst_min_index = min_element( + v.begin() + peak_indices[burst_end_indices[i]], + v.begin() + end_index + ) - v.begin(); + if (postburst_min_index == end_index){ + continue; + } + } + + postburst_min_indices.push_back(postburst_min_index); + postburst_min_values.push_back(v[postburst_min_index]); + } + + return postburst_min_indices.size(); +} + +int SpikeEvent::postburst_min_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); + vector postburst_min_indices; + vector postburst_min_values; + double stim_end = doubleFeatures.at("stim_end").front(); + int retVal = __postburst_min_indices( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), intFeatures.at("burst_end_indices"), + postburst_min_indices, postburst_min_values, stim_end); + if (retVal > 0) { + setVec(IntFeatureData, StringData, "postburst_min_indices", + postburst_min_indices); + setVec(DoubleFeatureData, StringData, "postburst_min_values", + postburst_min_values); + } + return retVal; +} + +int SpikeEvent::postburst_min_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +int SpikeEvent::time_to_interburst_min(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "peak_time"}); + const auto& intFeatures = getFeatures( + IntFeatureData, {"burst_end_indices", "interburst_min_indices"}); + + vector time_to_interburst_min; + const vector& time = doubleFeatures.at("T"); + const vector& peak_time = doubleFeatures.at("peak_time"); + const vector& burst_end_indices = intFeatures.at("burst_end_indices"); + const vector& interburst_min_indices = + intFeatures.at("interburst_min_indices"); + + if (burst_end_indices.size() < interburst_min_indices.size()) { + throw FeatureComputationError("burst_end_indices should not have less elements than interburst_min_indices"); + } + + for (size_t i = 0; i < interburst_min_indices.size(); i++) { + time_to_interburst_min.push_back(time[interburst_min_indices[i]] - + peak_time[burst_end_indices[i]]); + } + + setVec(DoubleFeatureData, StringData, "time_to_interburst_min", + time_to_interburst_min); + return time_to_interburst_min.size(); +} + +static int __postburst_slow_ahp_indices(const vector& t, + const vector& v, + const vector& peak_indices, + const vector& burst_end_indices, + vector& postburst_slow_ahp_indices, + vector& postburst_slow_ahp_values, + const double stim_end, + const double sahp_start) { + unsigned postburst_slow_ahp_index, stim_end_index, end_index, t_start_index; + stim_end_index = + distance(t.begin(), + find_if(t.begin(), t.end(), + [stim_end](double x) { return x >= stim_end; })); + end_index = distance(t.begin(), t.end()); + for (size_t i = 0; i < burst_end_indices.size(); i++) { + double t_start = t[peak_indices[burst_end_indices[i]]] + sahp_start; + + if (burst_end_indices[i] + 1 < peak_indices.size()){ + t_start_index = find_if( + t.begin() + peak_indices[burst_end_indices[i]], + t.begin() + peak_indices[burst_end_indices[i] + 1], + [t_start](double x) { return x >= t_start; } + ) - t.begin(); + postburst_slow_ahp_index = min_element( + v.begin() + t_start_index, + v.begin() + peak_indices[burst_end_indices[i] + 1] + ) - v.begin(); + } else if (peak_indices[burst_end_indices[i]] < stim_end_index){ + t_start_index = find_if( + t.begin() + peak_indices[burst_end_indices[i]], + t.begin() + stim_end_index, + [t_start](double x) { return x >= t_start; } + ) - t.begin(); + if (t_start_index < stim_end_index){ + postburst_slow_ahp_index = min_element( + v.begin() + t_start_index, v.begin() + stim_end_index + ) - v.begin(); + } else { + // edge case: stim_end_index is 1 index after stim_end + continue; + } + } else { + t_start_index = find_if( + t.begin() + peak_indices[burst_end_indices[i]], + t.begin() + end_index, + [t_start](double x) { return x >= t_start; } + ) - t.begin(); + if (t_start_index < end_index){ + postburst_slow_ahp_index = min_element( + v.begin() + t_start_index, v.begin() + end_index + ) - v.begin(); + } else{ + // edge case: end_index is 1 index after end + continue; + } + } + + postburst_slow_ahp_indices.push_back(postburst_slow_ahp_index); + postburst_slow_ahp_values.push_back(v[postburst_slow_ahp_index]); + } + + return postburst_slow_ahp_indices.size(); +} + +int SpikeEvent::postburst_slow_ahp_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_end", "sahp_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); + + vector postburst_slow_ahp_values; + vector postburst_slow_ahp_indices; + int retVal = __postburst_slow_ahp_indices( + doubleFeatures.at("T"), + doubleFeatures.at("V"), + intFeatures.at("peak_indices"), + intFeatures.at("burst_end_indices"), + postburst_slow_ahp_indices, + postburst_slow_ahp_values, + doubleFeatures.at("stim_end").front(), + // time after the spike in ms after which to start searching for minimum + doubleFeatures.at("sahp_start").empty() ? 5.0 : doubleFeatures.at("sahp_start").front() + ); + if (retVal >= 0) { + setVec(IntFeatureData, StringData, "postburst_slow_ahp_indices", + postburst_slow_ahp_indices); + setVec(DoubleFeatureData, StringData, "postburst_slow_ahp_values", + postburst_slow_ahp_values); + } + return retVal; +} + +int SpikeEvent::postburst_slow_ahp_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +int SpikeEvent::time_to_postburst_slow_ahp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "peak_time"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"postburst_slow_ahp_indices", "burst_end_indices"}); + + vector time_to_postburst_slow_ahp; + const vector& time = doubleFeatures.at("T"); + const vector& peak_time = doubleFeatures.at("peak_time"); + const vector& burst_end_indices = intFeatures.at("burst_end_indices"); + const vector& postburst_slow_ahp_indices = intFeatures.at("postburst_slow_ahp_indices"); + + if (burst_end_indices.size() < postburst_slow_ahp_indices.size()){ + GErrorStr += + "\nburst_end_indices should not have less elements than postburst_slow_ahp_indices\n"; + return -1; + } + + for (size_t i = 0; i < postburst_slow_ahp_indices.size(); i++) { + time_to_postburst_slow_ahp.push_back(time[postburst_slow_ahp_indices[i]] - + peak_time[burst_end_indices[i]]); + } + setVec(DoubleFeatureData, StringData, "time_to_postburst_slow_ahp", + time_to_postburst_slow_ahp); + return (time_to_postburst_slow_ahp.size()); +} + +static int __postburst_fast_ahp_indices(const vector& t, const vector& v, + const vector& peak_indices, + const vector& burst_end_indices, + const double stim_end, + vector& postburst_fast_ahp_indices, + vector& postburst_fast_ahp_values) { + vector start_indices, end_indices; + for (size_t i = 0; i < burst_end_indices.size(); i++) { + start_indices.push_back(peak_indices[burst_end_indices[i]]); + if (burst_end_indices[i] + 1 < peak_indices.size()){ + end_indices.push_back(peak_indices[burst_end_indices[i] + 1]); + } + } + + unsigned end_index = 0; + if (t[start_indices.back()] < stim_end) { + end_index = + distance(t.begin(), + find_if(t.begin(), t.end(), + [stim_end](double x) { return x >= stim_end; })); + } else { + end_index = distance(t.begin(), t.end()); + } + + if (end_indices.size() < start_indices.size()){ + end_indices.push_back(end_index); + } + + size_t fahpindex = 0; + for (size_t i = 0; i < start_indices.size(); i++) { + // can use first_min_element because dv/dt is very steep before fash ahp + // and noise is very unlikely to make voltage go up before reaching fast ahp + fahpindex = distance( + v.begin(), first_min_element(v.begin() + start_indices[i], + v.begin() + end_indices[i])); + + if (fahpindex != end_index - 1) { + postburst_fast_ahp_indices.push_back(fahpindex); + + EFEL_ASSERT(fahpindex < v.size(), + "fast AHP index falls outside of voltage array"); + postburst_fast_ahp_values.push_back(v[fahpindex]); + } + } + + return postburst_fast_ahp_indices.size(); +} + +int SpikeEvent::postburst_fast_ahp_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices"}); + + vector postburst_fast_ahp_indices; + vector postburst_fast_ahp_values; + int retVal = + __postburst_fast_ahp_indices( + doubleFeatures.at("T"), + doubleFeatures.at("V"), + intFeatures.at("peak_indices"), + intFeatures.at("burst_end_indices"), + doubleFeatures.at("stim_end").front(), + postburst_fast_ahp_indices, + postburst_fast_ahp_values + ); + + if (retVal > 0) { + setVec(IntFeatureData, StringData, "postburst_fast_ahp_indices", postburst_fast_ahp_indices); + setVec(DoubleFeatureData, StringData, "postburst_fast_ahp_values", + postburst_fast_ahp_values); + return postburst_fast_ahp_indices.size(); + } + return -1; +} + +int SpikeEvent::postburst_fast_ahp_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +static int __postburst_adp_peak_indices(const vector& t, const vector& v, + const vector& postburst_fast_ahp_indices, + const vector& postburst_slow_ahp_indices, + vector& postburst_adp_peak_indices, + vector& postburst_adp_peak_values) { + + if (postburst_slow_ahp_indices.size() > postburst_fast_ahp_indices.size()){ + GErrorStr += + "\n postburst_slow_ahp should not have more elements than " + "postburst_fast_ahp for postburst_adp_peak_indices calculation.\n"; + return -1; + } + size_t adppeakindex = 0; + for (size_t i = 0; i < postburst_slow_ahp_indices.size(); i++) { + if (postburst_slow_ahp_indices[i] < postburst_fast_ahp_indices[i]){ + continue; + } + adppeakindex = distance( + v.begin(), max_element(v.begin() + postburst_fast_ahp_indices[i], + v.begin() + postburst_slow_ahp_indices[i])); + + if (adppeakindex < postburst_slow_ahp_indices[i] - 1) { + postburst_adp_peak_indices.push_back(adppeakindex); + + EFEL_ASSERT(adppeakindex < v.size(), + "ADP peak index falls outside of voltage array"); + postburst_adp_peak_values.push_back(v[adppeakindex]); + } + } + + return postburst_adp_peak_indices.size(); +} + +int SpikeEvent::postburst_adp_peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"postburst_fast_ahp_indices", "postburst_slow_ahp_indices"}); + vector postburst_adp_peak_indices; + vector postburst_adp_peak_values; + int retVal = + __postburst_adp_peak_indices( + doubleFeatures.at("T"), + doubleFeatures.at("V"), + intFeatures.at("postburst_fast_ahp_indices"), + intFeatures.at("postburst_slow_ahp_indices"), + postburst_adp_peak_indices, + postburst_adp_peak_values + ); + + if (retVal > 0) { + setVec(IntFeatureData, StringData, "postburst_adp_peak_indices", postburst_adp_peak_indices); + setVec(DoubleFeatureData, StringData, "postburst_adp_peak_values", + postburst_adp_peak_values); + return retVal; + } + return -1; +} + +int SpikeEvent::postburst_adp_peak_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +int SpikeEvent::time_to_postburst_fast_ahp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "peak_time"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"postburst_fast_ahp_indices", "burst_end_indices"}); + + vector time_to_postburst_fast_ahp; + const vector& time = doubleFeatures.at("T"); + const vector& peak_time = doubleFeatures.at("peak_time"); + const vector& burst_end_indices = intFeatures.at("burst_end_indices"); + const vector& postburst_fast_ahp_indices = intFeatures.at("postburst_fast_ahp_indices"); + + if (burst_end_indices.size() < postburst_fast_ahp_indices.size()){ + GErrorStr += + "\nburst_end_indices should not have less elements than postburst_fast_ahp_indices\n"; + return -1; + } + + for (size_t i = 0; i < postburst_fast_ahp_indices.size(); i++) { + time_to_postburst_fast_ahp.push_back(time[postburst_fast_ahp_indices[i]] - + peak_time[burst_end_indices[i]]); + } + setVec(DoubleFeatureData, StringData, "time_to_postburst_fast_ahp", + time_to_postburst_fast_ahp); + return (time_to_postburst_fast_ahp.size()); +} + +int SpikeEvent::time_to_postburst_adp_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "peak_time"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"postburst_adp_peak_indices", "burst_end_indices"}); + + vector time_to_postburst_adp_peak; + const vector& time = doubleFeatures.at("T"); + const vector& peak_time = doubleFeatures.at("peak_time"); + const vector& burst_end_indices = intFeatures.at("burst_end_indices"); + const vector& postburst_adp_peak_indices = intFeatures.at("postburst_adp_peak_indices"); + + if (burst_end_indices.size() < postburst_adp_peak_indices.size()){ + GErrorStr += + "\nburst_end_indices should not have less elements than postburst_adp_peak_indices\n"; + return -1; + } + + for (size_t i = 0; i < postburst_adp_peak_indices.size(); i++) { + // there are not always an adp peak after each burst + // so make sure that the burst and adp peak indices are consistent + size_t k = 0; + while (burst_end_indices[i] + k + 1 < peak_time.size() && + peak_time[burst_end_indices[i] + k + 1] < time[postburst_adp_peak_indices[i]]){ + k++; + } + time_to_postburst_adp_peak.push_back(time[postburst_adp_peak_indices[i]] - + peak_time[burst_end_indices[i] + k]); + } + setVec(DoubleFeatureData, StringData, "time_to_postburst_adp_peak", + time_to_postburst_adp_peak); + return (time_to_postburst_adp_peak.size()); +} + +// index and voltage value at a given percentage of the duration of the interburst after fast AHP +int __interburst_percent_indices(const vector& t, const vector& v, + const vector& postburst_fast_ahp_indices, + const vector& peak_indices, + const vector& burst_end_indices, + vector& interburst_percent_indices, + vector& interburst_percent_values, + // percentage should be a value between 0 and 1 + double fraction) { + + double time_interval, time_at_fraction; + size_t index_at_fraction; + for (size_t i = 0; i < postburst_fast_ahp_indices.size(); i++) { + if (i < burst_end_indices.size()){ + if (burst_end_indices[i] + 1 < peak_indices.size()){ + time_interval = t[peak_indices[burst_end_indices[i] + 1]] - t[postburst_fast_ahp_indices[i]]; + time_at_fraction = t[postburst_fast_ahp_indices[i]] + time_interval * fraction; + index_at_fraction = + distance(t.begin(), + find_if(t.begin(), t.end(), + [time_at_fraction](double x){ return x >= time_at_fraction; })); + interburst_percent_indices.push_back(index_at_fraction); + interburst_percent_values.push_back(v[index_at_fraction]); + } + } + } + return interburst_percent_indices.size(); +} + +int SpikeEvent::interburst_XXpercent_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData, int percent) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "burst_end_indices", "postburst_fast_ahp_indices"}); + + vector interburst_XXpercent_indices; + vector interburst_XXpercent_values; + char featureNameIndices[30], featureNameValues[30]; + sprintf(featureNameIndices, "interburst_%dpercent_indices", percent); + sprintf(featureNameValues, "interburst_%dpercent_values", percent); + + int retVal = + __interburst_percent_indices( + doubleFeatures.at("T"), + doubleFeatures.at("V"), + intFeatures.at("postburst_fast_ahp_indices"), + intFeatures.at("peak_indices"), + intFeatures.at("burst_end_indices"), + interburst_XXpercent_indices, + interburst_XXpercent_values, + percent / 100. + ); + + if (retVal > 0) { + setVec(IntFeatureData, StringData, featureNameIndices, interburst_XXpercent_indices); + setVec(DoubleFeatureData, StringData, featureNameValues, + interburst_XXpercent_values); + return interburst_XXpercent_indices.size(); + } + return -1; +} + +static int __interburst_duration(const vector& peak_time, + const vector& burst_end_indices, + vector& interburst_duration) { + + double duration; + for (size_t i = 0; i < burst_end_indices.size(); i++) { + if (burst_end_indices[i] + 1 < peak_time.size()){ + duration = peak_time[burst_end_indices[i] + 1] - peak_time[burst_end_indices[i]]; + interburst_duration.push_back(duration); + } + } + return interburst_duration.size(); +} + +int SpikeEvent::interburst_duration(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"peak_time"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"burst_end_indices"}); + + vector interburst_duration; + int retVal = + __interburst_duration( + doubleFeatures.at("peak_time"), intFeatures.at("burst_end_indices"), interburst_duration); + + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "interburst_duration", interburst_duration); + return interburst_duration.size(); + } + return -1; +} + +// Check if a cell is transiently stuck (i.e. not firing any spikes) at the end +// of +// retval will be -1 if the cell gets stuck, retval will be 1 if the cell +// doesn't get stuck +int SpikeEvent::is_not_stuck(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& peak_time = getFeature(DoubleFeatureData, "peak_time"); + const vector& stim_start = + getFeature(DoubleFeatureData, "stim_start"); + const vector& stim_end = getFeature(DoubleFeatureData, "stim_end"); + bool stuck = true; + for (const auto& pt : peak_time) { + if (pt > stim_end[0] * 0.5 && pt < stim_end[0]) { + stuck = false; + break; + } + } + if (!stuck) { + vector tc = {1}; + setVec(IntFeatureData, StringData, "is_not_stuck", tc); + return tc.size(); + } else { + return -1; + } +} diff --git a/efel/cppcore/SpikeEvent.h b/efel/cppcore/SpikeEvent.h new file mode 100644 index 00000000..70f535a0 --- /dev/null +++ b/efel/cppcore/SpikeEvent.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "mapoperations.h" +#include "Utils.h" + +#include +#include + +using std::vector; + +namespace SpikeEvent { +int peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int peak_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int first_spike_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_second_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int inv_time_to_first_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int doublet_ISI(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int all_ISI_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_last_spike(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int number_initial_spikes(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int firing_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int adaptation_index(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int adaptation_index2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int burst_begin_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int burst_end_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int strict_burst_mean_freq(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int strict_interburst_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int interburst_min_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int interburst_min_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_min_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_min_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_interburst_min(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_slow_ahp_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); + +int postburst_slow_ahp_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_postburst_slow_ahp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_fast_ahp_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_fast_ahp_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_adp_peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int postburst_adp_peak_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_postburst_fast_ahp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_to_postburst_adp_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int interburst_XXpercent_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData, int percent); +int interburst_duration(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int is_not_stuck(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +} diff --git a/efel/cppcore/SpikeShape.cpp b/efel/cppcore/SpikeShape.cpp new file mode 100644 index 00000000..89b27af0 --- /dev/null +++ b/efel/cppcore/SpikeShape.cpp @@ -0,0 +1,2309 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EfelExceptions.h" + +using std::distance; +using std::find_if; +using std::max_element; +using std::min_element; +using std::transform; + + +int SpikeShape::peak_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); + vector peakV; + for (const auto& index : intFeatures.at("peak_indices")) { + peakV.push_back(doubleFeatures.at("V")[index]); + } + setVec(DoubleFeatureData, StringData, "peak_voltage", peakV); + return peakV.size(); +} + +int SpikeShape::AP_height(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"peak_voltage"}); + setVec(DoubleFeatureData, StringData, "AP_height", + doubleFeatures.at("peak_voltage")); + return doubleFeatures.at("peak_voltage").size(); +} + +// spike amplitude: peak_voltage - v[AP_begin_indices] +int SpikeShape::AP_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, + {"V", "stim_start", "stim_end", "peak_voltage", "peak_time"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); + if (doubleFeatures.at("peak_voltage").size() != doubleFeatures.at("peak_time").size()) + throw FeatureComputationError("AP_amplitude: Not the same amount of peak_time and peak_voltage entries"); + vector peakvoltage_duringstim; + for (size_t i = 0; i < doubleFeatures.at("peak_time").size(); i++) { + if (doubleFeatures.at("peak_time")[i] >= + doubleFeatures.at("stim_start")[0] && + doubleFeatures.at("peak_time")[i] <= doubleFeatures.at("stim_end")[0]) { + peakvoltage_duringstim.push_back(doubleFeatures.at("peak_voltage")[i]); + } + } + if (peakvoltage_duringstim.size() > intFeatures.at("AP_begin_indices").size()) + throw FeatureComputationError("AP_amplitude: More peak_voltage entries during the stimulus than AP_begin_indices entries"); + vector apamplitude; + apamplitude.resize(peakvoltage_duringstim.size()); + for (size_t i = 0; i < apamplitude.size(); i++) { + apamplitude[i] = + peakvoltage_duringstim[i] - + doubleFeatures.at("V")[intFeatures.at("AP_begin_indices")[i]]; + } + setVec(DoubleFeatureData, StringData, "AP_amplitude", apamplitude); + return apamplitude.size(); +} + +// Amplitude of the first spike +int SpikeShape::AP1_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_amplitudes = + getFeature(DoubleFeatureData, "AP_amplitude"); + vector AP1_amp; + AP1_amp.push_back(AP_amplitudes[0]); + setVec(DoubleFeatureData, StringData, "AP1_amp", AP1_amp); + return 1; +} + +// Amplitude of the second spike +int SpikeShape::AP2_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_amplitudes = + getFeature(DoubleFeatureData, "AP_amplitude"); + vector AP2_amp; + if (AP_amplitudes.size() < 2) { + throw FeatureComputationError( + "Size of AP_amplitude should be >= 2 for AP2_amp"); + } + AP2_amp.push_back(AP_amplitudes[1]); + setVec(DoubleFeatureData, StringData, "AP2_amp", AP2_amp); + return 1; +} + +int SpikeShape::mean_AP_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_amplitude = + getFeature(DoubleFeatureData, "AP_amplitude"); + double mean_amp = 0.0; + for (const auto& amplitude : AP_amplitude) { + mean_amp += amplitude; + } + + mean_amp /= AP_amplitude.size(); + vector mean_AP_amplitude = {mean_amp}; + + setVec(DoubleFeatureData, StringData, "mean_AP_amplitude", mean_AP_amplitude); + + return mean_AP_amplitude.size(); +} + +// Amplitude of the first spike +int SpikeShape::APlast_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_amplitudes = + getFeature(DoubleFeatureData, "AP_amplitude"); + vector APlast_amp; + APlast_amp.push_back(AP_amplitudes[AP_amplitudes.size() - 1]); + setVec(DoubleFeatureData, StringData, "APlast_amp", APlast_amp); + return 1; +} + +// *** AP_amplitude_change according to E22 *** +static int __AP_amplitude_change(const vector& apamplitude, + vector& apamplitudechange) { + if (apamplitude.size() < 1) { + return -1; + } + apamplitudechange.resize(apamplitude.size() - 1); + for (size_t i = 0; i < apamplitudechange.size(); i++) { + apamplitudechange[i] = + (apamplitude[i + 1] - apamplitude[0]) / apamplitude[0]; + } + return apamplitudechange.size(); +} +int SpikeShape::AP_amplitude_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"AP_amplitude"}); + const vector& apamplitude = features.at("AP_amplitude"); + + vector apamplitudechange; + int retval = __AP_amplitude_change(apamplitude, apamplitudechange); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_amplitude_change", + apamplitudechange); + } + return retval; +} + +// spike amplitude: peak_voltage - voltage_base +int SpikeShape::AP_amplitude_from_voltagebase(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"voltage_base", "peak_voltage"}); + vector apamplitude; + for (const auto& peak : doubleFeatures.at("peak_voltage")) { + apamplitude.push_back(peak - doubleFeatures.at("voltage_base")[0]); + } + setVec(DoubleFeatureData, StringData, "AP_amplitude_from_voltagebase", + apamplitude); + return apamplitude.size(); +} + +// Peak voltage of the first spike +int SpikeShape::AP1_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& peak_voltage = + getFeature(DoubleFeatureData, "peak_voltage"); + vector AP1_peak; + AP1_peak.push_back(peak_voltage[0]); + setVec(DoubleFeatureData, StringData, "AP1_peak", AP1_peak); + return 1; +} + +// Peak voltage of the second spike +int SpikeShape::AP2_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& peak_voltage = + getFeature(DoubleFeatureData, "peak_voltage"); + vector AP2_peak; + if (peak_voltage.size() < 2) { + throw FeatureComputationError( + "Size of peak_voltage should be >= 2 for AP2_peak"); + } + AP2_peak.push_back(peak_voltage[1]); + setVec(DoubleFeatureData, StringData, "AP2_peak", AP2_peak); + return 1; +} + +// Difference amplitude of the second to first spike +int SpikeShape::AP2_AP1_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_amplitudes = + getFeature(DoubleFeatureData, "AP_amplitude"); + vector AP2_AP1_diff; + if (AP_amplitudes.size() < 2) { + throw FeatureComputationError( + "Size of AP_amplitude should be >= 2 for AP2_AP1_diff"); + } + AP2_AP1_diff.push_back(AP_amplitudes[1] - AP_amplitudes[0]); + setVec(DoubleFeatureData, StringData, "AP2_AP1_diff", AP2_AP1_diff); + return 1; +} + +// Difference peak_amp of the second to first spike +int SpikeShape::AP2_AP1_peak_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& peak_voltage = + getFeature(DoubleFeatureData, "peak_voltage"); + vector AP2_AP1_peak_diff; + if (peak_voltage.size() < 2) { + throw FeatureComputationError( + "Size of peak_voltage should be >= 2 for AP2_AP1_peak_diff"); + } + AP2_AP1_peak_diff.push_back(peak_voltage[1] - peak_voltage[0]); + setVec(DoubleFeatureData, StringData, "AP2_AP1_peak_diff", + AP2_AP1_peak_diff); + return 1; +} + +// *** amp_drop_first_second *** +static int __amp_drop_first_second(const vector& peakvoltage, + vector& ampdropfirstsecond) { + ampdropfirstsecond.push_back(peakvoltage[0] - peakvoltage[1]); + return ampdropfirstsecond.size(); +} +int SpikeShape::amp_drop_first_second(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"peak_voltage"}); + const vector peakvoltage = features.at("peak_voltage"); + + if (peakvoltage.size() < 2) { + throw FeatureComputationError("At least 2 spikes needed for calculation of amp_drop_first_second."); + } + vector ampdropfirstsecond; + int retval = __amp_drop_first_second(peakvoltage, ampdropfirstsecond); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "amp_drop_first_second", + ampdropfirstsecond); + } + return retval; +} + +// *** amp_drop_first_last *** +static int __amp_drop_first_last(const vector& peakvoltage, + vector& ampdropfirstlast) { + ampdropfirstlast.push_back(peakvoltage[0] - peakvoltage.back()); + return ampdropfirstlast.size(); +} +int SpikeShape::amp_drop_first_last(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& peakVoltageFeature = + getFeatures(DoubleFeatureData, {"peak_voltage"}); + const vector& peakvoltage = peakVoltageFeature.at("peak_voltage"); + + if (peakvoltage.size() < 2) { + throw FeatureComputationError("At least 2 spikes needed for calculation of amp_drop_first_last."); + } + vector ampdropfirstlast; + int retval = __amp_drop_first_last(peakvoltage, ampdropfirstlast); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "amp_drop_first_last", + ampdropfirstlast); + } + return retval; +} +// end of amp_drop_first_last + +// *** amp_drop_second_last *** +static int __amp_drop_second_last(const vector& peakvoltage, + vector& ampdropsecondlast) { + ampdropsecondlast.push_back(peakvoltage[1] - peakvoltage.back()); + return ampdropsecondlast.size(); +} +int SpikeShape::amp_drop_second_last(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& peakVoltageFeatures = + getFeatures(DoubleFeatureData, {"peak_voltage"}); + const vector& peakvoltage = peakVoltageFeatures.at("peak_voltage"); + // Ensure there are at least 3 spikes for calculation + if (peakvoltage.size() < 3) { + throw FeatureComputationError("At least 3 spikes needed for calculation of amp_drop_second_last."); + } + vector ampdropsecondlast; + int retval = __amp_drop_second_last(peakvoltage, ampdropsecondlast); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "amp_drop_second_last", + ampdropsecondlast); + } + return retval; +} + +// *** max_amp_difference *** +static int __max_amp_difference(const vector& peakvoltage, + vector& maxampdifference) { + vector diff_peak_voltage; + if (peakvoltage.size() < 1) { + return -1; + } + diff_peak_voltage.resize(peakvoltage.size() - 1); + for (size_t i = 0; i < diff_peak_voltage.size(); i++) { + diff_peak_voltage[i] = peakvoltage[i] - peakvoltage[i + 1]; + } + maxampdifference.push_back( + *max_element(diff_peak_voltage.begin(), diff_peak_voltage.end())); + return maxampdifference.size(); +} + +int SpikeShape::max_amp_difference(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"peak_voltage"}); + + // Ensure there are at least 2 spikes for calculation + if (features.at("peak_voltage").size() < 2) { + throw FeatureComputationError("At least 2 spikes needed for calculation of max_amp_difference."); + } + vector maxampdifference; + int retval = + __max_amp_difference(features.at("peak_voltage"), maxampdifference); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "max_amp_difference", + maxampdifference); + } + return retval; +} + +// *** AP_amplitude_diff based on AP_amplitude_change but not normalized *** +static int __AP_amplitude_diff(const vector& apamplitude, + vector& apamplitudediff) { + if (apamplitude.size() <= 1) return -1; + apamplitudediff.resize(apamplitude.size() - 1); + for (size_t i = 0; i < apamplitudediff.size(); i++) { + apamplitudediff[i] = (apamplitude[i + 1] - apamplitude[i]); + } + return apamplitudediff.size(); +} + +int SpikeShape::AP_amplitude_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"AP_amplitude"}); + vector apamplitudediff; + int retval = + __AP_amplitude_diff(doubleFeatures.at("AP_amplitude"), apamplitudediff); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_amplitude_diff", apamplitudediff); + } + return retval; +} + +static int __min_AHP_indices(const vector& t, const vector& v, + const vector& peak_indices, + const double stim_start, const double stim_end, + const bool strict_stiminterval, + vector& min_ahp_indices, + vector& min_ahp_values) { + vector peak_indices_plus = peak_indices; + unsigned end_index = 0; + + if (strict_stiminterval) { + end_index = distance(t.begin(), + find_if(t.begin(), t.end(), [stim_end](double t_val) { + return t_val >= stim_end; + })); + } else { + end_index = distance(t.begin(), t.end()); + } + + size_t ahpindex = 0; + + peak_indices_plus.push_back(end_index); + + for (size_t i = 0; i < peak_indices_plus.size() - 1; i++) { + ahpindex = distance( + v.begin(), first_min_element(v.begin() + peak_indices_plus[i], + v.begin() + peak_indices_plus[i + 1])); + + if (ahpindex != end_index - 1) { + min_ahp_indices.push_back(ahpindex); + + EFEL_ASSERT(ahpindex < v.size(), + "AHP index falls outside of voltage array"); + min_ahp_values.push_back(v[ahpindex]); + } + } + + return min_ahp_indices.size(); +} + +// min_AHP_indices +// find the first minimum between two spikes, +// and the first minimum between the last spike and the time the stimulus ends +int SpikeShape::min_AHP_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + int retVal; + // Retrieve all required double features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); + + vector min_ahp_indices; + vector min_ahp_values; + double stim_start = doubleFeatures.at("stim_start")[0]; + double stim_end = doubleFeatures.at("stim_end")[0]; + // Get strict_stiminterval + vector strict_stiminterval_vec; + bool strict_stiminterval; + retVal = + getParam(IntFeatureData, "strict_stiminterval", strict_stiminterval_vec); + if (retVal <= 0) { + strict_stiminterval = false; + } else { + strict_stiminterval = bool(strict_stiminterval_vec[0]); + } + + retVal = + __min_AHP_indices(doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), stim_start, stim_end, + strict_stiminterval, min_ahp_indices, min_ahp_values); + + if (retVal == 0) return -1; + if (retVal > 0) { + setVec(IntFeatureData, StringData, "min_AHP_indices", min_ahp_indices); + setVec(DoubleFeatureData, StringData, "min_AHP_values", min_ahp_values); + } + return retVal; +} + +int SpikeShape::min_AHP_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return -1; +} + +// Difference with SpikeShape is that this function doesn't return -1 if there are no +// min_AHP_values +int SpikeShape::AHP_depth_abs(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& vAHP = getFeature(DoubleFeatureData, "min_AHP_values"); + setVec(DoubleFeatureData, StringData, "AHP_depth_abs", vAHP); + return vAHP.size(); +} + +// *** AHP_depth_abs_slow *** +// same as AHP_depth_abs but the minimum search starts +// 5 ms (or custom duration) after the spike, +// first ISI is ignored +static int __AHP_depth_abs_slow_indices(const vector& t, + const vector& v, + const vector& peakindices, + double sahp_start, + vector& adas_indices) { + adas_indices.resize(peakindices.size() - 2); + for (size_t i = 0; i < adas_indices.size(); i++) { + // start 5 ms (or custom duration) after last spike + double t_start = t[peakindices[i + 1]] + sahp_start; + adas_indices[i] = distance( + v.begin(), + min_element(v.begin() + distance(t.begin(), + find_if(t.begin() + peakindices[i + 1], + t.begin() + peakindices[i + 2], + [t_start](double val) { + return val >= t_start; + })), + v.begin() + peakindices[i + 2])); + } + return adas_indices.size(); +} + +int SpikeShape::AHP_depth_abs_slow(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "sahp_start"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); + if (intFeatures.at("peak_indices").size() < 3) + throw FeatureComputationError("At least 3 spikes needed for AHP_depth_abs_slow and AHP_slow_time."); + double sahp_start = (doubleFeatures.at("sahp_start").empty()) + ? 5 + : doubleFeatures.at("sahp_start")[0]; + vector adas_indices; + int retval = __AHP_depth_abs_slow_indices( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), sahp_start, adas_indices); + vector ahpdepthabsslow(adas_indices.size()); + vector ahpslowtime(adas_indices.size()); + for (size_t i = 0; i < adas_indices.size(); i++) { + ahpdepthabsslow[i] = doubleFeatures.at("V")[adas_indices[i]]; + ahpslowtime[i] = + (doubleFeatures.at("T")[adas_indices[i]] - + doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 1]]) / + (doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 2]] - + doubleFeatures.at("T")[intFeatures.at("peak_indices")[i + 1]]); + } + if (retval >= 0) { + setVec(DoubleFeatureData, StringData, "AHP_depth_abs_slow", + ahpdepthabsslow); + setVec(DoubleFeatureData, StringData, "AHP_slow_time", ahpslowtime); + } + return retval; +} + +int SpikeShape::AHP_slow_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return -1; +} + +// *** AHP_depth_slow *** +static int __AHP_depth_slow(const vector& voltagebase, + const vector& minahpvalues, + vector& ahpdepth) { + for (size_t i = 0; i < minahpvalues.size(); i++) { + ahpdepth.push_back(minahpvalues[i] - voltagebase[0]); + } + return ahpdepth.size(); +} + +int SpikeShape::AHP_depth_slow(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"voltage_base", "AHP_depth_abs_slow"}); + vector ahpdepthslow; + int retval = + __AHP_depth_slow(doubleFeatures.at("voltage_base"), + doubleFeatures.at("AHP_depth_abs_slow"), ahpdepthslow); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AHP_depth_slow", ahpdepthslow); + } + return retval; +} + +// *** AHP_depth *** +static int __AHP_depth(const vector& voltagebase, + const vector& minahpvalues, + vector& ahpdepth) { + for (size_t i = 0; i < minahpvalues.size(); i++) { + ahpdepth.push_back(minahpvalues[i] - voltagebase[0]); + } + return ahpdepth.size(); +} +int SpikeShape::AHP_depth(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"voltage_base", "min_AHP_values"}); + vector ahpdepth; + int retval = __AHP_depth(doubleFeatures.at("voltage_base"), + doubleFeatures.at("min_AHP_values"), ahpdepth); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AHP_depth", ahpdepth); + } + return retval; +} + +// *** AHP_depth_diff, returns AHP_depth[i+1] - AHP_depth[i] *** +static int __AHP_depth_diff(const vector& ahpdepth, + vector& ahpdepthdiff) { + if (ahpdepth.size() <= 1) return -1; + ahpdepthdiff.resize(ahpdepth.size() - 1); + for (size_t i = 0; i < ahpdepthdiff.size(); i++) { + ahpdepthdiff[i] = (ahpdepth[i + 1] - ahpdepth[i]); + } + return ahpdepthdiff.size(); +} +int SpikeShape::AHP_depth_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"AHP_depth"}); + vector ahpdepthdiff; + int retval = __AHP_depth_diff(doubleFeatures.at("AHP_depth"), ahpdepthdiff); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AHP_depth_diff", ahpdepthdiff); + } + return retval; +} + +// *** fast_AHP according to E13 and E21 *** +static int __fast_AHP(const vector& v, + const vector& apbeginindices, + const vector& minahpindices, + vector& fastahp) { + if (apbeginindices.size() < 1) { + return -1; + } + fastahp.resize(apbeginindices.size() - 1); + for (size_t i = 0; i < fastahp.size(); i++) { + fastahp[i] = v[apbeginindices[i]] - v[minahpindices[i]]; + } + return fastahp.size(); +} +int SpikeShape::fast_AHP(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "min_AHP_indices"}); + + const vector& v = doubleFeatures.at("V"); + const vector& apbeginindices = intFeatures.at("AP_begin_indices"); + const vector& minahpindices = intFeatures.at("min_AHP_indices"); + + vector fastahp; + int retval = __fast_AHP(v, apbeginindices, minahpindices, fastahp); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "fast_AHP", fastahp); + } + return retval; +} + +// *** fast_AHP_change according to E27 *** +static int __fast_AHP_change(const vector& fastahp, + vector& fastahpchange) { + if (fastahp.size() < 1) { + return -1; + } + fastahpchange.resize(fastahp.size() - 1); + for (size_t i = 0; i < fastahpchange.size(); i++) { + fastahpchange[i] = (fastahp[i + 1] - fastahp[0]) / fastahp[0]; + } + return fastahpchange.size(); +} +int SpikeShape::fast_AHP_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"fast_AHP"}); + const vector& fastahp = features.at("fast_AHP"); + vector fastahpchange; + int retval = __fast_AHP_change(fastahp, fastahpchange); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "fast_AHP_change", fastahpchange); + } + return retval; +} + +static int __AHP_depth_from_peak(const vector& v, + const vector& peakIndices, + const vector& minAHPIndices, + vector& ahpDepthFromPeak) { + if (peakIndices.size() < minAHPIndices.size()) return -1; + for (size_t i = 0; i < minAHPIndices.size(); i++) { + ahpDepthFromPeak.push_back(v[peakIndices[i]] - v[minAHPIndices[i]]); + } + return ahpDepthFromPeak.size(); +} + +int SpikeShape::AHP_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); + vector ahpDepthFromPeak; + const vector& V = doubleFeatures.at("V"); + const vector& peakIndices = intFeatures.at("peak_indices"); + const vector& minAHPIndices = intFeatures.at("min_AHP_indices"); + int retVal = + __AHP_depth_from_peak(V, peakIndices, minAHPIndices, ahpDepthFromPeak); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AHP_depth_from_peak", + ahpDepthFromPeak); + } + return retVal; +} + +// AHP_depth_from_peak of first spike +int SpikeShape::AHP1_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& ahpDepthFromPeak = + getFeature(DoubleFeatureData, "AHP_depth_from_peak"); + vector ahp1DepthFromPeak; + ahp1DepthFromPeak.push_back(ahpDepthFromPeak[0]); + setVec(DoubleFeatureData, StringData, "AHP1_depth_from_peak", + ahp1DepthFromPeak); + return 1; +} + +// AHP_depth_from_peak of second spike +int SpikeShape::AHP2_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& ahpDepthFromPeak = + getFeature(DoubleFeatureData, "AHP_depth_from_peak"); + vector ahp2DepthFromPeak; + if (ahpDepthFromPeak.size() < 2) { + throw FeatureComputationError( + "Size of AHP_depth_from_peak should be >= 2 for AHP2_depth_from_peak"); + } + ahp2DepthFromPeak.push_back(ahpDepthFromPeak[1]); + setVec(DoubleFeatureData, StringData, "AHP2_depth_from_peak", + ahp2DepthFromPeak); + return 1; +} + +static int __AHP_time_from_peak(const vector& t, + const vector& peakIndices, + const vector& minAHPIndices, + vector& ahpTimeFromPeak) { + for (size_t i = 0; i < peakIndices.size() && i < minAHPIndices.size(); i++) { + ahpTimeFromPeak.push_back(t[minAHPIndices[i]] - t[peakIndices[i]]); + } + return ahpTimeFromPeak.size(); +} + +int SpikeShape::AHP_time_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); + vector ahpTimeFromPeak; + const vector& T = doubleFeatures.at("T"); + const vector& peakIndices = intFeatures.at("peak_indices"); + const vector& minAHPIndices = intFeatures.at("min_AHP_indices"); + int retVal = + __AHP_time_from_peak(T, peakIndices, minAHPIndices, ahpTimeFromPeak); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AHP_time_from_peak", + ahpTimeFromPeak); + } + return retVal; +} + +// strict_stiminterval should be True when using this feature +static int __ADP_peak_indices(const vector& v, + const vector& min_AHP_indices, + const vector& min_between_peaks_indices, + vector& ADP_peak_indices, + vector& ADP_peak_values) { + if (min_AHP_indices.size() > min_between_peaks_indices.size()) { + throw FeatureComputationError("min_AHP_indices should not have less elements than min_between_peaks_indices"); + } + + unsigned adp_peak_index; + for (size_t i = 0; i < min_AHP_indices.size(); i++) { + adp_peak_index = max_element(v.begin() + min_AHP_indices[i], + v.begin() + min_between_peaks_indices[i]) - + v.begin(); + + ADP_peak_indices.push_back(adp_peak_index); + ADP_peak_values.push_back(v[adp_peak_index]); + } + + return ADP_peak_indices.size(); +} + +// strict_stiminterval should be True when using this feature +int SpikeShape::ADP_peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = getFeatures( + IntFeatureData, {"min_AHP_indices", "min_between_peaks_indices"}); + vector ADP_peak_indices; + vector ADP_peak_values; + int retVal = __ADP_peak_indices(doubleFeatures.at("V"), + intFeatures.at("min_AHP_indices"), + intFeatures.at("min_between_peaks_indices"), + ADP_peak_indices, ADP_peak_values); + if (retVal > 0) { + setVec(IntFeatureData, StringData, "ADP_peak_indices", ADP_peak_indices); + setVec(DoubleFeatureData, StringData, "ADP_peak_values", ADP_peak_values); + } + return retVal; +} + +// strict_stiminterval should be True when using this feature +int SpikeShape::ADP_peak_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +// strict_stiminterval should be True when using this feature +int SpikeShape::ADP_peak_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"min_AHP_values", "ADP_peak_values"}); + vector ADP_peak_amplitude; + const vector& min_AHP_values = doubleFeatures.at("min_AHP_values"); + const vector& ADP_peak_values = doubleFeatures.at("ADP_peak_values"); + + if (min_AHP_values.size() != ADP_peak_values.size()) + throw FeatureComputationError("min_AHP_values and ADP_peak_values should have the same number of elements"); + for (size_t i = 0; i < ADP_peak_values.size(); i++) { + ADP_peak_amplitude.push_back(ADP_peak_values[i] - min_AHP_values[i]); + } + setVec(DoubleFeatureData, StringData, "ADP_peak_amplitude", + ADP_peak_amplitude); + return ADP_peak_amplitude.size(); +} + +static int __depolarized_base(const vector& t, const vector& v, + double stimstart, double stimend, + const vector& apbi, + const vector& apendi, + vector& dep_base) { + int i, n, k, startIndex, endIndex, nPt; + double baseValue; + // to make sure it access minimum index of both length + n = std::min(apendi.size(), apbi.size()); + + if (n > 2) { + dep_base.clear(); + for (i = 0; i < n - 1; i++) { + nPt = 0; + baseValue = 0; + startIndex = apendi[i]; + endIndex = apbi[i + 1]; + for (k = startIndex; k < endIndex; k++) { + if (k >= 0 && k < v.size()) { + baseValue += v[k]; + ++nPt; + } + } + if (nPt > 0) { + baseValue /= nPt; + dep_base.push_back(baseValue); + } + } + return dep_base.size(); + } + return -1; +} + +int SpikeShape::depolarized_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_end_indices", "AP_begin_indices"}); + + vector dep_base; + int retVal = __depolarized_base( + doubleFeatures.at("T"), doubleFeatures.at("V"), + doubleFeatures.at("stim_start").front(), + doubleFeatures.at("stim_end").front(), intFeatures.at("AP_begin_indices"), + intFeatures.at("AP_end_indices"), dep_base); + + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "depolarized_base", dep_base); + } + return retVal; +} + +// min_voltage_between_spikes: minimal voltage between consecutive spikes +int SpikeShape::min_voltage_between_spikes(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"peak_indices"}); + + if (intFeatures.at("peak_indices").size() < 2) { + throw FeatureComputationError( + "Size of peak_indices should be >= 2 for min_voltage_between_spikes"); + } + + vector min_voltage_between_spikes; + for (size_t i = 0; i < intFeatures.at("peak_indices").size() - 1; i++) { + min_voltage_between_spikes.push_back(*min_element( + doubleFeatures.at("V").begin() + intFeatures.at("peak_indices")[i], + doubleFeatures.at("V").begin() + + intFeatures.at("peak_indices")[i + 1])); + } + + setVec(DoubleFeatureData, StringData, "min_voltage_between_spikes", + min_voltage_between_spikes); + return min_voltage_between_spikes.size(); +} + +static int __min_between_peaks_indices( + const vector& t, const vector& v, + const vector& peak_indices, const double stim_start, + const double stim_end, const bool strict_stiminterval, + vector& min_btw_peaks_indices, vector& min_btw_peaks_values) { + vector peak_indices_plus = peak_indices; + unsigned end_index = 0; + + if (strict_stiminterval) { + end_index = distance(t.begin(), + find_if(t.begin(), t.end(), [stim_end](double t_val) { + return t_val >= stim_end; + })); + } else { + end_index = distance(t.begin(), t.end()); + } + + size_t minindex = 0; + + peak_indices_plus.push_back(end_index); + + for (size_t i = 0; i < peak_indices_plus.size() - 1; i++) { + minindex = distance(v.begin(), + std::min_element(v.begin() + peak_indices_plus[i], + v.begin() + peak_indices_plus[i + 1])); + + min_btw_peaks_indices.push_back(minindex); + + EFEL_ASSERT(minindex < v.size(), + "min index falls outside of voltage array"); + min_btw_peaks_values.push_back(v[minindex]); + } + + return min_btw_peaks_indices.size(); +} + +// min_between_peaks_indices +// find the minimum between two spikes, +// and the minimum between the last spike and the time the stimulus ends. +// This is different from min_AHP_indices, for traces that have broad peaks +// with local minima and maxima in it (e.g. dendritic AP) +int SpikeShape::min_between_peaks_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "strict_stiminterval"}); + + vector min_btw_peaks_indices; + vector min_btw_peaks_values; + int retVal = __min_between_peaks_indices( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), doubleFeatures.at("stim_start").front(), + doubleFeatures.at("stim_end").front(), + intFeatures.at("strict_stiminterval").empty() + ? false + : intFeatures.at("strict_stiminterval").front(), + min_btw_peaks_indices, min_btw_peaks_values); + + if (retVal > 0) { + setVec(IntFeatureData, StringData, "min_between_peaks_indices", + min_btw_peaks_indices); + setVec(DoubleFeatureData, StringData, "min_between_peaks_values", + min_btw_peaks_values); + } + return retVal; +} + +int SpikeShape::min_between_peaks_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + return 1; +} + +// *** AP_duration_half_width according to E8 and E16 *** +static int __AP_duration_half_width(const vector& t, + const vector& apriseindices, + const vector& apfallindices, + vector& apdurationhalfwidth) { + apdurationhalfwidth.resize(apriseindices.size()); + for (size_t i = 0; i < apdurationhalfwidth.size(); i++) { + apdurationhalfwidth[i] = t[apfallindices[i]] - t[apriseindices[i]]; + } + return apdurationhalfwidth.size(); +} +int SpikeShape::AP_duration_half_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Fetching all required features in one go. + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_rise_indices", "AP_fall_indices"}); + + vector apdurationhalfwidth; + int retval = __AP_duration_half_width( + doubleFeatures.at("T"), intFeatures.at("AP_rise_indices"), + intFeatures.at("AP_fall_indices"), apdurationhalfwidth); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_duration_half_width", + apdurationhalfwidth); + } + return retval; +} + +// *** AP_duration_half_width_change according to E24 *** +static int __AP_duration_half_width_change( + const vector& apdurationhalfwidth, + vector& apdurationhalfwidthchange) { + if (apdurationhalfwidth.size() < 1) { + return -1; + } + apdurationhalfwidthchange.resize(apdurationhalfwidth.size() - 1); + for (size_t i = 0; i < apdurationhalfwidthchange.size(); i++) { + apdurationhalfwidthchange[i] = + (apdurationhalfwidth[i + 1] - apdurationhalfwidth[0]) / + apdurationhalfwidth[0]; + } + return apdurationhalfwidthchange.size(); +} +int SpikeShape::AP_duration_half_width_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = + getFeatures(DoubleFeatureData, {"AP_duration_half_width"}); + const vector& apdurationhalfwidth = + features.at("AP_duration_half_width"); + vector apdurationhalfwidthchange; + int retval = __AP_duration_half_width_change(apdurationhalfwidth, + apdurationhalfwidthchange); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_duration_half_width_change", + apdurationhalfwidthchange); + } + return retval; +} + +// *** AP_width *** +// +// spike width calculation according to threshold value for the first spike +// unfortunately spike width means the width of the spike on onset not at half +// maximum +static int __AP_width(const vector& t, const vector& v, + double stimstart, double stimend, double threshold, + const vector& peakindices, + const vector& minahpindices, + const bool strict_stiminterval, vector& apwidth) { + // printf("\n Inside AP_width...\n"); + // printf("\nStimStart = %f , thereshold = %f ", stimstart, threshold); + vector indices; + if (strict_stiminterval) { + int start_index = distance( + t.begin(), find_if(t.begin(), t.end(), + [stimstart](double x) { return x >= stimstart; })); + int end_index = distance( + t.begin(), find_if(t.begin(), t.end(), + [stimend](double x) { return x >= stimend; })); + indices.push_back(start_index); + for (size_t i = 0; i < minahpindices.size(); i++) { + if (start_index < minahpindices[i] && minahpindices[i] < end_index) { + indices.push_back(minahpindices[i]); + } + } + } else { + indices.push_back(0); + for (size_t i = 0; i < minahpindices.size(); i++) { + indices.push_back(minahpindices[i]); + } + } + for (size_t i = 0; i < indices.size() - 1; i++) { + /* + // FWHM (not used): + // half maximum + double v_hm = (v[peakindices[i]] + threshold) / 2.; + // half maximum indices + int hm_index1 = distance(v.begin(), find_if(v.begin() + indices[i], + v.begin() + indices[i + 1], bind2nd(greater_equal(), v_hm))); + int hm_index2 = distance(v.begin(), find_if(v.begin() + peakindices[i], + v.begin() + indices[i + 1], bind2nd(less_equal(), v_hm))); + apwidth.push_back(t[hm_index2] - t[hm_index1]); + */ + auto onset_index = distance( + v.begin(), find_if(v.begin() + indices[i], v.begin() + indices[i + 1], + [threshold](double x) { return x >= threshold; })); + // int end_index = distance(v.begin(), find_if(v.begin() + peakindices[i], + // v.begin() + indices[i + 1], bind2nd(less_equal(), threshold))); + auto end_index = distance( + v.begin(), find_if(v.begin() + onset_index, v.begin() + indices[i + 1], + [threshold](double x) { return x <= threshold; })); + apwidth.push_back(t[end_index] - t[onset_index]); + } + return apwidth.size(); +} + +int SpikeShape::AP_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"T", "V", "Threshold", "stim_start", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, + {"peak_indices", "min_AHP_indices", "strict_stiminterval"}); + bool strict_stiminterval = + intFeatures.at("strict_stiminterval").size() > 0 + ? bool(intFeatures.at("strict_stiminterval")[0]) + : false; + vector apwidth; + int retval = __AP_width( + doubleFeatures.at("T"), doubleFeatures.at("V"), + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], + doubleFeatures.at("Threshold")[0], intFeatures.at("peak_indices"), + intFeatures.at("min_AHP_indices"), strict_stiminterval, apwidth); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_width", apwidth); + } + return retval; +} + +// *** AP_duration according to E7 and E15 *** +static int __AP_duration(const vector& t, + const vector& apbeginindices, + const vector& endindices, + vector& apduration) { + apduration.resize(std::min(apbeginindices.size(), endindices.size())); + for (size_t i = 0; i < apduration.size(); i++) { + // printf("%d, %d, %d\n", t.size(), apbeginindices.size(), + // endindices.size()) + apduration[i] = t[endindices[i]] - t[apbeginindices[i]]; + } + return apduration.size(); +} +int SpikeShape::AP_duration(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "AP_end_indices"}); + + vector apduration; + int retval = + __AP_duration(doubleFeatures.at("T"), intFeatures.at("AP_begin_indices"), + intFeatures.at("AP_end_indices"), apduration); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_duration", apduration); + } + return retval; +} + +// *** AP_duration_change according to E23 *** +static int __AP_duration_change(const vector& apduration, + vector& apdurationchange) { + if (apduration.size() < 1) { + return -1; + } + apdurationchange.resize(apduration.size() - 1); + for (size_t i = 0; i < apdurationchange.size(); i++) { + apdurationchange[i] = (apduration[i + 1] - apduration[0]) / apduration[0]; + } + return apdurationchange.size(); +} +int SpikeShape::AP_duration_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"AP_duration"}); + const vector& apduration = features.at("AP_duration"); + + vector apdurationchange; + int retval = __AP_duration_change(apduration, apdurationchange); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_duration_change", + apdurationchange); + } + return retval; +} + +// AP_width_between_threshold +// spike width calculation according to threshold value. +// min_between_peaks_indices are used to split the different APs +// do not add width if threshold crossing has not been found +static int __AP_width_between_threshold( + const vector& t, const vector& v, double stimstart, + double threshold, const vector& min_between_peaks_indices, + vector& ap_width_threshold) { + vector indices(min_between_peaks_indices.size() + 1); + int start_index = distance( + t.begin(), find_if(t.begin(), t.end(), + [stimstart](double x) { return x >= stimstart; })); + indices[0] = start_index; + copy(min_between_peaks_indices.begin(), min_between_peaks_indices.end(), + indices.begin() + 1); + + for (size_t i = 0; i < indices.size() - 1; i++) { + int onset_index = distance( + v.begin(), find_if(v.begin() + indices[i], v.begin() + indices[i + 1], + [threshold](double x) { return x >= threshold; })); + int end_index = distance( + v.begin(), find_if(v.begin() + onset_index, v.begin() + indices[i + 1], + [threshold](double x) { return x <= threshold; })); + if (end_index != indices[i + 1]) { + ap_width_threshold.push_back(t[end_index] - t[onset_index]); + } + } + + return ap_width_threshold.size(); +} + +int SpikeShape::AP_width_between_threshold(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "Threshold", "stim_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"min_between_peaks_indices"}); + + vector ap_width_threshold; + int retval = __AP_width_between_threshold( + doubleFeatures.at("T"), doubleFeatures.at("V"), + doubleFeatures.at("stim_start").front(), + doubleFeatures.at("Threshold").front(), + intFeatures.at("min_between_peaks_indices"), ap_width_threshold); + if (retval == 0) + return -1; + else if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_width_between_threshold", + ap_width_threshold); + } + return retval; +} + +// spike half width +// for spike amplitude = v_peak - v_AHP +static int __spike_width1(const vector& t, const vector& v, + const vector& peak_indices, + const vector& min_ahp_indices, double stim_start, + vector& spike_width1) { + int start_index = distance( + t.begin(), find_if(t.begin(), t.end(), [stim_start](double t_val) { + return t_val >= stim_start; + })); + vector min_ahp_indices_plus(min_ahp_indices.size() + 1, start_index); + copy(min_ahp_indices.begin(), min_ahp_indices.end(), + min_ahp_indices_plus.begin() + 1); + for (size_t i = 1; i < min_ahp_indices_plus.size(); i++) { + double v_half = (v[peak_indices[i - 1]] + v[min_ahp_indices_plus[i]]) / 2.; + // interpolate this one time step where the voltage is close to v_half in + // the rising and in the falling edge + double v_dev; + double delta_v; + double t_dev_rise; + double t_dev_fall; + double delta_t; + int rise_index = distance( + v.begin(), find_if(v.begin() + min_ahp_indices_plus[i - 1], + v.begin() + peak_indices[i - 1], + [v_half](double v_val) { return v_val >= v_half; })); + v_dev = v_half - v[rise_index]; + delta_v = v[rise_index] - v[rise_index - 1]; + delta_t = t[rise_index] - t[rise_index - 1]; + t_dev_rise = delta_t * v_dev / delta_v; + int fall_index = distance( + v.begin(), find_if(v.begin() + peak_indices[i - 1], + v.begin() + min_ahp_indices_plus[i], + [v_half](double v_val) { return v_val <= v_half; })); + v_dev = v_half - v[fall_index]; + delta_v = v[fall_index] - v[fall_index - 1]; + delta_t = t[fall_index] - t[fall_index - 1]; + t_dev_fall = delta_t * v_dev / delta_v; + spike_width1.push_back(t[fall_index] - t_dev_rise - t[rise_index] + + t_dev_fall); + } + return spike_width1.size(); +} + +int SpikeShape::spike_width1(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); + + vector spike_width1; + // Calculate spike width + int retVal = __spike_width1(doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), + intFeatures.at("min_AHP_indices"), + doubleFeatures.at("stim_start")[0], spike_width1); + + if (retVal >= 0) { + setVec(DoubleFeatureData, StringData, "spike_half_width", spike_width1); + } + return retVal; +} + +// Width of the first spike +int SpikeShape::AP1_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& spike_half_width = + getFeature(DoubleFeatureData, "spike_half_width"); + vector AP1_width; + AP1_width.push_back(spike_half_width[0]); + setVec(DoubleFeatureData, StringData, "AP1_width", AP1_width); + return 1; +} + +// Width of the second spike +int SpikeShape::AP2_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& spike_half_width = + getFeature(DoubleFeatureData, "spike_half_width"); + vector AP2_width; + if (spike_half_width.size() < 2) { + throw FeatureComputationError( + "Size of spike_half_width should be >= 2 for AP2_width"); + } + AP2_width.push_back(spike_half_width[1]); + setVec(DoubleFeatureData, StringData, "AP2_width", AP2_width); + return 1; +} + +// Width of the last spike +int SpikeShape::APlast_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& spike_half_width = + getFeature(DoubleFeatureData, "spike_half_width"); + vector APlast_width; + APlast_width.push_back(spike_half_width[spike_half_width.size() - 1]); + setVec(DoubleFeatureData, StringData, "APlast_width", APlast_width); + return 1; +} + +// To find spike width using Central difference derivative vec1[i] = +// ((vec[i+1]+vec[i-1])/2)/dx and half width is between +// MinAHP and APThreshold +static int __spike_width2(const vector& t, const vector& V, + const vector& PeakIndex, + const vector& minAHPIndex, + vector& spike_width2) { + vector v, dv1, dv2; + double dx = t[1] - t[0]; + double VoltThreshold, VoltMax, HalfV, T0, V0, V1, fraction, TStart, TEnd; + for (size_t i = 0; i < minAHPIndex.size() && i < PeakIndex.size() - 1; i++) { + v.clear(); + dv1.clear(); + dv2.clear(); + + for (int j = minAHPIndex[i]; j <= PeakIndex[i + 1]; j++) { + if (j < 0) { + throw FeatureComputationError("Invalid index"); + } + v.push_back(V[j]); + } + + getCentralDifferenceDerivative(dx, v, dv1); + getCentralDifferenceDerivative(dx, dv1, dv2); + double dMax = dv2[0]; + + size_t index = 0; + for (size_t j = 1; j < dv2.size(); ++j) { + if (dMax <= dv2[j]) { + dMax = dv2[j]; + index = j; + } + } + + // Take voltage at point where double derivative is maximum + index += minAHPIndex[i]; + VoltThreshold = V[index]; + VoltMax = V[PeakIndex[i + 1]]; + HalfV = (VoltMax + VoltThreshold) / 2; + + // Find voltage where it crosses HalfV in the rising phase of action + // potential + for (size_t j = 0; j < v.size(); ++j) { + if (v[j] > HalfV) { // point is found where v is crossing HalfV + index = minAHPIndex[i] + j; + break; + } + } + + // index is the index where v crossed HalfV now use linear interpolation to + // find actual time at HalfV + T0 = t[index - 1]; + V0 = V[index - 1]; + V1 = V[index]; + fraction = (HalfV - V0) / (V1 - V0); + TStart = T0 + (fraction * dx); + + // Find voltage where it crosses HalfV in the falling phase of the action + // potential + for (size_t j = PeakIndex[i + 1]; j < V.size(); j++) { + if (V[j] < HalfV) { + index = j; + break; + } + } + + if (index == V.size()) { + throw FeatureComputationError("Falling phase of last spike is missing."); + } + + T0 = t[index - 1]; + V0 = V[index - 1]; + V1 = V[index]; + fraction = (HalfV - V0) / (V1 - V0); + TEnd = T0 + (fraction * dx); + + spike_width2.push_back(TEnd - TStart); + } + + return spike_width2.size(); +} + +int SpikeShape::spike_width2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); + if (intFeatures.at("peak_indices").size() <= 1) { + throw FeatureComputationError("More than one spike is needed for spikewidth2 calculation."); + } + vector spike_width2; + int retVal = __spike_width2(doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), + intFeatures.at("min_AHP_indices"), spike_width2); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "spike_width2", spike_width2); + } + return retVal; +} + +// spike width at spike start +static int __AP_begin_width(const vector& t, const vector& v, + double stimstart, + const vector& AP_begin_indices, + const vector& min_ahp_indices, + vector& AP_begin_width) { + // int start_index = distance(t.begin(), find_if(t.begin(), t.end(), + // std::bind2nd(std::greater_equal(), stim_start))); + /// vector min_ahp_indices_plus(min_ahp_indices.size() + 1, start_index); + // copy(min_ahp_indices.begin(), min_ahp_indices.end(), + // min_ahp_indices_plus.begin()); + + // keep only min_ahp_indices values that are after stim start + // because AP_begin_indices are all after stim start + // if not done, could cause cases where AP_begin_indices[i] > min_ahp_indices[i] + // leading to segmentation faults + auto it = find_if(t.begin(), t.end(), + [stimstart](double val) { return val >= stimstart; }); + int stimbeginindex = distance(t.begin(), it); + vector strict_min_ahp_indices; + for (size_t i = 0; i < min_ahp_indices.size(); i++) { + if (min_ahp_indices[i] > stimbeginindex) { + strict_min_ahp_indices.push_back(min_ahp_indices[i]); + } + } + + if (AP_begin_indices.size() < strict_min_ahp_indices.size()) return -1; + for (size_t i = 0; i < strict_min_ahp_indices.size(); i++) { + double v_start = v[AP_begin_indices[i]]; + // interpolate this one time step where the voltage is close to v_start in + // the falling edge + int rise_index = AP_begin_indices[i]; + int fall_index = distance( + v.begin(), + find_if(v.begin() + rise_index + 1, + v.begin() + strict_min_ahp_indices[i], + [v_start](const auto& val) { return val <= v_start; })); + // v_dev = v_start - v[fall_index]; + // delta_v = v[fall_index] - v[fall_index - 1]; + // delta_t = t[fall_index] - t[fall_index - 1]; + // t_dev_fall = delta_t * v_dev / delta_v; + // printf("%f %f\n",delta_v, t_dev_fall); + AP_begin_width.push_back(t[fall_index] - t[rise_index] /*+ t_dev_fall*/); + } + return AP_begin_width.size(); +} + +int SpikeShape::AP_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"min_AHP_indices", "AP_begin_indices"}); + vector AP_begin_width; + const vector& V = doubleFeatures.at("V"); + const vector& t = doubleFeatures.at("T"); + const vector& stim_start = doubleFeatures.at("stim_start"); + const vector& min_AHP_indices = intFeatures.at("min_AHP_indices"); + const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); + int retVal = __AP_begin_width(t, V, stim_start[0], AP_begin_indices, + min_AHP_indices, AP_begin_width); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AP_begin_width", AP_begin_width); + } + return retVal; +} + +int SpikeShape::AP1_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_begin_width = + getFeature(DoubleFeatureData, "AP_begin_width"); + vector AP1_begin_width; + AP1_begin_width.push_back(AP_begin_width[0]); + setVec(DoubleFeatureData, StringData, "AP1_begin_width", AP1_begin_width); + return 1; +} + +int SpikeShape::AP2_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_begin_width = + getFeature(DoubleFeatureData, "AP_begin_width"); + vector AP2_begin_width; + if (AP_begin_width.size() < 2) { + throw FeatureComputationError("There are less than 2 spikes in the trace."); + } + AP2_begin_width.push_back(AP_begin_width[1]); + setVec(DoubleFeatureData, StringData, "AP2_begin_width", AP2_begin_width); + return 1; +} + +// Difference amplitude of the second to first spike +int SpikeShape::AP2_AP1_begin_width_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_begin_widths = + getFeature(DoubleFeatureData, "AP_begin_width"); + vector AP2_AP1_begin_width_diff; + if (AP_begin_widths.size() < 2) { + throw FeatureComputationError("There are less than 2 spikes in the trace."); + } + AP2_AP1_begin_width_diff.push_back(AP_begin_widths[1] - AP_begin_widths[0]); + setVec(DoubleFeatureData, StringData, "AP2_AP1_begin_width_diff", + AP2_AP1_begin_width_diff); + return 1; +} + +// +// *** AP begin indices *** +// +static int __AP_begin_indices(const vector& t, const vector& v, + double stimstart, double stimend, + const vector& pi, const vector& ahpi, + vector& apbi, double dTh, + int derivative_window) { + const double derivativethreshold = dTh; + vector dvdt(v.size()); + vector dv; + vector dt; + getCentralDifferenceDerivative(1., v, dv); + getCentralDifferenceDerivative(1., t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + + // restrict to time interval where stimulus is applied + vector minima, peak_indices; + auto stimbeginindex = + distance(t.begin(), find_if(t.begin(), t.end(), [stimstart](double time) { + return time >= stimstart; + })); + + if (stimbeginindex > 1) { + // to avoid skipping AP_begin when it is exactly at stimbeginindex + // also because of float precision and interpolation, sometimes + // stimbeginindex can be slightly above 'real' stim begin index + minima.push_back(stimbeginindex - 2); + } else if (stimbeginindex == 1) { + minima.push_back(stimbeginindex - 1); + } else { + minima.push_back(stimbeginindex); + } + for (size_t i = 0; i < ahpi.size(); i++) { + if (ahpi[i] > stimbeginindex) { + minima.push_back(ahpi[i]); + } + // if(t[ahpi[i]] > stimend) { + // break; + //} + } + for (size_t i = 0; i < pi.size(); i++) { + if (pi[i] > stimbeginindex) { + peak_indices.push_back(pi[i]); + } + } + int endindex = distance(t.begin(), t.end()); + if (minima.size() < peak_indices.size()) + throw FeatureComputationError("More peaks than min_AHP in AP_begin_indices."); + + // printf("Found %d minima\n", minima.size()); + for (size_t i = 0; i < peak_indices.size(); i++) { + // assure that the width of the slope is bigger than 4 + int newbegin = peak_indices[i]; + int begin = minima[i]; + int width = derivative_window; + bool skip = false; + + // Detect where the derivate crosses derivativethreshold, and make sure + // this happens in a window of 'width' sampling point + do { + begin = distance(dvdt.begin(), + find_if( + // use reverse iterator to get last occurence + // and avoid false positive long before the spike + dvdt.rbegin() + v.size() - newbegin, + dvdt.rbegin() + v.size() - minima[i], + [derivativethreshold](double val) { + return val <= derivativethreshold; + }) + .base()); + // cover edge case to avoid going out of index in the while condition + if (begin > endindex - width) { + begin = endindex - width; + } + // printf("%d %d\n", newbegin, minima[i+1]); + if (begin == minima[i]) { + // printf("Skipping %d %d\n", newbegin, minima[i+1]); + // could not find a spike in between these minima + skip = true; + break; + } + newbegin = begin - 1; + } while (find_if(dvdt.begin() + begin, dvdt.begin() + begin + width, + [derivativethreshold](double val) { + return val < derivativethreshold; + }) != dvdt.begin() + begin + width); + if (skip) { + continue; + } + apbi.push_back(begin); + } + return apbi.size(); +} + +int SpikeShape::AP_begin_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"T", "V", "stim_start", "stim_end"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "min_AHP_indices"}); + + vector apbi; + + // Get DerivativeThreshold + vector dTh; + int retVal = getParam(DoubleFeatureData, "DerivativeThreshold", dTh); + if (retVal <= 0) { + // derivative at peak start according to eCode specification 10mV/ms + // according to Shaul 12mV/ms + dTh.push_back(12.0); + } + + // Get DerivativeWindow + vector derivative_window; + retVal = getParam(IntFeatureData, "DerivativeWindow", derivative_window); + if (retVal <= 0) + throw FeatureComputationError("DerivativeWindow not set."); + + // Calculate feature + retVal = __AP_begin_indices( + doubleFeatures.at("T"), doubleFeatures.at("V"), + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], + intFeatures.at("peak_indices"), intFeatures.at("min_AHP_indices"), apbi, + dTh[0], derivative_window[0]); + + // Save feature value + if (retVal >= 0) { + setVec(IntFeatureData, StringData, "AP_begin_indices", apbi); + } + return retVal; +} + +static int __AP_end_indices(const vector& t, const vector& v, + const double stimstart, const vector& pi, + vector& apei, double derivativethreshold) { + + vector dvdt(v.size()); + vector dv; + vector dt; + vector peak_indices; + int max_slope; + getCentralDifferenceDerivative(1., v, dv); + getCentralDifferenceDerivative(1., t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + + auto stimbeginindex = distance(t.begin(), + find_if(t.begin(), t.end(), + [stimstart](double time){ return time >= stimstart; })); + + for (size_t i = 0; i < pi.size(); i++) { + if (pi[i] > stimbeginindex) { + peak_indices.push_back(pi[i]); + } + } + peak_indices.push_back(v.size() - 1); + + for (size_t i = 0; i < peak_indices.size() - 1; i++) { + size_t start_index = peak_indices[i] + 1; + size_t end_index = peak_indices[i + 1]; + + if (start_index >= end_index || start_index >= dvdt.size() || end_index >= dvdt.size()) { + continue; + } + + auto min_element_it = std::min_element(dvdt.begin() + start_index, dvdt.begin() + end_index); + auto max_slope = std::distance(dvdt.begin(), min_element_it); + // assure that the width of the slope is bigger than 4 + auto threshold_it = std::find_if(dvdt.begin() + max_slope, dvdt.begin() + end_index, + [derivativethreshold](double x) { return x >= derivativethreshold; }); + + if (threshold_it != dvdt.begin() + end_index) { + apei.push_back(std::distance(dvdt.begin(), threshold_it)); + } + } + return apei.size(); +} + +int SpikeShape::AP_end_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& T = getFeature(DoubleFeatureData, "T"); + const auto& V = getFeature(DoubleFeatureData, "V"); + const auto& stim_start = getFeature(DoubleFeatureData, "stim_start"); + const auto& peak_indices = getFeature(IntFeatureData, "peak_indices"); + + vector dTh; + int retVal = getParam(DoubleFeatureData, "DownDerivativeThreshold", dTh); + double downDerivativeThreshold = (retVal <= 0) ? -12.0 : dTh[0]; + + vector AP_end_indices; + retVal = __AP_end_indices(T, V, stim_start[0], peak_indices, AP_end_indices, + downDerivativeThreshold); + if (retVal >= 0) { + setVec(IntFeatureData, StringData, "AP_end_indices", AP_end_indices); + } + return retVal; +} + +static int __AP_begin_voltage(const vector& t, const vector& v, + const vector& AP_begin_indices, + vector& AP_begin_voltage) { + for (size_t i = 0; i < AP_begin_indices.size(); i++) { + AP_begin_voltage.push_back(v[AP_begin_indices[i]]); + } + return AP_begin_voltage.size(); +} + +int SpikeShape::AP_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); + vector AP_begin_voltage; + const vector& V = doubleFeatures.at("V"); + const vector& t = doubleFeatures.at("T"); + const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); + int retVal = __AP_begin_voltage(t, V, AP_begin_indices, AP_begin_voltage); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AP_begin_voltage", AP_begin_voltage); + } + return retVal; +} + +int SpikeShape::AP1_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_begin_voltage = + getFeature(DoubleFeatureData, "AP_begin_voltage"); + vector AP1_begin_voltage; + AP1_begin_voltage.push_back(AP_begin_voltage[0]); + setVec(DoubleFeatureData, StringData, "AP1_begin_voltage", AP1_begin_voltage); + return 1; +} + +int SpikeShape::AP2_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& AP_begin_voltage = + getFeature(DoubleFeatureData, "AP_begin_voltage"); + vector AP2_begin_voltage; + + if (AP_begin_voltage.size() < 2) { + throw FeatureComputationError("There are less than 2 spikes in the trace."); + } + AP2_begin_voltage.push_back(AP_begin_voltage[1]); + setVec(DoubleFeatureData, StringData, "AP2_begin_voltage", AP2_begin_voltage); + return 1; +} + +static int __AP_begin_time(const vector& t, const vector& v, + const vector& AP_begin_indices, + vector& AP_begin_time) { + for (size_t i = 0; i < AP_begin_indices.size(); i++) { + AP_begin_time.push_back(t[AP_begin_indices[i]]); + } + return AP_begin_time.size(); +} + +int SpikeShape::AP_begin_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V", "T"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); + vector AP_begin_time; + const vector& V = doubleFeatures.at("V"); + const vector& t = doubleFeatures.at("T"); + const vector& AP_begin_indices = intFeatures.at("AP_begin_indices"); + int retVal = __AP_begin_time(t, V, AP_begin_indices, AP_begin_time); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AP_begin_time", AP_begin_time); + } + return retVal; +} + +// +// *** Action potential peak upstroke *** +// +static int __AP_peak_upstroke(const vector& t, const vector& v, + const vector& pi, // peak indices + const vector& apbi, // AP begin indices + vector& pus) { // AP peak upstroke + vector dvdt(v.size()); + vector dv; + vector dt; + getCentralDifferenceDerivative(1., v, dv); + getCentralDifferenceDerivative(1., t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + + // Make sure that each value of pi is greater than its apbi counterpart + vector new_pi; + size_t j = 0; + for (size_t i = 0; i < apbi.size(); i++) { + while (j < pi.size() && pi[j] < apbi[i]) { + j++; + } + + if (j < pi.size() && pi[j] >= apbi[i]) { + new_pi.push_back(pi[j]); + j++; + } + } + + for (size_t i = 0; i < std::min(apbi.size(), new_pi.size()); i++) { + pus.push_back( + *std::max_element(dvdt.begin() + apbi[i], dvdt.begin() + new_pi[i])); + } + + return pus.size(); +} + +int SpikeShape::AP_peak_upstroke(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double and int features at once + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); + + vector pus; + int retVal = __AP_peak_upstroke( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), intFeatures.at("AP_begin_indices"), pus); + + if (retVal >= 0) { + setVec(DoubleFeatureData, StringData, "AP_peak_upstroke", pus); + } + return retVal; +} + +// +// *** Action potential peak downstroke *** +// +static int __AP_peak_downstroke(const vector& t, + const vector& v, + const vector& pi, // peak indices + const vector& ahpi, // min AHP indices + vector& pds) { // AP peak downstroke + vector dvdt(v.size()); + vector dv; + vector dt; + getCentralDifferenceDerivative(1., v, dv); + getCentralDifferenceDerivative(1., t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + + for (size_t i = 0; i < std::min(ahpi.size(), pi.size()); i++) { + pds.push_back( + *std::min_element(dvdt.begin() + pi[i], dvdt.begin() + ahpi[i])); + } + + return pds.size(); +} + +int SpikeShape::AP_peak_downstroke(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"min_AHP_indices", "peak_indices"}); + + vector pds; + int retVal = __AP_peak_downstroke( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("peak_indices"), intFeatures.at("min_AHP_indices"), pds); + + if (retVal >= 0) { + setVec(DoubleFeatureData, StringData, "AP_peak_downstroke", pds); + } + return retVal; +} + +// *** AP rise indices *** +// +static int __AP_rise_indices(const vector& v, const vector& apbi, + const vector& pi, vector& apri) { + apri.resize(std::min(apbi.size(), pi.size())); + for (size_t i = 0; i < apri.size(); i++) { + double halfheight = (v[pi[i]] + v[apbi[i]]) / 2.; + vector vpeak; + if (pi[i] < apbi[i]) { + // For some reason the peak and begin indices are out of sync + // Peak should always be later than begin index + return -1; + } + vpeak.resize(pi[i] - apbi[i]); + transform(v.begin() + apbi[i], v.begin() + pi[i], vpeak.begin(), + [halfheight](double val) { return fabs(val - halfheight); }); + apri[i] = distance(vpeak.begin(), min_element(vpeak.begin(), vpeak.end())) + + apbi[i]; + } + return apri.size(); +} +int SpikeShape::AP_rise_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); + vector apriseindices; + int retval = __AP_rise_indices(doubleFeatures.at("V"), + intFeatures.at("AP_begin_indices"), + intFeatures.at("peak_indices"), apriseindices); + if (retval > 0) { + setVec(IntFeatureData, StringData, "AP_rise_indices", apriseindices); + } + return retval; +} + +// *** AP fall indices *** +// +static int __AP_fall_indices(const vector& v, const vector& apbi, + const vector& apei, const vector& pi, + vector& apfi) { + apfi.resize(std::min(apbi.size(), pi.size())); + for (size_t i = 0; i < apfi.size(); i++) { + if (pi[i] >= v.size() || apbi[i] >= v.size() || apei[i] >= v.size() || pi[i] > apei[i]) { + continue; + } + double halfheight = (v[pi[i]] + v[apbi[i]]) / 2.; + vector vpeak(&v[pi[i]], &v[apei[i]]); + transform(vpeak.begin(), vpeak.end(), vpeak.begin(), + [halfheight](double val) { return fabs(val - halfheight); }); + apfi[i] = distance(vpeak.begin(), min_element(vpeak.begin(), vpeak.end())) + + pi[i]; + } + return apfi.size(); +} +int SpikeShape::AP_fall_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"V"}); + const auto& intFeatures = getFeatures( + IntFeatureData, {"AP_begin_indices", "AP_end_indices", "peak_indices"}); + vector apfallindices; + int retval = __AP_fall_indices(doubleFeatures.at("V"), + intFeatures.at("AP_begin_indices"), + intFeatures.at("AP_end_indices"), + intFeatures.at("peak_indices"), apfallindices); + if (retval > 0) { + setVec(IntFeatureData, StringData, "AP_fall_indices", apfallindices); + } + return retval; +} + +// *** AP_rise_time according to E9 and E17 *** +static int __AP_rise_time(const vector& t, const vector& v, + const vector& apbeginindices, + const vector& peakindices, + double beginperc, double endperc, + vector& aprisetime) { + aprisetime.resize(std::min(apbeginindices.size(), peakindices.size())); + // Make sure that we do not use peaks starting before the 1st AP_begin_index + // Because AP_begin_indices only takes into account peaks after stimstart + vector newpeakindices; + if (apbeginindices.size() > 0) { + newpeakindices = peaks_after_stim_start(apbeginindices[0], peakindices); + } + double begin_v; + double end_v; + double begin_indice; + double end_indice; + double apamplitude; + for (size_t i = 0; i < aprisetime.size(); i++) { + // do not use AP_amplitude feature because it does not take into account + // peaks after stim_end + apamplitude = v[newpeakindices[i]] - v[apbeginindices[i]]; + begin_v = v[apbeginindices[i]] + beginperc * apamplitude; + end_v = v[apbeginindices[i]] + endperc * apamplitude; + + // Get begin indice + size_t j = apbeginindices[i]; + // change slightly begin_v for almost equal case + // truncature error can change begin_v even when beginperc == 0.0 + while (j < newpeakindices[i] && v[j] < begin_v - 0.0000000000001){ + j++; + } + begin_indice = j; + + // Get end indice + j = newpeakindices[i]; + // change slightly end_v for almost equal case + // truncature error can change end_v even when beginperc == 0.0 + while (j > apbeginindices[i] && v[j] > end_v + 0.0000000000001) { + j--; + } + end_indice = j; + + aprisetime[i] = t[end_indice] - t[begin_indice]; + } + return aprisetime.size(); +} +int SpikeShape::AP_rise_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Fetching all required features in one go. + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, + {"T", "V", "rise_start_perc", "rise_end_perc"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); + vector aprisetime; + int retval = __AP_rise_time( + doubleFeatures.at("T"), doubleFeatures.at("V"), + intFeatures.at("AP_begin_indices"), intFeatures.at("peak_indices"), + doubleFeatures.at("rise_start_perc").empty() + ? 0.0 + : doubleFeatures.at("rise_start_perc").front(), + doubleFeatures.at("rise_end_perc").empty() + ? 1.0 + : doubleFeatures.at("rise_end_perc").front(), + aprisetime); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_rise_time", aprisetime); + } + return retval; +} + +// *** AP_fall_time according to E10 and E18 *** +static int __AP_fall_time(const vector& t, + const double stimstart, + const vector& peakindices, + const vector& apendindices, + vector& apfalltime) { + apfalltime.resize(std::min(peakindices.size(), apendindices.size())); + // Make sure that we do not use peaks starting before stim start + // Because AP_end_indices only takes into account peaks after stim start + vector newpeakindices = peaks_after_stim_start(stimstart, peakindices, t); + + for (size_t i = 0; i < apfalltime.size(); i++) { + apfalltime[i] = t[apendindices[i]] - t[newpeakindices[i]]; + } + return apfalltime.size(); +} +int SpikeShape::AP_fall_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "stim_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "AP_end_indices"}); + + const vector& t = doubleFeatures.at("T"); + const double stim_start = doubleFeatures.at("stim_start")[0]; + const vector& peakindices = intFeatures.at("peak_indices"); + const vector& apendindices = intFeatures.at("AP_end_indices"); + + vector apfalltime; + int retval = __AP_fall_time(t, stim_start, peakindices, apendindices, apfalltime); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_fall_time", apfalltime); + } + return retval; +} + +// *** AP_rise_rate according to E11 and E19 *** +static int __AP_rise_rate(const vector& t, const vector& v, + const vector& apbeginindices, + const vector& peakindices, + vector& apriserate) { + apriserate.resize(std::min(peakindices.size(), apbeginindices.size())); + vector newpeakindices; + if (apbeginindices.size() > 0) { + newpeakindices = peaks_after_stim_start(apbeginindices[0], peakindices); + } + for (size_t i = 0; i < apriserate.size(); i++) { + apriserate[i] = (v[newpeakindices[i]] - v[apbeginindices[i]]) / + (t[newpeakindices[i]] - t[apbeginindices[i]]); + } + return apriserate.size(); +} +int SpikeShape::AP_rise_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"AP_begin_indices", "peak_indices"}); + + const vector& t = doubleFeatures.at("T"); + const vector& v = doubleFeatures.at("V"); + const vector& apbeginindices = intFeatures.at("AP_begin_indices"); + const vector& peakindices = intFeatures.at("peak_indices"); + + vector apriserate; + int retval = __AP_rise_rate(t, v, apbeginindices, peakindices, apriserate); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_rise_rate", apriserate); + } + return retval; +} + +// *** AP_fall_rate according to E12 and E20 *** +static int __AP_fall_rate(const vector& t, const vector& v, + const double stimstart, + const vector& peakindices, + const vector& apendindices, + vector& apfallrate) { + apfallrate.resize(std::min(apendindices.size(), peakindices.size())); + vector newpeakindices = peaks_after_stim_start(stimstart, peakindices, t); + + for (size_t i = 0; i < apfallrate.size(); i++) { + apfallrate[i] = (v[apendindices[i]] - v[newpeakindices[i]]) / + (t[apendindices[i]] - t[newpeakindices[i]]); + } + return apfallrate.size(); +} +int SpikeShape::AP_fall_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures(DoubleFeatureData, {"T", "V", "stim_start"}); + const auto& intFeatures = + getFeatures(IntFeatureData, {"peak_indices", "AP_end_indices"}); + + const vector& t = doubleFeatures.at("T"); + const vector& v = doubleFeatures.at("V"); + const double stim_start = doubleFeatures.at("stim_start")[0]; + const vector& peakindices = intFeatures.at("peak_indices"); + const vector& apendindices = intFeatures.at("AP_end_indices"); + + vector apfallrate; + int retval = __AP_fall_rate(t, v, stim_start, peakindices, apendindices, apfallrate); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_fall_rate", apfallrate); + } + return retval; +} + +// *** AP_rise_rate_change according to E25 *** +static int __AP_rise_rate_change(const vector& apriserate, + vector& apriseratechange) { + if (apriserate.size() < 1) { + return -1; + } + apriseratechange.resize(apriserate.size() - 1); + + for (size_t i = 0; i < apriseratechange.size(); i++) { + apriseratechange[i] = (apriserate[i + 1] - apriserate[0]) / apriserate[0]; + } + return apriseratechange.size(); +} +int SpikeShape::AP_rise_rate_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"AP_rise_rate"}); + const vector& apriserate = features.at("AP_rise_rate"); + vector apriseratechange; + int retval = __AP_rise_rate_change(apriserate, apriseratechange); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_rise_rate_change", + apriseratechange); + } + return retval; +} + +// *** AP_fall_rate_change according to E26 *** +static int __AP_fall_rate_change(const vector& apfallrate, + vector& apfallratechange) { + if (apfallrate.size() < 1) { + return -1; + } + apfallratechange.resize(apfallrate.size() - 1); + for (size_t i = 0; i < apfallratechange.size(); i++) { + apfallratechange[i] = (apfallrate[i + 1] - apfallrate[0]) / apfallrate[0]; + } + return apfallratechange.size(); +} + +int SpikeShape::AP_fall_rate_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& features = getFeatures(DoubleFeatureData, {"AP_fall_rate"}); + const vector& apfallrate = features.at("AP_fall_rate"); + vector apfallratechange; + int retval = __AP_fall_rate_change(apfallrate, apfallratechange); + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "AP_fall_rate_change", + apfallratechange); + } + return retval; +} + +static int __AP_phaseslope(const vector& v, const vector& t, + double stimStart, double stimEnd, + vector& ap_phaseslopes, vector apbi, + double range) { + vector dvdt(v.size()); + vector dv; + vector dt; + int apbegin_index, range_max_index, range_min_index; + double ap_phaseslope; + getCentralDifferenceDerivative(1., v, dv); + getCentralDifferenceDerivative(1., t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + + for (size_t i = 0; i < apbi.size(); i++) { + apbegin_index = apbi[i]; + range_min_index = apbegin_index - int(range); + range_max_index = apbegin_index + int(range); + if (range_min_index < 0 || range_max_index < 0) return -1; + if (range_min_index > (int)t.size() || range_max_index > (int)t.size()) + return -1; + if (v[range_max_index] - v[range_min_index] == 0) return -1; + ap_phaseslope = (dvdt[range_max_index] - dvdt[range_min_index]) / + (v[range_max_index] - v[range_min_index]); + ap_phaseslopes.push_back(ap_phaseslope); + // printf("slope %f, mint %f, minv %f, mindvdt %f\n", ap_phaseslope, + // t[range_min_index], v[range_min_index], dvdt[range_min_index]); + // printf("slope %f, maxt %f, maxv %f, maxdvdt %f\n", ap_phaseslope, + // t[range_max_index], v[range_max_index], dvdt[range_max_index]); + } + + return ap_phaseslopes.size(); +} +/// Calculate the slope of the V, dVdt plot at the beginning of every spike +/// (at the point where the derivative crosses the DerivativeThreshold) +int SpikeShape::AP_phaseslope(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, + {"V", "T", "stim_start", "stim_end", "AP_phaseslope_range"}); + const auto& intFeatures = getFeatures(IntFeatureData, {"AP_begin_indices"}); + vector ap_phaseslopes; + int retVal = __AP_phaseslope(doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0], ap_phaseslopes, + intFeatures.at("AP_begin_indices"), + doubleFeatures.at("AP_phaseslope_range")[0]); + + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "AP_phaseslope", ap_phaseslopes); + } + return retVal; +} diff --git a/efel/cppcore/SpikeShape.h b/efel/cppcore/SpikeShape.h new file mode 100644 index 00000000..17a21733 --- /dev/null +++ b/efel/cppcore/SpikeShape.h @@ -0,0 +1,241 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "mapoperations.h" +#include "Utils.h" + +#include +#include + +using std::vector; + +namespace SpikeShape { +int peak_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_height(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP1_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) +int mean_AP_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int APlast_amp(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_amplitude_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_amplitude_from_voltagebase(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP1_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_AP1_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_AP1_peak_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int amp_drop_first_second(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int amp_drop_first_last(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int amp_drop_second_last(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int max_amp_difference(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_amplitude_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int min_AHP_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int min_AHP_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth_abs(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth_abs_slow(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_slow_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth_slow(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int fast_AHP(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int fast_AHP_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP1_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP2_depth_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AHP_time_from_peak(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int ADP_peak_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int ADP_peak_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int ADP_peak_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int depolarized_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int min_voltage_between_spikes(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int min_between_peaks_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int min_between_peaks_values(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_duration_half_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_duration_half_width_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_duration(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_duration_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_width_between_threshold(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int spike_width1(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP1_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int APlast_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int spike_width2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP1_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_begin_width(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_AP1_begin_width_diff(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_begin_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_end_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP1_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP2_begin_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_begin_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_peak_upstroke(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_peak_downstroke(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_rise_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_fall_indices(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_rise_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_fall_time(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_rise_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_fall_rate(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_rise_rate_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_fall_rate_change(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int AP_phaseslope(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +} \ No newline at end of file diff --git a/efel/cppcore/Subthreshold.cpp b/efel/cppcore/Subthreshold.cpp new file mode 100644 index 00000000..0e18e1f1 --- /dev/null +++ b/efel/cppcore/Subthreshold.cpp @@ -0,0 +1,965 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EfelExceptions.h" + +using std::distance; +using std::find_if; +using std::max_element; +using std::min_element; +using std::transform; + + +// *** The average voltage during the last 90% of the stimulus duration. *** +int Subthreshold::steady_state_voltage_stimend(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_end", "stim_start"}); + + const vector& voltages = doubleFeatures.at("V"); + const vector& times = doubleFeatures.at("T"); + const double stimStart = doubleFeatures.at("stim_start")[0]; + const double stimEnd = doubleFeatures.at("stim_end")[0]; + + double start_time = stimEnd - 0.1 * (stimEnd - stimStart); + auto start_it = find_if(times.begin(), times.end(), + [start_time](double x) { return x >= start_time; }); + auto stop_it = find_if(times.begin(), times.end(), + [stimEnd](double x) { return x >= stimEnd; }); + + size_t start_index = distance(times.begin(), start_it); + size_t stop_index = distance(times.begin(), stop_it); + + double mean = accumulate(voltages.begin() + start_index, + voltages.begin() + stop_index, 0.0); + size_t mean_size = stop_index - start_index; + + vector ssv; + if (mean_size > 0) { + mean /= mean_size; + ssv.push_back(mean); + setVec(DoubleFeatureData, StringData, "steady_state_voltage_stimend", ssv); + return 1; + } + return -1; +} + +// steady state of the voltage response during hyperpolarizing stimulus, +// elementary feature for E29 +// *** steady_state_hyper +static int __steady_state_hyper(const vector& v, + const vector& t, double stimend, + vector& steady_state_hyper) { + // Find the iterator pointing to the first time value greater than or equal to + // stimend + auto it_stimend = find_if( + t.begin(), t.end(), [stimend](double t_val) { return t_val >= stimend; }); + + // Calculate the index, ensuring you account for the offset of -5 + int i_end = distance(t.begin(), it_stimend) - 5; + + const int offset = 30; + if (i_end < 0 || i_end < offset) { + return -1; + } + + size_t i_begin = static_cast(i_end - offset); + + double sum = 0.; + + for (size_t i = i_begin; i < static_cast(i_end); i++) { + sum += v[i]; + } + + double mean = sum / (i_end - i_begin); + steady_state_hyper.push_back(mean); + return 1; +} + +int Subthreshold::steady_state_hyper(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required features at once + const auto& features = getFeatures(DoubleFeatureData, {"V", "T", "stim_end"}); + + vector steady_state_hyper; + int retval = + __steady_state_hyper(features.at("V"), features.at("T"), + features.at("stim_end").front(), steady_state_hyper); + + if (retval > 0) { + setVec(DoubleFeatureData, StringData, "steady_state_hyper", + steady_state_hyper); + } + return retval; +} + +// *** steady state voltage *** +static int __steady_state_voltage(const vector& v, + const vector& t, double stimEnd, + vector& ssv) { + int mean_size = 0; + double mean = 0; + for (int i = t.size() - 1; t[i] > stimEnd; i--) { + mean += v[i]; + mean_size++; + } + mean /= mean_size; + ssv.push_back(mean); + return 1; +} + +int Subthreshold::steady_state_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_end"}); + if (doubleFeatures.at("stim_end").size() != 1) return -1; + + vector ssv; + int retVal = + __steady_state_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_end")[0], ssv); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "steady_state_voltage", ssv); + } + return retVal; +} + +int Subthreshold::voltage_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& v = getFeature(DoubleFeatureData, "V"); + const vector& t = getFeature(DoubleFeatureData, "T"); + const vector& stimStart = getFeature(DoubleFeatureData, "stim_start"); + + // Retrieve percentage values or use defaults. + double vb_start_perc = 0.9; // Default value + double vb_end_perc = 1.0; // Default value + try { + auto vb_start_perc_vec = + getFeature(DoubleFeatureData, "voltage_base_start_perc"); + if (vb_start_perc_vec.size() == 1) vb_start_perc = vb_start_perc_vec[0]; + } catch (const EmptyFeatureError&) { + // If there's an error, use the default value. + } + + try { + auto vb_end_perc_vec = + getFeature(DoubleFeatureData, "voltage_base_end_perc"); + if (vb_end_perc_vec.size() == 1) vb_end_perc = vb_end_perc_vec[0]; + } catch (const EmptyFeatureError&) { + // If there's an error, use the default value. + } + + // Calculate start and end times based on stimStart and percentages. + double startTime = stimStart[0] * vb_start_perc; + double endTime = stimStart[0] * vb_end_perc; + + // Validate start and end times. + if (startTime >= endTime) + throw FeatureComputationError("voltage_base: startTime >= endTime"); + + const auto& precisionThreshold = + getFeature(DoubleFeatureData, "precision_threshold"); + + // Find index range for the time vector within the specified start and end + // times. + std::pair time_index = + get_time_index(t, startTime, endTime, precisionThreshold[0]); + + // Extract sub-vector of voltages based on calculated indices. + vector subVector(v.begin() + time_index.first, + v.begin() + time_index.second); + + // Determine computation mode and calculate voltage base. + std::string computation_mode; + + int retVal = getStrParam(StringData, "voltage_base_mode", computation_mode); + if (retVal < 0) return -1; + double vBase; + + // Perform computation based on the mode. + if (computation_mode == "mean") + vBase = vec_mean(subVector); + else if (computation_mode == "median") + vBase = vec_median(subVector); + else + throw FeatureComputationError("Undefined computational mode. Only mean and median are enabled."); + + vector vRest = {vBase}; + setVec(DoubleFeatureData, StringData, "voltage_base", vRest); + return 1; +} + +int Subthreshold::current_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"I", "T", "stim_start"}); + double cb_start_perc = 0.9; // Default value + double cb_end_perc = 1.0; // Default value + + try { + cb_start_perc = getFeature(DoubleFeatureData, "current_base_start_perc")[0]; + } catch (const std::runtime_error&) { + } // Use default value if not found or an error occurs + + try { + cb_end_perc = getFeature(DoubleFeatureData, "current_base_end_perc")[0]; + } catch (const std::runtime_error&) { + } // Use default value if not found or an error occurs + + double startTime = doubleFeatures.at("stim_start")[0] * cb_start_perc; + double endTime = doubleFeatures.at("stim_start")[0] * cb_end_perc; + + if (startTime >= endTime) + throw FeatureComputationError("current_base: startTime >= endTime"); + + vector precisionThreshold; + int retVal = + getParam(DoubleFeatureData, "precision_threshold", precisionThreshold); + if (retVal < 0) return -1; + + std::pair time_index = get_time_index( + doubleFeatures.at("T"), startTime, endTime, precisionThreshold[0]); + + vector subVector(doubleFeatures.at("I").begin() + time_index.first, + doubleFeatures.at("I").begin() + time_index.second); + + double iBase; + std::string computation_mode; + retVal = getStrParam(StringData, "current_base_mode", computation_mode); + if (retVal < 0) return -1; + if (computation_mode == "mean") + iBase = vec_mean(subVector); + else if (computation_mode == "median") + iBase = vec_median(subVector); + else + throw FeatureComputationError("Undefined computational mode. Only mean and median are enabled."); + + vector iRest{iBase}; + setVec(DoubleFeatureData, StringData, "current_base", iRest); + return 1; +} + +// *** timeconstant *** +// requires hyperpolarizing stimulus +// +// the exponential fit works iteratively +// +static int __time_constant(const vector& v, const vector& t, + double stimStart, double stimEnd, + vector& tc) { + // value of the derivative near the minimum + double min_derivative = 5e-3; + // minimal required length of the decay (indices) + size_t min_length = 10; + // minimal required time length in ms + int t_length = 70; + size_t stimstartindex; + for (stimstartindex = 0; t[stimstartindex] < stimStart; stimstartindex++) + ; + // increment stimstartindex to skip a possible transient + stimstartindex += 10; + // int stimendindex; + // for(stimendindex = 0; t[stimendindex] < stimEnd; stimendindex++) ; + // int stimmiddleindex = (stimstartindex + stimendindex) / 2; + double mid_stim = (stimStart + stimEnd) / 2.; + auto it_mid_stim = + find_if(t.begin() + stimstartindex, t.end(), + [mid_stim](double val) { return val >= mid_stim; }); + int stimmiddleindex = distance(t.begin(), it_mid_stim); + + if (stimstartindex >= v.size() || stimmiddleindex < 0 || + static_cast(stimmiddleindex) >= v.size()) { + return -1; + } + vector part_v(&v[stimstartindex], &v[stimmiddleindex]); + + vector part_t(&t[stimstartindex], &t[stimmiddleindex]); + vector dv; + vector dt; + vector dvdt(part_t.size()); + // calculate |dV/dt12 + // getCentralDifferenceDerivative(1.,part_v,dv); + // getCentralDifferenceDerivative(1.,part_t,dt); + getfivepointstencilderivative(part_v, dv); + getfivepointstencilderivative(part_t, dt); + transform(dv.begin(), dv.end(), dt.begin(), dvdt.begin(), + std::divides()); + // find start of the decay + int i_start = 0; + while (find_if(dvdt.begin() + i_start, dvdt.begin() + i_start + 5, + [min_derivative](double val) { + return val > -min_derivative; + }) != dvdt.begin() + i_start + 5) { + if (dvdt.begin() + i_start + 5 == dvdt.end()) { + throw FeatureComputationError("Could not find the decay."); + } + i_start++; + } + // find the flat + // bool foundflat = false; + int i_flat; + for (i_flat = i_start; + t[i_flat + stimstartindex] < t[stimmiddleindex] - t_length; i_flat++) { + if (dvdt[i_flat] + min_derivative > 0.) { + double sum = 0.; + int length = 0; + for (int i_mean = 0; + t[i_flat + i_mean + stimstartindex] - t[i_flat + stimstartindex] < + t_length; + i_mean++) { + sum += dvdt[i_flat + i_mean]; + length++; + } + double mean = sum / (double)length; + if (mean + min_derivative > 0.) { + // foundflat = true; + break; + } + } + } + // if(!foundflat) { + // GErrorStr += "\nCould not locate plateau within range.\n"; + // return -1; + //} + // containing the flat: + vector dvdt_decay(dvdt.begin() + i_start, dvdt.begin() + i_flat); + vector t_decay(part_t.begin() + i_start, part_t.begin() + i_flat); + vector v_decay(part_v.begin() + i_start, part_v.begin() + i_flat); + if (dvdt_decay.size() < min_length) { + throw FeatureComputationError("Trace fall time too short."); + } + + // fit to exponential + // + vector log_v(dvdt_decay.size(), 0.); + + // golden section search algorithm + const double PHI = 1.618033988; + vector x(3, .0); + // time_constant is searched in between 0 and 1000 ms + x[2] = min_derivative * 1000.; + x[1] = (x[0] * PHI + x[2]) / (1. + PHI); + // calculate residuals at x[1] + for (size_t i = 0; i < log_v.size(); i++) { + log_v[i] = log(v_decay[i] - v_decay.back() + x[1]); + } + + linear_fit_result fit; + fit = slope_straight_line_fit(t_decay, log_v); + double residuum = fit.normalized_std; + bool right = true; + double newx; + while (x[2] - x[0] > .01) { + // calculate new x value according to the golden section + if (right) { + newx = (x[1] * PHI + x[2]) / (1. + PHI); + } else { + newx = (x[0] + PHI * x[1]) / (1. + PHI); + } + // calculate residuals at newx + for (size_t i = 0; i < log_v.size(); i++) { + log_v[i] = log(v_decay[i] - v_decay.back() + newx); + } + fit = slope_straight_line_fit(t_decay, log_v); + + if (fit.normalized_std < residuum) { + if (right) { + x[0] = x[1]; + x[1] = newx; + } else { + x[2] = x[1]; + x[1] = newx; + } + residuum = fit.normalized_std; + } else { + if (right) { + x[2] = newx; + } else { + x[0] = newx; + } + right = !right; + } + } + tc.push_back(-1. / fit.slope); + return 1; +} + +int Subthreshold::time_constant(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + vector tc; + int retVal = __time_constant(doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0], tc); + if (retVal >= 0) { + setVec(DoubleFeatureData, StringData, "time_constant", tc); + } + return retVal; +} + +size_t get_index(const vector& times, double t) { + return distance(times.begin(), find_if(times.begin(), times.end(), + [t](double x) { return x >= t; })); +} + +double __decay_time_constant_after_stim(const vector& times, + const vector& voltage, + const double decay_start_after_stim, + const double decay_end_after_stim, + const double stimStart, + const double stimEnd) { + const size_t stimStartIdx = get_index(times, stimStart); + const size_t decayStartIdx = + get_index(times, stimEnd + decay_start_after_stim); + + const size_t decayEndIdx = get_index(times, stimEnd + decay_end_after_stim); + + const double reference = voltage[stimStartIdx]; + + vector decayValues(decayEndIdx - decayStartIdx); + vector decayTimes(decayEndIdx - decayStartIdx); + + for (size_t i = 0; i != decayValues.size(); ++i) { + const double u0 = std::abs(voltage[decayStartIdx + i] - reference); + + decayValues[i] = log(u0); + decayTimes[i] = times[decayStartIdx + i]; + } + + if (decayTimes.size() < 1 || decayValues.size() < 1) { + throw FeatureComputationError("No data points to calculate decay_time_constant_after_stim"); + } + linear_fit_result fit; + fit = slope_straight_line_fit(decayTimes, decayValues); + + const double tau = -1.0 / fit.slope; + return std::abs(tau); +} + +// *** Decay time constant measured during decay after the stimulus*** +int Subthreshold::decay_time_constant_after_stim(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + + double decay_start_after_stim, decay_end_after_stim; + + try { + const auto& decayStartFeatures = + getFeatures(DoubleFeatureData, {"decay_start_after_stim"}); + decay_start_after_stim = decayStartFeatures.at("decay_start_after_stim")[0]; + } catch (const std::runtime_error&) { + decay_start_after_stim = 1.0; // Default value if not found + } + + try { + const auto& decayEndFeatures = + getFeatures(DoubleFeatureData, {"decay_end_after_stim"}); + decay_end_after_stim = decayEndFeatures.at("decay_end_after_stim")[0]; + } catch (const std::runtime_error&) { + decay_end_after_stim = 10.0; // Default value if not found + } + // Validate decay times + if (decay_start_after_stim >= decay_end_after_stim) + throw FeatureComputationError("Error decay_start_after_stim small larger than decay_end_after_stim"); + + // Perform calculation + const double val = __decay_time_constant_after_stim( + doubleFeatures.at("T"), doubleFeatures.at("V"), decay_start_after_stim, + decay_end_after_stim, doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0]); + + // Store the result + vector dtcas{val}; + setVec(DoubleFeatureData, StringData, "decay_time_constant_after_stim", + dtcas); + + return 1; +} + +// Calculate the time constants after each step for a stimuli containing several +// steps, as for example SpikeRec protocols +int Subthreshold::multiple_decay_time_constant_after_stim( + mapStr2intVec& IntFeatureData, mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"V", "T", "multi_stim_start", "multi_stim_end"}); + vector stimsEnd, stimsStart; + + stimsEnd = doubleFeatures.at("multi_stim_end"); + stimsStart = doubleFeatures.at("multi_stim_start"); + + // Attempt to get decay parameters, using defaults if not found or if not + // exactly one element + double decay_start_after_stim = 1.0; + double decay_end_after_stim = 10.0; + try { + decay_start_after_stim = getFeature(DoubleFeatureData, "decay_start_after_stim")[0]; + } catch (const std::runtime_error&) { + } // Use default value + try { + decay_end_after_stim = getFeature(DoubleFeatureData, "decay_end_after_stim")[0]; + } catch (const std::runtime_error&) { + } // Use default value + vector dtcas; + for (size_t i = 0; i < stimsStart.size(); i++) { + double ret_dtcas = __decay_time_constant_after_stim( + doubleFeatures.at("T"), doubleFeatures.at("V"), decay_start_after_stim, + decay_end_after_stim, stimsStart[i], stimsEnd[i]); + dtcas.push_back(ret_dtcas); + } + setVec(DoubleFeatureData, StringData, + "multiple_decay_time_constant_after_stim", dtcas); + return 1; +} + +// compute time constant for the decay from the sag to the steady_state_voltage +// noisy data is expected, so no golden section search is used +// because with noisy data, x>0 often gives a worse logarithmic fit +static int __sag_time_constant(const vector& times, + const vector& voltage, + const double minimum_voltage, + const double steady_state_v, + const double sag_amplitude, + const double stimStart, const double stimEnd, + vector& sagtc) { + // minimal required length of each decay (indices) + size_t min_length = 10; + + // get start index + const size_t decayStartIdx = distance( + voltage.begin(), + find_if(voltage.begin(), voltage.end(), + [minimum_voltage](double v) { return v <= minimum_voltage; })); + + // voltage at which 90% of the sag amplitude has decayed + double steady_state_90 = steady_state_v - sag_amplitude * 0.1; + // get end index + const size_t decayEndIdx = distance( + voltage.begin(), + find_if(voltage.begin() + decayStartIdx, voltage.end(), + [steady_state_90](double v) { return v >= steady_state_90; })); + + // voltage reference by which the voltage (i the decay interval) + // is going to be substracted + // there should be no '0' in (decay_v - v_reference), + // so no problem when the log is taken + double v_reference = voltage[decayEndIdx]; + + // decay interval + vector VInterval(&voltage[decayStartIdx], &voltage[decayEndIdx]); + vector TInterval(×[decayStartIdx], ×[decayEndIdx]); + + // compute time constant + vector decayValues(decayEndIdx - decayStartIdx); + for (size_t i = 0; i < VInterval.size(); ++i) { + const double u0 = std::abs(VInterval[i] - v_reference); + decayValues[i] = log(u0); + } + if (decayValues.size() < min_length) { + throw FeatureComputationError("Not enough data points to compute time constant."); + } + linear_fit_result fit; + fit = slope_straight_line_fit(TInterval, decayValues); + + // append tau + sagtc.push_back(std::abs(1.0 / fit.slope)); + + return 1; +} + +// *** Decay time constant measured from minimum voltage to steady-state +// voltage*** +int Subthreshold::sag_time_constant(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"V", "T", "stim_end", "stim_start", "minimum_voltage", + "steady_state_voltage_stimend", "sag_amplitude"}); + vector sagtc; + int retVal = __sag_time_constant( + doubleFeatures.at("T"), doubleFeatures.at("V"), + doubleFeatures.at("minimum_voltage")[0], + doubleFeatures.at("steady_state_voltage_stimend")[0], + doubleFeatures.at("sag_amplitude")[0], doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0], sagtc); + + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "sag_time_constant", sagtc); + } + return retVal; +} + +int Subthreshold::sag_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"steady_state_voltage_stimend", + "voltage_deflection_vb_ssse", "minimum_voltage"}); + + vector sag_amplitude; + if (doubleFeatures.at("voltage_deflection_vb_ssse")[0] <= 0) { + sag_amplitude.push_back( + doubleFeatures.at("steady_state_voltage_stimend")[0] - + doubleFeatures.at("minimum_voltage")[0]); + } else + throw FeatureComputationError("sag_amplitude: voltage_deflection is positive"); + + if (!sag_amplitude.empty()) { + setVec(DoubleFeatureData, StringData, "sag_amplitude", sag_amplitude); + } + return sag_amplitude.empty() ? -1 : 1; +} + +int Subthreshold::sag_ratio1(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double features at once + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"sag_amplitude", "voltage_base", "minimum_voltage"}); + + vector sag_ratio1; + if (doubleFeatures.at("minimum_voltage")[0] == doubleFeatures.at("voltage_base")[0]) + throw FeatureComputationError("voltage_base equals minimum_voltage"); + + sag_ratio1.push_back(doubleFeatures.at("sag_amplitude")[0] / + (doubleFeatures.at("voltage_base")[0] - + doubleFeatures.at("minimum_voltage")[0])); + + if (!sag_ratio1.empty()) { + setVec(DoubleFeatureData, StringData, "sag_ratio1", sag_ratio1); + } + return sag_ratio1.empty() ? -1 : 1; +} + +int Subthreshold::sag_ratio2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + // Retrieve all required double features at once + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, + {"voltage_base", "minimum_voltage", "steady_state_voltage_stimend"}); + + vector sag_ratio2; + if (doubleFeatures.at("minimum_voltage")[0] == doubleFeatures.at("voltage_base")[0]) + throw FeatureComputationError("voltage_base equals minimum_voltage"); + + sag_ratio2.push_back( + (doubleFeatures.at("voltage_base")[0] - + doubleFeatures.at("steady_state_voltage_stimend")[0]) / + (doubleFeatures.at("voltage_base")[0] - + doubleFeatures.at("minimum_voltage")[0])); + + if (!sag_ratio2.empty()) { + setVec(DoubleFeatureData, StringData, "sag_ratio2", sag_ratio2); + } + return sag_ratio2.empty() ? -1 : 1; +} + +// *** ohmic input resistance *** + +static int __ohmic_input_resistance(double voltage_deflection, + double stimulus_current, + vector& oir) { + if (stimulus_current == 0) + throw FeatureComputationError("Stimulus current is zero which will result in division by zero."); + oir.push_back(voltage_deflection / stimulus_current); + return 1; +} + +int Subthreshold::ohmic_input_resistance(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"voltage_deflection", "stimulus_current"}); + vector oir; + int retVal = + __ohmic_input_resistance(doubleFeatures.at("voltage_deflection")[0], + doubleFeatures.at("stimulus_current")[0], oir); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "ohmic_input_resistance", oir); + } + return retVal; +} + +// *** ohmic input resistance based on voltage_deflection_vb_ssse*** + +int Subthreshold::ohmic_input_resistance_vb_ssse(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"voltage_deflection_vb_ssse", "stimulus_current"}); + const double stimulus_current = doubleFeatures.at("stimulus_current")[0]; + if (stimulus_current == 0) + throw FeatureComputationError("Stimulus current is zero which will result in division by zero."); + vector ohmic_input_resistance_vb_ssse; + ohmic_input_resistance_vb_ssse.push_back( + doubleFeatures.at("voltage_deflection_vb_ssse")[0] / stimulus_current); + setVec(DoubleFeatureData, StringData, "ohmic_input_resistance_vb_ssse", + ohmic_input_resistance_vb_ssse); + + return 1; +} + +/// *** Voltage deflection between voltage_base and steady_state_voltage_stimend +int Subthreshold::voltage_deflection_vb_ssse(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = getFeatures( + DoubleFeatureData, {"voltage_base", "steady_state_voltage_stimend"}); + + vector voltage_deflection_vb_ssse; + voltage_deflection_vb_ssse.push_back( + doubleFeatures.at("steady_state_voltage_stimend")[0] - + doubleFeatures.at("voltage_base")[0]); + setVec(DoubleFeatureData, StringData, "voltage_deflection_vb_ssse", + voltage_deflection_vb_ssse); + return 1; +} + +// *** voltage deflection *** + +static int __voltage_deflection(const vector& v, + const vector& t, double stimStart, + double stimEnd, vector& vd) { + const size_t window_size = 5; + + size_t stimendindex = 0; + double base = 0.; + int base_size = 0; + for (size_t i = 0; i < t.size(); i++) { + if (t[i] < stimStart) { + base += v[i]; + base_size++; + } + if (t[i] > stimEnd) { + stimendindex = (int)i; + break; + } + } + if (base_size == 0) return -1; + base /= base_size; + double wind_mean = 0.; + if (!(stimendindex >= 2 * window_size && v.size() > 0 && + stimendindex > window_size && stimendindex - window_size < v.size())) { + return -1; + } + for (size_t i = stimendindex - 2 * window_size; + i < stimendindex - window_size; i++) { + wind_mean += v[i]; + } + wind_mean /= window_size; + vd.push_back(wind_mean - base); + return 1; +} + +int Subthreshold::voltage_deflection(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + vector vd; + int retVal = __voltage_deflection( + doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_start")[0], doubleFeatures.at("stim_end")[0], vd); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "voltage_deflection", vd); + } + return retVal; +} + +static int __voltage_deflection_begin(const vector& v, + const vector& t, double stimStart, + double stimEnd, vector& vd) { + double deflection_range_percentage = 0.10; + double range_begin = + stimStart + (stimEnd - stimStart) * (deflection_range_percentage / 2); + double range_stop = + range_begin + (stimEnd - stimStart) * (deflection_range_percentage); + double base = 0.; + int base_size = 0; + for (size_t i = 0; i < t.size(); i++) { + if (t[i] < stimStart) { + base += v[i]; + base_size++; + } else { + break; + } + } + base /= base_size; + double volt = 0; + int volt_size = 0; + for (size_t i = 0; i < t.size(); i++) { + if (t[i] > range_stop) { + break; + } + if (t[i] > range_begin) { + volt += v[i]; + volt_size++; + } + } + volt /= volt_size; + + vd.push_back(volt - base); + return 1; +} + +int Subthreshold::voltage_deflection_begin(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& v = getFeature(DoubleFeatureData, "V"); + const vector& t = getFeature(DoubleFeatureData, "T"); + const vector& stimStart = getFeature(DoubleFeatureData, "stim_start"); + const vector& stimEnd = getFeature(DoubleFeatureData, "stim_end"); + vector vd; + int retVal = __voltage_deflection_begin(v, t, stimStart[0], stimEnd[0], vd); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "voltage_deflection_begin", vd); + } + return retVal; +} + +// The mean voltage after the stimulus in (stim_end + 25%*end_period, stim_end + +// 75%*end_period) +int Subthreshold::voltage_after_stim(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const vector& v = getFeature(DoubleFeatureData, "V"); + const vector& t = getFeature(DoubleFeatureData, "T"); + const vector& stimEnd = getFeature(DoubleFeatureData, "stim_end"); + double startTime = stimEnd[0] + (t.back() - stimEnd[0]) * .25; + double endTime = stimEnd[0] + (t.back() - stimEnd[0]) * .75; + int nCount = 0; + double vSum = 0; + + for (size_t i = 0; i < t.size(); i++) { + if (t[i] >= startTime) { + vSum += v[i]; + nCount++; + } + if (t[i] > endTime) break; + } + + if (nCount == 0) return -1; + + vector vRest = {vSum / nCount}; + setVec(DoubleFeatureData, StringData, "voltage_after_stim", vRest); + + return 1; +} + +static int __maxmin_voltage(const vector& v, const vector& t, + double stimStart, double stimEnd, + vector& maxV, vector& minV) { + if (stimStart > t[t.size() - 1]) + throw FeatureComputationError("Stimulus start larger than max time in trace"); + + if (stimEnd > t[t.size() - 1]) stimEnd = t.back(); + + size_t stimstartindex = 0; + while (t[stimstartindex] < stimStart && stimstartindex <= t.size()) + stimstartindex++; + + if (stimstartindex >= t.size()) { + throw FeatureComputationError("Stimulus start index not found"); + } + + size_t stimendindex = 0; + while (t[stimendindex] < stimEnd && stimstartindex <= t.size()) + stimendindex++; + + if (stimendindex >= t.size()) { + throw FeatureComputationError("Stimulus end index not found"); + } + + maxV.push_back(*max_element(&v[stimstartindex], &v[stimendindex])); + minV.push_back(*min_element(&v[stimstartindex], &v[stimendindex])); + + return 1; +} + +int Subthreshold::maximum_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + vector maxV, minV; + int retVal = __maxmin_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0], maxV, minV); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "maximum_voltage", maxV); + } + return retVal; +} + +// *** maximum voltage *** +// +int Subthreshold::minimum_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"V", "T", "stim_start", "stim_end"}); + vector maxV, minV; + int retVal = __maxmin_voltage(doubleFeatures.at("V"), doubleFeatures.at("T"), + doubleFeatures.at("stim_start")[0], + doubleFeatures.at("stim_end")[0], maxV, minV); + if (retVal > 0) { + setVec(DoubleFeatureData, StringData, "minimum_voltage", minV); + } + return retVal; +} + +// *** Diff between maximum voltage during stimulus and voltage_base *** +int Subthreshold::maximum_voltage_from_voltagebase(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData) { + const auto& doubleFeatures = + getFeatures(DoubleFeatureData, {"maximum_voltage", "voltage_base"}); + + vector maximum_voltage_from_voltagebase; + maximum_voltage_from_voltagebase.push_back( + doubleFeatures.at("maximum_voltage")[0] - + doubleFeatures.at("voltage_base")[0]); + setVec(DoubleFeatureData, StringData, "maximum_voltage_from_voltagebase", + maximum_voltage_from_voltagebase); + return 1; +} diff --git a/efel/cppcore/Subthreshold.h b/efel/cppcore/Subthreshold.h new file mode 100644 index 00000000..4e3a00bc --- /dev/null +++ b/efel/cppcore/Subthreshold.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2015-2024, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "mapoperations.h" +#include "Utils.h" + +#include +#include + +using std::vector; + +namespace Subthreshold { +int steady_state_voltage_stimend(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int steady_state_hyper(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int steady_state_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int current_base(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int time_constant(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int decay_time_constant_after_stim(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int multiple_decay_time_constant_after_stim(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int sag_time_constant(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int sag_amplitude(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int sag_ratio1(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int sag_ratio2(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int ohmic_input_resistance(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int ohmic_input_resistance_vb_ssse(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage_deflection_vb_ssse(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage_deflection(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage_deflection_begin(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int voltage_after_stim(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int maximum_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int minimum_voltage(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +int maximum_voltage_from_voltagebase(mapStr2intVec& IntFeatureData, + mapStr2doubleVec& DoubleFeatureData, + mapStr2Str& StringData); +} \ No newline at end of file diff --git a/efel/cppcore/cfeature.cpp b/efel/cppcore/cfeature.cpp index e2d9b125..834e5446 100644 --- a/efel/cppcore/cfeature.cpp +++ b/efel/cppcore/cfeature.cpp @@ -30,10 +30,10 @@ using std::endl; cFeature::cFeature(const string& strDepFile, const string& outdir) : logger(outdir) { FillFptrTable(); - mapFptrLib["LibV1"] = &FptrTableV1; - mapFptrLib["LibV2"] = &FptrTableV2; - mapFptrLib["LibV3"] = &FptrTableV3; - mapFptrLib["LibV5"] = &FptrTableV5; + mapFptrLib["BasicFeatures"] = &FptrTableBF; + mapFptrLib["SpikeEvent"] = &FptrTableSE; + mapFptrLib["SpikeShape"] = &FptrTableSS; + mapFptrLib["Subthreshold"] = &FptrTableST; fillfeaturetypes(); diff --git a/setup.py b/setup.py index db090cc9..2a9fa13e 100644 --- a/setup.py +++ b/setup.py @@ -30,19 +30,19 @@ cppcore_dir = os.path.join('efel', 'cppcore') cppcore_sources = ['cppcore.cpp', 'Utils.cpp', - 'LibV1.cpp', - 'LibV2.cpp', - 'LibV3.cpp', - 'LibV5.cpp', + 'BasicFeatures.cpp', + 'SpikeEvent.cpp', + 'SpikeShape.cpp', + 'Subthreshold.cpp', 'FillFptrTable.cpp', 'DependencyTree.cpp', 'cfeature.cpp', 'mapoperations.cpp'] cppcore_headers = ['Utils.h', - 'LibV1.h', - 'LibV2.h', - 'LibV3.h', - 'LibV5.h', + 'BasicFeatures.h', + 'SpikeEvent.h', + 'SpikeShape.h', + 'Subthreshold.h', 'FillFptrTable.h', 'DependencyTree.h', 'cfeature.h',