# Figure 6

```python
from sympy import symbols, exp, solve, logcombine, simplify, Piecewise, lambdify, N, init_printing, Eq
import numpy
import scipy.stats as ss
from sympy.physics.units import seconds, siemens, volts, farads, amperes, milli, micro, nano, pico, ms, s, kg, meters
from matplotlib import pyplot as plt
import matplotlib
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.patches as patches
plt.style.use('neuron_color')
import os
import sys
sys.path.append('../')
from Linearity import Neuron
import lmfit
from pickle import dump
```

```python
def simpleaxis(axes, every=False, outward=False, hideTitle=True):
    if not isinstance(axes, (list, numpy.ndarray)):
        axes = [axes]
    for ax in axes:
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        if (outward):
            ax.spines['bottom'].set_position(('outward', 10))
            ax.spines['left'].set_position(('outward', 10))
        if every:
            ax.spines['bottom'].set_visible(False)
            ax.spines['left'].set_visible(False)
        ax.get_xaxis().tick_bottom()
        ax.get_yaxis().tick_left()
        if hideTitle:
            ax.set_title('')
```

```python
from IPython.display import display, Markdown, Image
```

```python
init_printing()
```

## 6 A Circuit diagram

```python
prefix = '/home/bhalla/Documents/Codes/data'
```

## 6 B: Fitting voltage clamp data to get parameters

```python
analysisFile = prefix + '/media/sahil/NCBS_Shares_BGStim/patch_data/170530/c1_EI/plots/c1_EI.pkl'
plotDir = os.path.dirname(analysisFile)
neuron = Neuron.load(analysisFile)
```

$g(t) = \bar{g}\frac{( e^\frac{\delta_{onset} - t }{\tau_{decay}} - e^\frac{\delta_{onset} - t }{\tau_{rise}})}{- \left(\frac{\tau_{rise}}{\tau_{decay}}\right)^{\frac{\tau_{decay}}{\tau_{decay} - \tau_{rise}}} + \left(\frac{\tau_{rise}}{\tau_{decay}}\right)^{\frac{\tau_{rise}}{\tau_{decay} - \tau_{rise}}}}$

```python
def fitFunctionToPSP(time, vector, t_0=0, g_max=0):
    ''' Fits using lmfit '''

    def _doubleExponentialFunction(t, t_0, tOn, tOff, g_max):
        ''' Returns the shape of an EPSP as a double exponential function '''
        tPeak = t_0 + float(((tOff * tOn)/(tOff-tOn)) * numpy.log(tOff/tOn))
        A = 1./(numpy.exp(-(tPeak-t_0)/tOff) - numpy.exp(-(tPeak-t_0)/tOn))
        g = [ g_max * A * (numpy.exp(-(t_point-t_0)/tOff) - numpy.exp(-(t_point-t_0)/tOn)) if  t_point >= t_0 else 0. for t_point in t]
        return numpy.array(g)

    model = lmfit.Model(_doubleExponentialFunction)
    # Fixing values of variables from data
    # Onset time
    if not t_0:
        model.set_param_hint('t_0', value =max(time)/10., min=0., max = max(time))
    else:
        model.set_param_hint('t_0', value = t_0, vary=False)
    # g_max
    if not g_max:
        model.set_param_hint('g_max', value = max(vector)/10., min = 0., max = max(vector))
    else:
        model.set_param_hint('g_max', value = g_max, vary=False)

    model.set_param_hint('tOn', value =max(time)/5.1 , min = 0., max = max(time))
    model.set_param_hint('t_ratio', value =10., min=1.05)
    model.set_param_hint('tOff', min = 0., expr='tOn*t_ratio')
    model.set_param_hint('t_peak', expr = 't_0 + ((tOff * tOn)/(tOff-tOn)) * log(tOff/tOn)')
    pars = model.make_params()

    result = model.fit(vector, pars, t=time)
    # print (result.fit_report())
    return result
```

```python
n = {key:value for key,value in neuron}
for numSq in set(n[1]).intersection(set(n[2])):
    for i in set(n[1][numSq].trial).intersection(set(n[2][numSq].trial)):
        if i == 3 and numSq == 7:
            exc = -1e9*n[1][numSq].trial[i].interestWindow
            inh = 1e9*n[2][numSq].trial[i].interestWindow
            time = numpy.arange(len(n[1][numSq].trial[i].interestWindow))*n[1][numSq].trial[i].samplingTime
            exc_fit = fitFunctionToPSP(time, exc)
            inh_fit = fitFunctionToPSP(time, inh)
            print (exc_fit.fit_report())
            print (inh_fit.fit_report())
            fig,ax = plt.subplots()
            ax.plot(time*1e3, exc, alpha=0.6, c='indigo')
            ax.set_xlabel("Time (ms)")
            ax.set_ylabel("Current (pA)")
            ax.plot(time*1e3, exc_fit.best_fit, label="Excitation", c='indigo')

            ax.plot(time*1e3, -inh, alpha=0.6, c='k')
            ax.plot(time*1e3, -inh_fit.best_fit, label="Inhibition", c='k')
            ax.annotate('Excitation', (50,100), (50,100), xycoords='data',color='indigo')
            ax.annotate('Inhibition', (50,-300), (50,-300), xycoords='data',color='k')
            fig.set_figwidth(1.5)
            fig.set_figheight(1.5)
            simpleaxis(ax)
            dump(fig,file('figures/fig6/6b.pkl','wb'))
            plt.show()
```

```python
samplingRate = 20  # kHz, to get milliseconds
sample_every = 10 # points
timeStep, maxTime = (sample_every*1.)/ samplingRate, 100.  # ms
trange = numpy.arange(
    0., maxTime, timeStep)  # We will always use 100. ms timecourse of PSPs.
```

```python
#### Range of $g_e$ explored
emax = 3.
e_step = 0.5
erange = numpy.arange(0., emax, e_step)
#### Range of proportionality ($P$) between $E$ and $I$
prop_array = numpy.arange(0, 6, 1)
```

```python
## Setting up the variables, parameters and units for simulation
t, P, e_r, e_d, delta_e, rho_e, g_e, i_r, i_d, delta_i, rho_i, g_i, b, Cm, g_L = symbols(
    't P \\tau_{er} \\tau_{ed} \\delta_e \\rho_e \\bar{g}_e \\tau_{ir} \\tau_{id} \\delta_i \\rho_i \\bar{g}_i \\beta C_m \\bar{g}_L',
    positive=True,
    real=True)
leak_rev, e_rev, i_rev, Vm = symbols(
    'Leak_{rev} Exc_{rev} Inh_{rev} V_m', real=True)
SymbolDict = {
    t: "Time (ms)",
    P: "Proportion of $g_i/g_e$",
    e_r: "Excitatory Rise (ms)",
    e_d: "Excitatory Fall (ms)",
    delta_e: "Excitatory onset time (ms)",
    rho_e: "Excitatory $tau$ ratio (fall/rise)",
    g_e: "Excitatory max conductance",
    i_r: "Inhibitory Rise (ms)",
    i_d: "Inhibitory Fall(ms)",
    delta_i: "Inhibitory onset time(ms)",
    rho_i: "Inhibitory $tau$ ratio (fall/rise)",
    g_i: "Inhibitory max conductance",
    b: "Inhibitory/Excitatory $tau$ rise ratio"
}
unitsDict = {
    's': seconds,
    'exp': exp,
    'S': siemens,
    'V': volts,
    'A': amperes,
    'm': meters,
    'kg': kg
}  # This is for lamdify
nS, pF, mV, pA = nano * siemens, pico * farads, milli * volts, pico*amperes
### Estimates from data and averaging them to get a number
estimateDict = {
    P: (1,5),
    #e_r: (1.5 * ms, 5 * ms),
    #e_d: (8. * ms, 20. * ms),
    e_r: (7. * ms, 7. * ms),
    e_d: (16. * ms, 16. * ms),
    delta_e: (0. * ms, 0. * ms),
    rho_e: (2., 7.),
    g_e: (0.02 * nS, 0.25 * nS),
    #i_r: (1.5 * ms, 5. * ms),
    #i_d: (14. * ms, 60. * ms),
    i_r: (13. * ms, 13. * ms),
    i_d: (27. * ms, 27. * ms),
    delta_i: (2. * ms, 4. * ms),
    rho_i: (5., 20.),
    g_i: (0.04 * nS, 0.5 * nS),
    b: (0.5, 5.)
}
averageEstimateDict = {
    key: value[0] + value[1] / 2
    for key, value in estimateDict.items()
}

### Approximating the rest from literature
approximateDict = {
    g_L: 6.25 * nS, # Changing from 10 to 6.25
    e_rev: 0. * mV,
    i_rev: -70. * mV,
    leak_rev: -65. * mV,
    Cm: 100 * pF
}
sourceDict = {
    g_L: "None",
    e_rev: "None",
    i_rev: "None",
    leak_rev: "None",
    Cm: "Neuroelectro.org"
}
```

| Variable  |  Meaning |  Range |
|---|---|---|
|$t$|Time (ms)|0-100|
|$P$|Proportion of $g_i/g_e$|2-4|
|$\tau_{er}$|Excitatory Rise (ms)|1.5-5|
|$\tau_{ed}$|Excitatory Fall (ms)|8-20|
|$\delta_e$|Excitatory onset time (ms)|0-0|
|$\rho_e$|Excitatory $tau$ ratio (fall/rise)|2-7|
|$\bar{g}_e$|Excitatory max conductance|0.02-0.25|
|$\tau_{ir}$|Inhibitory Rise (ms)|1.5-5|
|$\tau_{id}$|Inhibitory Fall(ms)|14-60|
|$\delta_i$|Inhibitory onset time(ms)|3-15|
|$\rho_i$|Inhibitory $tau$ ratio (fall/rise)|5-20|
|$\bar{g}_i$|Inhibitory max conductance|0.04-0.5|
|$\beta$|Inhibitory/Excitatory $tau$ rise ratio|0.5-5|

| Variable  |  Meaning | Source | Value |
|---|---|---|
|$g_L$|Leak conductance| Fernandos and White, J. Neuro. (2010) | 10 nS |
|$Exc_{rev}$|Excitatory reversal|Calculated (Methods)| 0 mV|
|$Inh_{rev}$|Inhibitory reversal |Calculated (Methods)| -70 mV |
|$Leak_{rev}$|Leak reversal |Fernandos and White, J. Neuro. (2010)| -65 mV |
|$C_m$|Membrane capacitance |neuroelectro.org| 100 pF|

---

```python
### Double exponential to explain the net synaptic conductance.
alpha = exp(-(t - delta_e) / e_d) - exp(-(t - delta_e) / e_r)
alpha_prime = alpha.diff(t)
theta_e = solve(alpha_prime, t)  # Time to peak
theta_e = logcombine(theta_e[0])
simplify(theta_e.subs(averageEstimateDict))
alpha_star = simplify(alpha.subs(t, theta_e).doit())

### Finding maximum of the curve and substituting ratio of taus
g_E = Piecewise((0. * nS, t / ms < delta_e / ms), (g_e * (alpha / alpha_star),
                                                   True))
### Final equation for Excitation normalized to be maximum at $g_e$

### Doing the same with inhibition
g_I = g_E.xreplace({
    g_e: g_i,
    rho_e: rho_i,
    e_r: i_r,
    e_d: i_d,
    delta_e: delta_i
})
alpha_I = alpha.xreplace({e_r: i_r, e_d: i_d, delta_e: delta_i})
alpha_star_I = alpha_star.xreplace({e_r: i_r, e_d: i_d})
g_I = Piecewise((0. * nS, t / ms < delta_i / ms),
                (g_i * (alpha_I / alpha_star_I), True))
```

```python
alpha_prime/alpha_star
```

```python
### Now finding the control peak using difference of these double-exponentials
compartment = Eq((1 / Cm) * (g_E * (Vm - e_rev) + g_I * (Vm - i_rev) + g_L *
                             (Vm - leak_rev)), Vm.diff(t))
Vm_t = solve(compartment, Vm, rational=False, simplify=True)
check_vm_t = Vm_t[0].subs({ i: averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e,g_i, P] }).subs(approximateDict).subs({ g_i: P * g_e })
```

```python
### Now finding the control peak using difference of these double-exponentials (checking with this form of the equation)
compartment = Eq((1 / Cm) * (g_E * (e_rev - Vm) + g_I * (i_rev - Vm) + g_L *
                             (leak_rev - Vm)), Vm.diff(t))
Vm_t = solve(compartment, Vm, rational=False, simplify=True)
check_vm_t = Vm_t[0].subs({ i: averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e,g_i, P] }).subs(approximateDict).subs({ g_i: P * g_e })
```

```python
f = lambdify((g_e, P, t), check_vm_t/mV, (unitsDict, "numpy"))
```

## 6 C Divisive Inhibition: Inhibition proportional to Excitation, or $g_i = P \times g_e$

```python
di_exc =  [[float(f(e * nS, 0., dt * ms)) for dt in trange] for e in erange]
di_control = {prop: [[float(f(e * nS, prop, dt * ms)) for dt in trange] for e in erange] for prop in prop_array}
```

