{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "execution": {}, "id": "view-in-github" }, "source": [ "  " ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "# Tutorial 1: Vectors\n", "\n", "**Week 0, Day 3: Linear Algebra**\n", "\n", "**By Neuromatch Academy**\n", "\n", "__Content creators:__ Ella Batty\n", "\n", "__Content reviewers:__ Keith van Antwerp, Pooya Pakarian, Anoop Kulkarni\n", "\n", "__Production editors:__ Siddharth Suresh, Ella Batty" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Tutorial Objectives\n", "*Estimated timing of tutorial: 1 hour, 25 minutes*\n", "\n", "During today, we will learn the basics of linear algebra, focusing on the topics that underlie the material on future days in the NMA Computational Neuroscience course. In this tutorial, we focus on vectors: their definition, their properties & operations, and how we can use them to define our coordinate system.\n", "\n", "By the end of this tutorial, you will:\n", "* Be able to provide an example of how linear algebra is used in computational neuroscience\n", "* Be able to describe vectors, their properties (dimensionality/length), and their operations (scalar multiplication, vector addition, dot product) geometrically\n", "* Be able to determine and explain the number of basis vectors necessary for a given vector space\n" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Setup\n", "\n", "Execute the following cells to set up the notebook environment" ] }, { "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 = \"W0D3_T1\"" ] }, { "cell_type": "code", "execution_count": null, "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, "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "#@title Figure settings\n", "import ipywidgets as widgets # interactive display\n", "from ipywidgets import fixed\n", "%config InlineBackend.figure_format = 'retina'\n", "plt.style.use(\"https://raw.githubusercontent.com/NeuromatchAcademy/course-content/master/nma.mplstyle\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting functions\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @title Plotting functions\n", "from matplotlib.patches import FancyArrowPatch\n", "from mpl_toolkits.mplot3d import proj3d\n", "\n", "\n", "def visualize_vectors(v, v_unit):\n", " \"\"\" Plots a 2D vector and the corresponding unit vector\n", "\n", " Args:\n", " v (ndarray): array of size (2,) with the vector coordinates\n", " v_unit (ndarray): array of size (2, ) with the unit vector coordinates\n", "\n", " \"\"\"\n", " fig, ax = plt.subplots()\n", "\n", " # Set up plot aesthetics\n", " ax.spines['top'].set_color('none')\n", " ax.spines['bottom'].set_position('zero')\n", " ax.spines['left'].set_position('zero')\n", " ax.spines['right'].set_color('none')\n", " ax.set(xlim = [-6, 6], ylim = [-6, 6])\n", " ax.grid(True, alpha=.4, linewidth=1, zorder=0)\n", "\n", " # Plot vectors\n", " v_arr = ax.arrow(0, 0, v, v, width=0.08, color='#648FFF',\n", " length_includes_head=True, zorder=2);\n", " v_unit_arr = ax.arrow(0, 0, v_unit, v_unit, width=0.08,\n", " color='#DC267F',\n", " length_includes_head=True, zorder=3);\n", " ax.set(xlim = [-4, 4], ylim = [-4, 4]);\n", "\n", " # Add legend\n", " leg = ax.legend([v_arr, v_unit_arr],\n", " [r\"Vector $\\mathbf{v}$\",\n", " r\"Normalized vector $\\tilde{\\mathbf{v}}$\"],\n", " handlelength=0, fontsize=20, loc='upper left')\n", " for handle, label in zip(leg.legendHandles, leg.texts):\n", " label.set_color(handle.get_facecolor())\n", " handle.set_visible(False)\n", "\n", "class Arrow3D(FancyArrowPatch):\n", " def __init__(self, xs, ys, zs, *args, **kwargs):\n", " FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)\n", " self._verts3d = xs, ys, zs\n", "\n", " def draw(self, renderer):\n", " xs3d, ys3d, zs3d = self._verts3d\n", " xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)\n", " self.set_positions((xs,ys),(xs,ys))\n", " FancyArrowPatch.draw(self, renderer)\n", "\n", " def do_3d_projection(self, renderer=None):\n", " xs3d, ys3d, zs3d = self._verts3d\n", " xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)\n", " self.set_positions((xs, ys), (xs, ys))" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Section 0: Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Video 1: Why do we care about linear algebra?\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 1: Why do we care about linear algebra?\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'qsK7pHrJU5M'), ('Bilibili', 'BV1vf4y1b7mr')]\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])\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}_Why_do_we_care_about_linear_algebra_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "\n", "# Section 1: Intro to vectors" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 1.1: What is a vector?\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 2: Vector Definition & Properties\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 2: Vector Definition & Properties\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'Vi6L9fBk2Uc'), ('Bilibili', 'BV1sX4y1P7wT')]\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])\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}_Vector_Definition_&_Properties_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "This video covers the definition of vectors, the dimensionality and length of vectors, and zero/unit vectors.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "A vector $\\mathbf{x}$ can be considered from at least two perspectives: as an ordered list of numbers or as an arrow with the base at the origin of a coordinate system. These are two ways of looking at a single thing: in the arrow case, the tip of the arrow is defined by a coordinate (which can be represented by the ordered list).\n", "

