{ "cells": [ { "cell_type": "markdown", "id": "bc470a90", "metadata": { "colab_type": "text", "execution": {}, "id": "view-in-github" }, "source": [ "\"Open Β  \"Open" ] }, { "cell_type": "markdown", "id": "08842220", "metadata": { "execution": {} }, "source": [ "# Tutorial 2: Differential Equations\n", "\n", "**Week 0, Day 4: Calculus**\n", "\n", "**By Neuromatch Academy**\n", "\n", "**Content creators:** John S Butler, Arvind Kumar with help from Rebecca Brady\n", "\n", "**Content reviewers:** Swapnil Kumar, Sirisha Sripada, Matthew McCann, Tessy Tom\n", "\n", "**Production editors:** Matthew McCann, Ella Batty" ] }, { "cell_type": "markdown", "id": "7d80998b", "metadata": { "execution": {} }, "source": [ "---\n", "# Tutorial Objectives\n", "\n", "*Estimated timing of tutorial: 45 minutes*\n", "\n", "A great deal of neuroscience can be modeled using differential equations, from gating channels to single neurons to a network of neurons to blood flow to behavior. A simple way to think about differential equations is they are equations that describe how something changes.\n", "\n", "The most famous of these in neuroscience is the Nobel Prize-winning Hodgkin-Huxley equation, which describes a neuron by modeling the gating of each axon. But we will not start there; we will start a few steps back.\n", "\n", "Differential Equations are mathematical equations that describe how something like a population or a neuron changes over time. Differential equations are so useful because they can generalize a process such that one equation can be used to describe many different outcomes.\n", "The general form of a first-order differential equation is:\n", "\n", "\\begin{equation}\n", "\\frac{d}{dt}y(t) = f\\left( t,y(t) \\right)\n", "\\end{equation}\n", "\n", "which can be read as \"the change in a process $y$ over time $t$ is a function $f$ of time $t$ and itself $y$\". This might initially seem like a paradox as you are using a process $y$ you want to know about to describe itself, a bit like the MC Escher drawing of two hands painting [each other](https://en.wikipedia.org/wiki/Drawing_Hands). But that is the beauty of mathematics - this can be solved sometimes, and when it cannot be solved exactly, we can use numerical methods to estimate the answer (as we will see in the next tutorial).\n", "\n", "In this tutorial, we will see how __differential equations are motivated by observations of physical responses__. We will break down the population differential equation, then the integrate and fire model, which leads nicely into raster plots and frequency-current curves to rate models.\n", "\n", "**Steps:**\n", "- Get an intuitive understanding of a linear population differential equation (humans, not neurons)\n", "- Visualize the relationship between the change in population and the population\n", "- Breakdown the Leaky Integrate and Fire (LIF) differential equation\n", "- Code the exact solution of a LIF for a constant input\n", "- Visualize and listen to the response of the LIF for different inputs" ] }, { "cell_type": "markdown", "id": "kq1fWqhyNJ3i", "metadata": { "execution": {} }, "source": [ "---\n", "# Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Install and import feedback gadget\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1-6UCje-fVs6", "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 = \"W0D4_T2\"" ] }, { "cell_type": "code", "execution_count": null, "id": "a5985801", "metadata": { "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, "id": "d40abcd8", "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 IPython.display as ipd\n", "from matplotlib import gridspec\n", "import ipywidgets as widgets # interactive display\n", "%config InlineBackend.figure_format = 'retina'\n", "# use NMA plot style\n", "plt.style.use(\"https://raw.githubusercontent.com/NeuromatchAcademy/content-creation/main/nma.mplstyle\")\n", "my_layout = widgets.Layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting Functions\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6XJo_I6_NbJg", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Plotting Functions\n", "\n", "def plot_dPdt(alpha=.3):\n", " \"\"\" Plots change in population over time\n", " Args:\n", " alpha: Birth Rate\n", " Returns:\n", " A figure two panel figure\n", " left panel: change in population as a function of population\n", " right panel: membrane potential as a function of time\n", " \"\"\"\n", "\n", " with plt.xkcd():\n", " time=np.arange(0, 10 ,0.01)\n", " fig = plt.figure(figsize=(12,4))\n", " gs = gridspec.GridSpec(1, 2)\n", "\n", " ## dpdt as a fucntion of p\n", " plt.subplot(gs[0])\n", " plt.plot(np.exp(alpha*time), alpha*np.exp(alpha*time))\n", " plt.xlabel(r'Population $p(t)$ (millions)')\n", " plt.ylabel(r'$\\frac{d}{dt}p(t)=\\alpha p(t)$')\n", "\n", " ## p exact solution\n", " plt.subplot(gs[1])\n", " plt.plot(time, np.exp(alpha*time))\n", " plt.ylabel(r'Population $p(t)$ (millions)')\n", " plt.xlabel('time (years)')\n", " plt.show()\n", "\n", "\n", "def plot_V_no_input(V_reset=-75):\n", " \"\"\"\n", " Args:\n", " V_reset: Reset Potential\n", " Returns:\n", " A figure two panel figure\n", " left panel: change in membrane potential as a function of membrane potential\n", " right panel: membrane potential as a function of time\n", " \"\"\"\n", " E_L=-75\n", " tau_m=10\n", " t=np.arange(0,100,0.01)\n", " V= E_L+(V_reset-E_L)*np.exp(-(t)/tau_m)\n", " V_range=np.arange(-90,0,1)\n", " dVdt=-(V_range-E_L)/tau_m\n", "\n", " with plt.xkcd():\n", " time=np.arange(0, 10, 0.01)\n", " fig = plt.figure(figsize=(12, 4))\n", " gs = gridspec.GridSpec(1, 2)\n", "\n", " plt.subplot(gs[0])\n", " plt.plot(V_range,dVdt)\n", " plt.hlines(0,min(V_range),max(V_range), colors='black', linestyles='dashed')\n", " plt.vlines(-75, min(dVdt), max(dVdt), colors='black', linestyles='dashed')\n", " plt.plot(V_reset,-(V_reset - E_L)/tau_m, 'o', label=r'$V_{reset}$')\n", " plt.text(-50, 1, 'Positive')\n", " plt.text(-50, -2, 'Negative')\n", " plt.text(E_L - 1, max(dVdt), r'$E_L$')\n", " plt.legend()\n", " plt.xlabel('Membrane Potential V (mV)')\n", " plt.ylabel(r'$\\frac{dV}{dt}=\\frac{-(V(t)-E_L)}{\\tau_m}$')\n", "\n", " plt.subplot(gs[1])\n", " plt.plot(t,V)\n", " plt.plot(t[0],V_reset,'o')\n", " plt.ylabel(r'Membrane Potential $V(t)$ (mV)')\n", " plt.xlabel('time (ms)')\n", " plt.ylim([-95, -60])\n", "\n", " plt.show()\n", "\n", "\n", "## LIF PLOT\n", "def plot_IF(t, V,I,Spike_time):\n", " \"\"\"\n", " Args:\n", " t : time\n", " V : membrane Voltage\n", " I : Input\n", " Spike_time : Spike_times\n", " Returns:\n", " figure with three panels\n", " top panel: Input as a function of time\n", " middle panel: membrane potential as a function of time\n", " bottom panel: Raster plot\n", " \"\"\"\n", "\n", " with plt.xkcd():\n", " fig = plt.figure(figsize=(12, 4))\n", " gs = gridspec.GridSpec(3, 1, height_ratios=[1, 4, 1])\n", "\n", " # PLOT OF INPUT\n", " plt.subplot(gs[0])\n", " plt.ylabel(r'$I_e(nA)$')\n", " plt.yticks(rotation=45)\n", " plt.hlines(I,min(t),max(t),'g')\n", " plt.ylim((2, 4))\n", " plt.xlim((-50, 1000))\n", "\n", " # PLOT OF ACTIVITY\n", " plt.subplot(gs[1])\n", " plt.plot(t,V)\n", " plt.xlim((-50, 1000))\n", " plt.ylabel(r'$V(t)$(mV)')\n", "\n", " # PLOT OF SPIKES\n", " plt.subplot(gs[2])\n", " plt.ylabel(r'Spike')\n", " plt.yticks([])\n", " plt.scatter(Spike_time, 1 * np.ones(len(Spike_time)), color=\"grey\", marker=\".\")\n", " plt.xlim((-50, 1000))\n", " plt.xlabel('time(ms)')\n", " plt.show()\n", "\n", "\n", "## Plotting the differential Equation\n", "def plot_dVdt(I=0):\n", " \"\"\"\n", " Args:\n", " I : Input Current\n", " Returns:\n", " figure of change in membrane potential as a function of membrane potential\n", " \"\"\"\n", "\n", " with plt.xkcd():\n", " E_L = -75\n", " tau_m = 10\n", " V = np.arange(-85, 0, 1)\n", " g_L = 10.\n", " fig = plt.figure(figsize=(6, 4))\n", "\n", " plt.plot(V,(-(V-E_L) + I*10) / tau_m)\n", " plt.hlines(0, min(V), max(V), colors='black', linestyles='dashed')\n", " plt.xlabel('V (mV)')\n", " plt.ylabel(r'$\\frac{dV}{dt}$')\n", " plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Helper Functions\n" ] }, { "cell_type": "code", "execution_count": null, "id": "d88bf487", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Helper Functions\n", "\n", "## EXACT SOLUTION OF LIF\n", "def Exact_Integrate_and_Fire(I,t):\n", " \"\"\"\n", " Args:\n", " I : Input Current\n", " t : time\n", " Returns:\n", " Spike : Spike Count\n", " Spike_time : Spike time\n", " V_exact : Exact membrane potential\n", " \"\"\"\n", "\n", " Spike = 0\n", " tau_m = 10\n", " R = 10\n", " t_isi = 0\n", " V_reset = E_L = -75\n", " V_exact = V_reset * np.ones(len(t))\n", " V_th = -50\n", " Spike_time = []\n", "\n", " for i in range(0, len(t)):\n", "\n", " V_exact[i] = E_L + R*I + (V_reset - E_L - R*I) * np.exp(-(t[i]-t_isi)/tau_m)\n", "\n", " # Threshold Reset\n", " if V_exact[i] > V_th:\n", " V_exact[i-1] = 0\n", " V_exact[i] = V_reset\n", " t_isi = t[i]\n", " Spike = Spike+1\n", " Spike_time = np.append(Spike_time, t[i])\n", "\n", " return Spike, Spike_time, V_exact" ] }, { "cell_type": "markdown", "id": "O2Q5d9D2hLZq", "metadata": { "execution": {} }, "source": [ "---\n", "# Section 0: Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Video 1: Why do we care about differential equations?\n" ] }, { "cell_type": "code", "execution_count": null, "id": "DIZfUH4rQNSR", "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 1: Why do we care about differential equations?\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', 'LhX-mUd8lPo'), ('Bilibili', 'BV1v64y197bW')]\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, "id": "Or-35flNhTBo", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Why_do_we_care_about_differential_equations_Video\")" ] }, { "cell_type": "markdown", "id": "AcwNWVdsQgLz", "metadata": { "execution": {} }, "source": [ "---\n", "# Section 1: Population differential equation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Video 2: Population differential equation\n" ] }, { "cell_type": "code", "execution_count": null, "id": "LRwtiFPVQ3Pz", "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 2: Population differential equation\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', 'czgGyoUsRoQ'), ('Bilibili', 'BV1pg41137CU')]\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, "id": "WLss9oNLhSI-", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Population_differential_equation_Video\")" ] }, { "cell_type": "markdown", "id": "286b4298", "metadata": { "execution": {} }, "source": [ "This video covers our first example of a differential equation: a differential equation which models the change in population.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "To get an intuitive feel of a differential equations, we will start with a population differential equation, which models the change in population [1], that is human population not neurons, we will get to neurons later. Mathematically it is written like:\n", "\\begin{align*}\n", "\\\\\n", "\\frac{d}{dt}\\,p(t) &= \\alpha p(t),\\\\\n", "\\end{align*}\n", "\n", "where $p(t)$ is the population of the world and $\\alpha$ is a parameter representing birth rate.\n", "\n", "Another way of thinking about the models is that the equation\n", "\\begin{align*}\n", "\\\\\n", "\\frac{d}{dt}\\,p(t) &= \\alpha p(t),\\\\\n", "\\text{can be written as:}\\\\\n", "\\text{\"Change in Population\"} &= \\text{ \"Birth rate times Current population.\"}\n", "\\end{align*}\n", "\n", "The equation is saying something reasonable maybe not the perfect model but a good start.\n", "
" ] }, { "cell_type": "markdown", "id": "njGc4mc4ASXb", "metadata": { "execution": {} }, "source": [ "### Think! 1.1: Interpretating the behavior of a linear population equation\n", "\n", "Using the plot below of change of population $\\frac{d}{dt} p(t) $ as a function of population $p(t)$ with birth-rate $\\alpha=0.3$, discuss the following questions:\n", "\n", "1. Why is the population differential equation known as a linear differential equation?\n", "2. How does population size affect the rate of change of the population?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Execute the code to plot the rate of change of population as a function of population\n" ] }, { "cell_type": "code", "execution_count": null, "id": "8e37083e", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Execute the code to plot the rate of change of population as a function of population\n", "p = np.arange(0, 100, 0.1)\n", "\n", "with plt.xkcd():\n", "\n", " dpdt = 0.3*p\n", " fig = plt.figure(figsize=(6, 4))\n", " plt.plot(p, dpdt)\n", " plt.xlabel(r'Population $p(t)$ (millions)')\n", " plt.ylabel(r'$\\frac{d}{dt}p(t)=\\alpha p(t)$')\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "il7f6AWkXk0B", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_66bcf7d1.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1whSBsnBhbAG", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Interpretating_the_behavior_of_a_linear_population_equation_Discussion\")" ] }, { "cell_type": "markdown", "id": "5846bbb5", "metadata": { "execution": {} }, "source": [ "## Section 1.1: Exact solution of the population equation" ] }, { "cell_type": "markdown", "id": "uqcubpZNBIGl", "metadata": { "execution": {} }, "source": [ "### Section 1.1.1: Initial condition\n", "\n", "The linear population differential equation is an initial value differential equation because we need an initial population value to solve it. Here we will set our initial population at time $0$ to $1$:\n", "\n", "\\begin{equation}\n", "p(0) = 1\n", "\\end{equation}\n", "\n", "Different initial conditions will lead to different answers but will not change the differential equation. This is one of the strengths of a differential equation." ] }, { "cell_type": "markdown", "id": "oYBK4NWYBL5t", "metadata": { "execution": {} }, "source": [ "### Section 1.1.2: Exact Solution\n", "\n", "To calculate the exact solution of a differential equation, we must integrate both sides. Instead of numerical integration (as you delved into in the last tutorial), we will first try to solve the differential equations using analytical integration. As with derivatives, we can find analytical integrals of simple equations by consulting [a list](https://en.wikipedia.org/wiki/Lists_of_integrals). We can then get integrals for more complex equations using some mathematical tricks - the harder the equation, the more obscure the trick.\n", "\n", "The linear population equation\n", "\\begin{equation}\n", "\\frac{d}{dt}p(t) = \\alpha p(t), \\, p(0)=P_0\n", "\\end{equation}\n", "\n", "has the exact solution:\n", "\n", "\\begin{equation}\n", "p(t) = P_0 e^{\\alpha t}.\n", "\\end{equation}\n", "\n", "The exact solution written in words is:\n", "\n", "\\begin{equation}\n", "\\text{\"Population\"} = \\text{\"grows/declines exponentially as a function of time and birth rate\"}.\n", "\\end{equation}\n", "\n", "Most differential equations do not have a known exact solution, so we will show how the solution can be estimated in the next tutorial on numerical methods.\n", "\n", "A small aside: a good deal of progress in mathematics was due to mathematicians writing taunting letters to each other, saying they had a trick to solve something better than everyone else. So do not worry too much about the tricks." ] }, { "cell_type": "markdown", "id": "1257fcc0", "metadata": { "execution": {} }, "source": [ "#### Example Exact Solution of the Population Equation\n", "\n", "Let's consider the population differential equation with a birth rate $\\alpha=0.3$:\n", "\n", "\\begin{equation}\n", "\\frac{d}{dt}p(t) = 0.3p(t)\n", "\\end{equation}\n", "\n", "with the initial condition\n", "\n", "\\begin{equation}\n", "p(0)=1.\n", "\\end{equation}\n", "\n", "It has an exact solution\n", "\n", "\\begin{equation}\n", "p(t)=e^{0.3 t}.\n", "\\end{equation}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Execute code to plot the exact solution\n" ] }, { "cell_type": "code", "execution_count": null, "id": "eeb717b8", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Execute code to plot the exact solution\n", "t = np.arange(0, 10, 0.1) # Time from 0 to 10 years in 0.1 steps\n", "\n", "with plt.xkcd():\n", "\n", " p = np.exp(0.3 * t)\n", "\n", " fig = plt.figure(figsize=(6, 4))\n", " plt.plot(t, p)\n", " plt.ylabel('Population (millions)')\n", " plt.xlabel('time (years)')\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "792841c9", "metadata": { "execution": {} }, "source": [ "## Section 1.2: Parameters of the differential equation\n", "\n", "*Estimated timing to here from start of tutorial: 12 min*\n", "\n", "One of the goals when designing a differential equation is to make it generalizable. This means that the differential equation will give reasonable solutions for different countries with different birth rates $\\alpha$." ] }, { "cell_type": "markdown", "id": "8zD98917MDtW", "metadata": { "execution": {} }, "source": [ "### Interactive Demo 1.2: Parameter Change\n", "\n", "Play with the widget to see the relationship between $\\alpha$ and the population differential equation as a function of population (left-hand side) and the population solution as a function of time (right-hand side). Pay close attention to the transition point from positive to negative.\n", "\n", "How do changing parameters of the population equation affect the outcome?\n", "\n", "1. What happens when $\\alpha < 0$?\n", "2. What happens when $\\alpha > 0$?\n", "3. What happens when $\\alpha = 0$?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget!\n" ] }, { "cell_type": "code", "execution_count": null, "id": "490c0be0", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to enable the widget!\n", "my_layout.width = '450px'\n", "@widgets.interact(\n", " alpha=widgets.FloatSlider(.3, min=-1., max=1., step=.1, layout=my_layout)\n", ")\n", "def Pop_widget(alpha):\n", " plot_dPdt(alpha=alpha)\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "KDgZUHS5YDQp", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_203fd17e.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1AgWDO58hnSv", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Parameter_change_Interactive_Demo\")" ] }, { "cell_type": "markdown", "id": "c3a78544", "metadata": { "execution": {} }, "source": [ "The population differential equation is an over-simplification and has some pronounced limitations:\n", "1. Population growth is not exponential as there are limited resources, so the population will level out at some point.\n", "2. It does not include any external factors on the populations, like weather, predators, and prey.\n", "\n", "These kinds of limitations can be addressed by extending the model.\n", "\n", "While it might not seem that the population equation has direct relevance to neuroscience, a similar equation is used to describe the accumulation of evidence for decision-making. This is known as the Drift Diffusion Model, and you will see it in more detail in the Linear System Day in Neuromatch (W2D2).\n", "\n", "Another differential equation similar to the population equation is the Leaky Integrate and Fire model, which you may have seen in the Python pre-course materials on W0D1 and W0D2. It will turn up later in Neuromatch as well. Below we will delve into the motivation of the differential equation." ] }, { "cell_type": "markdown", "id": "ae82611f", "metadata": { "execution": {} }, "source": [ "---\n", "# Section 2: The leaky integrate and fire (LIF) model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Video 3: The leaky integrate and fire model\n" ] }, { "cell_type": "code", "execution_count": null, "id": "hGBNAqnVVY3E", "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 3: The leaky integrate and fire model\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', 'ZfWO6MLCa1s'), ('Bilibili', 'BV1rb4y1C79n')]\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, "id": "eWa8PXDrh2NB", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_The_leaky_integrate_and_fire_model_Video\")" ] }, { "cell_type": "markdown", "id": "4qgkjHOtWcEL", "metadata": { "execution": {} }, "source": [ "This video covers the Leaky Integrate and Fire model (a linear differential equation which describes the membrane potential of a single neuron).\n", "\n", "
\n", " Click here for text recap of full LIF equation from video \n", "\n", "The Leaky Integrate and Fire Model is a linear differential equation that describes the membrane potential ($V$) of a single neuron which was proposed by Louis Γ‰douard Lapicque in 1907 [2].\n", "\n", "The subthreshold membrane potential dynamics of a LIF neuron is described by\n", "\\begin{align}\n", "\\tau_m\\frac{dV}{dt} = -(V-E_L) + R_mI\\,\n", "\\end{align}\n", "\n", "\n", "where $\\tau_m$ is the time constant, $V$ is the membrane potential, $E_L$ is the resting potential, $R_m$ is membrane resistance, and $I$ is the external input current.\n", "\n", "
\n", "\n", "In the next few sections, we will break down the full LIF equation and then build it back up to get an intuitive feel of the different facets of the differential equation.\n" ] }, { "cell_type": "markdown", "id": "c05087c6", "metadata": { "execution": {} }, "source": [ "## Section 2.1: LIF without input\n", "\n", "*Estimated timing to here from start of tutorial: 18 min*\n", "\n", "As seen in the video, we will first model an LIF neuron without input, which results in the equation:\n", "\n", "\\begin{equation}\n", "\\frac{dV}{dt} = \\frac{-(V-E_L)}{\\tau_m}.\n", "\\end{equation}\n", "\n", "where $\\tau_m$ is the time constant, $V$ is the membrane potential, and $E_L$ is the resting potential.\n", "\n", "
\n", " Click here for further details (from video) \n", "\n", "Removing the input gives the equation\n", "\n", "\\begin{equation}\n", "\\tau_m\\frac{dV}{dt} = -V+E_L,\n", "\\end{equation}\n", "\n", "which can be written in words as:\n", "\n", "\\begin{align}\n", "\\begin{matrix}\\text{\"Time constant multiplied by the} \\\\ \\text{change in membrane potential\"}\\end{matrix}&=\\begin{matrix}\\text{\"Minus Current} \\\\ \\text{membrane potential\"} \\end{matrix}+\n", "\\begin{matrix}\\text{\"resting potential\"}\\end{matrix}.\\\\\n", "\\end{align}\n", "\n", "\n", "The equation can be re-arranged to look even more like the population equation:\n", "\n", "\\begin{align}\n", "\\frac{dV}{dt} &= \\frac{-(V-E_L)}{\\tau_m}.\\\\\n", "\\end{align}\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "XRl1cRt5ppJ1", "metadata": { "execution": {} }, "source": [ "### Think! 2.1: Effect of membrane potential $V$ on the LIF model\n", "\n", "The plot the below shows the change in membrane potential $\\frac{dV}{dt}$ as a function of membrane potential $V$ with the parameters set as:\n", "* `E_L = -75`\n", "* `V_reset = -50`\n", "* `tau_m = 10.`\n", "\n", "1. What is the effect on $\\frac{dV}{dt}$ when $V>-75$ mV?\n", "2. What is the effect on $\\frac{dV}{dt}$ when $V<-75$ mV\n", "3. What is the effect on $\\frac{dV}{dt}$ when $V=-75$ mV?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to plot the relationship between dV/dt and V\n" ] }, { "cell_type": "code", "execution_count": null, "id": "8dd9906c", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to plot the relationship between dV/dt and V\n", "# Parameter definition\n", "E_L = -75\n", "tau_m = 10\n", "\n", "# Range of Values of V\n", "V = np.arange(-90, 0, 1)\n", "dV = -(V - E_L) / tau_m\n", "\n", "with plt.xkcd():\n", "\n", " fig = plt.figure(figsize=(6, 4))\n", " plt.plot(V, dV)\n", " plt.hlines(0, min(V), max(V), colors='black', linestyles='dashed')\n", " plt.vlines(-75, min(dV), max(dV), colors='black', linestyles='dashed')\n", "\n", " plt.text(-50, 1, 'Positive')\n", " plt.text(-50, -2, 'Negative')\n", " plt.text(E_L, max(dV) + 1, r'$E_L$')\n", " plt.xlabel(r'$V(t)$ (mV)')\n", " plt.ylabel(r'$\\frac{dV}{dt}=\\frac{-(V-E_L)}{\\tau_m}$')\n", " plt.ylim(-8, 2)\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "h0MIi-MJYdk5", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_1b9f465e.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "q0zy29uhh8lG", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Effect_of_membrane_potential_Interactive_Demo\")" ] }, { "cell_type": "markdown", "id": "f7bb8882", "metadata": { "execution": {} }, "source": [ "### Section 2.1.1: Exact Solution of the LIF model without input\n", "\n", "The LIF model has the exact solution:\n", "\n", "\\begin{equation}\n", "V(t) = E_L+(V_{reset}-E_L)e^{\\frac{-t}{\\tau_m}}\n", "\\end{equation}\n", "\n", "where $\\tau_m$ is the time constant, $V$ is the membrane potential, $E_L$ is the resting potential, and $V_{reset}$ is the initial membrane potential.\n", "\n", "
\n", " Click here for further details (from video) \n", "\n", "Similar to the population equation, we need an initial membrane potential at time $0$ to solve the LIF model.\n", "\n", "With this equation\n", "\\begin{align}\n", "\\frac{dV}{dt} &= \\frac{-(V-E_L)}{\\tau_m}\\,\\\\\n", "V(0)&=V_{reset},\n", "\\end{align}\n", "where is $V_{reset}$ is called the reset potential.\n", "\n", "The LIF model has the exact solution:\n", "\n", "\\begin{align*}\n", "V(t)=&\\ E_L+(V_{reset}-E_L)e^{\\frac{-t}{\\tau_m}}\\\\\n", "\\text{ which can be written as: }\\\\\n", "\\begin{matrix}\\text{\"Current membrane} \\\\ \\text{potential}\"\\end{matrix}=&\\text{\"Resting potential\"}+\\begin{matrix}\\text{\"Reset potential minus resting potential} \\\\ \\text{times exponential with rate one over time constant.\"}\\end{matrix}\\\\\n", "\\end{align*}\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "31910ff4", "metadata": { "execution": {} }, "source": [ "#### Interactive Demo 2.1.1: Initial Condition $V_{reset}$\n", "\n", "This exercise is to get an intuitive feel of how the different initial conditions $V_{reset}$ impact the differential equation of the LIF and the exact solution for the equation:\n", "\n", "\\begin{equation}\n", "\\frac{dV}{dt} = \\frac{-(V-E_L)}{\\tau_m}\n", "\\end{equation}\n", "\n", "with the parameters set as:\n", "* `E_L = -75,`\n", "* `tau_m = 10.`\n", "\n", "The panel on the left-hand side plots the change in membrane potential $\\frac{dV}{dt}$ as a function of membrane potential $V$ and the right-hand side panel plots the exact solution $V$ as a function of time $t,$ the green dot in both panels is the reset potential $V_{reset}$.\n", "\n", "Pay close attention to when $V_{reset}=E_L=-75$mV.\n", "\n", "1. How does the solution look with initial values of $V_{reset} < -75$?\n", "2. How does the solution look with initial values of $V_{reset} > -75$?\n", "3. How does the solution look with initial values of $V_{reset} = -75$?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget!\n" ] }, { "cell_type": "code", "execution_count": null, "id": "03458759", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "#@markdown Make sure you execute this cell to enable the widget!\n", "my_layout.width = '450px'\n", "@widgets.interact(\n", " V_reset=widgets.FloatSlider(-77., min=-91., max=-61., step=2,\n", " layout=my_layout)\n", ")\n", "\n", "def V_reset_widget(V_reset):\n", " plot_V_no_input(V_reset)" ] }, { "cell_type": "markdown", "id": "IQmxKOuRaVGr", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_90eb4589.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "Q9OCCAmdiVYQ", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Initial_condition_Vreset_Interactive_Demo\")" ] }, { "cell_type": "markdown", "id": "5dcd105e", "metadata": { "execution": {} }, "source": [ "## Section 2.2: LIF with input\n", "\n", "*Estimated timing to here from start of tutorial: 24 min*\n", "\n", "We will re-introduce the input $I$ and membrane resistance $R_m$ giving the original equation:\n", "\n", "\\begin{equation}\n", "\\tau_m\\frac{dV}{dt} = -(V-E_L) + \\color{blue}{R_mI}\n", "\\end{equation}\n", "\n", "The input can be other neurons or sensory information." ] }, { "cell_type": "markdown", "id": "9862281c", "metadata": { "execution": {} }, "source": [ "### Interactive Demo 2.2: The Impact of Input\n", "\n", "The interactive plot below manipulates $I$ in the differential equation.\n", "\n", "- With increasing input, how does the $\\frac{dV}{dt}$ change? How would this impact the solution?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget!\n" ] }, { "cell_type": "code", "execution_count": null, "id": "fc6ce7c7", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to enable the widget!\n", "my_layout.width = '450px'\n", "@widgets.interact(\n", " I=widgets.FloatSlider(3., min=0., max=20., step=2,\n", " layout=my_layout)\n", ")\n", "\n", "def Pop_widget(I):\n", " plot_dVdt(I=I)\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "HSNtznIwY6wA", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_1f82b290.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "2S4tiJYAidIt", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_The_impact_of_Input_Interactive_Demo\")" ] }, { "cell_type": "markdown", "id": "968432a1", "metadata": { "execution": {} }, "source": [ "### Section 2.2.1: LIF exact solution\n", "\n", "The LIF with a constant input has a known exact solution:\n", "\\begin{equation}\n", "V(t) = E_L+R_mI+(V_{reset}-E_L-R_mI)e^{\\frac{-t}{\\tau_m}}\n", "\\end{equation}\n", "\n", "which is written as:\n", "\n", "\\begin{align*}\n", "\\begin{matrix}\\text{\"Current membrane} \\\\ \\text{potential\"}\\end{matrix}=&\\text{\"Resting potential\"}+\\begin{matrix}\\text{\"Reset potential minus resting potential} \\\\ \\text{times exponential with rate one over time constant.\" }\\end{matrix}\\\\\n", "\\end{align*}" ] }, { "cell_type": "markdown", "id": "91b7c87a", "metadata": { "execution": {} }, "source": [ "The plot below shows the exact solution of the membrane potential with the parameters set as:\n", "* `V_reset = -75,`\n", "* `E_L = -75,`\n", "* `tau_m = 10,`\n", "* `R_m = 10,`\n", "* `I = 10.`\n", "\n", "Ask yourself, does the result make biological sense? If not, what would you change? We'll delve into this in the next section" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to see the exact solution\n" ] }, { "cell_type": "code", "execution_count": null, "id": "db5816d4", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to see the exact solution\n", "dt = 0.5\n", "t_rest = 0\n", "\n", "t = np.arange(0, 1000, dt)\n", "\n", "tau_m = 10\n", "R_m = 10\n", "V_reset = E_L = -75\n", "\n", "I = 10\n", "\n", "V = E_L + R_m*I + (V_reset - E_L - R_m*I) * np.exp(-(t)/tau_m)\n", "\n", "with plt.xkcd():\n", "\n", " fig = plt.figure(figsize=(6, 4))\n", " plt.plot(t,V)\n", " plt.ylabel('V (mV)')\n", " plt.xlabel('time (ms)')\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "0a31b8d1", "metadata": { "execution": {} }, "source": [ "## Section 2.3: Maths is one thing, but neuroscience matters\n", "\n", "*Estimated timing to here from start of tutorial: 30 min*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 4: Adding firing to the LIF\n" ] }, { "cell_type": "code", "execution_count": null, "id": "CtWroVVASxG1", "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 4: Adding firing to the LIF\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', 'rLQk-vXRaX0'), ('Bilibili', 'BV1gX4y1P7pZ')]\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, "id": "KljGW16eihTo", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Adding_firing_to_the_LIF_Video\")" ] }, { "cell_type": "markdown", "id": "9f12c68e", "metadata": { "execution": {} }, "source": [ "This video first recaps the introduction of input to the leaky integrate and fire model and then delves into how we add spiking behavior (or firing) to the model.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "While the mathematics of the exact solution is exact, it is not biologically valid as a neuron spikes and definitely does not plateau at a very positive value.\n", "\n", "To model the firing of a spike, we must have a threshold voltage $V_{th}$ such that if the voltage $V(t)$ goes above it, the neuron spikes\n", "$$V(t)>V_{th}.$$\n", "We must record the time of spike $t_{isi}$ and count the number of spikes\n", "$$t_{isi}=t, $$\n", "$$π‘†π‘π‘–π‘˜π‘’=π‘†π‘π‘–π‘˜π‘’+1.$$\n", "Then reset the membrane voltage $V(t)$\n", "$$V(t_{isi} )=V_{Reset}.$$\n", "\n", "To take into account the spike the exact solution becomes:\n", "\\begin{align*}\n", "V(t)=&\\ E_L+R_mI+(V_{reset}-E_L-R_mI)e^{\\frac{-(t-t_{isi})}{\\tau_m}},&\\qquad V(t)V_{th}\\\\\n", "Spike=&Spike+1,&\\\\\n", "t_{isi}=&t,\\\\\n", "\\end{align*}\n", "while this does make the neuron spike, it introduces a discontinuity which is not as elegant mathematically as it could be, but it gets results so that is good.\n", "" ] }, { "cell_type": "markdown", "id": "acc43e8a", "metadata": { "execution": {} }, "source": [ "### Interactive Demo 2.3.1: Input on spikes\n", "\n", "This exercise shows the relationship between the firing rate and the Input for the exact solution `V` of the LIF:\n", "\n", "\\begin{equation}\n", "V(t) = E_L+R_mI+(V_{reset}-E_L-R_mI)e^{\\frac{-(t-t_{isi})}{\\tau_m}},\n", "\\end{equation}\n", "\n", "with the parameters set as:\n", "* `V_reset = -75,`\n", "* `E_L = -75,`\n", "* `tau_m = 10,`\n", "* `R_m = 10.`\n", "\n", "Below is a figure with three panels;\n", "* the top panel is the input, $I,$\n", "* the middle panel is the membrane potential $V(t)$. To illustrate the spike, $V(t)$ is set to $0$ and then reset to $-75$ mV when there is a spike.\n", "* the bottom panel is the raster plot with each dot indicating a spike.\n", "\n", "First, as electrophysiologists normally listen to spikes when conducting experiments, listen to the music of the firing rate for a single value of $I$.\n", "\n", "**Note:** The audio doesn't work in some browsers so don't worry about it if you can't hear anything." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to be able to hear the neuron\n" ] }, { "cell_type": "code", "execution_count": null, "id": "55785e26", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to be able to hear the neuron\n", "I = 3\n", "t = np.arange(0, 1000, dt)\n", "Spike, Spike_time, V = Exact_Integrate_and_Fire(I, t)\n", "\n", "plot_IF(t, V, I, Spike_time)\n", "ipd.Audio(V, rate=len(V))" ] }, { "cell_type": "markdown", "id": "54d5af0e", "metadata": { "execution": {} }, "source": [ "Manipulate the input into the LIF to see the impact of input on the firing pattern (rate).\n", "\n", "* What is the effect of $I$ on spiking?\n", "* Is this biologically valid?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget!\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ebf27a22", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Make sure you execute this cell to enable the widget!\n", "my_layout.width = '450px'\n", "@widgets.interact(\n", " I=widgets.FloatSlider(3, min=2.0, max=4., step=.1,\n", " layout=my_layout)\n", ")\n", "\n", "def Pop_widget(I):\n", " Spike, Spike_time, V = Exact_Integrate_and_Fire(I, t)\n", " plot_IF(t, V, I, Spike_time)" ] }, { "cell_type": "markdown", "id": "GjwI3QF3ZLtY", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D4_Calculus/solutions/W0D4_Tutorial2_Solution_cdb15334.py)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Submit your feedback\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ouprca8SipAs", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Input_on_spikes_Interactive_Demo\")" ] }, { "cell_type": "markdown", "id": "c3cf4d7b", "metadata": { "execution": {} }, "source": [ "## Section 2.4 Firing Rate as a function of Input\n", "\n", "*Estimated timing to here from start of tutorial: 38 min*\n", "\n", "The firing frequency of a neuron plotted as a function of current is called an input-output curve (F–I curve). It is also known as a transfer function, which you came across in the previous tutorial. This function is one of the starting points for the rate model, which extends from modeling single neurons to the firing rate of a collection of neurons.\n", "\n", "By fitting this to a function, we can start to generalize the firing pattern of many neurons, which can be used to build rate models. This will be discussed later in Neuromatch." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " *Execture this cell to visualize the FI curve*\n" ] }, { "cell_type": "code", "execution_count": null, "id": "96bcf5e7", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown *Execture this cell to visualize the FI curve*\n", "I_range = np.arange(2.0, 4.0, 0.1)\n", "Spike_rate = np.ones(len(I_range))\n", "\n", "for i, I in enumerate(I_range):\n", " Spike_rate[i], _, _ = Exact_Integrate_and_Fire(I, t)\n", "\n", "with plt.xkcd():\n", " fig = plt.figure(figsize=(6, 4))\n", " plt.plot(I_range,Spike_rate)\n", " plt.xlabel('Input Current (nA)')\n", " plt.ylabel('Spikes per Second (Hz)')\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "58fdcbe8", "metadata": { "execution": {} }, "source": [ "The LIF model is a very nice differential equation to start with in computational neuroscience as it has been used as a building block for many papers that simulate neuronal response.\n", "\n", "__Strengths of LIF model:__\n", "+ Has an exact solution;\n", "+ Easy to interpret;\n", "+ Great to build network of neurons.\n", "\n", "__Weaknesses of the LIF model:__\n", "- Spiking is a discontinuity;\n", "- Abstraction from biology;\n", "- Cannot generate different spiking patterns." ] }, { "cell_type": "markdown", "id": "697364ff", "metadata": { "execution": {} }, "source": [ "---\n", "# Summary\n", "\n", "*Estimated timing of tutorial: 45 min*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Video 5: Summary\n" ] }, { "cell_type": "code", "execution_count": null, "id": "_Jmpnq0mSihx", "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 5: Summary\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', 'VzwLAW5p4ao'), ('Bilibili', 'BV1jV411x7t9')]\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, "id": "VNxbJDK5iuFA", "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Submit your feedback\n", "content_review(f\"{feedback_prefix}_Summary_Video\")" ] }, { "cell_type": "markdown", "id": "x0H0zhTNWlop", "metadata": { "execution": {} }, "source": [ "In this tutorial, we have seen two differential equations, the population differential equations and the leaky integrate and fire model.\n", "\n", "We learned about the following:\n", "* The motivation for differential equations.\n", "* An intuitive relationship between the solution and the differential equation form.\n", "* How different parameters of the differential equation impact the solution.\n", "* The strengths and limitations of the simple differential equations." ] }, { "cell_type": "markdown", "id": "eaec6994", "metadata": { "execution": {} }, "source": [ "---\n", "## Links to Neuromatch Computational Neuroscience Days\n", "\n", "Differential equations turn up in a number of different Neuromatch days:\n", "* The LIF model is discussed in more details in [Model Types](https://compneuro.neuromatch.io/tutorials/W1D1_ModelTypes/chapter_title.html) and [Biological Neuron Models](https://compneuro.neuromatch.io/tutorials/W2D3_BiologicalNeuronModels/chapter_title.html).\n", "* Drift Diffusion model which is a differential equation for decision making is discussed in [Linear Systems](https://compneuro.neuromatch.io/tutorials/W2D2_LinearSystems/chapter_title.html).\n", "* Systems of differential equations are discussed in [Linear Systems](https://compneuro.neuromatch.io/tutorials/W2D2_LinearSystems/chapter_title.html) and [Dynamic Networks](https://compneuro.neuromatch.io/tutorials/W2D4_DynamicNetworks/chapter_title.html).\n", "\n", "---\n", "## References\n", "1. Lotka, A. L, (1920) Analytical note on certain rhythmic relations inorganic systems.Proceedings of the National Academy of Sciences, 6(7):410–415,1920. doi: [10.1073/pnas.6.7.410](https://doi.org/10.1073/pnas.6.7.410)\n", "\n", "2. Brunel N, van Rossum MC. Lapicque's 1907 paper: from frogs to integrate-and-fire. Biol Cybern. 2007 Dec;97(5-6):337-9. doi: [10.1007/s00422-007-0190-0](https://doi.org/10.1007/s00422-007-0190-0). Epub 2007 Oct 30. PMID: 17968583.\n", "\n", "\n", "## Bibliography\n", "1. Dayan, P., & Abbott, L. F. (2001). Theoretical neuroscience: computational and mathematical modeling of neural systems. Computational Neuroscience Series.\n", "2. Strogatz, S. (2014). Nonlinear dynamics and chaos: with applications to physics, biology, chemistry, and engineering (studies in nonlinearity), Westview Press; 2nd edition\n", "\n", "### Supplemental Popular Reading List\n", "1. Lindsay, G. (2021). Models of the Mind: How Physics, Engineering and Mathematics Have Shaped Our Understanding of the Brain. Bloomsbury Publishing.\n", "2. Strogatz, S. (2004). Sync: The emerging science of spontaneous order. Penguin UK.\n", "\n", "### Popular Podcast\n", "1. Strogatz, S. (Host). (2020), Joy of X https://www.quantamagazine.org/tag/the-joy-of-x/ Quanta Magazine\n" ] } ], "metadata": { "colab": { "collapsed_sections": [], "include_colab_link": true, "name": "W0D4_Tutorial2", "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": 5 }