```python
fig, ax = plt.subplots()
# plt.style.context('neuron-color')
handles, labels = [], []
for prop in prop_array:
    v_max, e_max = [], []
    for con_trace,e_t in zip(di_control[prop], di_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max, v_max, s=10))
    ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Control  $V_{max}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, di_control[prop][5])
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6c.pkl','wb'))
# ax.set_title("Divisive Inhibition")
plt.show()
```

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    ttp, e_max = [], []
    for con_trace,e_t in zip(di_control[prop], di_exc):
        ttp.append(numpy.argmax(con_trace))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max[1:], ttp[1:], s=10))
    ax.plot(e_max[1:], ttp[1:], '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Time to peak  $t_{peak}$")
ax.set_xlim(0,15)
ax.set_ylim(0,55)
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
# dump(fig,file('figures/fig6/6g.pkl','wb'))
plt.show()
```

```python
handles, labels = [], []
for prop in prop_array:
    fig, ax = plt.subplots()
    ttp, e_max = [], []
    for con_trace,e_t in zip(di_control[prop], di_exc):
        ax.plot(trange, con_trace,c='k')
    fig.set_figwidth(12)
    plt.show()

```

```python
threshold = 5.5
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    v_max, e_max, spk_t = [], [], []
    for con_trace,e_t in zip(di_control[prop], di_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
        spiking = numpy.where((numpy.array(con_trace) - float(approximateDict[leak_rev]/mV)) > threshold)[0]
        if len(spiking):
            spk_t.append(spiking[0])
        else:
            spk_t.append(numpy.nan)
#         print(numpy.where(e_t>threshold))
    handles.append(ax.plot(erange, spk_t, '.-'))
#     ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Spike Time  $t_{sp}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, dn_control[prop][5])
# ax.hlines(y=threshold, linestyle='--')
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='right')
fig.set_figwidth(2)
fig.set_figheight(2)
simpleaxis(ax)
#dump(fig,file('figures/fig6/6e.pkl','wb'))
# ax.set_title("Divisive Normalization", fontsize=18)
plt.show()
```

```python
print ( "Constant $delta_i$ was {:.1f} ms".format(averageEstimateDict[delta_i]/ms))
```

    Constant $delta_i$ was 4.0 ms

6 DEF: Divisive Normalization: Inhibition proportional to Excitation, or $g_i = P \times g_e$ and $\delta_i$ inversely proportional to $g_e$

## 6 D Changing $\delta_i$ = $\delta_{min} + me^{-k\times{g_e}}$

```python
time_erange = numpy.linspace(0.,4.,10)
```

```python
d = lambda minDelay,k,e: minDelay + m*exp(-(k*e))
nS = nano*siemens
k, m, minDelay = 1.43/nS, 18.15*ms, 2.54*ms
maxDelay = (minDelay + m)/ms
```

```python
fig, ax = plt.subplots()
ax.scatter(time_erange, [d(minDelay,k,e*nS)/ms for e in time_erange], s=40, facecolor='k', edgecolor='k')
ax.set_xlabel("$g_{exc}$ (nS)")
ax.set_ylabel("$\\delta_i$ (ms)")
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
ax.set_xlim(0,4.5)
ax.set_ylim(0, 13 )
ax.set_xticks(range(4))
ax.set_yticks(range(0,13,2))
simpleaxis(ax)
dump(fig,file('figures/fig6/6d.pkl','wb'))
plt.show()
```

```python
check_vm = simplify(Vm_t[0].subs({i:averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e, g_i, delta_i]}).subs(approximateDict).subs({g_i: P*g_e, delta_i: d(minDelay,k,g_e)}).evalf())
f = lambdify((g_e, P, t), check_vm/mV, (unitsDict, "numpy"))
```

```python
dn_exc =  [[float(f(e * nS, 0., dt * ms)) for dt in trange] for e in erange]
dn_control = {prop: [[float(f(e * nS, prop, dt * ms)) for dt in trange] for e in erange] for prop in prop_array}
```

## 6 E Divisive Normalization

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    v_max, e_max = [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max, v_max, s=10))
    ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Control  $V_{max}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, dn_control[prop][5])
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6e.pkl','wb'))
# ax.set_title("Divisive Normalization", fontsize=18)
plt.show()
```

### Synapses to threshold

```python
threshold = 5.5
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    v_max, e_max, spk_t = [], [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
        spiking = numpy.where((numpy.array(con_trace) - float(approximateDict[leak_rev]/mV)) > threshold)[0]
        if len(spiking):
            spk_t.append(spiking[0])
        else:
            spk_t.append(numpy.nan)
#         print(numpy.where(e_t>threshold))
    handles.append(ax.plot(erange, spk_t, '.-'))
#     ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Spike Time  $t_{sp}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, dn_control[prop][5])
# ax.hlines(y=threshold, linestyle='--')
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='right')
fig.set_figwidth(2)
fig.set_figheight(2)
simpleaxis(ax)
#dump(fig,file('figures/fig6/6e.pkl','wb'))
# ax.set_title("Divisive Normalization", fontsize=18)
plt.show()
```

```python
fig, ax = plt.subplots()
threshold = 5.5
handles, labels = [], []
for prop in prop_array[:1]:
    v_max, e_max, spk_t = [], [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
        spiking = numpy.where((numpy.array(con_trace) - float(approximateDict[leak_rev]/mV)) >= threshold)[0]
        time = numpy.linspace(0., 100., len(con_trace))
        if len(spiking):
            spk_t.append(spiking[0])
#             print(spiking[0])
            ax.plot(time[:len(time)/4], numpy.array(con_trace[:len(time)/4]) - float(approximateDict[leak_rev]/mV))
            #ax.plot(time[spiking[0]], con_trace[spiking[0]] - float(approximateDict[leak_rev]/mV), 'o',markersize=4, color='k')
        else:
            spk_t.append(numpy.nan)
#         print(numpy.where(e_t>threshold))

    #handles.append(ax.plot(erange, spk_t, '.-'))
#     ax.plot(e_max, v_max, '--')
    #labels.append("$P= {}$".format(prop))
ax.hlines(y=5, xmin=0, xmax=ax.get_xlim()[1], linestyles='--')
ax.set_ylim(0,10.)
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Spike Time  $t_{sp}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, dn_control[prop][5])
# ax.hlines(y=threshold, linestyle='--')
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='right')
fig.set_figwidth(2)
fig.set_figheight(2)
simpleaxis(ax)
dump(fig,file('figures/fig6/6e.pkl','wb'))
ax.set_title("Divisive Normalization", fontsize=18)
plt.show()
```

```python
fig, ax = plt.subplots()
for prop in prop_array:
    v_max, e_max = [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    e_max = numpy.array(e_max)
    v_max = numpy.array(v_max)
    handles.append(ax.scatter(erange, e_max/v_max, s=10))
    ax.plot(erange, e_max/v_max, '--')
ax.set_xlabel("Excitation $g_{exc}$")
ax.set_ylabel("Gain")
fig.set_figwidth(2)
fig.set_figheight(2)
simpleaxis(ax)
plt.show()
```

## 5 B Model subtraction scheme

```python
fig, ax = plt.subplots()
handles, labels = [], []
prop = 4

i_max, e_max = [], []
trace_c, trace_e = numpy.array(dn_control[prop][-1]), numpy.array(dn_exc[-1])
ax.plot(trange, trace_c, label="PSP")
ax.plot(trange, trace_e, label="EPSP")
trace_i =  float(approximateDict[leak_rev]/mV) + (trace_c - trace_e)
ax.plot(trange, trace_i, label="Derived IPSP")
ax.set_xlabel("Time")
ax.set_ylabel("$V_m$")
fig.set_figwidth(3)
fig.set_figheight(3)
simpleaxis(ax)
dump(fig,file('figures/fig5/5b.pkl','wb'))
plt.legend()
plt.show()
```

## 6 F Excitation - Derived Inhibition plot

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    i_max, e_max = [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        i_t = numpy.array(e_t) - numpy.array(con_trace)
        i_max.append(numpy.max(i_t))
#         i_max.append(max(e_t) - max(con_trace))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max, i_max, s=10))
    ax.plot(e_max, i_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Derived Inhibition  $V_{max}$")
xlim = ax.get_xlim()
ax.set_ylim (xlim)
ax.plot(xlim, xlim, '--')
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6f.pkl','wb'))
plt.show()
```

## 6 G Time to peak

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    ttp, e_max = [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        ttp.append(numpy.argmax(con_trace))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max[1:], ttp[1:], s=10))
    ax.plot(e_max[1:], ttp[1:], '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Time to peak  $t_{peak}$")
ax.set_xlim(0,15)
ax.set_ylim(0,55)
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6g.pkl','wb'))
plt.show()
```

```python
handles, labels = [], []
for prop in prop_array:
    fig, ax = plt.subplots()
    ttp, e_max = [], []
    for con_trace,e_t in zip(dn_control[prop], dn_exc):
        ax.plot(trange, con_trace,c='k')
    fig.set_figwidth(12)
    plt.show()
```

## 6 H Permutation of P

```python
check_vm = simplify(Vm_t[0].subs({i:averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e, g_i, delta_i]}).subs(approximateDict).subs({delta_i: d(minDelay,k,g_e)}).evalf())
f = lambdify((g_e, g_i, t), check_vm/mV, (unitsDict, "numpy"))
```

```python
p_perm_dn_exc =  [[float(f(e * nS, 0., dt * ms)) for dt in trange] for e in erange]
p_perm_dn_control = {prop: [[float(f(e * nS, i * nS, dt * ms)) for dt in trange] for (e,i) in zip(erange, numpy.random.permutation(erange*prop))] for prop in prop_array}
```

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    v_max, e_max = [], []
    for con_trace,e_t in zip(p_perm_dn_control[prop], p_perm_dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max, v_max, s=10))
    ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Control  $V_{max}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, p_perm_dn_control[prop][5])
#ax2.set_xlabel("Time")
#ax2.set_ylabel("Membrane potential (mV)")
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6h.pkl','wb'))
# ax.set_title("Divisive Normalization with E and I balance permuted", fontsize=18)
plt.show()
```

## 6 I Permutation of $\delta_i$

```python
check_vm = simplify(Vm_t[0].subs({i:averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e, g_i, delta_i]}).subs(approximateDict).subs({g_i: P*g_e}).evalf())
f = lambdify((g_e, P, delta_i, t), check_vm/mV, (unitsDict, "numpy"))
```

```python
d_perm_dn_exc =  [[float(f(e * nS, 0., d(minDelay,k, e* nS), dt * ms)) for dt in trange] for e in erange]
d_perm_dn_control = {prop: [[float(f(e * nS, prop, delay, dt * ms)) for dt in trange] for e,delay in zip(erange, numpy.random.permutation([d(minDelay,k, e* nS) for e in erange])) ] for prop in prop_array}
```

```python
fig, ax = plt.subplots()
handles, labels = [], []
for prop in prop_array:
    v_max, e_max = [], []
    for con_trace,e_t in zip(d_perm_dn_control[prop], d_perm_dn_exc):
        v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
        e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
    handles.append(ax.scatter(e_max, v_max, s=10))
    ax.plot(e_max, v_max, '--')
    labels.append("$P= {}$".format(prop))
ax.set_xlabel("Excitation $V_{max}$")
ax.set_ylabel("Control  $V_{max}$")
# left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
# ax2 = fig.add_axes([left, bottom, width, height])
# for prop in prop_array:
#     ax2.plot(trange, d_perm_dn_control[prop][5])
# fig.legend(handles, labels, loc ='center right')
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
dump(fig,file('figures/fig6/6i.pkl','wb'))
# ax.set_title("Divisive Normalization", fontsize=18)
plt.show()
```

## 6 J Phase plot Divisive Normalization

```python
import lmfit
def DN_model(x,a=1):
    # Divisive normalization model
    return (a*x)/(x+a)
DN_Model = lmfit.Model(DN_model)
```

```python
check_vm = simplify(Vm_t[0].subs({i:averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e, g_i, delta_i]}).subs(approximateDict).subs({g_i: P*g_e}).evalf())
f = lambdify((g_e, P, delta_i, t), check_vm/mV, (unitsDict, "numpy"))
```

```python
inhib = simplify(Vm_t[0].subs({i:averageEstimateDict[i] for i in averageEstimateDict if i not in [g_e, g_i, delta_i]}).subs(approximateDict).evalf())
g = lambdify((g_e, g_i, delta_i, t), inhib/mV, (unitsDict, "numpy"))
```

```python
phase_dn_control = {}
phase_dn_exc = {}
phase_dn_inh = {}

# prop_array = numpy.logspace(-1,1,7)
# k_array = numpy.logspace(-1,1,7)

prop_array = numpy.linspace(0,6,7)
k_array = numpy.linspace(0.,3.,7)
```

```python
for k in k_array:
    phase_dn_exc[k] = [[float(f(e * nS, 0., d(minDelay,k/nS, e* nS), dt * ms)) for dt in trange] for e in erange]
    phase_dn_control[k] = {prop: [[float(f(e * nS, prop, delay, dt * ms)) for dt in trange] for e,delay in zip(erange, [d(minDelay,k/nS, e* nS) for e in erange]) ] for prop in prop_array}
#     phase_dn_inh[k] = {prop: [[float(g(0 * nS, prop*e, delay, dt * ms)) for dt in trange] for e,delay in zip(erange, [d(minDelay,k/nS, e* nS) for e in erange]) ] for prop in prop_array}
```

```python
phase_dn_inh = {}
for k in k_array:
    phase_dn_inh[k] = {prop: [[float(g(0 * nS, prop*e* nS, delay, dt * ms)) for dt in trange] for e,delay in zip(erange, [d(minDelay,k/nS, e* nS) for e in erange]) ] for prop in prop_array}
```

```python
phaseMat_init = numpy.zeros((len(k_array),len(prop_array)))