\n", "\n", "The **dimensionality of a vector** is determined by the number of components in the ordered list (or the dimensionality of the space in which the arrow exists). For example, $\\mathbf{x} = \\begin{bmatrix} 4 \\\\ 2 \\\\ 1 \\end{bmatrix}$ is a 3 dimensional vector. We can refer to components by $\\mathbf{x}_i$ where i indicates the position of the component. In this vector, $\\mathbf{x_1} = 4$, $\\mathbf{x_2} = 2$, and $\\mathbf{x_3} = 1$.\n", "\n", "

\n", "One defining property of a vector is its length: $||\\mathbf{x}||$. This is the length of the arrow and can be computed as the square root of the sum of all components squared:\n", "\n", "\\begin{equation}\n", "||\\mathbf{x}|| = \\sqrt{\\sum_{i=1}^N \\mathbf{x}_i^2}\n", "\\end{equation}\n", "\n", "

\n", "We have two special types of vectors with specific names. **Zero vectors** have length 0 (and all components equal 0). **Unit vectors** have length 1. You can normalize a vector and create a unit vector, $\\tilde{\\mathbf{x}}$, by dividing by its length,$||\\mathbf{x}||$ :\n", "\n", "\\begin{equation}\n", "\\tilde{\\mathbf{x}} = \\frac{\\mathbf{x}}{||\\mathbf{x}||}\n", "\\end{equation}" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Coding Exercise 1.1: Normalizing vectors\n", "\n", "In this exercise, you will first create the below vector, $\\mathbf{v}$, using a numpy array. You will then implement a function, normalize_vector, that outputs a normalized version of the input vector (by dividing each component by the vector length). You will then visualize both the original vector and the unit vector.\n", "\n", "First think: how do you think the normalized unit vector will compare to the original vector in terms of direction and length?\n", "\n", "\n", "\\begin{equation}\n", "\\mathbf{v} =\n", "\\begin{bmatrix}\n", "4 \\\\\n", "1\n", "\\end{bmatrix}\n", "\\end{equation}" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": {} }, "outputs": [], "source": [ "def normalize_vector(input_vector):\n", " \"\"\" Normalizes a vector to length 1\n", "\n", " Inputs:\n", " input_vector (ndarray): array of shape (n_dim, ) where n_dim is the\n", " dimensionality of the vector\n", "\n", " Outputs:\n", " (ndarray): normalized vector of length 1\n", " \"\"\"\n", "\n", " #################################################################################\n", " ## TODO for students: complete normalize_vector function ##\n", " # Fill out function and remove\n", " raise NotImplementedError(\"Student exercise: complete normalize_vector function\")\n", " #################################################################################\n", "\n", " # Compute vector length (use np.linalg.norm)\n", " vector_length = ...\n", "\n", " # Create normalized vector\n", " normalized_vector = ...\n", "\n", " return normalized_vector\n", "\n", "# Create vector v from above (use np.array)\n", "v = ...\n", "\n", "# Call the function\n", "v_unit = normalize_vector(v)\n", "\n", "# Visualize the vectors\n", "visualize_vectors(v, v_unit)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D3_LinearAlgebra/solutions/W0D3_Tutorial1_Solution_121dde78.py)\n", "\n", "*Example output:*\n", "\n", " \n", "\n" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "\n", "\n", "Note that the unit vector still points in the same direction as the original (it overlays it). Dividing each component by a constant (the length) does not change the direction. The only change is the length - the normalized vector has a length of 1." ] }, { "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}_Normalizing_vectors_Exercise\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 1.2: Vector operations\n", "\n", "*Estimated timing to here from start of tutorial: 20 min*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 3: Linear Combinations of Vectors\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 3: Linear Combinations of Vectors\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'e8Y6kNICH-8'), ('Bilibili', 'BV1rL411p7iR')]\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])\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}_Linear_combinations_of_vectors_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "This video covers vector operations: scalar multiplication, vector addition, and linear combinations.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "We have several mathematical operations we perform on or with vectors. **Scalar multiplication** of a vector changes its length but not its direction (except for sometimes reversing by 180 degrees as we'll see in the next demo). When we multiply a vector, $\\mathbf{x}$, by a scalar, $a$, we multiply each individual component by the scalar:\n", "\n", "$$a\\mathbf{x} = \\begin{bmatrix}\n", " a\\mathbf{x}_1 \\\\ a\\mathbf{x}_2 \\\\ \\vdots \\\\ a\\mathbf{x}_N\n", "\\end{bmatrix}$$\n", "\n", "You can visualize **vector addition** as the stacking of the two arrows head to tail. In essence, you are moving the second vector so its base is at the tip of the first, without changing its direction. The tip of this stacked second vector is the new added vector's tip. In order words, if you visualize two vectors as sides of a parallelogram, adding them will create a vector with the 4th vertex of that parallelogram as its tip. Numerically, this means that we add the corresponding components of each vector:\n", "\n", "\n", " $$\\mathbf{x} + \\mathbf{y} = \\begin{bmatrix}\n", " \\mathbf{x}_{1} + \\mathbf{y}_1 \\\\ \\mathbf{x}_{2} + \\mathbf{y}_2\\\\ \\vdots \\\\ \\mathbf{x}_{N} + \\mathbf{y}_N\n", "\\end{bmatrix}$$\n", "\n", "\n", "We call a group of 2 or more vectors a **set of vectors**. A **linear combination** of a set of vectors is a combination of the set using scalar multiplication and vector addition. Essentially, we are multiplying each vector in the set by a scalar and then adding all the scalar multiplied vectors together: the output of this (another vector) is a linear combination of the set. More formally defined, a vector, $\\mathbf{u}$, is a linear combination of a set of vectors $\\mathbf{v}^1, \\mathbf{v}^2, ..., \\mathbf{v}^N$ with (scalar) weights $c_1, c_2, ...c_N$ if:\n", "\n", "$$\\mathbf{u} = c_1\\mathbf{v}^1 + c_2\\mathbf{v}^2 + ... + c_n\\mathbf{v}^N$$.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Interactive Demo: Linear combination of vectors\n", "In the following demo, we will demonstrate a linear combination of two vectors, $\\mathbf{x}$ and $\\mathbf{y}$.\n", "\n", "\\begin{equation}\n", "\\mathbf{z} = a\\mathbf{x} + b\\mathbf{y}\n", "\\end{equation}\n", "\n", "where $\\mathbf{x} = \\begin{bmatrix}3 \\\\ 1 \\end{bmatrix}$ and $\\mathbf{y} = \\begin{bmatrix}-1 \\\\ 2 \\end{bmatrix}$.\n", "\n", "You will play with the scalar multiples $a$ and $b$ to visualize both scalar multiplication and vector addition. Think and talk through the following questions:\n", "\n", "1. How does $a\\mathbf{x}$ compare to $\\mathbf{x}$ when $a$ is negative?\n", "2. How does $a\\mathbf{x}$ compare to $\\mathbf{x}$ when $a$ is a fraction?\n", "3. Can you get $\\mathbf{z}$ to point to anywhere in the 2D space with combinations of $a$ and $b$?\n", "4. Would this be the case no matter what $\\mathbf{x}$ and $\\mathbf{y}$ are, as long as they are both 2D vectors?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget! Move the sliders for “a” and “b”. After releasing the slider, be patient for a couple of seconds to see the desired change.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ " #@markdown Make sure you execute this cell to enable the widget! Move the sliders for “a” and “b”. After releasing the slider, be patient for a couple of seconds to see the desired change.\n", "\n", "\n", "def plot_arrows(x, y, a_times_x, b_times_y):\n", " fig, ax = plt.subplots(figsize=(10, 7))\n", "\n", " ax.spines['top'].set_color('none')\n", " ax.spines['bottom'].set_position('zero')\n", " ax.spines['left'].set_position('zero')\n", " ax.spines['right'].set_color('none')\n", "\n", " ax.set_aspect('equal', adjustable='box')\n", " ax.set(xlim = [-10, 10], ylim = [-10, 10], xticks = np.arange(-10, 10), yticks = np.arange(-10, 10), xticklabels = [], yticklabels = [])\n", "\n", " ax.grid(alpha=.4)\n", "\n", " z = a_times_x + b_times_y\n", " z_arr = ax.arrow(0, 0, z, z, width=.08, color='k', length_includes_head = True);\n", "\n", " x_orig, = ax.plot([0, x], [0, x], '--', color='#648FFF')\n", " y_orig, = ax.plot([0, y], [0, y], '--', color='#DC267F')\n", "\n", " ax_arr = ax.arrow(0, 0, a_times_x, a_times_x, width=.08, color='#648FFF', length_includes_head = True);\n", " by_arr = ax.arrow(0, 0, b_times_y, b_times_y, width=.08, color='#DC267F', length_includes_head = True);\n", "\n", " ax.plot([a_times_x, z], [a_times_x, z], '--k')\n", " ax.plot([b_times_y, z], [b_times_y, z], '--k')\n", "\n", "\n", " leg = ax.legend([x_orig, y_orig, ax_arr, by_arr, z_arr], [r\"$\\mathbf{x}$\", r\"$\\mathbf{y}$\", r\"$a\\mathbf{x}$\", r\"$b\\mathbf{y}$\", r\"$\\mathbf{z} = a\\mathbf{x} + b\\mathbf{y}$\"], handlelength = 2, fontsize = 25, loc = 'center left', bbox_to_anchor=(1.05, .5))\n", " for handle, label in zip(leg.legendHandles, leg.texts):\n", " try:\n", " label.set_color(handle.get_facecolor())\n", " except:\n", " label.set_color(handle.get_color())\n", " #handle.set_visible(False)\n", "\n", "@widgets.interact(a = widgets.FloatSlider(value=1.0, min=-2, max=2, step=0.1), b = widgets.FloatSlider(value=1.0, min=-2, max=2, step=0.1))\n", "def plot_linear_combination(a, b):\n", " x = np.array([3, 1])\n", " y = np.array([-1, 2])\n", "\n", " plot_arrows(x, y, a*x, b*y)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D3_LinearAlgebra/solutions/W0D3_Tutorial1_Solution_a10917f5.py)\n", "\n" ] }, { "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}_Linear_combinations_of_vectors_Interactive_Demo\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Section 2: Defining space through vectors" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 2.1: Span & Linear Independence\n", "\n", "*Estimated timing to here from start of tutorial: 35 min*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 4: Span and Linear Independence\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 4: Span and Linear Independence\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'g7DoHnZkUlw'), ('Bilibili', 'BV1iv411H7bK')]\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])\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}_Span_and_Linear_Independence_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "This video covers the span of a set of vectors and when a set of vectors is linearly independent.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "The **span of a set of vectors** is the set of all possible linear combinations of those vectors. In the last demo, you saw that the vector $\\mathbf{x}$ and the vector $\\mathbf{y}$ spanned 2D space (${\\rm I\\!R}^2$). This means you can get to any point in 2D space (corresponding to vector $\\mathbf{v}$ with some combination of $a$ and $b$, the scalar multiples in a linear combination of $\\mathbf{x}$ and $\\mathbf{y}$:\n", "\n", "$$\\mathbf{v} = a\\mathbf{x} + b\\mathbf{y}$$\n", "\n", "Even more specifically, in this situation, each point in 2D space is identified by a unique and single combination of $a$ and $b$: if you know the point, you know what $a$ and $b$ are.\n", "\n", "A set of vectors is linearly dependent if one can be written as a linear combination of the others. If this is not the case, the set of vectors is linearly independent.\n", "
" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Think! 2.1: Determing dependence\n", "\n", "Let's say we have four vectors:\n", "\n", "\\begin{equation}\n", "\\mathbf{a} =\n", "\\begin{bmatrix} 4 \\\\ 2 \\\\ 0 \\end{bmatrix},\n", "\\mathbf{b} = \\begin{bmatrix} 1 \\\\ 0 \\\\ 3 \\end{bmatrix},\n", "\\mathbf{c} = \\begin{bmatrix} 5 \\\\ 2 \\\\ 3 \\end{bmatrix},\n", "\\mathbf{d} = \\begin{bmatrix} 3 \\\\ 3 \\\\ 2 \\end{bmatrix}\n", "\\end{equation}\n", "\n", "1. Are these vectors linearly independent or dependent? Why?\n", "2. What is the span of the set of vectors $\\{\\mathbf{a}, \\mathbf{b}, \\mathbf{c}, \\mathbf{d}\\}$?\n", "3. What is the span of the set of vectors $\\{\\mathbf{a}, \\mathbf{b}, \\mathbf{c}\\}$?\n", "4. What is the span of a set just consisting of vector $\\{\\mathbf{a}$} (in general terms)?\n", "5. What is the span of the set of vectors $\\{\\mathbf{a}, \\mathbf{b}$} (in general terms)?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Execute this cell to visualize vectors\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "# @markdown Execute this cell to visualize vectors\n", "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111, projection='3d')\n", "a = Arrow3D([0, 4], [0, 2],\n", " [0, 0], mutation_scale=20,\n", " lw=3, arrowstyle=\"-|>\", color=\"#648FFF\")\n", "\n", "ax.add_artist(a)\n", "b = Arrow3D([0, 1], [0, 0],\n", " [0, 3], mutation_scale=20,\n", " lw=3, arrowstyle=\"-|>\", color=\"#785EF0\")\n", "\n", "ax.add_artist(b)\n", "c = Arrow3D([0, 5], [0, 2],\n", " [0, 3], mutation_scale=20,\n", " lw=3, arrowstyle=\"-|>\", color=\"#DC267F\")\n", "\n", "ax.add_artist(c)\n", "d = Arrow3D([0, 3], [0, 3],\n", " [0, 2], mutation_scale=20,\n", " lw=3, arrowstyle=\"-|>\", color=\"#FFB000\")\n", "\n", "ax.add_artist(d)\n", "\n", "leg = ax.legend([a, b, c, d], ['a', 'b', 'c', 'd'], handlelength = 0, fontsize = 20, loc = 'upper left')\n", "for handle, label in zip(leg.legendHandles, leg.texts):\n", " label.set_color(handle.get_facecolor())\n", " handle.set_visible(False)\n", "ax.set(xlim = [0, 5], ylim = [5, 0], zlim = [0, 5]); #, xlabel = 'Neuron 1 Firing Rate', ylabel = 'Neuron 2 Firing Rate', zlabel = 'Neuron 3 Firing Rate');" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "Check out this visualization prepared by Oğul Can Yurdakul to better see the vectors: https://www.geogebra.org/3d/hherq78z!" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D3_LinearAlgebra/solutions/W0D3_Tutorial1_Solution_b89ad2ef.py)\n", "\n" ] }, { "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}_Determing_Dependence_Discussion\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 2.2: Basis vectors\n", "\n", "*Estimated timing to here from start of tutorial: 50 min*\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 5: Basis vectors\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 5: Basis vectors\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'kgjwmHZH-So'), ('Bilibili', 'BV1Wh411h7qZ')]\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])\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}_Basis_vectors_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "This video covers what a basis is, the standard basis, and the properties of basis vectors.\n", "
\n", " Click here for text recap of video \n", "\n", "\n", "If a set of vectors spans 2D space are linearly independent, they form a **basis** for 2D space. This means that we can identify all points in 2D space with reference to these two vectors, instead of to the traditional x-axis and y-axis coordinates. In fact, you've been using a basis all along when you identify points by their x-axis and y-axis coordinates: the **standard basis**! In 2D space, the standard basis vectors ($\\tilde{\\mathbf{e}}_1$ and $\\tilde{\\mathbf{e}}_2$) are the unit vectors corresponding to the x and y axes:\n", "\n", "\\begin{equation}\n", "\\tilde{\\mathbf{e}}_1 = \\begin{bmatrix} 1\\\\ 0\\\\ \\end{bmatrix},\n", "\\tilde{\\mathbf{e}}_2 = \\begin{bmatrix} 0\\\\ 1\\\\ \\end{bmatrix}\n", "\\end{equation}\n", "\n", "When you refer to a point on a 2D plot as (4, 2), you are really identifying the scalar multiples to get to that point as a linear combination of the standard basis. The standard basis is convenient - and what we're used to - but it is essential for a variety of applications of linear algebra (including some we'll encounter in this course) to be able to switch our frame of reference to a different basis. In this example, we can now refer to points as (a, b) where a and b are the scalar multiples to get to that point as a linear combination of $\\mathbf{x}$ and $\\mathbf{y}$.\n", "\n", "Let's return to this idea of linear independence of vectors. \n", "\n", "I've been referring to 2D space a lot but there are lots of **vector spaces** for which we can use basis vectors. We'll leave the formal definition of vector spaces to the bonus material as it's a little unwieldy but basically it's a set of vectors where all linear combinations of all vectors are included in the set. 1D space (${\\rm I\\!R}^1$), 2D space (${\\rm I\\!R}^2$), 3D space (${\\rm I\\!R}^3$), and so on are all vector spaces. These are not the only ones though: subsets of these vector spaces can also be vector spaces themselves. For example, a 1D line through a 2D plane is a vector space. And we have even more unusual vector spaces - I give an example of one in the bonus material.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "The dot product between two vectors is a scalar value computed as the sum of element-wise multiplication of vector components.\n", "\n", "\\begin{equation}\n", "\\text{Dot product of } \\mathbf{x} \\text{ and } \\mathbf{y} = \\mathbf{x} \\cdot \\mathbf{y} = \\sum_{i=1}^{N} \\mathbf{x}_i\\mathbf{y}_i\n", "\\end{equation}\n", "\n", "If we have vector $\\mathbf{x} = \\begin{bmatrix}2\\\\ 3\\\\\\end{bmatrix}$ and $\\mathbf{y} = \\begin{bmatrix}1\\\\ 4\\\\\\end{bmatrix}$, then:\n", "\n", "\\begin{equation}\n", "\\mathbf{x} \\cdot \\mathbf{y} = 2*1 + 3*4 = 14\\text{.}\n", "\\end{equation}\n", "\n", "We will discuss more about what the dot product represents geometrically after the next demo.\n", "\n", "\n", "We are using the dot product to model neural firing. Specifically, we have multiple retinal neurons connected to a LGN neuron. The LGN neuron response is a weighted sum of the retinal firing rates (where the weights are the strengths of the synapses). We can capture this as the dot product between a vector of retinal firing rates and a vector of weights from each retinal neuron.\n", "\n", "
\n", "\n", "In code, we can compute the dot product between two vectors represented by numpy arrays using np.dot:\n", "\n", "python\n", "x = np.array([2, 3])\n", "y = np.array([1, 4])\n", "dot_prod = np.dot(x, y)\n", "\n", "\n", "In the following demo, we have two retinal neurons with varying firing rates ($r_1$ and $r_2$). We will allow our firing rates to be negative in this example. We can represent our retinal firing rates with the vector $\\mathbf{r} = \\begin{bmatrix} r_1\\\\ r_2\\\\ \\end{bmatrix}$.\n", "\n", "We have weights from each of these to an LGN neuron: the weight of the connection from the first retinal neuron is 1 and the weight from the second is 2. See the circuit below. We can represent our weights with the vector $\\mathbf{w} = \\begin{bmatrix} 1\\\\ 2\\\\ \\end{bmatrix}$.\n", "\n", "The LGN firing rate is the dot product of the retinal firing rate vector and the weight vector:\n", "\n", "\\begin{equation}\n", "g = \\mathbf{w}\\cdot\\mathbf{r} = w_1r_1 + w_2r_2\n", "\\end{equation}" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "### Interactive Demo 3.1: LGN firing\n", "\n", "\n", "In the following demo you will try various combinations of firing rates of the retinal neurons (thus moving the vector that corresponds to the firing rates). When you move the retinal firing rates around, you will see the heatmap in that area appear, which shows the corresponding LGN neuron firing rate (for that combo of retina firing). Remember that this value is computed as the dot product between the retinal firing rate vector and the weight vector.\n", "\n", "Play with the following demo and then discuss these questions:\n", "\n", "\n", "1. For a given length of the retinal firing rate vector, what direction maximally excites the LGN neuron (highest postsynaptic firing rate)? How does it relate to the weights vector?\n", "2. For a given length of the retinal firing rate vector, what direction minimally excites the LGN neuron?\n", "3. When does the LGN neuron have 0 firing rate? What is the relationship between the weights and retinal firing rate vector?\n", "4. For a given direction, how does the LGN firing rate vary with length of the retinal firing rate vector?\n", "5. Let's say the retinal neurons are tightly linked and the length of the retinal firing rate vector has to be 1. In other words, it has to be a unit vector. How would you find the retinal firing rates that maximally excite the LGN neuron?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Make sure you execute this cell to enable the widget!\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "hide-input" ] }, "outputs": [], "source": [ "#@markdown Make sure you execute this cell to enable the widget!\n", "\n", "step = .1\n", "x_vec = np.arange(-4, 4.001, step)\n", "y_vec = np.arange(-4, 4.001, step)\n", "n_pixels = x_vec.shape\n", "\n", "\n", "heatmap = np.zeros((n_pixels, n_pixels))\n", "y = np.array([1, 3])\n", "for i, coord1 in enumerate(x_vec):\n", " for j, coord2 in enumerate(x_vec):\n", " heatmap[i, j] = np.dot(np.array([coord1, coord2]), y)\n", "circle_mask = np.zeros((n_pixels, n_pixels))\n", "\n", "for i, coord_i in enumerate(x_vec):\n", " for j, coord_j in enumerate(y_vec):\n", " circle_mask[i, j] = np.sqrt(coord_i**2 + coord_j**2)\n", "\n", "circle_mask = circle_mask < 4\n", "\n", "heatmap = heatmap * circle_mask\n", "mask = np.zeros((n_pixels, n_pixels))\n", "\n", "def plot_heatmap(x, y, mask):\n", "\n", " fig, ax = plt.subplots()\n", "\n", " ax.spines['top'].set_color('none')\n", " ax.spines['bottom'].set_position('zero')\n", " ax.spines['left'].set_position('zero')\n", " ax.spines['right'].set_color('none')\n", "\n", " heatmap[np.where(x_vec == x), np.where(x_vec == x)] = np.dot(x, y)\n", "\n", " masked_x = np.abs(x_vec - x) < 1\n", " masked_y = np.abs(y_vec - x) < 1\n", " mask += np.outer(masked_x, masked_y)\n", " mask = np.minimum(mask, 1)\n", "\n", " im = ax.imshow((heatmap * mask).T, vmin = -15, vmax = 15, origin = 'lower', alpha = .5, extent=[-n_pixels/2., n_pixels/2., -n_pixels/2., n_pixels/2. ], cmap = 'bwr')\n", " cbar = plt.colorbar(im, ax = ax)\n", " cbar.set_label('Response of LGN neuron (g)', rotation=270, labelpad=20)\n", " ax.set(xticklabels = [], yticklabels = [], xlabel = '$\\mathbf{r}_1$')\n", " ax.set_ylabel('$\\mathbf{r}_2$', rotation=0)\n", "\n", " ax.xaxis.set_label_coords(0, .45)\n", " ax.yaxis.set_label_coords(.5, 1)\n", " fr_arr = ax.arrow(0, 0, (1/step)*x, (1/step)*x, width=.5, color='#40B0A6', length_includes_head = True);\n", " we_arr = ax.arrow(0, 0, (1/step)*y, (1/step)*y, width=.5, color='k', length_includes_head = True);\n", "\n", "\n", " leg = ax.legend([fr_arr, we_arr], ['Retina firing rate vector', 'Weight vector'],\n", " handlelength = 0, frameon=False, fontsize = 17,loc = 'center',\n", " bbox_to_anchor=(.5, -.1))\n", " for handle, label in zip(leg.legendHandles, leg.texts):\n", " label.set_color(handle.get_facecolor())\n", " handle.set_visible(False)\n", "\n", " return mask\n", "\n", "style = {'description_width': 'initial'}\n", "\n", "@widgets.interact(neuron1_firing = widgets.FloatSlider(value=1.0, min=-4, max=4, step=1, style=style), neuron2_firing = widgets.FloatSlider(value=1.0, min=-4, max=4, step=1, style=style), mask = fixed(mask), heatmap = fixed(heatmap))\n", "def plot_linear_combination(neuron1_firing, neuron2_firing, mask):\n", " firing_rates = np.array([neuron1_firing, neuron2_firing])\n", " weights = np.array([1, 2])\n", " mask = plot_heatmap(firing_rates, weights, mask)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "execution": {} }, "source": [ "[*Click for solution*](https://github.com/NeuromatchAcademy/precourse/tree/main/tutorials/W0D3_LinearAlgebra/solutions/W0D3_Tutorial1_Solution_35f26c32.py)\n", "\n" ] }, { "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}_LGN_firing_Interactive_Demo\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Section 3.2: The geometry of the dot product\n", "\n", "*Estimated timing to here from start of tutorial: 1 hr, 20 min*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Video 7: The geometry of the dot product\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "execution": {}, "tags": [ "remove-input" ] }, "outputs": [], "source": [ "# @title Video 7: The geometry of the dot product\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] == 'Youtube':\n", " video = YouTubeVideo(id=video_ids[i], 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], source=video_ids[i], width=W,\n", " height=H, fs=fs, autoplay=False)\n", " if video_ids[i] == 'Bilibili':\n", " print(f'Video available at https://www.bilibili.com/video/{video.id}')\n", " elif video_ids[i] == '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', 'S2wKFY5iWUM'), ('Bilibili', 'BV1bo4y1C7JL')]\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])\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}_The_geometry_of_the_dot_product_Video\")" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "This video covers the geometry of the dot product: how it relates to the length of the vectors and the angle between them.\n", "\n", "
\n", " Click here for text recap of video \n", "\n", "An alternate way of defining the dot product is as the multiple of the lengths of the two vectors and the angle between them $\\theta$:\n", "\n", "\\begin{equation}\n", "\\mathbf{x} \\cdot \\mathbf{y} = ||\\mathbf{x}|| \\cdot ||\\mathbf{y}|| \\cdot \\text{cos}\\theta\n", "\\end{equation}\n", "\n", "This means that:\n", "\n", "- the dot product is largest when the vectors are the same direction (angle $0^{\\text{o}}$)\n", "\n", "- the dot product is smallest when the vectors are the opposite direction (angle $180^{\\text{o}}$)\n", "\n", "- the dot product is 0 when the vectors are perpendicular (angle $90^{\\text{o}}$)\n", "\n", "- for fixed vector lengths, the dot product is a measure of similarity between the vectors" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Summary\n", "\n", "*Estimated timing of tutorial: 1 hour, 25 minutes*\n", "\n", "Hopefully, this tutorial has helped you better understand the world of vectors. Specifically, you learned:\n", "\n", "* A geometrical view of vectors, their properties, and their operations\n", "\n", "* How we can define space in terms of basis vectors\n", "\n", "We will build on this knowledge in the next tutorial when we tackle matrices.\n" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "# Bonus\n" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "## Bonus Section 1: Vector space" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Bonus Section 1.1: Definition of vector space\n", "\n", "A vector space is formally defined as a collection of vectors that satisfy the below axioms for every vector.\n", "\n", "Let $\\mathbf{x}$, $\\mathbf{y}$, and $\\mathbf{z}$ be vectors in a vector space $V$, and $c$ and $d$ scalars.\n", "\n", "1. $\\mathbf{x}$ + $\\mathbf{y}$ in $V$\n", "2. $\\mathbf{x}$ + $\\mathbf{y}$ = $\\mathbf{y}$ + $\\mathbf{x}$\n", "3. ($\\mathbf{x}$ + $\\mathbf{y}$) + $\\mathbf{z}$ = $\\mathbf{x}$ + ($\\mathbf{y}$ + $\\mathbf{z}$)\n", "4. Zero vector ($\\mathbf{0}$) exists in $V$ such that $\\mathbf{x} + \\mathbf{0} = \\mathbf{x}$\n", "5. Inverse $-\\mathbf{x}$ exists in $V$ such that $\\mathbf{x} + -\\mathbf{x} = \\mathbf{0}$\n", "6. $c\\mathbf{x}$ in $V$\n", "7. $c(\\mathbf{x}+\\mathbf{y}) = c\\mathbf{x} + c\\mathbf{y})$\n", "8. $(c + d)\\mathbf{x} = c\\mathbf{x}+d\\mathbf{x}$\n", "9. $c(d\\mathbf{x}) = (cd)\\mathbf{x}$\n", "10. $1\\mathbf{x} = \\mathbf{x}$" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "## Bonus Section 1.2: Polynomial vector space example\n", "\n", "We will mostly work with the vector spaces you've seen in this tutorial: different N-dimensional spaces of real numbers or subspaces within those. Other things can also be vector spaces though! As an example, one vector space is the set of all polynomials of degree 3 ($P_3$).\n", "\n", "Remember that a polynomial of degree 3 is:\n", "\n", "\\begin{equation}\n", "p(x) = c_o + c_1x + c_2x^2 + c_3x^3\n", "\\end{equation}\n", "\n", "We can have the basis for this vector space be:\n", "\n", "\\begin{equation}\n", "\\text{Basis: } \\{ 1, x, x^2, x^3 \\}\n", "\\end{equation}\n", "\n", "The components of a vector representing a specific polynomial would then be:\n", "\n", "\\begin{equation}\n", "\\mathbf{x} = \\begin{bmatrix} c_0 \\\\c_1\\\\ c_2\\\\ c_3 \\end{bmatrix}\n", "\\end{equation}\n", "\n", "These vectors obey all of the axioms in the previous section as you add polynomials, multiple them with a scalar, etc.!" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "---\n", "## Bonus Section 2: Proof of dot product equivalent definitions\n", "\n", "Let's prove that our two definitions of a dot product are equivalent:\n", "\n", "\\begin{align}\n", "\\mathbf{x}\\cdot\\mathbf{y} &= \\sum_{i=1}^N \\mathbf{x}_i\\mathbf{y}_i \\\\\n", "&= ||\\mathbf{x}||||\\mathbf{y}||cos(\\theta)\n", "\\end{align}\n", "\n", "where $\\mathbf{x}$ and $\\mathbf{y}$ are vectors, and $\\theta$ is the angle between them." ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "execution": {} }, "source": [ "We start by using the law of cosines to write out the formula for $||\\mathbf{a}||$, where $\\mathbf{a}$ is defined in the figure above.\n", "\n", "\\begin{equation}\n", "\\|\\mathbf{a}\\|^2 = \\color{blue}{\\|\\mathbf{x}\\|^2 + \\|\\mathbf{y}\\|^2 -2\\|\\mathbf{x}\\|\\|\\mathbf{y}\\|cos(\\theta)}\n", "\\end{equation}\n", "\n", "
\n", "We can then write $\\mathbf{a}$ as a vector subtraction of $\\mathbf{x}$ and $\\mathbf{y}$. Technically, $\\mathbf{a}$ is pictured above not starting at the origin but we ultimately only care about it's length and direction.\n", "\n", "\\begin{align*}\n", "\\mathbf{a} &= \\mathbf{y} - \\mathbf{x} \\\\\n", "\\|\\mathbf{a}\\| &= \\|\\mathbf{y} - \\mathbf{x}\\| \\\\\n", "\\end{align*}\n", "\n", "One key fact we will use is that the squared length of a vector is the sum of each component squared, which is equivalent to the dot product of a vector with itself. We can now write $||a||^2$ in terms of the dot product. We can substitute in our definition from above for $\\mathbf{a}$ in terms of $\\mathbf{x}$ and $\\mathbf{y}$.\n", "\n", "\\begin{align*}\n", " \\|\\mathbf{a}\\|^2 &= \\mathbf{a}\\cdot\\mathbf{a} \\\\\n", " &= (\\mathbf{y} - \\mathbf{x})\\cdot(\\mathbf{y} - \\mathbf{x}) \\\\\n", " &= \\mathbf{y}\\cdot\\mathbf{y} - 2(\\mathbf{x}\\cdot\\mathbf{y}) + \\mathbf{x}\\cdot\\mathbf{x} \\\\\n", "&= \\color{green}{\\|\\mathbf{y}\\|^2 - 2(\\mathbf{x}\\cdot\\mathbf{y}) + \\|\\mathbf{x}\\|^2}\\\\\n", "\\end{align*}\n", "\n", "We can now equate our two definitions for $||a||^2$ from above and solve for the dot product!\n", "\n", "\\begin{align*}\n", "\\color{blue}{\\|\\mathbf{x}\\|^2 + \\|\\mathbf{y}\\|^2 -2\\|\\mathbf{x}\\|\\|\\mathbf{y}\\|cos(\\theta)} &= \\color{green}{\\|\\mathbf{y}\\|^2 - 2(\\mathbf{x}\\cdot\\mathbf{y}) + \\|\\mathbf{x}\\|^2}\\\\\n", " -2\\|\\mathbf{x}\\|\\|\\mathbf{y}\\|cos(\\theta) &= - 2(\\mathbf{x}\\cdot\\mathbf{y}) \\\\\n", "\\|\\mathbf{x}\\|\\|\\mathbf{y}\\|cos(\\theta) &= \\mathbf{x}\\cdot\\mathbf{y} \\\\\n", "\\end{align*}\n", "\n", "We see our geometrical definition of the dot product!" ] } ], "metadata": { "colab": { "collapsed_sections": [], "include_colab_link": true, "name": "W0D3_Tutorial1", "provenance": [], "toc_visible": true }, "kernel": { "display_name": "Python 3", "language": "python", "name": "python3" }, "kernelspec": { "display_name": "Python 3", "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 }