{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {},
"id": "view-in-github"
},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"# Tutorial 1: LIF Neuron Part II\n",
"\n",
"**Week 0, Day 2: Python Workshop 2**\n",
"\n",
"**By Neuromatch Academy**\n",
"\n",
"**Content creators:** Marco Brigham and the [CCNSS](https://www.ccnss.org/) team\n",
"\n",
"**Content reviewers:** Michael Waskom, Karolina Stosio, Spiros Chavlis\n",
"\n",
"**Production editors:** Ella Batty, Spiros Chavlis"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"## Tutorial objectives\n",
"\n",
"We learned basic Python and NumPy concepts in the previous tutorial. These new and efficient coding techniques can be applied repeatedly in tutorials from the NMA course, and elsewhere.\n",
"\n",
"In this tutorial, we'll introduce spikes in our LIF neuron and evaluate the refractory period's effect in spiking dynamics!"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Setup"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Install and import feedback gadget\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Install and import feedback gadget\n",
"\n",
"!pip3 install vibecheck datatops --quiet\n",
"\n",
"from vibecheck import DatatopsContentReviewContainer\n",
"def content_review(notebook_section: str):\n",
" return DatatopsContentReviewContainer(\n",
" \"\", # No text prompt\n",
" notebook_section,\n",
" {\n",
" \"url\": \"https://pmyvdlilci.execute-api.us-east-1.amazonaws.com/klab\",\n",
" \"name\": \"neuromatch-precourse\",\n",
" \"user_key\": \"8zxfvwxw\",\n",
" },\n",
" ).render()\n",
"\n",
"\n",
"feedback_prefix = \"W0D2_T1\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "both",
"execution": {}
},
"outputs": [],
"source": [
"# Imports\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Figure settings\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Figure settings\n",
"import logging\n",
"logging.getLogger('matplotlib.font_manager').disabled = True\n",
"import ipywidgets as widgets # interactive display\n",
"%config InlineBackend.figure_format = 'retina'\n",
"plt.style.use(\"https://raw.githubusercontent.com/NeuromatchAcademy/content-creation/main/nma.mplstyle\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Helper functions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Helper functions\n",
"\n",
"t_max = 150e-3 # second\n",
"dt = 1e-3 # second\n",
"tau = 20e-3 # second\n",
"el = -60e-3 # milivolt\n",
"vr = -70e-3 # milivolt\n",
"vth = -50e-3 # milivolt\n",
"r = 100e6 # ohm\n",
"i_mean = 25e-11 # ampere\n",
"\n",
"\n",
"def plot_all(t_range, v, raster=None, spikes=None, spikes_mean=None):\n",
" \"\"\"\n",
" Plots Time evolution for\n",
" (1) multiple realizations of membrane potential\n",
" (2) spikes\n",
" (3) mean spike rate (optional)\n",
"\n",
" Args:\n",
" t_range (numpy array of floats)\n",
" range of time steps for the plots of shape (time steps)\n",
"\n",
" v (numpy array of floats)\n",
" membrane potential values of shape (neurons, time steps)\n",
"\n",
" raster (numpy array of floats)\n",
" spike raster of shape (neurons, time steps)\n",
"\n",
" spikes (dictionary of lists)\n",
" list with spike times indexed by neuron number\n",
"\n",
" spikes_mean (numpy array of floats)\n",
" Mean spike rate for spikes as dictionary\n",
"\n",
" Returns:\n",
" Nothing.\n",
" \"\"\"\n",
"\n",
" v_mean = np.mean(v, axis=0)\n",
" fig_w, fig_h = plt.rcParams['figure.figsize']\n",
" plt.figure(figsize=(fig_w, 1.5 * fig_h))\n",
"\n",
" ax1 = plt.subplot(3, 1, 1)\n",
" for j in range(n):\n",
" plt.scatter(t_range, v[j], color=\"k\", marker=\".\", alpha=0.01)\n",
" plt.plot(t_range, v_mean, 'C1', alpha=0.8, linewidth=3)\n",
" plt.xticks([])\n",
" plt.ylabel(r'$V_m$ (V)')\n",
"\n",
" if raster is not None:\n",
" plt.subplot(3, 1, 2)\n",
" spikes_mean = np.mean(raster, axis=0)\n",
" plt.imshow(raster, cmap='Greys', origin='lower', aspect='auto')\n",
"\n",
" else:\n",
" plt.subplot(3, 1, 2, sharex=ax1)\n",
" for j in range(n):\n",
" times = np.array(spikes[j])\n",
" plt.scatter(times, j * np.ones_like(times), color=\"C0\", marker=\".\", alpha=0.2)\n",
"\n",
" plt.xticks([])\n",
" plt.ylabel('neuron')\n",
"\n",
" if spikes_mean is not None:\n",
" plt.subplot(3, 1, 3, sharex=ax1)\n",
" plt.plot(t_range, spikes_mean)\n",
" plt.xlabel('time (s)')\n",
" plt.ylabel('rate (Hz)')\n",
"\n",
" plt.tight_layout()\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 1: Histograms\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 1: Histograms\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 1: Histograms\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'J24tne-IwvY'), ('Bilibili', 'BV1GC4y1h7Ex')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Histograms_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"\n",
"
\n",
"\n",
"\n",
"
\n",
"\n",
"Another important statistic is the sample [histogram](https://en.wikipedia.org/wiki/Histogram). For our LIF neuron, it provides an approximate representation of the distribution of membrane potential $V_m(t)$ at time $t=t_k\\in[0,t_{max}]$. For $N$ realizations $V\\left(t_k\\right)$ and $J$ bins is given by:\n",
"\n",
"
\n",
"\\begin{equation}\n",
"N = \\sum_{j=1}^{J} m_j\n",
"\\end{equation}\n",
"
\n",
"\n",
"where $m_j$ is a function that counts the number of samples $V\\left(t_k\\right)$ that fall into bin $j$.\n",
"\n",
"The function `plt.hist(data, nbins)` plots an histogram of `data` in `nbins` bins. The argument `label` defines a label for `data` and `plt.legend()` adds all labels to the plot.\n",
"\n",
"```python\n",
"plt.hist(data, bins, label='my data')\n",
"plt.legend()\n",
"plt.show()\n",
"```\n",
"\n",
"The parameters `histtype='stepfilled'` and `linewidth=0` may improve histogram appearance (depending on taste). You can read more about [different histtype settings](https://matplotlib.org/gallery/statistics/histogram_histtypes.html).\n",
"\n",
"The function `plt.hist` returns the `pdf`, `bins`, and `patches` with the histogram bins, the edges of the bins, and the individual patches used to create the histogram.\n",
"\n",
"```python\n",
"pdf, bins, patches = plt.hist(data, bins)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 2: Nano recap of histograms\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 2: Nano recap of histograms\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', '71f1J98zj80'), ('Bilibili', 'BV1Zv411B7mD')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_histograms_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 1: Plotting a histogram\n",
"\n",
"Plot an histogram of $J=50$ bins of $N=10000$ realizations of $V(t)$ for $t=t_{max}/10$ and $t=t_{max}$.\n",
"\n",
"We'll make a small correction in the definition of `t_range` to ensure increments of `dt` by using `np.arange` instead of `np.linspace`.\n",
"\n",
"```python\n",
"numpy.arange(start, stop, step)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"#################################################\n",
"## TODO for students: fill out code to plot histogram ##\n",
"# Fill out code and comment or remove the next line\n",
"raise NotImplementedError(\"Student exercise: You need to plot histogram\")\n",
"#################################################\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize t_range, step_end, n, v_n, i and nbins\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 10000\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"nbins = 50\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r * i[:, step])\n",
"\n",
"# Initialize the figure\n",
"plt.figure()\n",
"plt.ylabel('Frequency')\n",
"plt.xlabel('$V_m$ (V)')\n",
"\n",
"# Plot a histogram at t_max/10 (add labels and parameters histtype='stepfilled' and linewidth=0)\n",
"plt.hist(...)\n",
"\n",
"# Plot a histogram at t_max (add labels and parameters histtype='stepfilled' and linewidth=0)\n",
"plt.hist(...)\n",
"\n",
"# Add legend\n",
"plt.legend()\n",
"plt.show()\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize t_range, step_end, n, v_n, i and nbins\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 10000\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"nbins = 50\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r * i[:, step])\n",
"\n",
"# Initialize the figure\n",
"with plt.xkcd():\n",
" plt.figure()\n",
" plt.ylabel('Frequency')\n",
" plt.xlabel('$V_m$ (V)')\n",
"\n",
" # Plot a histogram at t_max/10 (add labels and parameters histtype='stepfilled' and linewidth=0)\n",
" plt.hist(v_n[:,int(step_end / 10)], nbins, histtype='stepfilled',\n",
" linewidth=0, label=f't={t_max / 10} s')\n",
"\n",
" # Plot a histogram at t_max (add labels and parameters histtype='stepfilled' and linewidth=0)\n",
" plt.hist(v_n[:, -1], nbins, histtype='stepfilled',\n",
" linewidth=0, label=f't={t_max} s')\n",
" # Add legend\n",
" plt.legend()\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Plotting_a_histogram_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 2: Dictionaries & introducing spikes\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 3: Dictionaries & introducing spikes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 3: Dictionaries & introducing spikes\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'ioKkiukDkNg'), ('Bilibili', 'BV1H54y1q7oS')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Dictionaries_&_introducing_spikes_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"A spike occures whenever $V(t)$ crosses $V_{th}$. In that case, a spike is recorded, and $V(t)$ resets to $V_{reset}$ value. This is summarized in the *reset condition*:\n",
"\n",
"\\begin{equation}\n",
"V(t) = V_{reset}\\quad \\text{ if } V(t)\\geq V_{th}\n",
"\\end{equation}\n",
"\n",
"For more information about spikes or action potentials see [here](https://en.wikipedia.org/wiki/Action_potential) and [here](https://www.khanacademy.org/test-prep/mcat/organ-systems/neuron-membrane-potentials/a/neuron-action-potentials-the-creation-of-a-brain-signal).\n",
"\n",
"\n",
"\n",
"
\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 4: Nano recap of dictionaries\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 4: Nano recap of dictionaries\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'nvpHtzuZggg'), ('Bilibili', 'BV1GC4y1h7hi')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_dictionaries_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 2: Adding spiking to the LIF neuron\n",
"\n",
"Insert the reset condition, and collect the spike times of each realization in a dictionary variable `spikes`, with $N=500$.\n",
"\n",
"We've used `plt.plot` for plotting lines and also for plotting dots at `(x,y)` coordinates, which is a [scatter plot](https://en.wikipedia.org/wiki/Scatter_plot). From here on, we'll use use `plt.plot` for plotting lines and for scatter plots: `plt.scatter`.\n",
"\n",
"```python\n",
"plt.scatter(x, y, color=\"k\", marker=\".\")\n",
"```\n",
"\n",
"A *raster plot* represents spikes from multiple neurons by plotting dots at spike times from neuron `j` at plot height `j`, i.e.\n",
"\n",
"```python\n",
"plt.scatter(spike_times, j*np.ones_like(spike_times))\n",
"```\n",
"\n",
"\n",
"
\n",
"\n",
"\n",
"In this exercise, we use `plt.subplot` for multiple plots in the same figure. These plots can share the same `x` or `y` axis by specifying the parameter `sharex` or `sharey`. Add `plt.tight_layout()` at the end to automatically adjust subplot parameters to fit the figure area better. Please see the example below for a row of two plots sharing axis `y`.\n",
"\n",
"```python\n",
"# initialize the figure\n",
"plt.figure()\n",
"\n",
"# collect axis of 1st figure in ax1\n",
"ax1 = plt.subplot(1, 2, 1)\n",
"plt.plot(t_range, my_data_left)\n",
"plt.ylabel('ylabel')\n",
"\n",
"# share axis x with 1st figure\n",
"plt.subplot(1, 2, 2, sharey=ax1)\n",
"plt.plot(t_range, my_data_right)\n",
"\n",
"# automatically adjust subplot parameters to figure\n",
"plt.tight_layout()\n",
"plt.show()\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize spikes and spikes_n\n",
"spikes = {j: [] for j in range(n)}\n",
"spikes_n = np.zeros([step_end])\n",
"\n",
"#################################################\n",
"## TODO for students: add spikes to LIF neuron ##\n",
"# Fill out function and remove\n",
"raise NotImplementedError(\"Student exercise: add spikes to LIF neuron\")\n",
"#################################################\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Loop over simulations\n",
" for j in range(n):\n",
"\n",
" # Check if voltage above threshold\n",
" if v_n[j, step] >= vth:\n",
"\n",
" # Reset to reset voltage\n",
" v_n[j, step] = ...\n",
"\n",
" # Add this spike time\n",
" spikes[j] += ...\n",
"\n",
" # Add spike count to this step\n",
" spikes_n[step] += ...\n",
"\n",
"# Collect mean Vm and mean spiking rate\n",
"v_mean = np.mean(v_n, axis=0)\n",
"spikes_mean = spikes_n / n\n",
"\n",
"# Initialize the figure\n",
"plt.figure()\n",
"\n",
"# Plot simulations and sample mean\n",
"ax1 = plt.subplot(3, 1, 1)\n",
"for j in range(n):\n",
" plt.scatter(t_range, v_n[j], color=\"k\", marker=\".\", alpha=0.01)\n",
"plt.plot(t_range, v_mean, 'C1', alpha=0.8, linewidth=3)\n",
"plt.ylabel('$V_m$ (V)')\n",
"\n",
"# Plot spikes\n",
"plt.subplot(3, 1, 2, sharex=ax1)\n",
"# for each neuron j: collect spike times and plot them at height j\n",
"for j in range(n):\n",
" times = ...\n",
" plt.scatter(...)\n",
"\n",
"plt.ylabel('neuron')\n",
"\n",
"# Plot firing rate\n",
"plt.subplot(3, 1, 3, sharex=ax1)\n",
"plt.plot(t_range, spikes_mean)\n",
"plt.xlabel('time (s)')\n",
"plt.ylabel('rate (Hz)')\n",
"\n",
"plt.tight_layout()\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize spikes and spikes_n\n",
"spikes = {j: [] for j in range(n)}\n",
"spikes_n = np.zeros([step_end])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Loop over simulations\n",
" for j in range(n):\n",
"\n",
" # Check if voltage above threshold\n",
" if v_n[j, step] >= vth:\n",
"\n",
" # Reset to reset voltage\n",
" v_n[j, step] = vr\n",
"\n",
" # Add this spike time\n",
" spikes[j] += [t]\n",
"\n",
" # Add spike count to this step\n",
" spikes_n[step] += 1\n",
"\n",
"# Collect mean Vm and mean spiking rate\n",
"v_mean = np.mean(v_n, axis=0)\n",
"spikes_mean = spikes_n / n\n",
"\n",
"with plt.xkcd():\n",
" # Initialize the figure\n",
" plt.figure()\n",
"\n",
" # Plot simulations and sample mean\n",
" ax1 = plt.subplot(3, 1, 1)\n",
" for j in range(n):\n",
" plt.scatter(t_range, v_n[j], color=\"k\", marker=\".\", alpha=0.01)\n",
" plt.plot(t_range, v_mean, 'C1', alpha=0.8, linewidth=3)\n",
" plt.ylabel('$V_m$ (V)')\n",
"\n",
" # Plot spikes\n",
" plt.subplot(3, 1, 2, sharex=ax1)\n",
" # for each neuron j: collect spike times and plot them at height j\n",
" for j in range(n):\n",
" times = np.array(spikes[j])\n",
" plt.scatter(times, j * np.ones_like(times), color=\"C0\", marker=\".\", alpha=0.2)\n",
"\n",
" plt.ylabel('neuron')\n",
"\n",
" # Plot firing rate\n",
" plt.subplot(3, 1, 3, sharex=ax1)\n",
" plt.plot(t_range, spikes_mean)\n",
" plt.xlabel('time (s)')\n",
" plt.ylabel('rate (Hz)')\n",
"\n",
" plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Adding_spiking_to_the_LIF_neuron_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 3: Boolean indexes\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 5: Boolean indexes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 5: Boolean indexes\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'G0C1v848I9Y'), ('Bilibili', 'BV1W54y1q7eh')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Boolean_indexes_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"Boolean arrays can index NumPy arrays to select a subset of elements (also works with lists of booleans).\n",
"\n",
"The boolean array itself can be initiated by a condition, as shown in the example below.\n",
"\n",
"```python\n",
"a = np.array([1, 2, 3])\n",
"b = a>=2\n",
"print(b)\n",
"--> [False True True]\n",
"\n",
"print(a[b])\n",
"--> [2 3]\n",
"\n",
"print(a[a>=2])\n",
"--> [2 3]\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 6: Nano recap of Boolean indexes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 6: Nano recap of Boolean indexes\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'dFPgO5wnyLc'), ('Bilibili', 'BV1W54y1q7gi')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_Boolean_indexes_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 3: Using Boolean indexing\n",
"\n",
"We can avoid looping all neurons in each time step by identifying with boolean arrays the indexes of neurons that spiked in the previous step.\n",
"\n",
"In the example below, `v_rest` is a boolean numpy array with `True` in each index of `v_n` with value `vr` at time index `step`:\n",
"\n",
"```python\n",
"v_rest = (v_n[:,step] == vr)\n",
"print(v_n[v_rest,step])\n",
" --> [vr, ..., vr]\n",
"```\n",
"\n",
"The function `np.where` returns indexes of boolean arrays with `True` values.\n",
"\n",
"You may use the helper function `plot_all` that implements the figure from the previous exercise."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"help(plot_all)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize spikes and spikes_n\n",
"spikes = {j: [] for j in range(n)}\n",
"spikes_n = np.zeros([step_end])\n",
"\n",
"#################################################\n",
"## TODO for students: use Boolean indexing ##\n",
"# Fill out function and remove\n",
"raise NotImplementedError(\"Student exercise: using Boolean indexing\")\n",
"#################################################\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = ...\n",
"\n",
" # Set relevant values of v_n to resting potential using spiked\n",
" v_n[spiked,step] = ...\n",
"\n",
" # Collect spike times\n",
" for j in np.where(spiked)[0]:\n",
" spikes[j] += [t]\n",
" spikes_n[step] += 1\n",
"\n",
"# Collect mean spiking rate\n",
"spikes_mean = spikes_n / n\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"plot_all(t_range, v_n, spikes=spikes, spikes_mean=spikes_mean)\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize spikes and spikes_n\n",
"spikes = {j: [] for j in range(n)}\n",
"spikes_n = np.zeros([step_end])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = (v_n[:,step] >= vth)\n",
"\n",
" # Set relevant values of v_n to resting potential using spiked\n",
" v_n[spiked,step] = vr\n",
"\n",
" # Collect spike times\n",
" for j in np.where(spiked)[0]:\n",
" spikes[j] += [t]\n",
" spikes_n[step] += 1\n",
"\n",
"# Collect mean spiking rate\n",
"spikes_mean = spikes_n / n\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"with plt.xkcd():\n",
" plot_all(t_range, v_n, spikes=spikes, spikes_mean=spikes_mean)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Using_Boolean_indexing_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 4: Making a binary raster plot\n",
"\n",
"A *binary raster plot* represents spike times as `1`s in a binary grid initialized with `0`s. We start with a numpy array `raster` of zeros with shape `(neurons, time steps)`, and represent a spike from neuron `5` at time step `20` as `raster(5,20)=1`, for example.\n",
"\n",
"The *binary raster plot* is much more efficient than the previous method by plotting the numpy array `raster` as an image:\n",
"\n",
"```python\n",
"plt.imshow(raster, cmap='Greys', origin='lower', aspect='auto')\n",
"```\n",
"\n",
"**Suggestions**\n",
"* At each time step:\n",
" * Initialize boolean numpy array `spiked` with $V_n(t)\\geq V_{th}$\n",
" * Set to `vr` indexes of `v_n` using `spiked`\n",
" * Set to `1` indexes of numpy array `raster` using `spiked`"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"#################################################\n",
"## TODO for students: make a raster ##\n",
"# Fill out function and remove\n",
"raise NotImplementedError(\"Student exercise: make a raster \")\n",
"#################################################\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = (v_n[:,step] >= vth)\n",
"\n",
" # Set relevant values of v_n to v_reset using spiked\n",
" v_n[spiked,step] = vr\n",
"\n",
" # Set relevant elements in raster to 1 using spiked\n",
" raster[spiked,step] = ...\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"plot_all(t_range, v_n, raster)\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = (v_n[:,step] >= vth)\n",
"\n",
" # Set relevant values of v_n to v_reset using spiked\n",
" v_n[spiked,step] = vr\n",
"\n",
" # Set relevant elements in raster to 1 using spiked\n",
" raster[spiked,step] = 1.\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"with plt.xkcd():\n",
" plot_all(t_range, v_n, raster)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Making_a_binary_raster_plot_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 4: Refractory period\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 7: Refractory period\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 7: Refractory period\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'KVNdbRY5-nY'), ('Bilibili', 'BV1MT4y1E79j')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Refractory_period_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"The absolute refractory period is a time interval in the order of a few milliseconds during which synaptic input will not lead to a 2nd spike, no matter how strong. This effect is due to the biophysics of the neuron membrane channels, and you can read more about absolute and relative refractory period [here](https://content.byui.edu/file/a236934c-3c60-4fe9-90aa-d343b3e3a640/1/module5/readings/refractory_periods.html) and [here](https://en.wikipedia.org/wiki/Refractory_period_(physiology)).\n",
"\n",
"\n",
"
\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 8: Nano recap of refractory period\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 8: Nano recap of refractory period\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'DOoftC0JU2k'), ('Bilibili', 'BV1pA411e7je')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_refractory_period_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 5: Investigating refactory periods\n",
"\n",
"Investigate the effect of (absolute) refractory period $t_{ref}$ on the evolution of output rate $\\lambda(t)$. Add refractory period $t_{ref}=10$ ms after each spike, during which $V(t)$ is clamped to $V_{reset}$."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"#################################################\n",
"## TODO for students: add refactory period ##\n",
"# Fill out function and remove\n",
"raise NotImplementedError(\"Student exercise: add refactory period \")\n",
"#################################################\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize t_ref and last_spike\n",
"t_ref = 0.01\n",
"last_spike = -t_ref * np.ones([n])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = (v_n[:,step] >= vth)\n",
"\n",
" # Set relevant values of v_n to v_reset using spiked\n",
" v_n[spiked,step] = vr\n",
"\n",
" # Set relevant elements in raster to 1 using spiked\n",
" raster[spiked,step] = 1.\n",
"\n",
" # Initialize boolean numpy array clamped using last_spike, t and t_ref\n",
" clamped = ...\n",
"\n",
" # Reset clamped neurons to vr using clamped\n",
" v_n[clamped,step] = ...\n",
"\n",
" # Update numpy array last_spike with time t for spiking neurons\n",
" last_spike[spiked] = ...\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"plot_all(t_range, v_n, raster)\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize t_ref and last_spike\n",
"t_ref = 0.01\n",
"last_spike = -t_ref * np.ones([n])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step == 0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:, step] = v_n[:, step - 1] + (dt / tau) * (el - v_n[:, step - 1] + r*i[:, step])\n",
"\n",
" # Initialize boolean numpy array `spiked` with v_n > v_thr\n",
" spiked = (v_n[:,step] >= vth)\n",
"\n",
" # Set relevant values of v_n to v_reset using spiked\n",
" v_n[spiked,step] = vr\n",
"\n",
" # Set relevant elements in raster to 1 using spiked\n",
" raster[spiked,step] = 1.\n",
"\n",
" # Initialize boolean numpy array clamped using last_spike, t and t_ref\n",
" clamped = (last_spike + t_ref > t)\n",
"\n",
" # Reset clamped neurons to vr using clamped\n",
" v_n[clamped,step] = vr\n",
"\n",
" # Update numpy array last_spike with time t for spiking neurons\n",
" last_spike[spiked] = t\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"with plt.xkcd():\n",
" plot_all(t_range, v_n, raster)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Investigating_refractory_periods_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Interactive Demo 1: Random refractory period\n",
"\n",
"In the following interactive demo, we will investigate the effect of random refractory periods. We will use random refactory periods $t_{ref}$ with\n",
"$t_{ref} = \\mu + \\sigma\\,\\mathcal{N}$, where $\\mathcal{N}$ is the [normal distribution](https://en.wikipedia.org/wiki/Normal_distribution), $\\mu=0.01$ and $\\sigma=0.007$.\n",
"\n",
"Refractory period samples `t_ref` of size `n` is initialized with `np.random.normal`. We clip negative values to `0` with boolean indexes. (Why?) You can double-click the cell to see the hidden code.\n",
"\n",
"You can play with the parameters mu and sigma and visualize the resulting simulation. What is the effect of different $\\sigma$ values?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" Execute this cell to enable the demo\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @markdown Execute this cell to enable the demo\n",
"\n",
"def random_ref_period(mu, sigma):\n",
" # set random number generator\n",
" np.random.seed(2020)\n",
"\n",
" # initialize step_end, t_range, n, v_n, syn and raster\n",
" t_range = np.arange(0, t_max, dt)\n",
" step_end = len(t_range)\n",
" n = 500\n",
" v_n = el * np.ones([n,step_end])\n",
" syn = i_mean * (1 + 0.1*(t_max/dt)**(0.5)*(2*np.random.random([n,step_end])-1))\n",
" raster = np.zeros([n,step_end])\n",
"\n",
" # initialize t_ref and last_spike\n",
" t_ref = mu + sigma*np.random.normal(size=n)\n",
" t_ref[t_ref<0] = 0\n",
" last_spike = -t_ref * np.ones([n])\n",
"\n",
" # loop time steps\n",
" for step, t in enumerate(t_range):\n",
" if step==0:\n",
" continue\n",
"\n",
" v_n[:,step] = v_n[:,step-1] + dt/tau * (el - v_n[:,step-1] + r*syn[:,step])\n",
"\n",
" # boolean array spiked indexes neurons with v>=vth\n",
" spiked = (v_n[:,step] >= vth)\n",
" v_n[spiked,step] = vr\n",
" raster[spiked,step] = 1.\n",
"\n",
" # boolean array clamped indexes refractory neurons\n",
" clamped = (last_spike + t_ref > t)\n",
" v_n[clamped,step] = vr\n",
" last_spike[spiked] = t\n",
"\n",
" # plot multiple realizations of Vm, spikes and mean spike rate\n",
" plot_all(t_range, v_n, raster)\n",
"\n",
" # plot histogram of t_ref\n",
" plt.figure(figsize=(8,4))\n",
" plt.hist(t_ref, bins=32, histtype='stepfilled', linewidth=0, color='C1')\n",
" plt.xlabel(r'$t_{ref}$ (s)')\n",
" plt.ylabel('count')\n",
" plt.tight_layout()\n",
"\n",
"_ = widgets.interact(random_ref_period, mu = (0.01, 0.05, 0.01), \\\n",
" sigma = (0.001, 0.02, 0.001))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Random_refractory_period_Interactive_Demo\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 5: Using functions\n",
"Running key parts of your code inside functions improves your coding narrative by making it clearer and more flexible to future changes."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 9: Functions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 9: Functions\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'mkf8riqCjS4'), ('Bilibili', 'BV1sa4y1a7pq')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Functions_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 10: Nano recap of functions\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 10: Nano recap of functions\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', '0An_NnVWY_Q'), ('Bilibili', 'BV1pz411v74H')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_functions_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 6: Rewriting code with functions\n",
"\n",
"We will now re-organize parts of the code from the previous exercise with functions. You need to complete the function `spike_clamp()` to update $V(t)$ and deal with spiking and refractoriness"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"def ode_step(v, i, dt):\n",
" \"\"\"\n",
" Evolves membrane potential by one step of discrete time integration\n",
"\n",
" Args:\n",
" v (numpy array of floats)\n",
" membrane potential at previous time step of shape (neurons)\n",
"\n",
" i (numpy array of floats)\n",
" synaptic input at current time step of shape (neurons)\n",
"\n",
" dt (float)\n",
" time step increment\n",
"\n",
" Returns:\n",
" v (numpy array of floats)\n",
" membrane potential at current time step of shape (neurons)\n",
" \"\"\"\n",
" v = v + dt/tau * (el - v + r*i)\n",
"\n",
" return v\n",
"\n",
"\n",
"def spike_clamp(v, delta_spike):\n",
" \"\"\"\n",
" Resets membrane potential of neurons if v>= vth\n",
" and clamps to vr if interval of time since last spike < t_ref\n",
"\n",
" Args:\n",
" v (numpy array of floats)\n",
" membrane potential of shape (neurons)\n",
"\n",
" delta_spike (numpy array of floats)\n",
" interval of time since last spike of shape (neurons)\n",
"\n",
" Returns:\n",
" v (numpy array of floats)\n",
" membrane potential of shape (neurons)\n",
" spiked (numpy array of floats)\n",
" boolean array of neurons that spiked of shape (neurons)\n",
" \"\"\"\n",
"\n",
" ####################################################\n",
" ## TODO for students: complete spike_clamp\n",
" # Fill out function and remove\n",
" raise NotImplementedError(\"Student exercise: complete spike_clamp\")\n",
" ####################################################\n",
" # Boolean array spiked indexes neurons with v>=vth\n",
" spiked = ...\n",
" v[spiked] = ...\n",
"\n",
" # Boolean array clamped indexes refractory neurons\n",
" clamped = ...\n",
" v[clamped] = ...\n",
"\n",
" return v, spiked\n",
"\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize t_ref and last_spike\n",
"mu = 0.01\n",
"sigma = 0.007\n",
"t_ref = mu + sigma*np.random.normal(size=n)\n",
"t_ref[t_ref<0] = 0\n",
"last_spike = -t_ref * np.ones([n])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:,step] = ode_step(v_n[:,step-1], i[:,step], dt)\n",
"\n",
" # Reset membrane potential and clamp\n",
" v_n[:,step], spiked = spike_clamp(v_n[:,step], t - last_spike)\n",
"\n",
" # Update raster and last_spike\n",
" raster[spiked,step] = 1.\n",
" last_spike[spiked] = t\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"plot_all(t_range, v_n, raster)\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"def ode_step(v, i, dt):\n",
" \"\"\"\n",
" Evolves membrane potential by one step of discrete time integration\n",
"\n",
" Args:\n",
" v (numpy array of floats)\n",
" membrane potential at previous time step of shape (neurons)\n",
"\n",
" i (numpy array of floats)\n",
" synaptic input at current time step of shape (neurons)\n",
"\n",
" dt (float)\n",
" time step increment\n",
"\n",
" Returns:\n",
" v (numpy array of floats)\n",
" membrane potential at current time step of shape (neurons)\n",
" \"\"\"\n",
" v = v + dt/tau * (el - v + r*i)\n",
"\n",
" return v\n",
"\n",
"\n",
"def spike_clamp(v, delta_spike):\n",
" \"\"\"\n",
" Resets membrane potential of neurons if v>= vth\n",
" and clamps to vr if interval of time since last spike < t_ref\n",
"\n",
" Args:\n",
" v (numpy array of floats)\n",
" membrane potential of shape (neurons)\n",
"\n",
" delta_spike (numpy array of floats)\n",
" interval of time since last spike of shape (neurons)\n",
"\n",
" Returns:\n",
" v (numpy array of floats)\n",
" membrane potential of shape (neurons)\n",
" spiked (numpy array of floats)\n",
" boolean array of neurons that spiked of shape (neurons)\n",
" \"\"\"\n",
"\n",
" # Boolean array spiked indexes neurons with v>=vth\n",
" spiked = (v >= vth)\n",
" v[spiked] = vr\n",
"\n",
" # Boolean array clamped indexes refractory neurons\n",
" clamped = (t_ref > delta_spike)\n",
" v[clamped] = vr\n",
"\n",
" return v, spiked\n",
"\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize t_ref and last_spike\n",
"mu = 0.01\n",
"sigma = 0.007\n",
"t_ref = mu + sigma*np.random.normal(size=n)\n",
"t_ref[t_ref<0] = 0\n",
"last_spike = -t_ref * np.ones([n])\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Skip first iteration\n",
" if step==0:\n",
" continue\n",
"\n",
" # Compute v_n\n",
" v_n[:,step] = ode_step(v_n[:,step-1], i[:,step], dt)\n",
"\n",
" # Reset membrane potential and clamp\n",
" v_n[:,step], spiked = spike_clamp(v_n[:,step], t - last_spike)\n",
"\n",
" # Update raster and last_spike\n",
" raster[spiked,step] = 1.\n",
" last_spike[spiked] = t\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"with plt.xkcd():\n",
" plot_all(t_range, v_n, raster)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Rewriting_code_with_functions_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Section 6: Using classes\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 11: Classes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 11: Classes\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'dGRESMoNPh0'), ('Bilibili', 'BV1hz411v7ne')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Classes_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"Using classes helps with code reuse and reliability. Well-designed classes are like black boxes, receiving inputs and providing expected outputs. The details of how the class processes inputs and produces outputs are unimportant.\n",
"\n",
"See additional details here: [A Beginner's Python Tutorial/Classes](https://en.wikibooks.org/wiki/A_Beginner%27s_Python_Tutorial/Classes)\n",
"\n",
"*Attributes* are variables internal to the class, and *methods* are functions internal to the class."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 12: Nano recap of classes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 12: Nano recap of classes\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', '4YNpMpVW2qs'), ('Bilibili', 'BV12V41167yu')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Nano_recap_of_classes_Video\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"## Coding Exercise 7: Making a LIF class\n",
"In this exercise we'll practice with Python classes by implementing `LIFNeurons`, a class that evolves and keeps state of multiple realizations of LIF neurons.\n",
"\n",
"Several attributes are used to keep state of our neurons:\n",
"\n",
"```python\n",
"self.v current membrane potential\n",
"self.spiked neurons that spiked\n",
"self.last_spike last spike time of each neuron\n",
"self.t running time of the simulation\n",
"self.steps simulation step\n",
"```\n",
"\n",
"There is a single method:\n",
"\n",
"```python\n",
"self.ode_step() performs single step discrete time integration\n",
" for provided synaptic current and dt\n",
"```\n",
"\n",
"Complete the spike and clamp part of method `self.ode_step` (should be similar to function `spike_and_clamp` seen before)."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"execution": {}
},
"source": [
"```python\n",
"# Simulation class\n",
"class LIFNeurons:\n",
" \"\"\"\n",
" Keeps track of membrane potential for multiple realizations of LIF neuron,\n",
" and performs single step discrete time integration.\n",
" \"\"\"\n",
" def __init__(self, n, t_ref_mu=0.01, t_ref_sigma=0.002,\n",
" tau=20e-3, el=-60e-3, vr=-70e-3, vth=-50e-3, r=100e6):\n",
"\n",
" # Neuron count\n",
" self.n = n\n",
"\n",
" # Neuron parameters\n",
" self.tau = tau # second\n",
" self.el = el # milivolt\n",
" self.vr = vr # milivolt\n",
" self.vth = vth # milivolt\n",
" self.r = r # ohm\n",
"\n",
" # Initializes refractory period distribution\n",
" self.t_ref_mu = t_ref_mu\n",
" self.t_ref_sigma = t_ref_sigma\n",
" self.t_ref = self.t_ref_mu + self.t_ref_sigma * np.random.normal(size=self.n)\n",
" self.t_ref[self.t_ref<0] = 0\n",
"\n",
" # State variables\n",
" self.v = self.el * np.ones(self.n)\n",
" self.spiked = self.v >= self.vth\n",
" self.last_spike = -self.t_ref * np.ones([self.n])\n",
" self.t = 0.\n",
" self.steps = 0\n",
"\n",
"\n",
" def ode_step(self, dt, i):\n",
"\n",
" # Update running time and steps\n",
" self.t += dt\n",
" self.steps += 1\n",
"\n",
" # One step of discrete time integration of dt\n",
" self.v = self.v + dt / self.tau * (self.el - self.v + self.r * i)\n",
"\n",
" ####################################################\n",
" ## TODO for students: complete the `ode_step` method\n",
" # Fill out function and remove\n",
" raise NotImplementedError(\"Student exercise: complete the ode_step method\")\n",
" ####################################################\n",
"\n",
" # Spike and clamp\n",
" self.spiked = ...\n",
" self.v[self.spiked] = ...\n",
" self.last_spike[self.spiked] = ...\n",
" clamped = ...\n",
" self.v[clamped] = ...\n",
"\n",
" self.last_spike[self.spiked] = ...\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize neurons\n",
"neurons = LIFNeurons(n)\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Call ode_step method\n",
" neurons.ode_step(dt, i[:,step])\n",
"\n",
" # Log v_n and spike history\n",
" v_n[:,step] = neurons.v\n",
" raster[neurons.spiked, step] = 1.\n",
"\n",
"# Report running time and steps\n",
"print(f'Ran for {neurons.t:.3}s in {neurons.steps} steps.')\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"plot_all(t_range, v_n, raster)\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"execution": {}
},
"outputs": [],
"source": [
"# to_remove solution\n",
"\n",
"# Simulation class\n",
"class LIFNeurons:\n",
" \"\"\"\n",
" Keeps track of membrane potential for multiple realizations of LIF neuron,\n",
" and performs single step discrete time integration.\n",
" \"\"\"\n",
" def __init__(self, n, t_ref_mu=0.01, t_ref_sigma=0.002,\n",
" tau=20e-3, el=-60e-3, vr=-70e-3, vth=-50e-3, r=100e6):\n",
"\n",
" # Neuron count\n",
" self.n = n\n",
"\n",
" # Neuron parameters\n",
" self.tau = tau # second\n",
" self.el = el # milivolt\n",
" self.vr = vr # milivolt\n",
" self.vth = vth # milivolt\n",
" self.r = r # ohm\n",
"\n",
" # Initializes refractory period distribution\n",
" self.t_ref_mu = t_ref_mu\n",
" self.t_ref_sigma = t_ref_sigma\n",
" self.t_ref = self.t_ref_mu + self.t_ref_sigma * np.random.normal(size=self.n)\n",
" self.t_ref[self.t_ref<0] = 0\n",
"\n",
" # State variables\n",
" self.v = self.el * np.ones(self.n)\n",
" self.spiked = self.v >= self.vth\n",
" self.last_spike = -self.t_ref * np.ones([self.n])\n",
" self.t = 0.\n",
" self.steps = 0\n",
"\n",
"\n",
" def ode_step(self, dt, i):\n",
"\n",
" # Update running time and steps\n",
" self.t += dt\n",
" self.steps += 1\n",
"\n",
" # One step of discrete time integration of dt\n",
" self.v = self.v + dt / self.tau * (self.el - self.v + self.r * i)\n",
"\n",
" # Spike and clamp\n",
" self.spiked = (self.v >= self.vth)\n",
" self.v[self.spiked] = self.vr\n",
" self.last_spike[self.spiked] = self.t\n",
" clamped = (self.t_ref > self.t-self.last_spike)\n",
" self.v[clamped] = self.vr\n",
"\n",
" self.last_spike[self.spiked] = self.t\n",
"\n",
"# Set random number generator\n",
"np.random.seed(2020)\n",
"\n",
"# Initialize step_end, t_range, n, v_n and i\n",
"t_range = np.arange(0, t_max, dt)\n",
"step_end = len(t_range)\n",
"n = 500\n",
"v_n = el * np.ones([n, step_end])\n",
"i = i_mean * (1 + 0.1 * (t_max / dt)**(0.5) * (2 * np.random.random([n, step_end]) - 1))\n",
"\n",
"# Initialize binary numpy array for raster plot\n",
"raster = np.zeros([n,step_end])\n",
"\n",
"# Initialize neurons\n",
"neurons = LIFNeurons(n)\n",
"\n",
"# Loop over time steps\n",
"for step, t in enumerate(t_range):\n",
"\n",
" # Call ode_step method\n",
" neurons.ode_step(dt, i[:,step])\n",
"\n",
" # Log v_n and spike history\n",
" v_n[:,step] = neurons.v\n",
" raster[neurons.spiked, step] = 1.\n",
"\n",
"# Report running time and steps\n",
"print(f'Ran for {neurons.t:.3}s in {neurons.steps} steps.')\n",
"\n",
"# Plot multiple realizations of Vm, spikes and mean spike rate\n",
"with plt.xkcd():\n",
" plot_all(t_range, v_n, raster)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Making_a_LIF_class_Exercise\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"execution": {}
},
"source": [
"---\n",
"# Summary\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Video 12: Last concepts & recap\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"remove-input"
]
},
"outputs": [],
"source": [
"# @title Video 12: Last concepts & recap\n",
"from ipywidgets import widgets\n",
"from IPython.display import YouTubeVideo\n",
"from IPython.display import IFrame\n",
"from IPython.display import display\n",
"\n",
"\n",
"class PlayVideo(IFrame):\n",
" def __init__(self, id, source, page=1, width=400, height=300, **kwargs):\n",
" self.id = id\n",
" if source == 'Bilibili':\n",
" src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'\n",
" elif source == 'Osf':\n",
" src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'\n",
" super(PlayVideo, self).__init__(src, width, height, **kwargs)\n",
"\n",
"\n",
"def display_videos(video_ids, W=400, H=300, fs=1):\n",
" tab_contents = []\n",
" for i, video_id in enumerate(video_ids):\n",
" out = widgets.Output()\n",
" with out:\n",
" if video_ids[i][0] == 'Youtube':\n",
" video = YouTubeVideo(id=video_ids[i][1], width=W,\n",
" height=H, fs=fs, rel=0)\n",
" print(f'Video available at https://youtube.com/watch?v={video.id}')\n",
" else:\n",
" video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,\n",
" height=H, fs=fs, autoplay=False)\n",
" if video_ids[i][0] == 'Bilibili':\n",
" print(f'Video available at https://www.bilibili.com/video/{video.id}')\n",
" elif video_ids[i][0] == 'Osf':\n",
" print(f'Video available at https://osf.io/{video.id}')\n",
" display(video)\n",
" tab_contents.append(out)\n",
" return tab_contents\n",
"\n",
"\n",
"video_ids = [('Youtube', 'h4mSJBPocPo'), ('Bilibili', 'BV1MC4y1h7eA')]\n",
"tab_contents = display_videos(video_ids, W=730, H=410)\n",
"tabs = widgets.Tab()\n",
"tabs.children = tab_contents\n",
"for i in range(len(tab_contents)):\n",
" tabs.set_title(i, video_ids[i][0])\n",
"display(tabs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Submit your feedback\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"execution": {},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"# @title Submit your feedback\n",
"content_review(f\"{feedback_prefix}_Last_concepts_&_recap_Video\")"
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"include_colab_link": true,
"name": "W0D2_Tutorial1",
"provenance": [],
"toc_visible": true
},
"kernel": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.17"
}
},
"nbformat": 4,
"nbformat_minor": 0
}