for ind1, k in enumerate(k_array):
    for ind2, prop in enumerate(prop_array):
        v_max, e_max = [], []
        for con_trace,e_t in zip(phase_dn_control[k][prop], phase_dn_exc[k]):
            v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
            e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
        X, y = e_max, v_max
        DN_pars = DN_Model.make_params()
        DN_result = DN_Model.fit(y, DN_pars, x=X)

#         plt.plot(X, y, 'bo')
#         plt.plot(X, DN_result.best_fit, 'r-')
#         plt.xlim(0,1.2*max(e_max))
#         plt.ylim(0,1.2*max(e_max))
#         plt.show()
        phaseMat_init[ind1][ind2] = DN_result.params['a']
#         print(DN_result.fit_report())

# x,y = numpy.meshgrid(prop_array, k_array)

#cmap = LinearSegmentedColormap.from_list('gamma_purple', [(0.,'purple' ), (1., 'white')])
cmap = matplotlib.cm.inferno_r
cmap.set_bad(color='white')

print ("Max gamma is {}".format(numpy.max(phaseMat_init)))
gamma_cutOff = 40
cutOffmask = numpy.ma.masked_where(phaseMat_init > gamma_cutOff, phaseMat_init)
phaseMat = numpy.ma.masked_where(numpy.isnan(phaseMat_init), cutOffmask)

vmax = numpy.nanmax(phaseMat)
vmin = numpy.nanmin(phaseMat)

fig, ax = plt.subplots()

phaseMat
#heatmap = ax.pcolormesh(phaseMat, norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax), cmap = cmap, edgecolor='k', linewidths=.05)
heatmap = ax.pcolormesh(phaseMat, vmin=0, vmax=gamma_cutOff, cmap = cmap, edgecolor='k', linewidths=.05)
# ax.grid(True, which='minor', axis='both', linestyle='--', alpha=0.1, color='k')

ax.invert_yaxis()
ticks = numpy.arange(0,len(prop_array),2)
ax.xaxis.set_ticks(ticks+0.5)
ax.yaxis.set_ticks(ticks+0.5)

ax.yaxis.set(ticklabels=["{:.0f}".format(j) for j in k_array[ticks]])
ax.xaxis.set(ticklabels=["{:.0f}".format(j) for j in prop_array[ticks]])

# ax.axis([int(k_array.min()),int(k_array.max()),int(prop_array.min()),int(prop_array.max())])
# for axis in [ax.xaxis, ax.yaxis]:
#     axis.set_ticks([0,10,10], minor=True)
#     axis.set(ticks=[0,10,10], ticklabels=numpy.linspace(0,10,10)) #Skipping square labels

# ax.set_xlim((-1,1))
# ax.set_ylim((-1,1))

#Colorbar stuff
cbar = plt.colorbar(heatmap, label = "$\\gamma$", ticks=[0,20,40])
cbar.ax.get_yaxis().labelpad = 6
# tick_locator = matplotlib.ticker.MaxNLocator(nbins=5)
# cbar.locator = tick_locator
# cbar.update_ticks()

# ax.patch.set(hatch='xx', edgecolor='purple')

simpleaxis(ax,every=True,outward=False)
ax.set_aspect(1)
fig.set_figwidth(2.)
fig.set_figheight(2.)

ax.set_ylabel("K")
ax.set_xlabel("I/E")
# ax.set_title("Divisive Normalization", fontsize=18)

dump(fig,file('figures/supplementary/11a.pkl','wb'))
plt.show()
```

```python
print (k_array)
```

### Delay plots

```python
d = lambda minDelay,k,e: minDelay + m*exp(-(k*e))
nS = nano*siemens
m, minDelay = 18.15*ms, 2.54*ms
maxDelay = (minDelay + m)/ms
```

```python
k_sample_indices = [1,3,5]
fig, ax = plt.subplots(len(k_array[k_sample_indices]),1,sharey=True)
for axis,k in zip(ax,k_array[k_sample_indices]):
    axis.plot(time_erange, [d(minDelay,k/nS,e*nS)/ms for e in time_erange], '.-', c='k', markersize=5)
    axis.set_xlim(0,4.5)
    axis.set_ylim(0, 13 )
    axis.set_xticks(range(4))
    axis.set_yticks(range(0,13,6))
    axis.set_title("k={}".format(k))
ax[0].set_ylabel("$\\delta_i$ (ms)")
ax[-1].set_xlabel("$g_{exc}$ (nS)")
simpleaxis(ax,hideTitle=False)
fig.set_figwidth(1)
fig.set_figheight(3)
dump(fig,file('figures/supplementary/11b.pkl','wb'))
plt.show()
```

### I/E differences

```python
ie_sample_indices = [1,3,6]
fig, ax = plt.subplots(1,3,sharey=True)
for axis,i_by_e in zip(ax, prop_array[ie_sample_indices]):
    axis.plot(erange, i_by_e * erange, '.-', c='k', markersize=5)
    axis.set_xlabel("$g_{exc}$ (nS)")
    axis.set_xlim(0,4.5)
    axis.set_xticks(range(4))
#     axis.set_yticks(range(0,13,2))
#     axis.set_title("I/E={}".format(i_by_e))
ax[0].set_ylabel("$g_{inh}$ (nS)")
simpleaxis(ax,hideTitle=False)
fig.set_figwidth(3)
fig.set_figheight(1)
dump(fig,file('figures/supplementary/11c.pkl','wb'))
plt.show()
```

### DN traces for these values

```python
fig, ax = plt.subplots(len(k_sample_indices), len(ie_sample_indices), sharex=True, sharey=True)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=0, vmax=gamma_cutOff))
for ind1,k_index in enumerate(k_sample_indices):
    for ind2,prop_index in enumerate(ie_sample_indices):
        k, prop = k_array[k_index], prop_array[prop_index]
        for trace in phase_dn_control[k][prop]:
            if phaseMat[k_index][prop_index]:
                ax[ind1][ind2].plot(trange, trace, c=sm.to_rgba(float(phaseMat[k_index][prop_index])), linewidth=1)
            else:
                ax[ind1][ind2].plot(trange, trace, c='k', linewidth=1)
#         ax[ind1][ind2].set_title("K={},I/E={}".format(k,prop))
simpleaxis(fig.get_axes(),hideTitle=False)
fig.set_figwidth(3)
fig.set_figheight(3)
dump(fig,file('figures/supplementary/11d.pkl','wb'))
plt.show()
```

### SDN curve for these values

```python
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=0, vmax=gamma_cutOff))
fig, ax = plt.subplots(len(k_sample), len(ie_sample), sharex=True, sharey=True)
for ind1,k_index in enumerate(k_sample_indices):
    for ind2,prop_index in enumerate(ie_sample_indices):
        k, prop = k_array[k_index], prop_array[prop_index]

        obs_sdn = numpy.array([numpy.max(trace) for trace in phase_dn_control[k][prop]]) - float(approximateDict[leak_rev]/mV)
        exp_sdn = numpy.array([numpy.max(trace) for trace in phase_dn_exc[k]])  - float(approximateDict[leak_rev]/mV)

        if phaseMat[k_index][prop_index]:
            ax[ind1][ind2].plot(exp_sdn, obs_sdn, '.-', c=sm.to_rgba(float(phaseMat[k_index][prop_index])), markersize=5, linewidth=1)
            ax[ind1][ind2].set_title("$\gamma$ = " + "{:.2f}".format(phaseMat_init[k_index][prop_index]))
#             ax[ind1][ind2].set_title("K={}, I/E={}, ".format(k,prop) + "$\gamma$ = " + "{:.2e}".format(phaseMat_init[k_index][prop_index]))
        else:
            ax[ind1][ind2].plot(exp_sdn, obs_sdn, '.-', c='k', markersize=5, linewidth=1)
            #ax[ind1][ind2].set_title("$\gamma$ > 40")
#             ax[ind1][ind2].set_title("K={}, I/E={}, ".format(k,prop) + "$\gamma$ = " + "{:.2e}".format(phaseMat_init[k_index][prop_index]))

#     if phaseMat[k_index][prop_index]:
#             print (k_index, prop_index)
#             ax[ind1][ind2].set_title("$\gamma$ = " + "{:.2f}".format(phaseMat_init[k_index][prop_index]))
#         else:
#             print ("Didn't work, {},{}".format(k_index, prop_index))
#             ax[ind1][ind2].set_title("$\gamma$ > 40")
simpleaxis(fig.get_axes(),hideTitle=False)
fig.set_figwidth(3)
fig.set_figheight(3)
dump(fig,file('figures/supplementary/11e.pkl','wb'))
plt.show()
```

```python
exp_sdn, obs_sdn
```

```python
k = k_array[4]
p = prop_array[4]

numColors = 10
cm = matplotlib.cm.viridis_r
cgen = (cm(1.*i/numColors) for i in range(numColors))
maxTime = 200

fig, ax = plt.subplots()
for con_trace,exc_trace,inh_trace in zip(phase_dn_control[k][prop][1:], phase_dn_exc[k][1:], phase_dn_inh[k][prop][1:]):
    c = cgen.next()
    ax.plot(con_trace[:maxTime], '-', linewidth=2, c=c)
    ax.plot(exc_trace[:maxTime], '-', linewidth=2, c=c)
    ax.plot( [-65 - (a - b) for a,b in zip(exc_trace[:maxTime],con_trace[:maxTime])], '-', linewidth=2, c=c)
#     ax.plot(inh_trace[:maxTime], '-', linewidth=2, c=c)
    ax.hlines(y=max(con_trace[:maxTime]), xmin=0, xmax=maxTime, linestyles='--')
#     ax.hlines(y=max(con_trace[:maxTime]))
simpleaxis(ax,every=True)
fig.set_figheight(15)
fig.set_figwidth(15)
plt.show()
```

```python
fig, ax = plt.subplots()
for inh_trace in phase_dn_inh[k][p]:
    ax.plot(inh_trace)
plt.show()
```

```python
len(phase_dn_inh[8])
```

```python
# for ind1, k in enumerate(k_array):
#     for ind2, prop in enumerate(prop_array):
#         v_max, e_max = [], []
#         for con_trace,e_t in zip(phase_dn_control[k][prop], phase_dn_exc[k]):
#             v_max.append(max(con_trace) - float(approximateDict[leak_rev]/mV))
#             e_max.append(max(e_t) - float(approximateDict[leak_rev]/mV))
#         X, y = e_max, v_max
#         DN_pars = DN_Model.make_params()
#         DN_result = DN_Model.fit(y, DN_pars, x=X)
#         print (k, prop)
#         print(DN_result.fit_report())
#         f,ax = plt.subplots()
#         DN_result.plot_fit(ax)
#         plt.show()
```

## 6 K $\delta_i$ as a function of $g_e$

```python
prefix = '/home/bhalla/Documents/Codes/data'
n = Neuron.load(prefix + '/media/sahil/NCBS_Shares_BGStim/patch_data/170720/c5_EI/plots/c5_EI.pkl')
```

```python
def delay_excitation(x, a=1., b=1., c=1.):
    # Delay as a function of excitation
#     return a + b*numpy.exp(-c*x)
    return a+(x/b)
```

```python
def findOnsetTime(trial, step=0.5, slide = 0.05, minOnset = 2., maxOnset = 50., initpValTolerance=1.0, pValMinTolerance = 0.1):
    maxIndex = int(trial.F_sample*maxOnset*1e-3)
    if expType == 1:
        maxOnsetIndex = numpy.argmax(-trial.interestWindow[:maxIndex])
    elif expType == 2:
        maxOnsetIndex = numpy.argmax(trial.interestWindow[:maxIndex])
    else:
        maxOnsetIndex = numpy.argmax(trial.interestWindow[:maxIndex])

    window_size = len(trial.interestWindow)
    step_size = int(trial.F_sample*step*1e-3)

    overlap =  int(trial.F_sample*slide*1e-3)

    index_right = maxOnsetIndex
    index_left = index_right - step_size
    minOnsetIndex = int(trial.F_sample*minOnset*1e-3)

    baseMean = numpy.mean(trial.interestWindow[:minOnsetIndex])
    factor = 5
    thresholdGradient = 0.01
    pValTolerance = initpValTolerance

    l_window = trial.interestWindow[:minOnsetIndex]
    while (index_left>minOnset):
        r_window = trial.interestWindow[index_left:index_right] #, trial.baselineWindow #trial.interestWindow[index_left - step_size:index_left]
        stat, pVal = ss.ks_2samp(r_window, l_window)
        if pVal>pValTolerance:
            return float(index_right)/trial.F_sample

        else:
            index_left-=overlap
            index_right-=overlap
            if index_left<=minOnsetIndex:
                pValTolerance/=2
                if pValTolerance<pValMinTolerance:
#                             print ("Returning Nan")
                            return numpy.nan
                else:
                    index_right = maxOnsetIndex
                    index_left = maxOnsetIndex - step_size
