"""
This script generates backproagation threshold and AP threshold figures from the main text.
(See end of script.)
"""
import os, sys
filepath = os.getcwd()+'/'
import itertools
import numpy as np
from sorcery import print_args
import sqlite3
import pandas as pd
pd.set_option('display.max_colwidth', None)
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import rc
xlim = ylim = (None, None) #<---default (don't touch)
label_size = 20
legend_size = 17
line_width = 3
# matplotlib.rcParams['figure.titlesize'] = label_size
# matplotlib.rcParams['axes.titlesize'] = label_size
matplotlib.rcParams['xtick.labelsize'] = label_size
matplotlib.rcParams['ytick.labelsize'] = label_size
matplotlib.rcParams['axes.labelsize'] = label_size*1.3
matplotlib.rcParams['legend.fontsize'] = legend_size
matplotlib.rcParams['legend.title_fontsize'] = legend_size
matplotlib.rcParams['lines.linewidth'] = line_width
############################################################################
###matplotlib LaTeX settings################################################
rc('text', usetex=True) ; latex_preamble = r''
latex_preamble += r'\usepackage{stix}'
latex_preamble += r'\usepackage{amsmath}'
rc('text.latex', preamble=latex_preamble) ;
rc('font', **{
'family': 'STIXGeneral',
'serif': ['Times'],
'sans-serif': ['Helvetica']
})
# rc('font', **{
# 'family': 'sans-serif',
# 'sans-serif': ['Helvetica'] # You can replace 'Helvetica' with the specific sans-serif font you want to use
# })
############################################################################
matplotlib_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
# matplotlib_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
plotly_colors = ['#636EFA', '#EF553B', '#00CC96', '#AB63FA', '#FFA15A', '#19D3F3', '#FF6692', '#B6E880', '#FF97FF', '#FECB52']
plotly_symbols = ['o', 's', 'D', 'P', 'X', '^', 'v']
markers_dict = {}
markers_dict['0.2'] = 'p'
markers_dict['0.3'] = 'v'
markers_dict['0.4'] = 'P'
markers_dict['0.5'] = 'o'
markers_dict['0.6'] = 'X'
markers_dict['0.7'] = 'd'
markers_dict['0.8'] = '*'
### Get the default color cycle
default_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
### Create colors_dict
colors_dict = {}
color_iterator = itertools.cycle(default_colors)
for key in reversed(markers_dict.keys()):
if key == '0.5':
colors_dict[key] = 'black'
else:
colors_dict[key] = next(color_iterator)
################################################################################################
###Build the figure#############################################################################
################################################################################################
# xlabel=r'\textrm{Na\textsubscript{V} Separation} $x$'
xlabel=r'Na\textsubscript{V} Separation $x$'
legend_title = r'Crossover Location: $\kappa$'
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(1, 1, 1)
ax.tick_params(left=True, right=True)
def plotThresh(originalDF, propString, marker='o', identical_markers=False, alpha=1.0, legend=True, title=False, flip_legend_order=False, show=True, fix_crossOverPosition=False, scan_vals=None, filename=None, xlim=None, ylim=None):
if fix_crossOverPosition:
scan_vals = [0.5]
elif scan_vals==None:
scan_vals = np.sort(np.unique(originalDF['CrossOverPosition'][originalDF['CrossOverPosition']>0.001].to_numpy()))
scan_vals = list(scan_vals)
scan_vals.sort()
# scan_vals = reversed(scan_vals)
##############################################################################################################
###In some later simulations, we only simulated the x=0 setup once, since kappa has no effect in that case.
###This saves simulation time.
###The curves other than kappa=0.5 reuse this point.
###In this ugly loop, we find that point.
###Note, some past simulations repeated the simulations for this identical point, but this loop had to be added
###after the code was changed to only simulate the x=0 threshold once.
yvalue_at__kappa_equals_zero_point_five__xequalszero_value = None
for scan_value in scan_vals:
DF = originalDF[(originalDF['CrossOverPosition']==scan_value) & (originalDF['Qthresh_'+propString]!=np.inf)]
y = DF['Qthresh_'+propString].to_numpy()
x = DF['deviation'].to_numpy()
sort = np.argsort(x)
sort = np.flip(sort)
x = x[sort]
y = y[sort]
data = np.vstack((x, y))
keep_looking_for_kappa0point5 = True
threshold_point_at__x_equals_zero = None
if len(data[0]):
kappa = np.unique(DF['CrossOverPosition'].to_numpy())[0]
if keep_looking_for_kappa0point5 == True:
if kappa == 0.5:
keep_looking_for_kappa0point5 = False
x0_index = np.argwhere(x==0).flatten()
###For axonal stimulation, the threshold may be undefined at x=0.0 in the Hu-based model
###so we exclude that case from this procedure.
if x0_index.size>0:
y_at_x0 = y[x0_index]
# print(y)
yvalue_at__kappa_equals_zero_point_five__xequalszero_value = y_at_x0[0]
threshold_point_at__x_equals_zero = [0.0, yvalue_at__kappa_equals_zero_point_five__xequalszero_value]
print_args(threshold_point_at__x_equals_zero)
##############################################################################################################
legend_varname_flag = False
legend_varname = r'$ \kappa = '
for scan_value in scan_vals:
if legend_varname_flag == False:
legend_varname = r'$'
DF = originalDF[(originalDF['CrossOverPosition']==scan_value) & (originalDF['Qthresh_'+propString]!=np.inf)]
y = DF['Qthresh_'+propString].to_numpy()
x = DF['deviation'].to_numpy()
sort = np.argsort(x)
sort = np.flip(sort)
x=x[sort]
y = y[sort]
data = np.vstack((x, y))
print_args(data)
print_args(data)
# exit()
print_args(scan_value)
print_args(y)
print_args(x)
print_args(data[1])
print_args(data[0])
print_args(DF['CrossOverPosition'].to_numpy())
print_args(np.unique(DF['CrossOverPosition'].to_numpy()))
if type(xlim) is list or type(xlim) is tuple:
plt.xlim(xlim)
xmin = xlim[0]
select = np.argwhere(x>=xmin).flatten()
print_args(select)
x = x[select]
y = y[select]
print_args(x, y)
if type(ylim) is list or type(ylim) is tuple:
ymin, ymax = ylim
# if ymin is not None:
# plt.ylim(ymin=ymin)
if ymax is not None:
select = np.argwhere(y<=ymax).flatten()
x = x[select]
y = y[select]
print_args(x, y)
if len(data[0]):
kappa = np.unique(DF['CrossOverPosition'].to_numpy())[0]
if identical_markers:
marker=marker
else:
marker = markers_dict[str(kappa)]
color = colors_dict[str(kappa)]
if kappa == 0.5:
"""
plot the data at kappa=0.5 in black
"""
ax.plot(x, y, marker=marker, color='black', markersize=10, linewidth=1.0, label=legend_varname+str(scan_value)+r'$', alpha=alpha)
else:
"""Add the threshold point at x=0 if found...
This point is collected (as described above) from the kappa=0.5 data
and added to the other data sets, since more recent simulations
do not repeat the x=0.0 simulations for the other curves as it is
useless repetition: kappa has no effect at x=0."""
if threshold_point_at__x_equals_zero and 0 not in list(x):
x = np.array(list(x)+[threshold_point_at__x_equals_zero[0]])
y = np.array(list(y)+[threshold_point_at__x_equals_zero[1]])
"""
plot the data where kappa is NOT 0.5 using a variety of coloured symbols.
"""
ax.plot(x, y, marker=marker, color=color, markersize=10, linewidth=1.0, label=legend_varname+str(scan_value)+r'$', alpha=alpha)
legend_varname_flag = False
# plt.plot(data[0], data[1], marker='*',markersize=10, linewidth=1, label=r'gammaAIS = '+str(scan_value))
if propString=="backProp":
threshtext='BP'
else:
threshtext='FP'
plt.ylabel(r'$I_{'+threshtext+r'}'+r'\ \ [nA]$', fontsize=27)
# plt.xlabel(r'$𝒳𝔁𝑥$', fontsize=70)
# plt.xlabel(r'$𝑥$', fontsize=70)
plt.xlabel(xlabel)
if title:
# plt.title('BackProp threshold: stimulating at the soma', fontsize=20)
plt.title('stimulating at '+str(np.unique(originalDF['stimLoc'].to_numpy())), fontsize=20)
plt.tight_layout()
if type(ylim) is list or type(ylim) is tuple:
ymin, ymax = ylim
if ymin is not None:
plt.ylim(ymin=ymin)
if ymax is not None:
plt.ylim(ymax=ymax)
if legend:
if flip_legend_order:
# Get the handles and labels
handles, labels = ax.get_legend_handles_labels()
# Reverse the order
handles, labels = handles[::-1], labels[::-1]
# Create the legend with the reversed handles and labels
ax.legend(handles, labels, title=legend_title)
else:
ax.legend(title=legend_title)
if filename:
plt.savefig(filepath+filename+'.pdf')
if show:
plt.show()
###Figure 2 data
backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune = pd.read_csv('backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune.csv')
###Figure 3 data
backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune_BACKWARDais = pd.read_csv('backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune_BACKWARDais.csv')
###Figure 4 data
backprop_ais_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune = pd.read_csv('backprop_ais_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune.csv')
###Figure 5 data
forwardProp_AIS_crossOverPosition_newtune = pd.read_csv('forwardProp_AIS_crossOverPosition_newtune.csv')
###Plot Figure 2
# plotThresh(backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune,'backProp', filename='backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune', flip_legend_order=True, show=True, legend=True)
###Plot Figure 3
plotThresh(backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune_BACKWARDais,'backProp', filename='backprop_soma_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune_BACKWARDais', flip_legend_order=False, show=True, legend=True)
###Plot Figure 4
# plotThresh(backprop_ais_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune,'backProp', filename='backprop_ais_apicalTipsBPcriterionMinimumVtipMinus63mV_newtune', flip_legend_order=False, show=True, legend=True, xlim=(0.37, None), ylim=(None, 10.0), scan_vals=[0.4, 0.5, 0.6, 0.7, 0.8])
###Plot Figure 5
# plotThresh(forwardProp_AIS_crossOverPosition_newtune,'forwardProp', filename='forwardProp_AIS_crossOverPosition_newtune', flip_legend_order=True, show=True, legend=True, ylim=(None, 0.541))