Path: blob/main/latex-templates/templates/neuroscience/neural_network.tex
75 views
unlisted
\documentclass[a4paper, 11pt]{report}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{siunitx}6\usepackage{booktabs}7\usepackage{xcolor}8\usepackage[makestderr]{pythontex}910\definecolor{excitatory}{RGB}{52, 152, 219}11\definecolor{inhibitory}{RGB}{231, 76, 60}12\definecolor{activity}{RGB}{46, 204, 113}1314\title{Spiking Neural Networks:\\15Population Dynamics and Synchronization}16\author{Department of Neuroscience\\Technical Report NS-2024-003}17\date{\today}1819\begin{document}20\maketitle2122\begin{abstract}23This report presents a comprehensive analysis of spiking neural network dynamics. We implement leaky integrate-and-fire neurons, analyze population synchronization, compute spike train statistics, examine balanced excitation-inhibition, and investigate network oscillations. All simulations use PythonTeX for reproducibility.24\end{abstract}2526\tableofcontents2728\chapter{Introduction}2930The leaky integrate-and-fire (LIF) neuron model:31\begin{equation}32\tau_m \frac{dV}{dt} = -(V - V_{rest}) + R_m I_{syn}33\end{equation}3435When $V \geq V_{th}$: emit spike and reset to $V_{reset}$.3637\section{Network Connectivity}38Synaptic current:39\begin{equation}40I_{syn}(t) = \sum_j w_j \sum_k \delta(t - t_j^k - d)41\end{equation}4243\begin{pycode}44import numpy as np45import matplotlib.pyplot as plt46from scipy import signal47plt.rc('text', usetex=True)48plt.rc('font', family='serif')4950np.random.seed(42)5152# LIF parameters53tau_m = 20.0 # Membrane time constant (ms)54V_rest = -65.0 # Resting potential (mV)55V_th = -50.0 # Threshold (mV)56V_reset = -70.0 # Reset potential (mV)57R_m = 10.0 # Membrane resistance (MOhm)58tau_syn = 5.0 # Synaptic time constant (ms)5960# Network parameters61N = 200 # Total neurons62N_exc = int(0.8 * N)63N_inh = N - N_exc64p_conn = 0.1 # Connection probability65w_exc = 0.3 # Excitatory weight66w_inh = -1.2 # Inhibitory weight6768# Simulation parameters69dt = 0.1 # Time step (ms)70T = 1000 # Total time (ms)71n_steps = int(T / dt)7273# Initialize74V = V_rest + 5 * np.random.randn(N)75is_exc = np.arange(N) < N_exc7677# Create connectivity78W = np.zeros((N, N))79for i in range(N):80for j in range(N):81if i != j and np.random.rand() < p_conn:82W[i, j] = w_exc if is_exc[j] else w_inh8384# External input85I_ext = 15.08687# Recording88spike_times = [[] for _ in range(N)]89V_record = np.zeros((N, n_steps))90I_syn = np.zeros(N)9192# Simulation93for step in range(n_steps):94t = step * dt95I_syn *= np.exp(-dt / tau_syn)9697dV = dt / tau_m * (-(V - V_rest) + R_m * (I_ext + I_syn))98V += dV99100spiked = V >= V_th101for n in np.where(spiked)[0]:102spike_times[n].append(t)103I_syn += W[:, n]104105V[spiked] = V_reset106V_record[:, step] = V107\end{pycode}108109\chapter{Network Activity}110111\begin{pycode}112fig, axes = plt.subplots(2, 3, figsize=(14, 8))113114# Raster plot115ax = axes[0, 0]116for i in range(N):117if len(spike_times[i]) > 0:118color = '#3498db' if is_exc[i] else '#e74c3c'119ax.scatter(spike_times[i], [i]*len(spike_times[i]), c=color, s=0.5, marker='|')120121ax.set_xlabel('Time (ms)')122ax.set_ylabel('Neuron Index')123ax.set_title('Spike Raster')124ax.set_xlim([0, T])125126# Population rate127bin_size = 5128n_bins = int(T / bin_size)129pop_rate = np.zeros(n_bins)130exc_rate = np.zeros(n_bins)131inh_rate = np.zeros(n_bins)132133for i, st in enumerate(spike_times):134for t in st:135bin_idx = min(int(t / bin_size), n_bins - 1)136pop_rate[bin_idx] += 1137if is_exc[i]:138exc_rate[bin_idx] += 1139else:140inh_rate[bin_idx] += 1141142pop_rate = pop_rate / N * 1000 / bin_size143exc_rate = exc_rate / N_exc * 1000 / bin_size144inh_rate = inh_rate / N_inh * 1000 / bin_size145146t_bins = np.arange(0, T, bin_size)147ax = axes[0, 1]148ax.plot(t_bins, pop_rate, 'k-', linewidth=1, label='Total')149ax.plot(t_bins, exc_rate, 'b-', linewidth=1, alpha=0.7, label='Exc')150ax.plot(t_bins, inh_rate, 'r-', linewidth=1, alpha=0.7, label='Inh')151ax.set_xlabel('Time (ms)')152ax.set_ylabel('Rate (Hz)')153ax.set_title('Population Activity')154ax.legend(fontsize=8)155ax.grid(True, alpha=0.3)156157# Firing rate distribution158firing_rates = [len(st) / (T / 1000) for st in spike_times]159mean_rate = np.mean(firing_rates)160161ax = axes[0, 2]162ax.hist(firing_rates, bins=30, alpha=0.7, color='green', edgecolor='black')163ax.axvline(mean_rate, color='r', linewidth=2, label=f'Mean: {mean_rate:.1f} Hz')164ax.set_xlabel('Firing Rate (Hz)')165ax.set_ylabel('Count')166ax.set_title('Rate Distribution')167ax.legend()168ax.grid(True, alpha=0.3)169170# ISI distribution171all_isis = []172for st in spike_times:173if len(st) > 1:174all_isis.extend(np.diff(st))175176ax = axes[1, 0]177if len(all_isis) > 0:178ax.hist(all_isis, bins=50, alpha=0.7, color='purple', edgecolor='black')179cv_isi = np.std(all_isis) / np.mean(all_isis)180ax.axvline(np.mean(all_isis), color='r', linewidth=2)181ax.set_xlabel('ISI (ms)')182ax.set_ylabel('Count')183ax.set_title(f'ISI Distribution (CV={cv_isi:.2f})')184ax.grid(True, alpha=0.3)185186# Cross-correlogram187ax = axes[1, 1]188# Select two excitatory neurons with spikes189active_exc = [i for i in range(N_exc) if len(spike_times[i]) > 10]190if len(active_exc) >= 2:191st1 = np.array(spike_times[active_exc[0]])192st2 = np.array(spike_times[active_exc[1]])193194lags = np.arange(-50, 51, 1)195cc = np.zeros(len(lags))196for i, lag in enumerate(lags):197for t1 in st1:198cc[i] += np.sum(np.abs(st2 - t1 - lag) < 0.5)199200ax.bar(lags, cc, width=1, color='#3498db', alpha=0.7)201ax.set_xlabel('Lag (ms)')202ax.set_ylabel('Coincidences')203ax.set_title('Cross-Correlogram')204ax.grid(True, alpha=0.3)205206# Power spectrum of population activity207ax = axes[1, 2]208f_pop, psd_pop = signal.welch(pop_rate, fs=1000/bin_size, nperseg=len(pop_rate)//2)209ax.semilogy(f_pop, psd_pop, 'k-', linewidth=1.5)210ax.set_xlabel('Frequency (Hz)')211ax.set_ylabel('Power')212ax.set_title('Population Power Spectrum')213ax.grid(True, alpha=0.3, which='both')214ax.set_xlim([0, 100])215216plt.tight_layout()217plt.savefig('snn_activity.pdf', dpi=150, bbox_inches='tight')218plt.close()219220rate_exc = np.mean([firing_rates[i] for i in range(N_exc)])221rate_inh = np.mean([firing_rates[i] for i in range(N_exc, N)])222\end{pycode}223224\begin{figure}[htbp]225\centering226\includegraphics[width=0.95\textwidth]{snn_activity.pdf}227\caption{Network activity: (a) raster, (b) population rate, (c) rate distribution, (d) ISI, (e) cross-correlogram, (f) power spectrum.}228\end{figure}229230\chapter{Synchronization Analysis}231232\begin{pycode}233fig, axes = plt.subplots(2, 3, figsize=(14, 8))234235# Synchrony measure (population spike count variance)236sync_window = 5 # ms237n_sync_bins = int(T / sync_window)238spike_counts = np.zeros(n_sync_bins)239240for st in spike_times:241for t in st:242bin_idx = min(int(t / sync_window), n_sync_bins - 1)243spike_counts[bin_idx] += 1244245fano_factor = np.var(spike_counts) / np.mean(spike_counts) if np.mean(spike_counts) > 0 else 0246247ax = axes[0, 0]248ax.hist(spike_counts, bins=30, alpha=0.7, color='orange', edgecolor='black')249ax.axvline(np.mean(spike_counts), color='r', linewidth=2, label=f'FF={fano_factor:.2f}')250ax.set_xlabel('Spikes per bin')251ax.set_ylabel('Count')252ax.set_title('Spike Count Distribution')253ax.legend()254ax.grid(True, alpha=0.3)255256# Membrane potential traces257ax = axes[0, 1]258sample_neurons = [0, N_exc//2, N_exc, N_exc + N_inh//2]259t_plot = np.arange(0, T, dt)260for i, n in enumerate(sample_neurons[:3]):261label = 'Exc' if n < N_exc else 'Inh'262color = '#3498db' if n < N_exc else '#e74c3c'263ax.plot(t_plot[:1000], V_record[n, :1000] - i*30, color=color, linewidth=0.5, label=label)264265ax.set_xlabel('Time (ms)')266ax.set_ylabel('$V_m$ (offset)')267ax.set_title('Membrane Potentials')268ax.set_xlim([0, 100])269ax.grid(True, alpha=0.3)270271# E/I balance272ax = axes[0, 2]273t_balance = t_bins[10:]274ei_ratio = exc_rate[10:] / (inh_rate[10:] + 1)275ax.plot(t_balance, ei_ratio, 'g-', linewidth=1)276ax.axhline(N_exc/N_inh, color='r', linestyle='--', alpha=0.5, label='Expected')277ax.set_xlabel('Time (ms)')278ax.set_ylabel('E/I Rate Ratio')279ax.set_title('Excitation-Inhibition Balance')280ax.legend()281ax.grid(True, alpha=0.3)282283# Input-output curves284ax = axes[1, 0]285I_test = np.linspace(5, 30, 10)286rates_test = []287288for I in I_test:289V_test = V_rest * np.ones(20)290spikes_test = 0291for _ in range(int(500/dt)):292dV = dt / tau_m * (-(V_test - V_rest) + R_m * I)293V_test += dV294spiked = V_test >= V_th295spikes_test += np.sum(spiked)296V_test[spiked] = V_reset297rates_test.append(spikes_test / 20 / 0.5)298299ax.plot(I_test, rates_test, 'ko-', markersize=6)300ax.set_xlabel('Input Current ($\\mu$A/cm$^2$)')301ax.set_ylabel('Firing Rate (Hz)')302ax.set_title('Single Neuron F-I Curve')303ax.grid(True, alpha=0.3)304305# Pairwise correlations306ax = axes[1, 1]307n_pairs = 50308correlations = []309for _ in range(n_pairs):310i, j = np.random.choice(N_exc, 2, replace=False)311if len(spike_times[i]) > 5 and len(spike_times[j]) > 5:312# Simple correlation measure313bins_ij = np.arange(0, T, 10)314counts_i = np.histogram(spike_times[i], bins=bins_ij)[0]315counts_j = np.histogram(spike_times[j], bins=bins_ij)[0]316if np.std(counts_i) > 0 and np.std(counts_j) > 0:317corr = np.corrcoef(counts_i, counts_j)[0, 1]318correlations.append(corr)319320if len(correlations) > 0:321ax.hist(correlations, bins=20, alpha=0.7, color='#9b59b6', edgecolor='black')322ax.axvline(np.mean(correlations), color='r', linewidth=2)323ax.set_xlabel('Correlation')324ax.set_ylabel('Count')325ax.set_title('Pairwise Spike Correlations')326ax.grid(True, alpha=0.3)327328mean_corr = np.mean(correlations) if len(correlations) > 0 else 0329330# Connectivity matrix visualization331ax = axes[1, 2]332im = ax.imshow(W[:50, :50], cmap='RdBu', aspect='auto', vmin=-2, vmax=2)333ax.set_xlabel('Presynaptic')334ax.set_ylabel('Postsynaptic')335ax.set_title('Weight Matrix (50x50)')336plt.colorbar(im, ax=ax)337338plt.tight_layout()339plt.savefig('snn_sync.pdf', dpi=150, bbox_inches='tight')340plt.close()341\end{pycode}342343\begin{figure}[htbp]344\centering345\includegraphics[width=0.95\textwidth]{snn_sync.pdf}346\caption{Synchronization: (a) spike counts, (b) membrane traces, (c) E/I balance, (d) F-I curve, (e) correlations, (f) connectivity.}347\end{figure}348349\chapter{Numerical Results}350351\begin{pycode}352results_table = [353('Network size', f'{N}', 'neurons'),354('Mean firing rate', f'{mean_rate:.1f}', 'Hz'),355('Excitatory rate', f'{rate_exc:.1f}', 'Hz'),356('Inhibitory rate', f'{rate_inh:.1f}', 'Hz'),357('ISI CV', f'{cv_isi:.2f}', ''),358('Mean pairwise correlation', f'{mean_corr:.3f}', ''),359]360\end{pycode}361362\begin{table}[htbp]363\centering364\caption{Spiking network results}365\begin{tabular}{@{}lcc@{}}366\toprule367Parameter & Value & Units \\368\midrule369\py{results_table[0][0]} & \py{results_table[0][1]} & \py{results_table[0][2]} \\370\py{results_table[1][0]} & \py{results_table[1][1]} & \py{results_table[1][2]} \\371\py{results_table[2][0]} & \py{results_table[2][1]} & \py{results_table[2][2]} \\372\py{results_table[3][0]} & \py{results_table[3][1]} & \py{results_table[3][2]} \\373\py{results_table[4][0]} & \py{results_table[4][1]} & \py{results_table[4][2]} \\374\py{results_table[5][0]} & \py{results_table[5][1]} & \py{results_table[5][2]} \\375\bottomrule376\end{tabular}377\end{table}378379\chapter{Conclusions}380381\begin{enumerate}382\item Balanced E/I maintains stable asynchronous activity383\item ISI CV near 1 indicates irregular Poisson-like firing384\item Sparse connectivity produces weak correlations385\item Population oscillations emerge from network interactions386\item Inhibition shapes temporal precision of excitation387\item LIF networks capture essential cortical dynamics388\end{enumerate}389390\end{document}391392393