```

```python
# avg_exc_onset = {}
# avg_inh_onset = {}
# avg_exc_max = {}
# exc_onsets, inh_onsets = {}, {}
# exc_max,inh_max = {}, {}
# err_inh_onsets = {}
# scalingFactor = 1e6
# for expType, experiment in n:
#     for sqr in experiment:
#         for coord in experiment[sqr].coordwise:
#             if expType == 1:
#                 for trial in experiment[sqr].coordwise[coord].trials:
#                     exc_onsets[(sqr,trial.index)] = findOnsetTime(trial)*1e3
#                     exc_max[(sqr,trial.index)] = -trial.feature[5]*scalingFactor
#                     #exp[sqr].coordwise[coord].average_feature[5]
#             if expType == 2:
#                 list_inh_onset = []
#                 for trial in experiment[sqr].coordwise[coord].trials:
#                     inh_onsets[(sqr,trial.index)] = findOnsetTime(trial)*1e3
#                     list_inh_onset.append(inh_onsets[(sqr,trial.index)])
#                     inh_max[(sqr,trial.index)] = trial.feature[0]*scalingFactor
#                 avg_onset = numpy.nanmean([onset for onset in list_inh_onset if onset])
#                 err_onset = numpy.nanstd([onset for onset in list_inh_onset if onset])
#                 for trial in experiment[sqr].coordwise[coord].trials:
#                     avg_inh_onset[(sqr,trial.index)] = avg_onset
#                     err_inh_onsets[(sqr,trial.index)] = err_onset
#print (avg_exc_max, avg_exc_onset, avg_inh_onset)
```

```python
avg_exc_onset = {}
avg_inh_onset = {}
avg_exc_max = {}
exc_onsets, inh_onsets = {}, {}
exc_max,inh_max = {}, {}
err_exc_onset, err_inh_onset = {}, {}
scalingFactor = 1e6

for expType, experiment in n:
    for sqr in experiment:
        for coord in experiment[sqr].coordwise:
            if expType == 1:
                list_exc_onset = []
                list_exc_max = []
                for trial in experiment[sqr].coordwise[coord].trials:
                    onsetTime = findOnsetTime(trial)
                    if onsetTime:
                        exc_onsets[(sqr,trial.index)] = onsetTime*1e3
                        list_exc_onset.append(exc_onsets[(sqr,trial.index)])
                        list_exc_max.append(-trial.feature[5]*scalingFactor)
                    #exp[sqr].coordwise[coord].average_feature[5]
                avg_exc_onset[coord] = numpy.nanmean([onset for onset in list_exc_onset if onset])
                err_exc_onset[coord] = numpy.nanstd([onset for onset in list_exc_onset if onset])
                exc_max[coord] = numpy.nanmean([maxC for maxC in list_exc_max if maxC])
#                 for trial in experiment[sqr].coordwise[coord].trials:
#                     avg_exc_onset[(sqr,trial.index)] = avg_onset
#                     err_exc_onsets[(sqr,trial.index)] = err_onset
            if expType == 2:
                list_inh_onset = []
                for trial in experiment[sqr].coordwise[coord].trials:
                    onsetTime = findOnsetTime(trial)
                    if onsetTime:
                        inh_onsets[(sqr,trial.index)] = onsetTime*1e3
                        list_inh_onset.append(inh_onsets[(sqr,trial.index)])
                        inh_max[(sqr,trial.index)] = trial.feature[0]*scalingFactor
                avg_inh_onset[coord] = numpy.nanmean([onset for onset in list_inh_onset if onset])
                err_inh_onset[coord] = numpy.nanstd([onset for onset in list_inh_onset if onset])
#                 for trial in experiment[sqr].coordwise[coord].trials:
#                     avg_inh_onset[(sqr,trial.index)] = avg_onset
#                     err_inh_onsets[(sqr,trial.index)] = err_onset
```

```python
delay, max_current = [], []
del_err, max_err= [], []
inhibOnset = []
conductanceConversion = 70e-3
for key in set(avg_exc_onset).intersection(set(avg_inh_onset)):
    if avg_inh_onset[key] and avg_exc_onset[key]:
        if not numpy.isnan(avg_inh_onset[key]) and not numpy.isnan (avg_exc_onset[key]) and not numpy.isnan (exc_max[key]):
            delay.append(avg_inh_onset[key]- avg_exc_onset[key])
            max_current.append(exc_max[key])
#             del_err.append(err_inh_onset[key])
            inhibOnset.append(avg_inh_onset[key])
maxConductance = numpy.array(max_current)/conductanceConversion
#         del_err.append()
#         max_err.append()
```

```python
delay_Model = lmfit.Model(delay_excitation)
delay_pars = delay_Model.make_params()

delay = numpy.array(delay)
maxConductance = numpy.array(maxConductance)
# print (delay_result.params)
# print (delay_result.aic)
# print (delay_result.redchi)
```

```python
delay_result = delay_Model.fit(delay, delay_pars, x=maxConductance)
```

```python
fig, ax = plt.subplots()
ax.scatter(maxConductance, delay)
ax.set_ylim(0,)
plt.show()
```

```python
delay_result = delay_Model.fit(delay, delay_pars, x=maxConductance)
fig, ax = plt.subplots()
indices = numpy.argsort(maxConductance)
ax.scatter(maxConductance[indices], delay[indices], s=30, facecolor='k', edgecolor='k')
ax.plot(maxConductance[indices], delay_result.best_fit[indices], '-')
# print(conductance_std, delay_std)
# ax.errorbar(conductance_mean, delay_mean, xerr = conductance_std, yerr= delay_std, linestyle='',c='k')
ax.set_xticks(range(4))
ax.set_yticks(range(0,12,2))
ax.set_xlim(0,4.5)
ax.set_ylim(-3,12.5)
ax.set_xlabel("$g_e$ (nS)")
ax.set_ylabel("$\\delta_i$ (ms)")
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
# dump(fig,file('figures/fig6/6k.pkl','wb'))
plt.show()
# print ("{:.2f} + {:.2f}e^-{:.2f}E".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
print ("{:.2f} + E^-{:.2f}".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
print(delay_result.fit_report())
```

### Binning delays here

```python
bins = numpy.linspace(0,max(maxConductance),6)
digitized = numpy.digitize(maxConductance, bins)
conductance_mean = [maxConductance[digitized == i].mean() for i in range(len(bins))]
delay_mean = [delay[digitized == i].mean() for i in range(len(bins))]
conductance_std = [maxConductance[digitized == i].std(ddof=1) for i in range(len(bins))]
delay_std = [delay[digitized == i].std(ddof=1) for i in range(len(bins))]

delay_mean, conductance_mean, delay_std, conductance_std = map(list, zip(*[ (d,c,sd,sc) for d,c,sd,sc in zip(delay_mean, conductance_mean, delay_std, conductance_std) if not any(numpy.isnan([d,c,sd,sc]))]))
```

```python
print ("{:.2f} + {:.2f}e^-{:.2f}E".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
```

```python
delay_result = delay_Model.fit(delay_mean, delay_pars, x=conductance_mean)
fig, ax = plt.subplots()
ax.scatter(conductance_mean, delay_mean, s=30, facecolor='k', edgecolor='k')
# ax.plot(conductance_mean, delay_result.best_fit, '-')
print(conductance_std, delay_std)
ax.errorbar(conductance_mean, delay_mean, xerr = conductance_std, yerr= delay_std, linestyle='',c='k')
ax.set_xticks(range(4))
ax.set_yticks(range(0,12,2))
ax.set_xlim(0,4.5)
ax.set_ylim(0,12.5)
ax.set_xlabel("$g_e$ (nS)")
ax.set_ylabel("$\\delta_i$ (ms)")
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
simpleaxis(ax)
# dump(fig,file('figures/fig6/6k.pkl','wb'))
plt.show()
print ("{:.2f} + {:.2f}e^-{:.2f}E".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
```

```python
delay_result = delay_Model.fit(delay, delay_pars, x=maxConductance)
fig, ax = plt.subplots()
ax.errorbar(numpy.array(maxConductance), numpy.array(delay), fmt ='o', markersize=2, alpha=0.4)
#ax.scatter(numpy.array(maxConductance)*1e6, numpy.array(delay)*1e3)
current_linspace= numpy.linspace(0,1.1*numpy.max(maxConductance))
ax.plot(current_linspace, delay_result.eval(x=current_linspace), '-', label="${:.2f} + {:.2f} \\times e^{{-{:.2f} \\times E }}$".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))

ax.plot(1./(delay_result.params['c'].value), delay_result.eval(x=1./(delay_result.params['c'].value)), 'ko', markersize=2)
xmin, xmax = ax.get_xlim()
ax.hlines(y=0, xmin=xmin, xmax=xmax, linestyles='--', alpha=0.5)
ax.hlines(y=delay_result.params['a'].value, xmin=xmin, xmax=xmax, linestyles='--', alpha=0.5)
ax.set_xlabel("$g_{max}^{exc}$")
ax.set_ylabel("Delay $(\\delta_{inh})$")

ax.annotate("",
            xy=(xmax, 0.), xycoords='data',
            xytext=(xmax, delay_result.params['a'].value), textcoords='data',
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="arc3"),
            )
ax.text(1.01*xmax, 1., "$\\delta_{min}$")

ax.annotate("",
            xy=(0, 0), xycoords='data',
            xytext=(0, delay_result.params['b'].value + delay_result.params['a'].value), textcoords='data',
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="arc3"),
            )
ax.text(xmin*1.5, 10., "$\\delta_{max}$")

ax.annotate("",
            xy=(xmax, delay_result.params['a'].value), xycoords='data',
            xytext=(xmax,  delay_result.params['b'].value + delay_result.params['a'].value), textcoords='data',
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="arc3"),
            )
ax.text(1.01*xmax, 10., "$m$")

# ax.text(0.006, 6., "$k$")
ax.set_xlim(xmax= xmax*1.1)
simpleaxis(ax)
plt.legend()
fig.set_figwidth(6)
fig.set_figheight(6)
# dump(fig,file('figures/fig6/6k.pkl','wb'))
plt.show()
```

### Over all EI cells

```python
voltageClampFiles = '/media/sahil/NCBS_Shares_BGStim/patch_data/voltage_clamp_files.txt'
```

```python
with open (voltageClampFiles,'r') as r:
    dirnames = r.read().splitlines()
```

```python
a = ['161220 c2_EI',
 '170510 c2_EI',
 '170524 c3_EI',
 '170524 c1_EI',
 '170530 c2_EI',
 '170530 c1_EI',
 '170531 c2_EI',
 '170531 c4_EI',
 '170531 c1_EI',
 '170720 c5_EI',
 '170720 c3_EI',
 '170720 c4_EI',
 '170720 c2_EI']
dirnames = (['/home/bhalla/Documents/Codes/data/media/sahil/NCBS_Shares_BGStim/patch_data/' + '/'.join(j.split(' ')) + '/' for j in a])
```

```python
#Colorscheme for cells
color_cell = matplotlib.cm.plasma(numpy.linspace(0,1,len(dirnames)))
```

```python
neurons = []
for dirname in dirnames:
    cellIndex = dirname.split('/')[-2]
    filename = dirname + 'plots/' + cellIndex + '.pkl'
    neurons.append(Neuron.load(filename))
```

```python
all_delays = []
all_conductances = []
all_inh_conductances = []
scalingFactor = 1e6
for index, n in enumerate(neurons):
    avg_exc_onset = {}
    avg_inh_onset = {}
    avg_exc_max = {}
    exc_onsets, inh_onsets = {}, {}
    exc_max,inh_max = {}, {}
    err_exc_onset, err_inh_onset = {}, {}

    for expType, experiment in n:
        for sqr in experiment:
            for coord in experiment[sqr].coordwise:
                if expType == 1:
                    list_exc_onset = []
                    list_exc_max = []
                    for trial in experiment[sqr].coordwise[coord].trials:
                        onsetTime = findOnsetTime(trial)
                        if onsetTime:
                            exc_onsets[(sqr,trial.index)] = onsetTime*1e3
                            list_exc_onset.append(exc_onsets[(sqr,trial.index)])
                            list_exc_max.append(-trial.feature[5]*scalingFactor)
                        #exp[sqr].coordwise[coord].average_feature[5]
                    avg_exc_onset[coord] = numpy.nanmean([onset for onset in list_exc_onset if onset])
                    err_exc_onset[coord] = numpy.nanstd([onset for onset in list_exc_onset if onset])
                    exc_max[coord] = numpy.nanmean([maxC for maxC in list_exc_max if maxC])
    #                 for trial in experiment[sqr].coordwise[coord].trials:
    #                     avg_exc_onset[(sqr,trial.index)] = avg_onset
    #                     err_exc_onsets[(sqr,trial.index)] = err_onset
                if expType == 2:
                    list_inh_onset = []
                    list_inh_max = []
                    for trial in experiment[sqr].coordwise[coord].trials:
                        onsetTime = findOnsetTime(trial)
                        if onsetTime:
                            inh_onsets[(sqr,trial.index)] = onsetTime*1e3
                            list_inh_onset.append(inh_onsets[(sqr,trial.index)])
                            list_inh_max.append(trial.feature[0]*scalingFactor)
                    avg_inh_onset[coord] = numpy.nanmean([onset for onset in list_inh_onset if onset])
                    err_inh_onset[coord] = numpy.nanstd([onset for onset in list_inh_onset if onset])
                    inh_max[coord] = numpy.nanmean([maxC for maxC in list_inh_max if maxC])

    delay, max_conductance, max_inh_conductance = [], [], []
    inhibOnset = []
    conductanceConversion = 70e-3
    for key in set(avg_exc_onset).intersection(set(avg_inh_onset)):
        if avg_inh_onset[key] and avg_exc_onset[key]:
            if not numpy.isnan(avg_inh_onset[key]) and not numpy.isnan (avg_exc_onset[key]) and not numpy.isnan (exc_max[key]) and not numpy.isnan (inh_max[key]):
                delay.append(avg_inh_onset[key]- avg_exc_onset[key])
                max_conductance.append(exc_max[key]/conductanceConversion)
                max_inh_conductance.append(inh_max[key]/conductanceConversion)
    all_delays.append(delay)
    all_conductances.append(max_conductance)
    all_inh_conductances.append(max_inh_conductance)
    print ("Done {}".format(index))
```

    Done 0
    Done 1
    Done 2
    Done 3
    Done 4
    Done 5
    Done 6
    Done 7

    /usr/local/lib/python2.7/dist-packages/ipykernel_launcher.py:41: RuntimeWarning: Mean of empty slice
    /usr/local/lib/python2.7/dist-packages/numpy/lib/nanfunctions.py:1628: RuntimeWarning: Degrees of freedom <= 0 for slice.
      keepdims=keepdims)

    Done 8
    Done 9
    Done 10
    Done 11
    Done 12

```python
# all_delays = []
# all_conductances = []
# all_inh_conductances = []
# scalingFactor = 1e6
# for index, n in enumerate(neurons):
#     avg_exc_onset = {}
#     avg_inh_onset = {}
#     avg_exc_max = {}
#     exc_onsets, inh_onsets = {}, {}
#     exc_max,inh_max = {}, {}
#     err_inh_onsets = {}
#     for expType, experiment in n:
#         for sqr in experiment:
#             for coord in experiment[sqr].coordwise:
#                 if expType == 1:
#                     exc_onsets[(sqr,coord)] = []
#                     exc_max[(sqr,coord)] = []
#                     for trial in experiment[sqr].coordwise[coord].trials:
#                         onsetTime = findOnsetTime(trial)
#                         if onsetTime:
#                             exc_onsets[(sqr,coord)].append(onsetTime*1e3)
#                             exc_max[(sqr,coord)].append(-trial.feature[5]*scalingFactor)
#                             #exp[sqr].coordwise[coord].average_feature[5]
#                     exc_onsets[(sqr,coord)] = numpy.nanmean(exc_onsets[(sqr,coord)])
#                     exc_max[(sqr,coord)] = numpy.nanmean(exc_max[(sqr,coord)])

#                 if expType == 2:
#                     inh_onsets[(sqr,coord)] = []
#                     inh_max[(sqr,coord)] = []
#                     #list_inh_onset = []
#                     for trial in experiment[sqr].coordwise[coord].trials:
#                         onsetTime = findOnsetTime(trial)
#                         if onsetTime:
#                             inh_onsets[(sqr,coord)].append(onsetTime*1e3)
#                             #list_inh_onset.append(onsetTime*1e3)
#                             inh_max[(sqr,coord)].append(trial.feature[0]*scalingFactor)
#                     #avg_onset = numpy.nanmean([onset for onset in list_inh_onset if onset])
#                     #err_onset = numpy.nanstd([onset for onset in list_inh_onset if onset])
# #                     for trial in exp[sqr].coordwise[coord].trials:
# #                         avg_inh_onset[(sqr,trial.index)] = avg_onset
# #                         err_inh_onsets[(sqr,trial.index)] = err_onset
#                     inh_onsets[(sqr,coord)] = numpy.nanmean(inh_onsets[(sqr,coord)])
#                     inh_max[(sqr,coord)] = numpy.nanmean(inh_max[(sqr,coord)])

#     delay, max_conductance, max_inh_conductance = [], [], []
# #     del_err, max_err= [], []
#     inhibOnset = []
#     conductanceConversion = 70e-3
#     for key in set(exc_onsets).intersection(set(inh_onsets)):
#         if inh_onsets[key] and exc_onsets[key]:
# #             print ("Doing {}".format(index))
# #             print (inh_onsets[key], exc_onsets[key], exc_max[key])
#             if not numpy.isnan(inh_onsets[key]) and not numpy.isnan (exc_onsets[key]) and not numpy.isnan (exc_max[key]) and not numpy.isnan (inh_max[key]):
# #                 print ("Delay is {}".format(inh_onsets[key]- exc_onsets[key]))
#                 delay.append(inh_onsets[key]- exc_onsets[key])
#                 max_conductance.append(exc_max[key]/conductanceConversion)
#                 max_inh_conductance.append(inh_max[key]/conductanceConversion)
#     all_delays.append(delay)
#     all_conductances.append(max_conductance)
#     all_inh_conductances.append(max_inh_conductance)
#     print ("Done {}".format(index))
```

```python
fig, ax = plt.subplots()
cmap = matplotlib.cm.viridis

colors = matplotlib.cm.viridis(numpy.linspace(0, 1, len(all_inh_conductances)))

# norm = matplotlib.colors.Normalize(vmin=1, vmax=6)
slopeArr = []
for i, (g, gi, d, c) in enumerate(zip(all_conductances, all_inh_conductances, all_delays, colors)):
    g, gi, d = numpy.array(g), numpy.array(gi), numpy.array(d)
    indices = numpy.argsort(g)
    #slope, intercept, rval, pvalue, err = ss.linregress(g[indices], gi[indices])
    #cbar = ax.scatter(g,d, c= [slope]*len(g), s= 10, cmap='viridis', vmin=1.5, vmax=3.2)
    slope, intercept, lowConf, upperConf = ss.mstats.theilslopes(x=g[indices], y=d[indices])
    #slope, intercept, rval, pvalue, err = ss.linregress(g[indices], d[indices])
    cbar = ax.scatter(g,d, s=4, c=c, alpha=0.4, cmap=cmap)
    ax.plot(g[indices], slope*g[indices] + intercept, color=c, linewidth=.5)
    slopeArr.append(slope)

flattened_g = numpy.array([g for sublist in all_conductances for g in sublist])
flattened_d = numpy.array([d for sublist in all_delays for d in sublist])

ax.set_xlabel("$g_e$  (nS)")
ax.set_ylabel("$\\delta_i$ (ms)")
# plt.colorbar(cbar)
ax.set_ylim(ymin=-5)#, ymax=12)
# ax.set_xlim(xmin=0, xmax=5)
simpleaxis(ax)
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
plt.savefig("/media/sahil/NCBS_Shares_BGStim/patch_data/Paper_Figures/figures/fig6/6l_1.svg")
# dump(fig,file('figures/fig6/6l_1.pkl','wb'))
plt.show()
```

```python
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3)
cmap = matplotlib.cm.viridis

colors = matplotlib.cm.viridis(numpy.linspace(0, 1, len(all_inh_conductances)))

# norm = matplotlib.colors.Normalize(vmin=1, vmax=6)
slopeArr = []
for i, (g, gi, d, c) in enumerate(zip(all_conductances, all_inh_conductances, all_delays, colors)):
    g, gi, d = numpy.array(g), numpy.array(gi), numpy.array(d)
    ax1.scatter(gi/g,d,s=.1,color='k')
    ax2.scatter(g,d,s=.1,color='k')
    ax3.scatter(gi,d,s=.1,color='k')

flattened_g = numpy.array([g for sublist in all_conductances for g in sublist])
flattened_gi = numpy.array([g for sublist in all_inh_conductances for g in sublist])
flattened_gi_by_g = numpy.array([g for sublist in zip(all_conductances,all_inh_conductances) for g in sublist])
flattened_d = numpy.array([d for sublist in all_delays for d in sublist])

slope, intercept, rval, pvalue, err = ss.linregress(flattened_gi,flattened_d)
ax1.plot(gi/g, slope*(gi/g) + intercept)
slope, intercept, rval, pvalue, err = ss.linregress(g, d)
ax2.plot(gi/g, slope*(gi/g) + intercept)
slope, intercept, rval, pvalue, err = ss.linregress(gi, d)
ax3.plot(gi/g, slope*(gi/g) + intercept)

ax.set_xlabel("I/E")
ax.set_ylabel("$\\delta_i$ (ms)")
# plt.colorbar(cbar)
ax1.set_ylim(ymin=-5)
ax2.set_ylim(ymin=-5)
ax3.set_ylim(ymin=-5)
simpleaxis([ax1, ax2, ax3])
fig.set_figwidth(4.5)
fig.set_figheight(1.5)
plt.show()
```

```python
fig, ax = plt.subplots()
bins = numpy.linspace(-3,0.25,13)
print(bins)
ax.hist(slopeArr,bins=bins,color='k')
ax.vlines(x=0,ymin=0,ymax=7.,color='r')
simpleaxis(ax)
fig.set_figwidth(1.5)
fig.set_figheight(1.5)
dump(fig,file('figures/fig6/6l_2.pkl','wb'))
plt.show()
```

### Fitting through all cells

```python
cmap = matplotlib.cm.viridis
colors = matplotlib.cm.viridis(numpy.linspace(0, 1, len(all_inh_conductances)))
fig, ax = plt.subplots()
# norm = matplotlib.colors.Normalize(vmin=1, vmax=6)
slopeArr = []
adist, bdist = [],[]
flattened_g, flattened_d = [], []
for i, (g, gi, d, c) in enumerate(zip(all_conductances, all_inh_conductances, all_delays, colors)):
    g, gi, d = numpy.array(g), numpy.array(gi), numpy.array(d)
    indices = numpy.where(d>0)
    g, gi, d = g[indices], gi[indices], d[indices]
    flattened_g += list(g)
    flattened_d += list(d)
    indices = numpy.argsort(g)
#     delay_Model = lmfit.Model(delay_excitation)
#     delay_pars = delay_Model.make_params()

#     delay_result = delay_Model.fit(d, delay_pars, x=g)
#     indices = numpy.argsort(g)
# #     ax.scatter(g[indices], 1./d[indices], s=30, facecolor='k', edgecolor='k')
    ax.scatter(g[indices],1./d[indices], s=5, facecolor=colors[i], edgecolor=colors[i])
#     ax.plot(g[indices], delay_result.best_fit[indices], '--', color=colors[i], linewidth=1)
#     print ("{:.2f} + {:.2f}g_e^{:.2f}".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
#     adist.append(delay_result.params['a'].value)
#     bdist.append(delay_result.params['b'].value)
#     print(delay_result.fit_report())
#     ax.set_xlabel("$g_e$ (nS)")
#     ax.set_ylabel("$\\delta_i$ (ms)")
    # dump(fig,file('figures/fig6/6k.pkl','wb'))
# flattened_g = numpy.array([g for sublist in all_conductances for g in sublist])
# flattened_d = 1./numpy.array([d for sublist in all_delays for d in sublist])
# delay_Model = lmfit.Model(delay_excitation)
# delay_pars = delay_Model.make_params()

flattened_d_nonInv = flattened_d[:]
flattened_g = numpy.array(flattened_g)
flattened_d = 1./numpy.array(flattened_d)

#delay_result = delay_Model.fit(flattened_d, delay_pars, x=flattened_g)
slope, intercept, lowerr, higherr = ss.mstats.theilslopes(y=flattened_d,x=flattened_g)
indices = numpy.argsort(flattened_g)
ax.plot(flattened_g[indices], slope*flattened_g[indices] + intercept, '-',color='k')
#     ax.scatter(g[indices], d[indices], s=30, facecolor='k', edgecolor='k')
#ax.plot(flattened_g[indices], delay_result.best_fit[indices], '-',color='k')
#print ("{:.2f} * g_e^{:.2f}".format(delay_result.params['a'].value, delay_result.params['b'].value))#, delay_result.params['c'].value))
#print(delay_result.fit_report())
ax.set_xlabel("$g_e$ (nS)")
ax.set_ylabel("$\\delta_i$ (ms)")
fig.set_figwidth(4.5)
fig.set_figheight(4.5)
ax.set_xlim(0,4.5)
ax.set_ylim(0,15)
simpleaxis(ax)
plt.show()
# ax.set_xticks(range(4))
# ax.set_yticks(range(0,12,2))

# print ("{:.2f} + {:.2f}e^-{:.2f}E".format(delay_result.params['a'].value, delay_result.params['b'].value, delay_result.params['c'].value))
```

```python
from scipy.optimize import curve_fit
fig, ax = plt.subplots()
xArr = numpy.linspace(0.01,12,100)
ax.scatter(flattened_g,flattened_d_nonInv,s=8)
popt, pcov = curve_fit(delay_excitation, flattened_g, flattened_d_nonInv, bounds=(0, [2., 2, 1]))
ax.plot(xArr, delay_excitation(xArr, *popt), 'r-')
# ax.plot(xArr, (1.5/xArr) + 1.5, 'k--')
ax.set_xlim(-1,15)
ax.set_ylim(-1,15)
plt.show()
print (popt, pcov)
```

```python
print (slope, intercept)
print(lowerr,higherr)

residuals = (flattened_d - 1.5*flattened_g[indices]+1.5)
plt.hist(residuals,bins=30)
plt.vlines(x=0,ymin=0,ymax=200)
plt.show()
```

```python
residualArr = []
for i, (g, gi, d, c) in enumerate(zip(all_conductances, all_inh_conductances, all_delays, colors)):
    g, gi, d = numpy.array(g), numpy.array(gi), numpy.array(d)
    indices = numpy.where(d>0)
    g, gi, d = g[indices], gi[indices], d[indices]
    residuals = (d - 1.5*g+1.5)
    residualArr.append(residuals)
plt.hist(residualArr,stacked=True)
plt.show()
```

```python
fig, ax = plt.subplots(3,1)
ax[0].hist(adist)
ax[1].hist(bdist,bins=5)
plt.show()
print(numpy.mean(adist))
print(numpy.mean(bdist))
```

```python
slopeArr
```

```python
fig, ax = plt.subplots()
cmap = matplotlib.cm.viridis

colors = matplotlib.cm.viridis(numpy.linspace(0, 1, len(all_inh_conductances)))

# norm = matplotlib.colors.Normalize(vmin=1, vmax=6)
for i, (g, gi, d, c) in enumerate(zip(all_conductances, all_inh_conductances, all_delays, colors)):
    g, gi, d = numpy.array(g), numpy.array(gi), numpy.array(d)
    indices = numpy.argsort(g)

#     slope, intercept, rval, pvalue, err = ss.linregress(g[indices], gi[indices])
#     print(slope)
    #cbar = ax.scatter(g,d, c= [slope]*len(g), s= 10, cmap='viridis', vmin=1.5, vmax=3.2)
#     print(g)
    gmax = numpy.ceil(max(g))
    gmin = numpy.floor(min(g))
    print(gmin, min(g), gmax, max(g))
#     bins = numpy.linspace(gmin,gmax,(gmax - gmin) +1)
    print (gmin, gmax)
    bins = numpy.arange(gmin,gmax,1)
    indices = numpy.argsort(g)

    digitized = numpy.digitize(g[indices], bins)
#     bins = range(8)
    g_means = numpy.array([g[indices][digitized == i].mean() for i in bins])
    g_err = numpy.array([g[indices][digitized == i].std() for i in bins])
    d_means = numpy.array([d[indices][digitized == i].mean() for i in bins])
    d_err = numpy.array([d[indices][digitized == i].std() for i in bins])

    finiteYmask = numpy.isfinite(g_means)
    d_means = d_means[finiteYmask]
    g_means = g_means[finiteYmask]
    d_err = d_err[finiteYmask]
    g_err = g_err[finiteYmask]

    slope, intercept, rval, pvalue, err = ss.linregress(g_means, d_means)

    ax.errorbar(g_means, d_means, xerr = g_err, yerr = d_err, linestyle='')
    cbar = ax.scatter(g_means, d_means, s=10, c=c, alpha=0.5, cmap='viridis')
#     indices = numpy.argsort(g_means)
    print(g_means, d_means, intercept, slope)
    ax.plot(g_means, intercept + slope*g_means, c=c)
plt.show()
```

```python
delay_Model = lmfit.Model(delay_excitation)
delay_pars = delay_Model.make_params()

indices = numpy.argsort(flattened_g)
flattened_g = flattened_g[indices]
flattened_d_fit = delay_result.eval(x=flattened_g)

delay_result = delay_Model.fit(flattened_d, delay_pars, x=flattened_g)

fig, ax = plt.subplots()
ax.scatter(flattened_g, flattened_d, s=10, alpha=0.2,c='k')

print(delay_result.fit_report())
ax.plot(flattened_g, flattened_d_fit)
plt.show()
# slope, intercept, rval, pvalue, err = ss.linregress(flattened_g[indices], flattened_d[indices])
# x_axis = numpy.linspace(numpy.min(flattened_g), numpy.max(flattened_g), 100)
# y_axis = slope * x_axis + intercept

# ax.set_xlim(0,6)
# ax.set_ylim(-3,10)

# ax.plot(x_axis, y_axis, '--')
print ( delay_result.params['a'],delay_result.params['b'],delay_result.params['c'])
```

```python
keySet = set(inh_onsets).intersection(exc_onsets)
```

```python
for key in keySet:
    print (inh_onsets[key], exc_onsets[key])
```

Paper_Figures/Figure 6 (Conductance model), Figure 5B (Model derived inhibition).ipynb


# **[HW6] Practice_4**

1. Install packages
2. Gram-Schmidt orthonormalization
3. QR decomposition

선형대수 실습은, exercise 혹은 coding 문제풀이에 집중하기 보다는, 수업시간에 배웠던 개념들을 visualize 하면서 이를 폭넓게 이해하는 데에 초점을 맞추고 있습니다. 실습에서 사용한 예시 외에도, 다양한 matrix들을 visualize 하면서 개념을 익혀보시기 바랍니다.

# 1. Install packages

> 필요한 package를 설치하고 import합니다

```python
# visualization을 위한 helper code입니다.
from urllib.request import urlretrieve
URL = 'https://go.gwu.edu/engcomp4plot'
urlretrieve(URL, 'plot_helper.py')

import sys
sys.path.append('../scripts/')

# 다음 세 custom function (1)plot_vector, (2)plot_linear_transformation, (3) plot_linear_transformations
# 을 사용할 것입니다.
from plot_helper import *
```

```python
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import scipy as sp
import scipy.linalg
import sympy as sy

sy.init_printing()
np.set_printoptions(precision=3)
np.set_printoptions(suppress=True)
```

# 2. Gram-Schmidt orthonormalization

> Gram-Schmidt orthonormalization은, 주어진 basis를 orthonormal basis로 만들기 위한 method입니다. $\{x_1, x_2, x_3\}$을 $R^3$의 basis라고 합시다.

$$
x_1 = \begin{bmatrix} 3\\6\\2\end{bmatrix}, x_2 = \begin{bmatrix} 1\\2\\4\end{bmatrix}, x_3 = \begin{bmatrix} 2\\-2\\1\end{bmatrix}
$$

> $x_1, x_2, x_3$은 서로 orthogonal이 아닙니다 ($x_1 \cdot x_2 = 32 \neq 0$). Gram-Schmidt orthonormalization을 이용해서 이를 orthonormal basis $u_1, u_2, u_3$으로 바꿔보겠습니다. 이때 $u_1, u_2, u_3$는 최종적으로 다음을 만족해야 합니다.

1.  span $\{x_1, x_2, x_3\}$ = span $\{u_1, u_2, u_3\}$
2.  $u_i \perp u_j$ for $i,j \in \{1,2,3\}$ (i.e., $u_i \cdot u_j = 0 $)

```python
s = np.linspace(-1, 1, 10)
t = np.linspace(-1, 1, 10)
S, T = np.meshgrid(s, t)

vec = np.array([[[0,0,0,3, 6, 2]],
             [[0,0,0,1, 2, 4]],
             [[0,0,0,2, -2, 1]]])

# x1과 x_2의 linear combination, 즉 span
X = vec[0,:,3] * S + vec[1,:,3] * T
Y = vec[0,:,4] * S + vec[1,:,4] * T
Z = vec[0,:,5] * S + vec[1,:,5] * T

fig = plt.figure(figsize = (7, 7))
ax = fig.add_subplot(projection='3d')

# x1, x2가 span하는 공간을 격자로 나타냅니다.
ax.plot_wireframe(X, Y, Z, linewidth = 1.5, alpha = .3)

############################# x1 and x2 ##############################
colors = ['r','b','g']
s = ['$x_1$', '$x_2$', '$x_3$']
for i in range(vec.shape[0]):
    X,Y,Z,U,V,W = zip(*vec[i,:,:])
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False,
              color = colors[i], alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
              linestyles = 'solid',linewidths = 3)
    ax.text(vec[i,:,3][0], vec[i,:,4][0], vec[i,:,5][0], s = s[i], size = 15)

ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
plt.show()
```

> 위의 파란색 격자는 $x_1, x_2$가 span하는 2-dimensional subspace입니다. Gram-Schmidt를 위해, 우선 셋 중 하나의 vector를 고정하고 시작하겠습니다.

$$v_1 = x_1 \\ u_1 = \frac{v_1}{||v_1||}$$

> span $\{v_1\}$을 $\mathcal{W_1}$이라고 합시다. 이제 $v_2$를 구하기 위해, $x_2$를 $\mathcal{W_1}$에 orthogonal projection 한 vector를 먼저 구하도록 하겠습니다.

$$ \hat{x_2} = \text{Proj}_{\mathcal{W_1}}x_2 = \frac{x_2 \cdot u_1}{u_1 \cdot u_1}u_1 \\
v_2 = x_2 - \hat{x_2} = x_2 -  \frac{x_2 \cdot u_1}{u_1 \cdot u_1}u_1 \\
u_2 = \frac{v_2}{||v_2||}
$$

### Exercise

```python
# answer
x1 = np.array([3, 6, 2])
x2 = np.array([1, 2, 4])
x3 = np.array([2, -2, 1])

v1 = x1
u1 = x1 / np.linalg.norm(x1)
x2_hat = ((x2@u1)/(u1@u1)) * u1
v2 = x2 - x2_hat
u2 = v2 / np.linalg.norm(v2)

print(u1 @ u2)
print(x2_hat @ u2)
```

    -3.105951761656717e-17
    2.0863089266387665e-17

```python
s = np.linspace(-1, 1, 10)
t = np.linspace(-1, 1, 10)
S, T = np.meshgrid(s, t)

# x1과 x_2의 linear combination, 즉 span
X = x1[0] * S + x2[0] * T
Y = x1[1] * S + x2[1] * T
Z = x1[2] * S + x2[2] * T

fig = plt.figure(figsize = (10, 10))
ax = fig.add_subplot(projection='3d')

# x1, x2가 span하는 공간을 격자로 나타냅니다.
ax.plot_wireframe(X, Y, Z, linewidth = 1.5, alpha = .3)

############################# x1, x2, x3, x2_hat, v2 ##############################

vec = np.array([[0, 0, 0, x1[0], x1[1], x1[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x2[0], x2[1], x2[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x3[0], x3[1], x3[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'green', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x2_hat[0],x2_hat[1], x2_hat[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue', alpha = .6,arrow_length_ratio = .12, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, v2[0], v2[1], v2[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'purple', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

ax.text(x1[0], x1[1], x1[2], '$\mathbf{x}_1 = \mathbf{v}_1 $', size = 15)
ax.text(x2[0], x2[1], x2[2], '$\mathbf{x}_2$', size = 15)
ax.text(x3[0], x3[1], x3[2], '$\mathbf{x}_3$', size = 15)
ax.text(x2_hat[0], x2_hat[1], x2_hat[2], '$\hat{\mathbf{x_2}}$', size = 15)
ax.text(v2[0], v2[1], v2[2], '$\mathbf{v}_2$', size = 15)

ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')

################################# Dashed Line ##################################

point1 = [x2_hat[0], x2_hat[1], x2_hat[2]]
point2 = [x2[0], x2[1], x2[2]]
line1 = np.array([point1, point2])
ax.plot(line1[:,0], line1[:,1], line1[:, 2], c = 'b', lw = 3.5,alpha =0.5, ls = '--')

point1 = [v2[0], v2[1], v2[2]]
point2 = [x2[0], x2[1], x2[2]]
line1 = np.array([point1, point2])
ax.plot(line1[:,0], line1[:,1], line1[:, 2], c = 'b', lw = 3.5,alpha =0.5, ls = '--')
plt.show()
```

> $u_1, u_2$를 구했으므로, 이제 세 번째 basis인 $v_3$만 찾으면 됩니다. span $\{v_1, u_2\}$을 $\mathcal{W_2}$이라고 합시다. 이제 $v_3$를 구하기 위해, $x_3$를 $\mathcal{W_2}$에 orthogonal projection 한 vector를 먼저 구하도록 하겠습니다.

$$ \hat{x_3} = \text{Proj}_{\mathcal{W_2}}x_3 = \frac{x_3 \cdot u_1}{u_1 \cdot u_1}u_1  + \frac{x_3 \cdot u_2}{u_2 \cdot u_2}u_2 \\
v_3 = x_3 - \hat{x_3} = x_3 -  \frac{x_3 \cdot u_1}{u_1 \cdot u_1}u_1  - \frac{x_3 \cdot u_2}{u_2 \cdot u_2}u_2 \\
u_3 = \frac{v_3}{||v_3||}
$$

### Exercise

```python
# answer
x3_hat = (x3@u1)/(u1@u1)*u1 + (x3@u2)/(u2@u2)*u2
v3 = x3 - x3_hat
u3 = v3 / np.linalg.norm(v3)

print(u1 @ u3)
print(u2 @ u3)
print(x3_hat @ u3)
```

    5.418535247474389e-18
    1.1974035593932175e-18
    8.758042979726618e-18

```python
s = np.linspace(-1, 1, 10)
t = np.linspace(-1, 1, 10)
S, T = np.meshgrid(s, t)

# x1과 x_2의 linear combination, 즉 span
X = x1[0] * S + x2[0] * T
Y = x1[1] * S + x2[1] * T
Z = x1[2] * S + x2[2] * T

fig = plt.figure(figsize = (9, 9))
ax = fig.add_subplot(projection='3d')
# x1, x2가 span하는 공간을 격자로 나타냅니다.
ax.plot_wireframe(X, Y, Z, linewidth = 1.5, alpha = .3)

############################# x1, x2, x2_hat, v2, x3, x3_hat, v3 ##############################

vec = np.array([[0, 0, 0, x1[0], x1[1], x1[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x2[0], x2[1], x2[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x2_hat[0],x2_hat[1], x2_hat[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue', alpha = .6,arrow_length_ratio = .12, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, v2[0], v2[1], v2[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'purple', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x3[0], x3[1], x3[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, x3_hat[0], x3_hat[1], x3_hat[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'black', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

vec = np.array([[0, 0, 0, v3[0], v3[1], v3[2]]])
X, Y, Z, U, V, W = zip(*vec)
ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'purple', alpha = .6,arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

ax.text(x1[0], x1[1], x1[2], '$\mathbf{x}_1 = \mathbf{v}_1 $', size = 15)
ax.text(x2[0], x2[1], x2[2], '$\mathbf{x}_2$', size = 15)
ax.text(x2_hat[0], x2_hat[1], x2_hat[2], '$\hat{\mathbf{x}}_2$', size = 15)
ax.text(v2[0], v2[1], v2[2], '$\mathbf{v}_2$', size = 15)
ax.text(x3[0], x3[1], x3[2], '$\mathbf{x}_3$', size = 15)
ax.text(x3_hat[0], x3_hat[1], x3_hat[2], '$\hat{\mathbf{x}}_3$', size = 15)
ax.text(v3[0], v3[1], v3[2], '$\mathbf{v}_3$', size = 15)

ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')

################################# Dashed Line ##################################

point1 = [x2_hat[0], x2_hat[1], x2_hat[2]]
point2 = [x2[0], x2[1], x2[2]]
line1 = np.array([point1, point2])
ax.plot(line1[:,0], line1[:,1], line1[:, 2], c = 'b', lw = 3.5,alpha =0.5, ls = '--')

point1 = [v2[0], v2[1], v2[2]]
point2 = [x2[0], x2[1], x2[2]]
line1 = np.array([point1, point2])
ax.plot(line1[:,0], line1[:,1], line1[:, 2], c = 'b', lw = 3.5,alpha =0.5, ls = '--')

point1 = [x3_hat[0], x3_hat[1], x3_hat[2]]
point2 = [x3[0], x3[1], x3[2]]
line1 = np.array([point1, point2])
ax.plot(line1[:,0], line1[:,1], line1[:, 2], c = 'b', lw = 3.5,alpha =0.5, ls = '--')

################################ Axes ######################################
ax.set_xlim3d(-5, 5)
ax.set_ylim3d(-5, 5)
ax.set_zlim3d(-5, 5)
plt.show()
```

> 이제 우리는 orthonormal basis $\{u_1, u_2, u_3\}$을 구했습니다. 즉, 처음에 얘기했던 조건인 $u_i \cdot u_j = 0$이 만족된 것입니다. 그럼 첫 번째 조건, span $\{x_1, x_2, x_3\}$ = span $\{u_1, u_2, u_3\}$은 만족됐을까요?

> 결론부터 얘기하면, 만족하게 됩니다. Gram-Schmidt로 유도된 식을 보면,

1. $u_1$은 $x_1$의 linear combination ($u_1 = x_1$)
2. $u_2$은 $x_1, x_2$의 linear combination
3. $u_3$은 $x_1, x_2, x_3$의 linear combination

> 의 형태임을 알 수 있습니다. Span하는 subspace가 같다는 말은, 세 vector의 linear combination으로 만들 수 있는 vector가 같다는 말입니다. 따라서 $x_1, x_2, x_3$으로 만들 수 있는 vector를 $u_1, u_2, u_3$가 만들 수 있고, $u_1, u_2, u_3$으로 만들 수 있는 vector를 $x_1, x_2, x_3$가 만들 수 있다면 span $\{x_1, x_2, x_3\}$ = span $\{u_1, u_2, u_3\}$은 만족됩니다.

> 예를 들어보겠습니다. Linearly independent한 $x_1,x_2,x_3$에 대해 Span $\{x_1, x_2, x_3\}$과 span$\{x_1, x_2, 2x_2 + x_3\}$이 있다고 합시다. 이때 $\{x_1, x_2, x_3\}$의 linear combination $k = \alpha_1 x_1 + \alpha_2 x_2 + \alpha_3 x_3 $인 vector k가 있다고 하면, 이는 $ \alpha_1 x_1 + (\alpha_2-2\alpha_3) x_2 + \alpha_3 (2x_2 + x_3) $와 같습니다. 즉 k는 $\{x_1, x_2, 2x_2 + x_3\}$의 linear combination으로 만들 수 있는 것입니다. 이 과정을 반대로 하면, $\{x_1, x_2, 2x_2 + x_3\}$으로 만들 수 있는 vector는 $\{x_1, x_2, x_3\}$으로 만들 수 있음을 알 수 있습니다.

> 이를 일반화 하면, span $\{x_1,x_2,x_3\}$은 span $\{x_1 $+ linear combination of $(x_2,x_3)$, $x_2$ + linear combination of $(x_1, x_3)$, $x_3$ + linear combination of $(x_1,x_2)\}$ 와 같음을 알 수 있습니다. $u_1, u_2, u_3$은 이를 만족하므로, 첫 번째 조건을 만족하게 됩니다.

> 세 vector $u_1, u_2, u_3$을 각 column으로 하는 matrix $U$는 orthogonal matrix가 됩니다.

```python
U = np.vstack((u1,u2,u3)).T
U.T@U
```

    array([[ 1., -0.,  0.],
           [-0.,  1.,  0.],
           [ 0.,  0.,  1.]])

```python
U@U.T  # UU^T는 identity matrix가 됩니다.
```

    array([[ 1., -0., -0.],
           [-0.,  1., -0.],
           [-0., -0.,  1.]])

# 3. QR decomposition

Gram-Schmidt를 하면 자연스럽게 matrix $A$의 QR decomposition을 수행할 수 있습니다.

### Exercise

위의 예시를 가져옵시다.

* Matrix $A = \begin{bmatrix} 3&1&2\\6&2&-2\\2&4&1\end{bmatrix}$라고 할 때, 위의 Gram-Schmidt된 결과를 이용해서 A를 QR decompose 하시오 (hint: 아래 수식을 이용하시오).

$$
u_1 = \frac{v_1}{||v_1||} \rightarrow x_1 = ||v_1|| \; u_1
\\
u_2 = \frac{v_2}{||v_2||} = \frac{x_2 -  \frac{x_2 \cdot u_1}{u_1 \cdot u_1}u_1}{||v_2||} \rightarrow x_2 = \frac{x_2 \cdot u_1}{u_1 \cdot u_1}u_1 + ||v_2|| \; u_2
\\
u_3 = \frac{v_3}{||v_3||} =  \frac{x_3 -  \frac{x_3 \cdot u_1}{u_1 \cdot u_1}u_1  - \frac{x_3 \cdot u_2}{u_2 \cdot u_2}u_2 }{||v_3||} \rightarrow x_3 = \frac{x_3 \cdot u_1}{u_1 \cdot u_1}u_1 + \frac{x_3 \cdot u_2}{u_2 \cdot u_2}u_2 + ||v_3|| \; u_3  \\
$$

```python
A = np.array([[3,1,2],[6,2,-2],[2,4,1]])
# TODO:
```

> 아래 QR decomposition 한 것과 같게 나오거나, 부호만 바뀌어서 나와야 합니다 (Q&A 게시판 참조)

> Numpy의 함수를 이용하면, 이를 자동으로 구할 수 있습니다.

```python
Q,R = np.linalg.qr(A)
```

```python
Q
```

    array([[-0.429, -0.128, -0.894],
           [-0.857, -0.256,  0.447],
           [-0.286,  0.958,  0.   ]])

```python
R
```

    array([[-7.   , -3.286,  0.571],
           [ 0.   ,  3.194,  1.214],
           [ 0.   ,  0.   , -2.683]])


# Information Retrieval in High Dimensional Data

# Assignment #1, 03.11.2017

# Curse of Dimensionality
Group Number: G10

Group Members:
- Achtner Martin
- Arifi Ridon
- Ehrhardt Daniel
- Fichtner Lukas
- Hassis Rafif

## Task 1
Assume $X$ to be uniformly distributed in $C_1$

Determine d in dependence of p: $(X \in C_d) = q$ with $q \in [0, 1]$

$p$: Dimensionality of the hypercube

$d$: Edge length of the hypercube

$q$: Arbitrary probability between $0$ and $1$

$\rightarrow$ The probability of $X$ lying in $C_d$ can be expressed as the relation between their corresponding "volumes":

\begin{equation} P(X \in C_d) = q = \frac{d^p}{1^p} = d^p \end{equation}

\begin{equation} d = \sqrt[p]{q} \end{equation}

Let the components of the p-dimensional random variable $X^p$ be independent and
have the standard normal distribution. It is known that $P( | X^1 | \leqslant 2.576) = 0.99$.
For an arbitrary $p$, determine the probability $P(X^p \notin C_{5.152})$ for any of the
components of $X^p$ to lie outside of the interval $[-2.576, 2.576]$.

\begin{equation} P = P( | X^p | \leqslant 2.576)^p \end{equation}

### Evaluate the value for $p = 2$, $p = 3$ and $p = 500$.

```python
import numpy as np
import matplotlib.pyplot as plt
```

```python
p_values = (2, 3, 500)

Px = np.asarray([1 - 0.99**i for i in p_values])
for i in range(3):
    print('Px[{}] = {:.3f}'.format(p_values[i], Px[i]))
```

    Px[2] = 0.020
    Px[3] = 0.030
    Px[500] = 0.993

## Task 2

### Sample 100 uniformly distributed random vectors from the hypercube $[-1, 1]^p$ for $p = 2$.

```python
samples = np.random.uniform(low=-1.0, high=1.0, size=(2, 100))

np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)})
print('{}'.format(samples) )
```

    [[0.964 0.598 0.367 -0.816 0.701 0.582 -0.539 0.192 -0.931 -0.907 0.459
      -0.369 0.976 0.616 -0.359 -0.000 0.184 0.003 -0.787 -0.472 0.894 -0.051
      -0.783 -0.783 0.315 -0.327 -0.819 0.257 -0.459 -0.196 0.139 0.565 -0.066
      0.647 0.658 0.180 -0.639 -0.603 0.655 0.389 -0.244 -0.983 0.913 -0.038
      0.118 -0.826 0.885 0.186 -0.884 -0.790 0.968 -0.513 0.873 -0.193 -0.071
      0.327 -0.938 0.293 0.812 0.810 -0.098 0.376 0.933 0.585 0.063 -0.931
      -0.216 0.653 -0.443 0.657 0.535 0.371 -0.163 0.413 0.028 0.322 0.385
      -0.035 0.871 0.020 0.583 0.102 -0.857 -0.576 -0.779 0.307 -0.298 0.022
      -0.928 -0.420 0.648 -0.577 -0.179 0.900 -0.209 -0.636 -0.952 0.975 0.404
      -0.615]
     [0.880 0.655 -0.006 -0.946 -0.261 0.906 0.557 0.502 0.207 -0.861 0.835
      0.272 -0.020 0.536 -0.610 -0.582 -0.641 -0.982 0.639 0.469 0.120 0.305
      -0.184 0.038 0.225 -0.722 -0.288 0.015 0.222 -0.189 0.877 -0.339 -0.879
      -0.515 0.175 0.487 0.444 0.788 0.134 0.138 -0.426 -0.479 0.901 -0.209
      -0.044 0.853 -0.123 0.546 0.172 -0.266 -0.561 0.821 0.475 -0.665 -0.371
      0.132 -0.858 -0.267 -0.340 0.989 0.286 -0.438 -0.495 0.113 0.191 -0.365
      -0.930 0.132 0.312 -0.950 0.517 -0.148 -0.962 -0.481 0.631 -0.280 -0.493
      0.138 0.471 -0.320 -0.851 0.159 -0.658 0.114 -0.317 -0.500 -0.503 -0.014
      -0.202 0.262 0.579 0.335 -0.471 -0.658 0.120 0.037 0.202 0.665 -0.160
      0.419]]

### For each of the 100 vectors determine the minimum angle to all other vectors. Then compute the average of these minimum angles.

```python
%%time
min_angles = np.zeros((samples.shape[1]))

for i in range(samples.shape[1]):
    indices = [j for j in range(samples.shape[1]) if not j==i]
    angles = np.zeros(0)
    x = samples[:, i]
    for j in indices:
        y = samples[:, j]
        cosine = np.dot(x,y) / (np.linalg.norm(x) * np.linalg.norm(y))
        angles = np.append(angles, np.arccos(cosine))
    min_angles[i] = np.amin(angles)

print('Average minimum angle = ', np.mean(min_angles))
```

    Average minimum angle =  0.0295040143848
    CPU times: user 248 ms, sys: 0 ns, total: 248 ms
    Wall time: 251 ms

### Repeat the above for dimensions p = 1...1000 and use the results to plot the average minimum angle against the dimension.

```python
%%time

N_DIMENSIONS = 1000
SAMPLESIZE = 100
min_angles = np.zeros(SAMPLESIZE)
max_angles = np.zeros(SAMPLESIZE)
min_avg_angles = np.zeros(N_DIMENSIONS)
max_avg_angles = np.zeros(N_DIMENSIONS)

samples = np.random.uniform(low=-1.0, high=1.0, size=(N_DIMENSIONS, SAMPLESIZE))
for p in range(1, N_DIMENSIONS+1):
    for i in range(SAMPLESIZE):
        indices = [j for j in range(SAMPLESIZE) if not i==j]
        x = samples[:, i]
        angles = np.zeros(0)
        for j in indices:
            y = samples[:, j]
            cosine = np.dot(x,y) / (np.linalg.norm(x) * np.linalg.norm(y))
            angles = np.append(angles, np.arccos(cosine))
        min_angles[i] = np.amin(angles)
        max_angles[i] = np.amax(angles)
    min_avg_angles[N_DIMENSIONS-p] = np.mean(min_angles)
    max_avg_angles[N_DIMENSIONS-p] = np.mean(max_angles)
    samples = np.delete(samples, np.s_[-1:], axis=0)
```

    CPU times: user 4min 2s, sys: 0 ns, total: 4min 2s
    Wall time: 4min 2s

```python
plt.plot(range(1,N_DIMENSIONS+1), min_avg_angles/np.pi)
plt.ylabel('Average minimum angle (rad)')
plt.xlabel('Dimension')
plt.show()
```

### Give an interpretation of the result.

$\rightarrow$ The average angle between 2 vectors converges to $\frac{\pi}{2}$

### What conclusions can you draw for 2 randomly sampled vectors in a p-dimensional space?

$\rightarrow$ The higher the value of p, the more likely it is that the 2 vectors are orthogonal.

### Does the result change if the sample size increases?

```python
%%time

N_DIMENSIONS = 1000
SAMPLESIZE = 200
min_angles = np.zeros(SAMPLESIZE)
max_angles = np.zeros(SAMPLESIZE)
min_avg_angles = np.zeros(N_DIMENSIONS)
max_avg_angles = np.zeros(N_DIMENSIONS)

samples = np.random.uniform(low=-1.0, high=1.0, size=(N_DIMENSIONS, SAMPLESIZE))
for p in range(1, N_DIMENSIONS+1):
    for i in range(SAMPLESIZE):
        indices = [j for j in range(SAMPLESIZE) if not i==j]
        x = samples[:, i]
        angles = np.zeros(0)
        for j in indices:
            y = samples[:, j]
            cosine = np.dot(x,y) / (np.linalg.norm(x) * np.linalg.norm(y))
            angles = np.append(angles, np.arccos(cosine))
        min_angles[i] = np.amin(angles)
        max_angles[i] = np.amax(angles)
    min_avg_angles[N_DIMENSIONS-p] = np.mean(min_angles)
    max_avg_angles[N_DIMENSIONS-p] = np.mean(max_angles)
    samples = np.delete(samples, np.s_[-1:], axis=0)
```

    CPU times: user 16min 34s, sys: 252 ms, total: 16min 34s
    Wall time: 16min 34s

```python
plt.plot(range(1,N_DIMENSIONS+1), min_avg_angles/np.pi)
plt.ylabel('Average angle (rad)')
plt.xlabel('Dimension')
plt.show()
```

$\rightarrow$ The more samples are chosen in a space of dimension $p$ the more likely it is that 2 vectors point in the same direction. Hence the average angle converges slower and with more oscillations around $\frac{\pi}{2}$.

## Task 3

Draw a circle with radius $\frac{1}{2}$ around each corner (note that each circle touches its two neighboring circles). Now draw a circle around the origin with a radius such that it touches all of the four previously drawn circles. What radius does it have?

$$
\rightarrow r = \sqrt{0.5^2 + 0.5^2} - R = \sqrt{0.5^2 + 0.5^2} - 0.5 = \sqrt{0.25 + 0.25} -0.5 = \sqrt{0.5} - 0.5 = 0.207106... \approx 0.207
$$

Motivate your claim.

$\rightarrow$The distance from the center 0 to one of the corner points of the hypercube can be calculated using the Theorem of Pythagoras.

```python
def calculate_radius(d, p):
    return np.sqrt(np.sum(np.asarray([d**2 for i in range(p)]))) - 0.5

dimensions = np.asarray(range(1,21))
result = np.zeros((20,))

for p in dimensions:
    result[p-1] = calculate_radius(0.5, p)
    print('p = ' + str(p) + ', r = ' + str(result[p-1]))
```

    p = 1, r = 0.0
    p = 2, r = 0.207106781187
    p = 3, r = 0.366025403784
    p = 4, r = 0.5
    p = 5, r = 0.61803398875
    p = 6, r = 0.724744871392
    p = 7, r = 0.822875655532
    p = 8, r = 0.914213562373
    p = 9, r = 1.0
    p = 10, r = 1.08113883008
    p = 11, r = 1.15831239518
    p = 12, r = 1.23205080757
    p = 13, r = 1.30277563773
    p = 14, r = 1.37082869339
    p = 15, r = 1.4364916731
    p = 16, r = 1.5
    p = 17, r = 1.56155281281
    p = 18, r = 1.62132034356
    p = 19, r = 1.67944947177
    p = 20, r = 1.7360679775

$\rightarrow$ From the 4th dimension onwards the radius of the inner hypersphere exceeds the boundaries of the hypercube.

$\rightarrow$ From the 9th dimension onwards the radius of the inner hypersphere is equal or more than the size of the hypercube.

## Statistical Decision Making

## Task 4
Answer the following question. All answers must be justified.

$\begin{array}{c|ccc} p_X(X, Y)  & Y = 1 & Y = 2 & Y = 3 \\ \hline\hline X = 2 & 0.4 & 0.14 & 0.05 \\ X = 1 & 0.02 & 0.26 & 0.13\end{array}$

+ The numbers in Figure 1 describe the probability of the respective events (e.g. $P(X=1, Y=1) = 0.02).$ Is this table a probability table? Justify your answer.

$ \rightarrow $ The individual probabilities of alle possible events must be real, non-negative and sum up to 1.

```python
# Check sum of probabilties.
joint_prob = np.array([[0.4, 0.14, 0.05], [0.02, 0.26, 0.13]])
P_sum = np.sum(joint_prob)
print('Sum of all probabilities: {}'.format(P_sum))
```

    Sum of all probabilities: 1.0

```python
negative = False
arr = joint_prob.reshape(6,1)
for prob in arr:
    if prob < 0:
        negative = True
print('Is any probability negative?: {}'.format(negative))
```

    Is any probability negative?: False

So the answer ist yes, this table is a probability table.

+ By means of Figure 1, provide the conditional expectation $\mathbb{E}_{y|X=2}[Y]$ and the probability of the event $X=1$ under the condition that $Y=3.$

$ \rightarrow $ To calculate $\mathbb{E}_{y|X=2}[Y]$ we simply have to multiply the possible outcomes of $Y$ with their respective joint probabilities, add them up and divide them by the probybility that $X$ equals two.
$\mathbb{E}_{y|X=2}[Y] = \frac{\sum\limits_{i=1}^3 i \times P(X=1, Y=i)}{P(X=2)}$

```python
y_values = np.array([1,2,3])
E_y_x2 = np.dot(joint_prob[0], y_values)/np.sum(joint_prob[0])
print('Conditional Expectation: {:.2f}'.format(E_y_x2))
```

    Conditional Expectation: 1.41

$ \rightarrow $ Similiar formula for the second probability:
$ P(X=1|Y=3) = \frac{P(X=1,Y=3)}{P(Y=3)} = \frac{P(X=1,Y=3)}{\sum\limits_{i=1}^2 P(X=i,Y=3)} $

```python
P_x1_cond_y3 = joint_prob[1,2]/(joint_prob[0,2]+joint_prob[1,2])
print('Conditional Probability: {:.2f}'.format(P_x1_cond_y3))
```

    Conditional Probability: 0.72

+ Is the function $p(x,y)$ given by

\begin{equation} p(x,y) = \begin{cases} 1 & \text{for } 0 \leq x \leq 1 \text{, } 0\leq y \leq \frac{1}{2} \\0 & \text{otherwise}\end{cases} \end{equation}

a joint densitiy function for two random variables?

$ \rightarrow $ For this function to be a joint density function it must fulfill the property:

\begin{equation} \int\limits_{-\infty}^{\infty}\int\limits_{-\infty}^{\infty}p(x,y)dxdy = 1 \end{equation}

and regarding our function we can change this condition to

\begin{equation} \int\limits_{0}^{0.5}\int\limits_{0}^{1}1 dxdy = 1 \end{equation}

$ \int\limits_{0}^{\frac{1}{2}}\int\limits_{0}^{1}1 dxdy = \int\limits_{0}^{\frac{1}{2}}\left[x\right]_0^1dy = \int\limits_{0}^{\frac{1}{2}}1dy = \left[y\right]_0^{\frac{1}{2}} = \frac{1}{2} \neq 1$

Therefore this function is not a joint density function.

```python
# Validate Integral
from sympy import *
init_printing(use_unicode=True)
x, y = symbols('x y')
integrate(integrate(1, (x, 0, 1)), (y, 0, Rational(1, 2)))
```

+ For two random variables $X$ and $Y$, let the joint density function be given by

\begin{equation} p(x,y) = \begin{cases} 2e^{-(x+y)} & \text{for } 0 \leq x \leq y \text{, } 0\leq y \\0 & \text{otherwise.}\end{cases} \end{equation}

What are the marginal density functions for $X$ and $Y$ respectively?

$ \rightarrow $ The marginal density functions are defined as

$ f_x(x,y) = \int\limits_{-\infty}^{+\infty}f(x,y)dy$

   $ f_y(x,y) = \int\limits_{-\infty}^{+\infty}f(x,y)dx$

Compute $ f_x(x,y) $ first:

$ f_x(x,y) = \int\limits_{0}^{+\infty}2e^{-(x+y)}dy = \lim\limits_{\alpha \to \infty} \int\limits_{0}^{\alpha}2e^{-(x+y)}dy = \lim\limits_{\alpha \to \infty} \left[-2e^{-(x+y)}\right]_0^{\alpha} = \lim\limits_{\alpha \to \infty} \left.\left[-2(e^{-(x + \alpha)} - e^{-(x+0)})\right]\right|_{x\geq0} = 2e^{-x}$

Now for $ f_y(x,y) $:

$ f_y(x,y) = \int\limits_0^y 2e^{-(x+y)}dx = \left[-2e^{-(x+y)}\right]_0^y = -2e^{-2y} + 2e^{-y} $

```python
# Validate f_x(x, y)
fxy = 2*exp(-(x+y))
integrate(fxy, (y, 0, oo))
```

```python
# Validate f_y(x, y)
integrate(fxy, (x, 0, y))
```

+ Let the joint density function of two random variables $X$ and $Y$ be given by

\begin{equation} p(x,y) = \begin{cases} \frac{1}{15}(2x+4y) & \text{for } 0 < x < 3 \text{, } 0 < y < 1 \\0 & \text{otherwise.}\end{cases} \end{equation}

Determine the probability for $X \leq 2$ under the condition that $Y = \frac{1}{2}$.

$\rightarrow P(X \leq 2, Y=\frac{1}{2}) = \int\limits_0^2 \left.p(x,y)dx\right|_{y=\frac{1}{2}} = \int\limits_0^2 \frac{1}{15}(2x+2)dx = \left[\frac{1}{15}(x^2 + 2x)\right]_0^2 = \frac{1}{15}(4+4) -0 = \frac{8}{15}$

```python
# Validate P(X <=2, Y = 0.5)
pxy = Rational(1, 15)*(2*x + 4*y)
prob = integrate(pxy, (x, 0, 2))
y = Rational(1, 2)
prob
```


# Best report ever

Everything you see here is either markdown, LaTex, Python or BASH.

## The spectral function

It looks like this:
\begin{equation}
 A(\omega) = \mathrm{Im}|G(\omega)|
\end{equation}

## GW vs Cumulant

Mathematically very different:

\begin{equation}
 G^{GW} (\omega) = \frac1{ \omega - \epsilon - \Sigma (\omega) }
\end{equation}

\begin{equation}
 G^C(t_1, t_2) = G^0(t_1, t_2) e^{ i \int_{t_1}^{t_2} \int_{t'}^{t_2} dt' dt'' W (t', t'') }
\end{equation}

---
BUT they connect through $\mathrm{Im} W (\omega) = \frac1\pi \mathrm{Im} \Sigma ( \epsilon - \omega )$.

## Implementation

Using a multi-pole representation for $\Sigma^{GW}$:

\begin{equation}
 \mathrm{Im} W (\omega) = \frac1\pi \mathrm{Im} \Sigma ( \epsilon - \omega )
\end{equation}
\begin{equation}
 W (\tau) = - i \lambda \bigl[ e^{ i \omega_p \tau } \theta ( - \tau ) + e^{ - i \omega_p \tau } \theta ( \tau ) \bigr]
\end{equation}

## GW vs Cumulant

- GW:
\begin{equation}
 A(\omega) = \frac1\pi \frac{\mathrm{Im}\Sigma (\omega)}
 { [ \omega - \epsilon - \mathrm{Re}\Sigma (\omega) ]^2 +
 [ \mathrm{Im}\Sigma (\omega) ]^2}
\end{equation}

- Cumulant:

\begin{equation}
 A(\omega) = \frac1\pi \sum_{n=0}^{\infty} \frac{a^n}{n!} \frac{\Gamma}{ (\omega - \epsilon + n \omega_p)^2 + \Gamma^2 }
\end{equation}

---

# Now some executable code (Python)

I have implemented the formulas above in my Python code.
I can just run it from here., but **before** let me check
if my input file is correct...

```python
!gvim data/SF_Si_bulk/invar.in
```

Now I can run my script:

```python
%cd data/SF_Si_bulk/
%run ../../../../../Code/SF/sf.py
```

Not very elegant, I know. It's just for demo pourposes.

```python
cd ../../../
```

I have first to import a few modules/set up a few things:

```python
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
# plt.rcParams['figure.figsize'] = (9., 6.)
%matplotlib inline
```

Next I can read the data from a local folder:

```python
sf_c = np.genfromtxt(
    'data/SF_Si_bulk/Spfunctions/spftot_exp_kpt_1_19_bd_1_4_s1.0_p1.0_800ev_np1.dat')
sf_gw = np.genfromtxt(
    'data/SF_Si_bulk/Spfunctions/spftot_gw_s1.0_p1.0_800ev.dat')
#!gvim spftot_exp_kpt_1_19_bd_1_4_s1.0_p1.0_800ev_np1.dat
```

Now I can plot the stored arrays.

```python
plt.plot(sf_c[:,0], sf_c[:,1], label='1-pole cumulant')
plt.plot(sf_gw[:,0], sf_gw[:,1], label='GW')
plt.xlim(-50, 0)
plt.ylim(0, 300)
plt.title("Bulk Si - Spectral function - ib=1, ikpt=1")
plt.xlabel("Energy (eV)")
plt.grid(); plt.legend(loc='best')
```

# Creating a PDF document

I can create a PDF version of this notebook from itself, using the command line:

```python
!jupyter-nbconvert --to pdf cumulant-to-pdf.ipynb
```

```python
pwd
```

```python
!xpdf cumulant-to-pdf.pdf
```

```python

```