{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "view-in-github" }, "source": [ "\"Open" ] }, { "cell_type": "code", "execution_count": 42, "id": "3vcLB3ftYszF", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 17 }, "id": "3vcLB3ftYszF", "outputId": "888261a5-37ce-4a98-f30e-9242d0a04673" }, "outputs": [ { "data": { "application/javascript": "\ndeleteCell = function() {\n var me = document.currentScript;\n me.parentElement.parentElement.remove();\n};\ndeleteOutput= function(){\n var me = document.currentScript;\n me.parentElement.remove();\n};\ndeleteInput = function(){\n var me = document.currentScript;\n me.parentElement.parentElement.childNodes[1].remove();\n};\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.core.magic import register_cell_magic,register_line_cell_magic\n", "from IPython.display import Javascript, display\n", "import json\n", "\n", "\n", "\n", "display(Javascript(\n", " \"\"\"\n", "deleteCell = function() {\n", " var me = document.currentScript;\n", " me.parentElement.parentElement.remove();\n", "};\n", "deleteOutput= function(){\n", " var me = document.currentScript;\n", " me.parentElement.remove();\n", "};\n", "deleteInput = function(){\n", " var me = document.currentScript;\n", " me.parentElement.parentElement.childNodes[1].remove();\n", "};\n", "\"\"\"))\n", "\n", "@register_line_cell_magic\n", "def deleteCell(*arg):\n", " display(Javascript('deleteCell();'))\n", "\n", "@register_line_cell_magic\n", "def deleteOutput(*arg):\n", " display(Javascript('deleteOutput();'))\n", "\n", "@register_line_cell_magic\n", "def deleteInput(*arg):\n", " display(Javascript('deleteInput();'))\n", "\n", "%deleteInput" ] }, { "cell_type": "code", "execution_count": 43, "id": "qDjFyBKOoxHs", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "id": "qDjFyBKOoxHs", "outputId": "64243ee0-5036-44dd-f19c-7ebf063bc724" }, "outputs": [ { "data": { "application/javascript": "deleteCell();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: jupyterquiz in /usr/local/lib/python3.10/dist-packages (2.6.3)\n", "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.7.1)\n", "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.2.0)\n", "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)\n", "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.46.0)\n", "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.4.5)\n", "Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.23.5)\n", "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (23.2)\n", "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (9.4.0)\n", "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.1.1)\n", "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (2.8.2)\n", "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", "Requirement already satisfied: myst_nb in /usr/local/lib/python3.10/dist-packages (1.0.0)\n", "Requirement already satisfied: importlib_metadata in /usr/local/lib/python3.10/dist-packages (from myst_nb) (7.0.0)\n", "Requirement already satisfied: ipython in /usr/local/lib/python3.10/dist-packages (from myst_nb) (7.34.0)\n", "Requirement already satisfied: jupyter-cache>=0.5 in /usr/local/lib/python3.10/dist-packages (from myst_nb) (1.0.0)\n", "Requirement already satisfied: nbclient in /usr/local/lib/python3.10/dist-packages (from myst_nb) (0.9.0)\n", "Requirement already satisfied: myst-parser>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from myst_nb) (2.0.0)\n", "Requirement already satisfied: nbformat>=5.0 in /usr/local/lib/python3.10/dist-packages (from myst_nb) (5.9.2)\n", "Requirement already satisfied: pyyaml in /usr/local/lib/python3.10/dist-packages (from myst_nb) (6.0.1)\n", "Requirement already satisfied: sphinx>=5 in /usr/local/lib/python3.10/dist-packages (from myst_nb) (7.2.6)\n", "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.10/dist-packages (from myst_nb) (4.5.0)\n", "Requirement already satisfied: ipykernel in /usr/local/lib/python3.10/dist-packages (from myst_nb) (5.5.6)\n", "Requirement already satisfied: attrs in /usr/local/lib/python3.10/dist-packages (from jupyter-cache>=0.5->myst_nb) (23.1.0)\n", "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from jupyter-cache>=0.5->myst_nb) (8.1.7)\n", "Requirement already satisfied: sqlalchemy<3,>=1.3.12 in /usr/local/lib/python3.10/dist-packages (from jupyter-cache>=0.5->myst_nb) (2.0.23)\n", "Requirement already satisfied: tabulate in /usr/local/lib/python3.10/dist-packages (from jupyter-cache>=0.5->myst_nb) (0.9.0)\n", "Requirement already satisfied: docutils<0.21,>=0.16 in /usr/local/lib/python3.10/dist-packages (from myst-parser>=1.0.0->myst_nb) (0.18.1)\n", "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from myst-parser>=1.0.0->myst_nb) (3.1.2)\n", "Requirement already satisfied: markdown-it-py~=3.0 in /usr/local/lib/python3.10/dist-packages (from myst-parser>=1.0.0->myst_nb) (3.0.0)\n", "Requirement already satisfied: mdit-py-plugins~=0.4 in /usr/local/lib/python3.10/dist-packages (from myst-parser>=1.0.0->myst_nb) (0.4.0)\n", "Requirement already satisfied: jupyter-client>=6.1.12 in /usr/local/lib/python3.10/dist-packages (from nbclient->myst_nb) (6.1.12)\n", "Requirement already satisfied: jupyter-core!=5.0.*,>=4.12 in /usr/local/lib/python3.10/dist-packages (from nbclient->myst_nb) (5.5.0)\n", "Requirement already satisfied: traitlets>=5.4 in /usr/local/lib/python3.10/dist-packages (from nbclient->myst_nb) (5.7.1)\n", "Requirement already satisfied: fastjsonschema in /usr/local/lib/python3.10/dist-packages (from nbformat>=5.0->myst_nb) (2.19.0)\n", "Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.10/dist-packages (from nbformat>=5.0->myst_nb) (4.19.2)\n", "Requirement already satisfied: sphinxcontrib-applehelp in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.0.7)\n", "Requirement already satisfied: sphinxcontrib-devhelp in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.0.5)\n", "Requirement already satisfied: sphinxcontrib-jsmath in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.0.1)\n", "Requirement already satisfied: sphinxcontrib-htmlhelp>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (2.0.4)\n", "Requirement already satisfied: sphinxcontrib-serializinghtml>=1.1.9 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.1.9)\n", "Requirement already satisfied: sphinxcontrib-qthelp in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.0.6)\n", "Requirement already satisfied: Pygments>=2.14 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (2.16.1)\n", "Requirement already satisfied: snowballstemmer>=2.0 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (2.2.0)\n", "Requirement already satisfied: babel>=2.9 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (2.13.1)\n", "Requirement already satisfied: alabaster<0.8,>=0.7 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (0.7.13)\n", "Requirement already satisfied: imagesize>=1.3 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (1.4.1)\n", "Requirement already satisfied: requests>=2.25.0 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (2.31.0)\n", "Requirement already satisfied: packaging>=21.0 in /usr/local/lib/python3.10/dist-packages (from sphinx>=5->myst_nb) (23.2)\n", "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.10/dist-packages (from importlib_metadata->myst_nb) (3.17.0)\n", "Requirement already satisfied: ipython-genutils in /usr/local/lib/python3.10/dist-packages (from ipykernel->myst_nb) (0.2.0)\n", "Requirement already satisfied: tornado>=4.2 in /usr/local/lib/python3.10/dist-packages (from ipykernel->myst_nb) (6.3.2)\n", "Requirement already satisfied: setuptools>=18.5 in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (67.7.2)\n", "Requirement already satisfied: jedi>=0.16 in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (0.19.1)\n", "Requirement already satisfied: decorator in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (4.4.2)\n", "Requirement already satisfied: pickleshare in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (0.7.5)\n", "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (3.0.41)\n", "Requirement already satisfied: backcall in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (0.2.0)\n", "Requirement already satisfied: matplotlib-inline in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (0.1.6)\n", "Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.10/dist-packages (from ipython->myst_nb) (4.9.0)\n", "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from jedi>=0.16->ipython->myst_nb) (0.8.3)\n", "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->myst-parser>=1.0.0->myst_nb) (2.1.3)\n", "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat>=5.0->myst_nb) (2023.11.2)\n", "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat>=5.0->myst_nb) (0.31.1)\n", "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat>=5.0->myst_nb) (0.13.2)\n", "Requirement already satisfied: pyzmq>=13 in /usr/local/lib/python3.10/dist-packages (from jupyter-client>=6.1.12->nbclient->myst_nb) (23.2.1)\n", "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.10/dist-packages (from jupyter-client>=6.1.12->nbclient->myst_nb) (2.8.2)\n", "Requirement already satisfied: platformdirs>=2.5 in /usr/local/lib/python3.10/dist-packages (from jupyter-core!=5.0.*,>=4.12->nbclient->myst_nb) (4.1.0)\n", "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py~=3.0->myst-parser>=1.0.0->myst_nb) (0.1.2)\n", "Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.10/dist-packages (from pexpect>4.3->ipython->myst_nb) (0.7.0)\n", "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython->myst_nb) (0.2.12)\n", "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.25.0->sphinx>=5->myst_nb) (3.3.2)\n", "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.25.0->sphinx>=5->myst_nb) (3.6)\n", "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.25.0->sphinx>=5->myst_nb) (2.0.7)\n", "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.25.0->sphinx>=5->myst_nb) (2023.11.17)\n", "Requirement already satisfied: greenlet!=0.4.17 in /usr/local/lib/python3.10/dist-packages (from sqlalchemy<3,>=1.3.12->jupyter-cache>=0.5->myst_nb) (3.0.1)\n", "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.1->jupyter-client>=6.1.12->nbclient->myst_nb) (1.16.0)\n", "Requirement already satisfied: plotly in /usr/local/lib/python3.10/dist-packages (5.15.0)\n", "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (1.23.5)\n", "Requirement already satisfied: ipywidgets in /usr/local/lib/python3.10/dist-packages (7.7.1)\n", "Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from plotly) (8.2.3)\n", "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from plotly) (23.2)\n", "Requirement already satisfied: ipykernel>=4.5.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (5.5.6)\n", "Requirement already satisfied: ipython-genutils~=0.2.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (0.2.0)\n", "Requirement already satisfied: traitlets>=4.3.1 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (5.7.1)\n", "Requirement already satisfied: widgetsnbextension~=3.6.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (3.6.6)\n", "Requirement already satisfied: ipython>=4.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (7.34.0)\n", "Requirement already satisfied: jupyterlab-widgets>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from ipywidgets) (3.0.9)\n", "Requirement already satisfied: jupyter-client in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets) (6.1.12)\n", "Requirement already satisfied: tornado>=4.2 in /usr/local/lib/python3.10/dist-packages (from ipykernel>=4.5.1->ipywidgets) (6.3.2)\n", "Requirement already satisfied: setuptools>=18.5 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (67.7.2)\n", "Requirement already satisfied: jedi>=0.16 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (0.19.1)\n", "Requirement already satisfied: decorator in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (4.4.2)\n", "Requirement already satisfied: pickleshare in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (0.7.5)\n", "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (3.0.41)\n", "Requirement already satisfied: pygments in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (2.16.1)\n", "Requirement already satisfied: backcall in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (0.2.0)\n", "Requirement already satisfied: matplotlib-inline in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (0.1.6)\n", "Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.10/dist-packages (from ipython>=4.0.0->ipywidgets) (4.9.0)\n", "Requirement already satisfied: notebook>=4.4.1 in /usr/local/lib/python3.10/dist-packages (from widgetsnbextension~=3.6.0->ipywidgets) (6.5.5)\n", "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets) (0.8.3)\n", "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (3.1.2)\n", "Requirement already satisfied: pyzmq<25,>=17 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (23.2.1)\n", "Requirement already satisfied: argon2-cffi in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (23.1.0)\n", "Requirement already satisfied: jupyter-core>=4.6.1 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (5.5.0)\n", "Requirement already satisfied: nbformat in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (5.9.2)\n", "Requirement already satisfied: nbconvert>=5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (6.5.4)\n", "Requirement already satisfied: nest-asyncio>=1.5 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.5.8)\n", "Requirement already satisfied: Send2Trash>=1.8.0 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.8.2)\n", "Requirement already satisfied: terminado>=0.8.3 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.18.0)\n", "Requirement already satisfied: prometheus-client in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.19.0)\n", "Requirement already satisfied: nbclassic>=0.4.7 in /usr/local/lib/python3.10/dist-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.0.0)\n", "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.10/dist-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets) (2.8.2)\n", "Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.10/dist-packages (from pexpect>4.3->ipython>=4.0.0->ipywidgets) (0.7.0)\n", "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=4.0.0->ipywidgets) (0.2.12)\n", "Requirement already satisfied: platformdirs>=2.5 in /usr/local/lib/python3.10/dist-packages (from jupyter-core>=4.6.1->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (4.1.0)\n", "Requirement already satisfied: jupyter-server>=1.8 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.24.0)\n", "Requirement already satisfied: notebook-shim>=0.2.3 in /usr/local/lib/python3.10/dist-packages (from nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.2.3)\n", "Requirement already satisfied: lxml in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (4.9.3)\n", "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (4.11.2)\n", "Requirement already satisfied: bleach in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (6.1.0)\n", "Requirement already satisfied: defusedxml in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.7.1)\n", "Requirement already satisfied: entrypoints>=0.2.2 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.4)\n", "Requirement already satisfied: jupyterlab-pygments in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.3.0)\n", "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (2.1.3)\n", "Requirement already satisfied: mistune<2,>=0.8.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.8.4)\n", "Requirement already satisfied: nbclient>=0.5.0 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.9.0)\n", "Requirement already satisfied: pandocfilters>=1.4.1 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.5.0)\n", "Requirement already satisfied: tinycss2 in /usr/local/lib/python3.10/dist-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.2.1)\n", "Requirement already satisfied: fastjsonschema in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (2.19.0)\n", "Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.10/dist-packages (from nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (4.19.2)\n", "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.1->jupyter-client->ipykernel>=4.5.1->ipywidgets) (1.16.0)\n", "Requirement already satisfied: argon2-cffi-bindings in /usr/local/lib/python3.10/dist-packages (from argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (21.2.0)\n", "Requirement already satisfied: attrs>=22.2.0 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (23.1.0)\n", "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (2023.11.2)\n", "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.31.1)\n", "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=2.6->nbformat->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.13.2)\n", "Requirement already satisfied: anyio<4,>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (3.7.1)\n", "Requirement already satisfied: websocket-client in /usr/local/lib/python3.10/dist-packages (from jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.7.0)\n", "Requirement already satisfied: cffi>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from argon2-cffi-bindings->argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.16.0)\n", "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (2.5)\n", "Requirement already satisfied: webencodings in /usr/local/lib/python3.10/dist-packages (from bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (0.5.1)\n", "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.1.0->jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (3.6)\n", "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.1.0->jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.3.0)\n", "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.1.0->jupyter-server>=1.8->nbclassic>=0.4.7->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (1.2.0)\n", "Requirement already satisfied: pycparser in /usr/local/lib/python3.10/dist-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets) (2.21)\n" ] } ], "source": [ "%deleteCell\n", "# @title Установка либ { display-mode: \"both\" }\n", "\n", "!pip install jupyterquiz\n", "!pip install matplotlib\n", "!pip install myst_nb\n", "%pip install plotly numpy ipywidgets" ] }, { "cell_type": "markdown", "id": "e848ea44-b8e7-434d-93c5-daae03977df8", "metadata": { "id": "e848ea44-b8e7-434d-93c5-daae03977df8" }, "source": [ "# Newton's method" ] }, { "cell_type": "markdown", "id": "hxZMUeYyAAjt", "metadata": { "id": "hxZMUeYyAAjt" }, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "id": "70641e3e-677f-4a75-91cd-325d50985c93", "metadata": { "id": "70641e3e-677f-4a75-91cd-325d50985c93" }, "source": [ "This chapter covers Newton's method, starting with its definition and application in solving problems, then detailing the basic steps for finding roots. It later continues with an in-depth exploration of the method." ] }, { "cell_type": "markdown", "id": "1pFa-89NC3qL", "metadata": { "id": "1pFa-89NC3qL" }, "source": [ "### The problem" ] }, { "cell_type": "markdown", "id": "n_ybAEAYC-Cn", "metadata": { "id": "n_ybAEAYC-Cn" }, "source": [ "Computers are fantastic at crunching numbers and executing algorithms, but analytical solutions to equations often involve symbolic manipulation and reasoning that can be complex. Some equations don't have closed-form solutions, meaning their solutions can't be expressed using a finite number of basic mathematical operations and functions.\n", "\n", "For example, consider the equation $x^5 - x + 1 = 0$. This equation doesn't have a simple analytical solution using elementary functions. In such cases, numerical methods come to the rescue. Computers can iteratively approach the solution with numerical techniques, providing approximate answers.\n", "\n", "In essence, computers excel at finding numerical solutions efficiently, but analytical solutions may be elusive for certain types of equations." ] }, { "cell_type": "markdown", "id": "qXdBieDHHj95", "metadata": { "id": "qXdBieDHHj95" }, "source": [ "# Linear Approximations\n", "\n", "Newton’s method, also known as the Newton-Raphson method, is a numerical method intended for approximating the roots or zeros of a function. There are several versions of the method, we will start with the most basic one – the version that iteratively uses a tangent line of a function to approximate the roots.\n", "\n", "This basic version of Newton’s Method has a high convergence speed, especially when the initial guess is already a close approximate to the root of the function. However, this version might be sensitive to the choice of initial guess. It can also fail converging if the initial approximation is close to the singular points of a function.\n", "\n", "Linear Approximations are widely used in numerical methods to solve equations, and for optimization. This method is applied in engineering, physics, economics, computer science and other fields.\n", "\n", "Here is basic usage of this method" ] }, { "cell_type": "code", "execution_count": 44, "id": "P24C6Plju6Zi", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "P24C6Plju6Zi", "outputId": "83e7ab02-6066-43bb-c27b-300b64331ef0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Current approximation root: 26.03846153846154\n", "Current approximation root: 13.096040222701967\n", "Current approximation root: 6.700738029591109\n", "Current approximation root: 3.648843599411131\n", "Current approximation root: 2.372540661342378\n", "Current approximation root: 2.0292485070150263\n", "Current approximation root: 2.0002107861998297\n", "Current approximation root: 2.0000000111065352\n", "Current approximation root: 2.0\n", "Approximate root: 2.0\n", "Number of iterations: 9\n" ] } ], "source": [ "def newton_method(func, func_derivative, initial_guess, tol=1e-6, max_iter=100):\n", "\n", " x = initial_guess\n", " for iteration in range(max_iter):\n", " f_x = func(x)\n", " f_prime_x = func_derivative(x)\n", "\n", " # Avoid division by zero\n", " if abs(f_prime_x) < tol:\n", " raise ValueError(\"Derivative is close to zero. Newton's method may not converge.\")\n", "\n", " # Newton's method update\n", " x = x - f_x / f_prime_x\n", "\n", " print(f\"Current approximation root: {x}\")\n", "\n", " # Check for convergence\n", " if abs(f_x) < tol:\n", " return x, iteration + 1\n", "\n", " raise ValueError(\"Newton's method did not converge within the maximum number of iterations.\")\n", "\n", "# Example usage:\n", "def example_function(x):\n", " return x**2 - 4\n", "\n", "def example_derivative(x):\n", " return 2 * x\n", "\n", "initial_guess = 52\n", "root, num_iter = newton_method(example_function, example_derivative, initial_guess)\n", "\n", "print(f\"Approximate root: {root}\")\n", "print(f\"Number of iterations: {num_iter}\")" ] }, { "cell_type": "markdown", "id": "ZezuRHpj3Q-j", "metadata": { "id": "ZezuRHpj3Q-j" }, "source": [ "![iterations](assets/iterations.gif)" ] }, { "cell_type": "markdown", "id": "Af5CnUqHHEXh", "metadata": { "id": "Af5CnUqHHEXh" }, "source": [ "## One Dimensional Newton's Method" ] }, { "cell_type": "markdown", "id": "saX_HyoBHrVV", "metadata": { "id": "saX_HyoBHrVV" }, "source": [ "Newton's method, also known as the tangent method, provides an efficient way to solve equations numerically. This method allows you to find approximate values of the roots of functions. Here's how it works:\n", "\n", "1. **Selecting an initial guess:** Start by choosing an initial guess $x_0$, which can be any number.\n", "\n", "2. **Calculation of function and derivative:** Calculate the value of the function at the point $x_0$ as $f(x_0)$ and the value of the derivative of the function at this point as $f'(x_0)$.\n", "\n", "3. **Drawing up the equation of a tangent line:** The equation of a tangent line at the point $x_0$ is represented as: $y = f(x_0) + f'(x_0)(x - x_0)$.\n", "\n", "4. **Finding the root of the tangent line equation:** Find the root of the tangent line equation, that is, the value of $x_1$ at which $f(x_1) = 0$. This value $x_1$ becomes a new approximation to the root of the function.\n", "\n", "5. **Repeat the process:** Continue to apply these steps iteratively using formula at each iteration.\n", "```{math}\n", ":label: newton-step\n", "x_{n+1} = x_n - \\frac{f(x_n)}{f'(x_n)}\n", "```\n", "\n", "### Example\n", "To demonstrate this process, let's use the function $f(x)=x^{2}-2$\n", "\n", "1. For an initial guess we are going to choose $x_{0}=1.5$, since $1$ is too small and $2$ is too large.\n", "\n", "2. The value of the function at $x_{0}$ equals $f(x_{0})=f(1.5)=1.5^{2}-2=0.25$ The value of the derivative of the function at $x_{0}$ equals $f'(x_{0})=f'(1.5)=2$\n", "\n", "3. Drawing the equation of a tangent line $y=f(x_{0})+f'(x_{0})(x-x_{0})=0.25+3(x-1.5)=0.25+3x-4.5=3x-4.25$\n", "![tangent](assets/tangent.png)\n", "\n", "4. The root of the tangent line equation is $x=1.416667$, thus the $x_{1}=1.416667$\n", "\n", "5. Iterating the process, we get\n", "Second iteration: \n", "$x_{1}=1.416667$, $f(x_{1})=0.00694539$, $f'(x_{1})=2.83334$, $y=2.83334x-4.00695$, \n", "$x_{2}=1.414214$ \n", "Third iteration: \n", "$x_{2}=1.414214$, $f(x_{1})=0.00000124$, $f'(x_{1})=2.828428$, $y=2.828428x-4.00000124$, \n", "$x_{3}=1.414214$ \n", "Now since $x_{2}=x_{3}$ we can stop the iterating process and conclude that $x=1.414214$.\n", "\n", "\n", "Newton's method converges to the root of the function, providing an exact solution with a sufficiently close initial approximation. This method is widely used in various fields to solve equations." ] }, { "cell_type": "code", "execution_count": 45, "id": "As4PAlqVMbEr", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 245 }, "id": "As4PAlqVMbEr", "outputId": "f30a30bc-8291-4921-873c-51f042450811" }, "outputs": [ { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "var questionshsnlBmpGXnbK=[{\"title\": \"Quiz: One Dimensional Newton's Method\", \"question\": \"Consider the function $f(x) = x^2 - 4$ with an initial guess $x_0 = 4$. After one iteration of Newton's Method, what is the value of $x_1$?\", \"type\": \"numeric\", \"answers\": [{\"value\": \"2.5\", \"correct\": true}], \"explanation\": \"Using Newton's Method: $x_1 = x_0 - f(x_0) / f'(x_0)$. Here, $f'(x) = 2x$. So, $x1 = 3 - (4^2 - 4) / (2 * 4) = 2.5$.\"}];\n // Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
  1. Copy the text in this cell below \"Answer String\"
  2. Double click on the cell directly below the Answer String, labeled \"Replace Me\"
  3. Select the whole \"Replace Me\" text
  4. Paste in your answer string and press shift-Enter.
  5. Save the notebook using the save icon or File->Save Notebook menu item



  6. Answer String:
    ';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
    \";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n\n //console.log(answers);\n\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n\n if (fb.dataset.numcorrect == 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n \n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else {\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.textContent = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.textContent = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n var shuffled;\n if (shuffle_answers == \"True\") {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\n\n //Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n aDiv.append(lab);\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if (\"precision\" in ths.dataset) {\n var precision = ths.dataset.precision;\n // console.log(\"1:\", submission)\n submission = Math.round((1 * submission + Number.EPSILON) * 10 ** precision) / 10 ** precision;\n // console.log(\"Rounded to \", submission, \" precision=\", precision );\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.textContent = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n if (submission == answer.value) {\n if (\"feedback\" in answer) {\n fb.textContent = jaxify(answer.feedback);\n } else {\n fb.textContent = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n //console.log(answer.range);\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.textContent = jaxify(answer.feedback);\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n defaultFB = answer.feedback;\n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n return false;\n }\n\n}\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.textContent = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\nfunction jaxify(string) {\n var mystring = string;\n\n var count = 0;\n var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n\n var count2 = 0;\n var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n\n //console.log(loc);\n\n while ((loc >= 0) || (loc2 >= 0)) {\n\n /* Have to replace all the double $$ first with current implementation */\n if (loc2 >= 0) {\n if (count2 % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n }\n count2++;\n } else {\n if (count % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n }\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n }\n\n //console.log(mystring);\n return mystring;\n}\n\n\nfunction show_questions(json, mydiv) {\n console.log('show_questions');\n //var mydiv=document.getElementById(myid);\n var shuffle_questions = mydiv.dataset.shufflequestions;\n var num_questions = mydiv.dataset.numquestions;\n var shuffle_answers = mydiv.dataset.shuffleanswers;\n var max_width = mydiv.dataset.maxwidth;\n\n if (num_questions > json.length) {\n num_questions = json.length;\n }\n\n var questions;\n if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n //console.log(num_questions+\",\"+json.length);\n questions = getRandomSubarray(json, num_questions);\n } else {\n questions = json;\n }\n\n //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n\n // Iterate over questions\n questions.forEach((qa, index, array) => {\n //console.log(qa.question); \n\n var id = makeid(8);\n //console.log(id);\n\n\n // Create Div to contain question and answers\n var iDiv = document.createElement('div');\n //iDiv.id = 'quizWrap' + id + index;\n iDiv.id = 'quizWrap' + id;\n iDiv.className = 'Quiz';\n iDiv.setAttribute('data-qnum', index);\n iDiv.style.maxWidth =max_width+\"px\";\n mydiv.appendChild(iDiv);\n // iDiv.innerHTML=qa.question;\n \n var outerqDiv = document.createElement('div');\n outerqDiv.id = \"OuterquizQn\" + id + index;\n // Create div to contain question part\n var qDiv = document.createElement('div');\n qDiv.id = \"quizQn\" + id + index;\n \n if (qa.question) {\n iDiv.append(outerqDiv);\n\n //qDiv.textContent=qa.question;\n qDiv.innerHTML = jaxify(qa.question);\n outerqDiv.append(qDiv);\n }\n\n // Create div for code inside question\n var codeDiv;\n if (\"code\" in qa) {\n codeDiv = document.createElement('div');\n codeDiv.id = \"code\" + id + index;\n codeDiv.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeDiv.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = qa.code;\n outerqDiv.append(codeDiv);\n //console.log(codeDiv);\n }\n\n\n // Create div to contain answer part\n var aDiv = document.createElement('div');\n aDiv.id = \"quizAns\" + id + index;\n aDiv.className = 'Answer';\n iDiv.append(aDiv);\n\n //console.log(qa.type);\n\n var num_correct;\n if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n if (\"answer_cols\" in qa) {\n //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n }\n } else if (qa.type == \"numeric\") {\n //console.log(\"numeric\");\n make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n }\n\n\n //Make div for feedback\n var fb = document.createElement(\"div\");\n fb.id = \"fb\" + id;\n //fb.style=\"font-size: 20px;text-align:center;\";\n fb.className = \"Feedback\";\n fb.setAttribute(\"data-answeredcorrect\", 0);\n fb.setAttribute(\"data-numcorrect\", num_correct);\n iDiv.append(fb);\n\n\n });\n var preserveResponses = mydiv.dataset.preserveresponses;\n console.log(preserveResponses);\n console.log(preserveResponses == \"true\");\n if (preserveResponses == \"true\") {\n console.log(preserveResponses);\n // Create Div to contain record of answers\n var iDiv = document.createElement('div');\n iDiv.id = 'responses' + mydiv.id;\n iDiv.className = 'JCResponses';\n // Create a place to store responses as an empty array\n iDiv.setAttribute('data-responses', '[]');\n\n // Dummy Text\n iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n //iDiv.className = 'Quiz';\n mydiv.appendChild(iDiv);\n }\n//console.log(\"At end of show_questions\");\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n return false;\n}\n/* This is to handle asynchrony issues in loading Jupyter notebooks\n where the quiz has been previously run. The Javascript was generally\n being run before the div was added to the DOM. I tried to do this\n more elegantly using Mutation Observer, but I didn't get it to work.\n\n Someone more knowledgeable could make this better ;-) */\n\n function try_show() {\n if(document.getElementById(\"hsnlBmpGXnbK\")) {\n show_questions(questionshsnlBmpGXnbK, hsnlBmpGXnbK); \n } else {\n setTimeout(try_show, 200);\n }\n };\n \n {\n // console.log(element);\n\n //console.log(\"hsnlBmpGXnbK\");\n // console.log(document.getElementById(\"hsnlBmpGXnbK\"));\n\n try_show();\n }\n ", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%deleteInput\n", "from jupyterquiz import display_quiz\n", "\n", "quiz = [{\n", " \"title\": \"Quiz: One Dimensional Newton's Method\",\n", "\n", " \"question\": \"Consider the function $f(x) = x^2 - 4$ with an initial guess $x_0 = 4$. After one iteration of Newton's Method, what is the value of $x_1$?\",\n", " \"type\": \"numeric\",\n", " \"answers\": [\n", " {\"value\": \"2.5\", \"correct\": True}\n", " ],\n", " \"explanation\": \"Using Newton's Method: $x_1 = x_0 - f(x_0) / f'(x_0)$. Here, $f'(x) = 2x$. So, $x1 = 3 - (4^2 - 4) / (2 * 4) = 2.5$.\"\n", " }\n", " ]\n", "\n", "display_quiz(quiz)\n" ] }, { "cell_type": "markdown", "id": "7597U5UcvrLZ", "metadata": { "id": "7597U5UcvrLZ" }, "source": [ "## Pros and Cons\n", "\n", "Newton's Method is a powerful tool for finding roots of a function, though besides its advantages it also has some disadvantages too. We will consider the most important notable ones.\n", "\n", "### Rapid Convergence\n", "Newton's method has a quadratic convergence, thus allowing for a rapid root finding process.\n", "\n", "````{admonition} Proof for those interested\n", ":class: tip, dropdown\n", "Based on Taylor's theorem, a function $f(x)$ with a continuous second derivative can be represented by an expansion about a point that is close to a root of $f(x)$. If we suppose that the root is $α$, then the expansion of $f(α)$ about $x_{n}$ is\n", "\n", "```{math}\n", "f(\\alpha)=f(x_{n})+{f}'(x_{n})(\\alpha-x_{n})+R_{1}\n", "```\n", "\n", "where $R_{1}=\\frac{1}{2!}{f}''(\\xi _{n})(\\alpha - x_{n})^{2}$.\n", "\n", "Taking into consideration that $α$ is the root, we get\n", "\n", "```{math}\n", "0=f(\\alpha)=f(x_{n})+{f}'(x_{n})(\\alpha-x_{n})+\\frac{1}{2!}{f}''(\\xi_{n})(\\alpha - x_{n})^{2}\n", "```\n", "\n", "Further dividing by ${f}'(x_{n})$ gives us\n", "\n", "```{math}\n", "\\frac{f(x_{n})}{{f}'(x_{n})}+(\\alpha - x_{n})=\\frac{-{f}''(\\xi_{n})}{2{f}'(x_{n})}(\\alpha-x_{n})^{2}\n", "```\n", "\n", "Applying {eq}`newton-step`, we get\n", "\n", "```{math}\n", "\\alpha - x_{n+1}=\\frac{-{f}''(\\xi_{n})}{2{f}'(x_{n})}(\\alpha - x_{n})^{2}\n", "```\n", "\n", "where $\\xi_{n}=\\alpha - x_{n}$, thus\n", "\n", "```{math}\n", "\\xi_{n+1}=\\frac{-{f}''(\\xi_{n})}{2{f}'(x_{n})}\\xi_{n}^{2}\n", "```\n", "\n", "And taking the absolute values gives us\n", "\n", "```{math}\n", ":label: quadratic-convergence\n", "\\left |\\xi_{n+1} \\right |=\\frac{\\left |{f}''(\\xi_{n}) \\right |}{2\\left |{f}'(x_{n}) \\right |}\\xi_{n}^{2}\n", "```\n", "\n", "Thus, equation {eq}`quadratic-convergence` shows that the order of convergence is at least quadratic if\n", "\n", "1. ${f}'(x)\\neq 0$, for all $x\\in I$ where $I$ is the interval $\\left [ \\alpha - \\left | \\varepsilon_{0} \\right |, \\alpha + \\left | \\varepsilon_{0} \\right | \\right ]$\n", "2. ${f}''(x)$ is continuous, for all $x \\in I$\n", "3. $M\\left | \\varepsilon_{0} \\right |<1$\n", "\n", "````\n", "\n", "### Initial Guess Sensitivity\n", "Newton's method might not converge in case if initial guess was a poor estimate.\n", "\n", "````{admonition} Proof\n", "For example, let's consider a function $f(x)=x^{3}-2x+2$ with a $0$ as the initial guess. Using the formula {eq}`newton-step`, we get that $x_{1}$ is $1$.\n", "\n", "However, the second iteration gives us the value of $0$ again. So in this case we are stuck in a loop.\n", "````\n", "\n", "Newton's method continues to be one of the most important tools in numerical analysis and equations solving. Though its application requires careful consideration of the pros and cons in the context of a specific task." ] }, { "cell_type": "markdown", "id": "fv8neImZAjQP", "metadata": { "id": "fv8neImZAjQP" }, "source": [ "![problems](assets/NewtonProblems.png)" ] }, { "cell_type": "code", "execution_count": 46, "id": "t1JgfuZFf6Al", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 291 }, "id": "t1JgfuZFf6Al", "outputId": "d49fd10f-b2c4-4b94-ae34-e44c0a5b1d91" }, "outputs": [ { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
    " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "var questionstpcVvfttvzVt=[{\"question\": \"What does Newton's Method primarily use to find the root of a function?\", \"type\": \"multiple_choice\", \"answers\": [{\"answer\": \"First derivative of the function\", \"correct\": false, \"feedback\": \"\"}, {\"answer\": \"Second derivative of the function\", \"correct\": false, \"feedback\": \"\"}, {\"answer\": \"Integral of the function\", \"correct\": false, \"feedback\": \"\"}, {\"answer\": \"Both first and second derivatives of the function\", \"correct\": true, \"feedback\": \"Newton's Method uses both the first (for the slope) and second derivatives (for curvature) of the function to find its root.\"}]}];\n // Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
    1. Copy the text in this cell below \"Answer String\"
    2. Double click on the cell directly below the Answer String, labeled \"Replace Me\"
    3. Select the whole \"Replace Me\" text
    4. Paste in your answer string and press shift-Enter.
    5. Save the notebook using the save icon or File->Save Notebook menu item



    6. Answer String:
      ';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
      \";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n\n //console.log(answers);\n\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n\n if (fb.dataset.numcorrect == 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n \n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else {\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.textContent = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.textContent = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n var shuffled;\n if (shuffle_answers == \"True\") {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\n\n //Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n aDiv.append(lab);\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if (\"precision\" in ths.dataset) {\n var precision = ths.dataset.precision;\n // console.log(\"1:\", submission)\n submission = Math.round((1 * submission + Number.EPSILON) * 10 ** precision) / 10 ** precision;\n // console.log(\"Rounded to \", submission, \" precision=\", precision );\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.textContent = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n if (submission == answer.value) {\n if (\"feedback\" in answer) {\n fb.textContent = jaxify(answer.feedback);\n } else {\n fb.textContent = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n //console.log(answer.range);\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.textContent = jaxify(answer.feedback);\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n defaultFB = answer.feedback;\n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n return false;\n }\n\n}\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.textContent = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\nfunction jaxify(string) {\n var mystring = string;\n\n var count = 0;\n var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n\n var count2 = 0;\n var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n\n //console.log(loc);\n\n while ((loc >= 0) || (loc2 >= 0)) {\n\n /* Have to replace all the double $$ first with current implementation */\n if (loc2 >= 0) {\n if (count2 % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n }\n count2++;\n } else {\n if (count % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n }\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n }\n\n //console.log(mystring);\n return mystring;\n}\n\n\nfunction show_questions(json, mydiv) {\n console.log('show_questions');\n //var mydiv=document.getElementById(myid);\n var shuffle_questions = mydiv.dataset.shufflequestions;\n var num_questions = mydiv.dataset.numquestions;\n var shuffle_answers = mydiv.dataset.shuffleanswers;\n var max_width = mydiv.dataset.maxwidth;\n\n if (num_questions > json.length) {\n num_questions = json.length;\n }\n\n var questions;\n if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n //console.log(num_questions+\",\"+json.length);\n questions = getRandomSubarray(json, num_questions);\n } else {\n questions = json;\n }\n\n //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n\n // Iterate over questions\n questions.forEach((qa, index, array) => {\n //console.log(qa.question); \n\n var id = makeid(8);\n //console.log(id);\n\n\n // Create Div to contain question and answers\n var iDiv = document.createElement('div');\n //iDiv.id = 'quizWrap' + id + index;\n iDiv.id = 'quizWrap' + id;\n iDiv.className = 'Quiz';\n iDiv.setAttribute('data-qnum', index);\n iDiv.style.maxWidth =max_width+\"px\";\n mydiv.appendChild(iDiv);\n // iDiv.innerHTML=qa.question;\n \n var outerqDiv = document.createElement('div');\n outerqDiv.id = \"OuterquizQn\" + id + index;\n // Create div to contain question part\n var qDiv = document.createElement('div');\n qDiv.id = \"quizQn\" + id + index;\n \n if (qa.question) {\n iDiv.append(outerqDiv);\n\n //qDiv.textContent=qa.question;\n qDiv.innerHTML = jaxify(qa.question);\n outerqDiv.append(qDiv);\n }\n\n // Create div for code inside question\n var codeDiv;\n if (\"code\" in qa) {\n codeDiv = document.createElement('div');\n codeDiv.id = \"code\" + id + index;\n codeDiv.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeDiv.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = qa.code;\n outerqDiv.append(codeDiv);\n //console.log(codeDiv);\n }\n\n\n // Create div to contain answer part\n var aDiv = document.createElement('div');\n aDiv.id = \"quizAns\" + id + index;\n aDiv.className = 'Answer';\n iDiv.append(aDiv);\n\n //console.log(qa.type);\n\n var num_correct;\n if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n if (\"answer_cols\" in qa) {\n //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n }\n } else if (qa.type == \"numeric\") {\n //console.log(\"numeric\");\n make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n }\n\n\n //Make div for feedback\n var fb = document.createElement(\"div\");\n fb.id = \"fb\" + id;\n //fb.style=\"font-size: 20px;text-align:center;\";\n fb.className = \"Feedback\";\n fb.setAttribute(\"data-answeredcorrect\", 0);\n fb.setAttribute(\"data-numcorrect\", num_correct);\n iDiv.append(fb);\n\n\n });\n var preserveResponses = mydiv.dataset.preserveresponses;\n console.log(preserveResponses);\n console.log(preserveResponses == \"true\");\n if (preserveResponses == \"true\") {\n console.log(preserveResponses);\n // Create Div to contain record of answers\n var iDiv = document.createElement('div');\n iDiv.id = 'responses' + mydiv.id;\n iDiv.className = 'JCResponses';\n // Create a place to store responses as an empty array\n iDiv.setAttribute('data-responses', '[]');\n\n // Dummy Text\n iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n //iDiv.className = 'Quiz';\n mydiv.appendChild(iDiv);\n }\n//console.log(\"At end of show_questions\");\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n return false;\n}\n/* This is to handle asynchrony issues in loading Jupyter notebooks\n where the quiz has been previously run. The Javascript was generally\n being run before the div was added to the DOM. I tried to do this\n more elegantly using Mutation Observer, but I didn't get it to work.\n\n Someone more knowledgeable could make this better ;-) */\n\n function try_show() {\n if(document.getElementById(\"tpcVvfttvzVt\")) {\n show_questions(questionstpcVvfttvzVt, tpcVvfttvzVt); \n } else {\n setTimeout(try_show, 200);\n }\n };\n \n {\n // console.log(element);\n\n //console.log(\"tpcVvfttvzVt\");\n // console.log(document.getElementById(\"tpcVvfttvzVt\"));\n\n try_show();\n }\n ", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%deleteInput\n", "from jupyterquiz import display_quiz\n", "\n", "quiz = [{\n", " \"question\": \"What does Newton's Method primarily use to find the root of a function?\",\n", " \"type\": \"multiple_choice\",\n", " \"answers\": [\n", " {\n", " \"answer\": \"First derivative of the function\",\n", " \"correct\": False,\n", " \"feedback\": \"\"\n", " },\n", " {\n", " \"answer\": \"Second derivative of the function\",\n", " \"correct\": False,\n", " \"feedback\": \"\"\n", " },\n", " {\n", " \"answer\": \"Integral of the function\",\n", " \"correct\": False,\n", " \"feedback\": \"\"\n", " },\n", " {\n", " \"answer\": \"Both first and second derivatives of the function\",\n", " \"correct\": True,\n", " \"feedback\": \"Newton's Method uses both the first (for the slope) and second derivatives (for curvature) of the function to find its root.\"\n", " }\n", " ]\n", " }]\n", "\n", "display_quiz(quiz)\n" ] }, { "cell_type": "markdown", "id": "zGh8TkQ3HI3S", "metadata": { "id": "zGh8TkQ3HI3S" }, "source": [ "# Newton's method for optimization\n", "\n" ] }, { "cell_type": "markdown", "id": "MoaGa8-iHwoy", "metadata": { "id": "MoaGa8-iHwoy" }, "source": [ "Let's make **second order** Taylor expansion around $x_k$ given $f:\\mathbb{R} \\rightarrow \\mathbb{R}$ to solve optimization problem\n", "\n", "```{math}\n", ":label: newton-optimisation-problem\n", "\\min_{x \\in \\mathbb{R}} f(x)\n", "```\n", "\n", "$f(\\pmb{x}+\\Delta \\pmb{x})\\approx f(\\pmb{x}) + \\langle \\nabla f(\\pmb{x}),\\Delta \\pmb{x} \\rangle+\\frac{1}{2}\\langle \\Delta \\pmb{x}, B(\\pmb{x})\\Delta \\pmb{x} \\rangle$\n", "\n", "to find minimum, let's set gradient of above equation to zero\n", "\n", "this gives us\n", "\n", "$\\Delta \\pmb{x} = [B(\\pmb{x})]^{-1}\\nabla f(\\pmb{x})$\n", "\n", "let's label $B_k=B(\\pmb{x}_k), H_k=B_k^{-1}$\n", "\n", "this gives us iterative Newton's method step\n", "\n", "```{math}\n", ":label: newton-main-step\n", "\\pmb{x}_{k+1}=\\pmb{x}_k - H_k\\nabla f(\\pmb{x}_k)\n", "```\n", "\n", "where $\\nabla f(\\pmb{x})$ looks like vector\n", "$\n", "\\nabla f(\\pmb{x}) =\n", "\\begin{pmatrix}\n", "\\frac{\\partial{f(\\pmb{x})}}{\\partial{x_{1}}} & \\frac{\\partial{f(\\pmb{x})}}{\\partial{x_{2}}} & \\frac{\\partial{f(\\pmb{x})}}{\\partial{x_{3}}} & ... \\\\\n", "\\end{pmatrix}\n", "$\n", "\n", "and $B(\\pmb{x})$ looks like matrix\n", "$\n", "B(\\pmb{x}) =\n", "\\begin{pmatrix}\n", "\\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_1}\\partial{x_1}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_1}\\partial{x_2}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_1}\\partial{x_3}} & ... \\\\\n", "\\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_2}\\partial{x_1}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_2}\\partial{x_2}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_2}\\partial{x_3}} & ... \\\\\n", "\\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_3}\\partial{x_1}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_3}\\partial{x_2}} & \\frac{\\partial^2{f(\\pmb{x})}}{\\partial{x_3}\\partial{x_3}} & ... \\\\\n", "\\end{pmatrix}\n", "$\n", "\n", "````{admonition} Note\n", ":class: tip\n", "It can converge to a saddle point instead of to a local minimum\n", "````" ] }, { "cell_type": "code", "execution_count": 47, "id": "_w_kApHy3t8h", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "_w_kApHy3t8h", "outputId": "901bfb59-51de-4839-ee4e-486cfaa6ed8e" }, "outputs": [ { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "\n", "\n", "
      \n", "
      \n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# %% [code]\n", "%deleteInput\n", "import numpy as np\n", "import plotly.graph_objects as go\n", "from myst_nb import glue\n", "\n", "# Define the function and its Jacobian (partial derivatives)\n", "def func(x, y):\n", " return x**2 - 2*y**2 + 2\n", "\n", "def jac(x, y):\n", " return np.array([2 * x, -4 * y])\n", "\n", "def hec():\n", " return np.array([[2, 0], [0, -4]]) # Hessian matrix for this example\n", "\n", "# Newton's Method for Multivariate Optimization\n", "def newtons_method(initial_guess, max_iterations=20, tolerance=1e-6, lr=0.01):\n", " x, y = initial_guess\n", "\n", " x_vals = [x]\n", " y_vals = [y]\n", "\n", " for i in range(max_iterations):\n", " gradient = jac(x, y)\n", " hessian_inv = np.linalg.inv(hec())\n", "\n", " update = lr * np.dot(hessian_inv, gradient)\n", " x -= update[0]\n", " y -= update[1]\n", "\n", " x_vals.append(x)\n", " y_vals.append(y)\n", "\n", " if np.linalg.norm(gradient) < tolerance:\n", " break\n", "\n", " return x_vals, y_vals\n", "\n", "# Visualize the Optimization Path\n", "initial_guess = np.array([2.0, 2.0])\n", "x_opt, y_opt = newtons_method(initial_guess,lr=0.1)\n", "\n", "# Create a surface plot of the function\n", "x_vals = np.linspace(-3, 3, 100)\n", "y_vals = np.linspace(-3, 3, 100)\n", "X, Y = np.meshgrid(x_vals, y_vals)\n", "Z = func(X, Y)\n", "\n", "fig = go.Figure(\n", " data=[go.Surface(z=Z, x=X, y=Y, colorscale='Viridis', opacity=0.8)],\n", " layout=go.Layout(\n", " title=\"Start Title\",\n", " updatemenus=[dict(\n", " type=\"buttons\",\n", " buttons=[dict(label=\"Play\",\n", " method=\"animate\",\n", " args=[None])])]\n", " ),\n", "frames=[go.Frame(\n", " data=[go.Scatter3d(\n", " x=x_opt[:k+1],\n", " y=y_opt[:k+1],\n", " z=[func(x, y) for x, y in zip(x_opt[:k+1], y_opt[:k+1])],\n", " mode='markers+lines', marker=dict(size=5, color='red')\n", " )],\n", " traces= [0],\n", " name=f'frame{k}'\n", " )for k in range(len(x_opt)-1)]\n", ")\n", "\n", "# Add the surface plot\n", "fig.add_trace(go.Surface(z=Z, x=X, y=Y, colorscale='Viridis', opacity=0.8))\n", "\n", "\n", "# Set layout\n", "fig.update_layout(scene=dict(zaxis=dict(range=[-2, 10])))\n", "fig.update_layout(title=\"Saddle point\")\n", "\n", "# Set the backend to notebook for interactive plotting\n", "%matplotlib notebook\n", "\n", "fig.show()" ] }, { "cell_type": "markdown", "id": "VHxK726JKyT-", "metadata": { "id": "VHxK726JKyT-" }, "source": [ "```{admonition} Question\n", ":class: important, dropdown\n", "compare equation{eq}`newton-step` and equation{eq}`newton-main-step`.\n", "\n", "What's the difference?\n", "\n", "When to use the first and when to use the second?\n", "```\n", "\n", "### Implementation\n", "\n", "Now let's implement what we learned in the basic form." ] }, { "cell_type": "code", "execution_count": 48, "id": "9LscLJs4eJB5", "metadata": { "id": "9LscLJs4eJB5" }, "outputs": [], "source": [ "import numpy as np\n", "import plotly.graph_objects as go\n", "\n", "# Define the function and its Jacobian (partial derivatives)\n", "def func(x, y):\n", " return x**2 + 2*y**2 -2\n", "\n", "def jac(x, y):\n", " return np.array([2 * x, 4 * y])\n", "\n", "def hess():\n", " return np.array([[2, 0], [0, 4]]) # Hessian matrix for this example\n", "\n", "# Newton's Method for Multivariate Optimization\n", "def newtons_method(initial_guess, max_iterations=20, tolerance=1e-6, alpha=0.01):\n", " x, y = initial_guess\n", "\n", " x_vals = [x]\n", " y_vals = [y]\n", "\n", " for i in range(max_iterations):\n", " gradient = jac(x, y)\n", " hessian_inv = np.linalg.inv(hess())\n", "\n", " update = alpha * np.dot(hessian_inv, gradient)\n", " x -= update[0]\n", " y -= update[1]\n", "\n", " x_vals.append(x)\n", " y_vals.append(y)\n", "\n", " if np.linalg.norm(gradient) < tolerance:\n", " break\n", "\n", " return x_vals, y_vals\n", "\n", "\n" ] }, { "cell_type": "markdown", "id": "lP-b5OQfLHjo", "metadata": { "id": "lP-b5OQfLHjo" }, "source": [ "Note that the method returns an array. To get the final result just print the last element. Here is an example usage of it:" ] }, { "cell_type": "code", "execution_count": 49, "id": "zHl-vioULBeC", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zHl-vioULBeC", "outputId": "bc11b546-4dba-4c1b-f886-1d27619dd9d2" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "optimal x:1.9073486328125e-06\n", "optimal y:1.9073486328125e-06\n" ] } ], "source": [ "initial_guess = np.array([2.0, 2.0])\n", "x_opt, y_opt = newtons_method(initial_guess,alpha=0.5)\n", "print(f\"optimal x:{x_opt[-1]}\")\n", "print(f\"optimal y:{y_opt[-1]}\")" ] }, { "cell_type": "markdown", "id": "2kqpPNhmPvCx", "metadata": { "id": "2kqpPNhmPvCx" }, "source": [ "You can tweak ```alpha``` parameter and ```max_iterations``` to test the convergence.\n", "```{admonition} About $\\alpha$\n", ":class: tip\n", "In literature if $\\alpha \\in (0,1)$ then this method is called **damped** Newton's method\n", "```" ] }, { "cell_type": "code", "execution_count": 50, "id": "kMKY4gq5TSbU", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "kMKY4gq5TSbU", "outputId": "21c9bc86-b546-4927-bf2e-d17798404059" }, "outputs": [ { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "\n", "\n", "
      \n", "
      \n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%deleteInput\n", "# Create a surface plot of the function\n", "x_vals = np.linspace(-3, 3, 100)\n", "y_vals = np.linspace(-3, 3, 100)\n", "x, Y = np.meshgrid(x_vals, y_vals)\n", "Z = func(x, Y)\n", "\n", "fig = go.Figure(\n", " data=[go.Surface(z=Z, x=x, y=Y, colorscale='Viridis', opacity=0.8)],\n", " layout=go.Layout(\n", " title=\"Start Title\",\n", " updatemenus=[dict(\n", " type=\"buttons\",\n", " buttons=[dict(label=\"Play\",\n", " method=\"animate\",\n", " args=[None])])]\n", " ),\n", "frames=[go.Frame(\n", " data=[go.Scatter3d(\n", " x=x_opt[:k+1],\n", " y=y_opt[:k+1],\n", " z=[func(x, y) for x, y in zip(x_opt[:k+1], y_opt[:k+1])],\n", " mode='markers+lines', marker=dict(size=5, color='red')\n", " )],\n", " traces= [0],\n", " name=f'frame{k}'\n", " )for k in range(len(x_opt)-1)]\n", ")\n", "\n", "# Add the surface plot\n", "fig.add_trace(go.Surface(z=Z, x=x, y=Y, colorscale='Viridis', opacity=0.8))\n", "\n", "# Set layout\n", "fig.update_layout(scene=dict(zaxis=dict(range=[-2, 10])))\n", "fig.update_layout(title=\"Newton's Method in 3D\")\n", "\n", "fig.show()" ] }, { "cell_type": "markdown", "id": "bVt1TC2aHYqx", "metadata": { "id": "bVt1TC2aHYqx" }, "source": [ "# Application in ML" ] }, { "cell_type": "markdown", "id": "OvD7xhSnp8cy", "metadata": { "id": "OvD7xhSnp8cy" }, "source": [ "Newton's Method is a very useful tool for the optimization process in Machine Learning. It allows us to adjust several parameters of a model at once, unlike other methods (e.g. Gradient Descent) that only let us adjust one parameter at a time.\n", "\n", "In other words, Newton's Method looks at both the slope and the curvature – first and second derivatives respectively. This can make it much faster in terms of optimization.\n", "\n", "However, it is also worth noting that Newton's Method might be trickier to use when a model is very complex. Thus, we can say that Newton's Method is quicker but more risky, while Gradient Descent is more steady but slow.\n", "\n", "To solve the optimization problem in ML\n", "\n", "```{math}\n", " \\mathcal L(\\pmb{w}) \\to \\min\\limits_{\\pmb{w}}\n", "```\n", "\n", "do the followind steps:\n", "\n", "1. initialize $\\pmb{w}$ by some random values (e.g., from $\\mathcal N(0, 1)$)\n", "2. choose **tolerance** $\\varepsilon > 0$ and **learning rate** $\\eta > 0$\n", "3. while $\\Vert \\nabla\\mathcal L(\\pmb{w}) \\Vert > \\varepsilon$ do the **iteration step!**\n", "\n", "\n", "```{math}\n", " \\pmb{w} := \\pmb{w} - \\eta(\\nabla^2\\mathcal L(\\pmb{w}))^{-1} \\nabla \\mathcal L\n", "```\n", " \n", "4. return $\\pmb{w}$\n", "\n", "```{admonition} Carefull!\n", ":class: tip, dropdown\n", "If condition $\\Vert \\nabla\\mathcal L(w) \\Vert > \\varepsilon$ holds for too long, the loop in step 3 terminates after some number iterations `max_iter`.\n", "```" ] }, { "cell_type": "markdown", "id": "eAdtqZT5v007", "metadata": { "id": "eAdtqZT5v007" }, "source": [ "![newton](assets/newton.png)\n", "\n", "Gradient descent (green) and Newton's method (red) in comparison for minimizing a function (with small step sizes)." ] }, { "cell_type": "code", "execution_count": 51, "id": "pamGla7FcqXO", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 303 }, "id": "pamGla7FcqXO", "outputId": "8e1d6b1d-30c1-44c3-9ff5-b65529d4fae1" }, "outputs": [ { "data": { "application/javascript": "deleteInput();", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
      " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": "var questionsqQLEgurnkwem=[{\"question\": \"Which of the following is an advantage of Newton's Method?\", \"type\": \"multiple_choice\", \"answers\": [{\"answer\": \"Requires less memory compared to other methods\", \"correct\": false, \"feedback\": \"Incorrect, try again\"}, {\"answer\": \"Typically converges faster than gradient descent for well-behaved functions\", \"correct\": true, \"feedback\": \"Newton's Method can converge more quickly for certain types of functions due to its use of both first and second derivatives.\"}, {\"answer\": \"Does not require calculation of second derivatives\", \"correct\": false, \"feedback\": \"Incorrect, try again\"}, {\"answer\": \"Suitable for very large datasets\", \"correct\": false, \"feedback\": \"Incorrect, try again\"}]}];\n // Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
      1. Copy the text in this cell below \"Answer String\"
      2. Double click on the cell directly below the Answer String, labeled \"Replace Me\"
      3. Select the whole \"Replace Me\" text
      4. Paste in your answer string and press shift-Enter.
      5. Save the notebook using the save icon or File->Save Notebook menu item



      6. Answer String:
        ';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
        \";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n\n //console.log(answers);\n\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n\n if (fb.dataset.numcorrect == 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n \n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.textContent = jaxify(label.dataset.feedback);\n } else {\n fb.textContent = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else {\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.textContent = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.textContent = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n var shuffled;\n if (shuffle_answers == \"True\") {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\n\n //Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n aDiv.append(lab);\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if (\"precision\" in ths.dataset) {\n var precision = ths.dataset.precision;\n // console.log(\"1:\", submission)\n submission = Math.round((1 * submission + Number.EPSILON) * 10 ** precision) / 10 ** precision;\n // console.log(\"Rounded to \", submission, \" precision=\", precision );\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.textContent = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n if (submission == answer.value) {\n if (\"feedback\" in answer) {\n fb.textContent = jaxify(answer.feedback);\n } else {\n fb.textContent = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n //console.log(answer.range);\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.textContent = jaxify(answer.feedback);\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n defaultFB = answer.feedback;\n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n return false;\n }\n\n}\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.textContent = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\nfunction jaxify(string) {\n var mystring = string;\n\n var count = 0;\n var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n\n var count2 = 0;\n var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n\n //console.log(loc);\n\n while ((loc >= 0) || (loc2 >= 0)) {\n\n /* Have to replace all the double $$ first with current implementation */\n if (loc2 >= 0) {\n if (count2 % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n }\n count2++;\n } else {\n if (count % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n }\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n }\n\n //console.log(mystring);\n return mystring;\n}\n\n\nfunction show_questions(json, mydiv) {\n console.log('show_questions');\n //var mydiv=document.getElementById(myid);\n var shuffle_questions = mydiv.dataset.shufflequestions;\n var num_questions = mydiv.dataset.numquestions;\n var shuffle_answers = mydiv.dataset.shuffleanswers;\n var max_width = mydiv.dataset.maxwidth;\n\n if (num_questions > json.length) {\n num_questions = json.length;\n }\n\n var questions;\n if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n //console.log(num_questions+\",\"+json.length);\n questions = getRandomSubarray(json, num_questions);\n } else {\n questions = json;\n }\n\n //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n\n // Iterate over questions\n questions.forEach((qa, index, array) => {\n //console.log(qa.question); \n\n var id = makeid(8);\n //console.log(id);\n\n\n // Create Div to contain question and answers\n var iDiv = document.createElement('div');\n //iDiv.id = 'quizWrap' + id + index;\n iDiv.id = 'quizWrap' + id;\n iDiv.className = 'Quiz';\n iDiv.setAttribute('data-qnum', index);\n iDiv.style.maxWidth =max_width+\"px\";\n mydiv.appendChild(iDiv);\n // iDiv.innerHTML=qa.question;\n \n var outerqDiv = document.createElement('div');\n outerqDiv.id = \"OuterquizQn\" + id + index;\n // Create div to contain question part\n var qDiv = document.createElement('div');\n qDiv.id = \"quizQn\" + id + index;\n \n if (qa.question) {\n iDiv.append(outerqDiv);\n\n //qDiv.textContent=qa.question;\n qDiv.innerHTML = jaxify(qa.question);\n outerqDiv.append(qDiv);\n }\n\n // Create div for code inside question\n var codeDiv;\n if (\"code\" in qa) {\n codeDiv = document.createElement('div');\n codeDiv.id = \"code\" + id + index;\n codeDiv.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeDiv.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = qa.code;\n outerqDiv.append(codeDiv);\n //console.log(codeDiv);\n }\n\n\n // Create div to contain answer part\n var aDiv = document.createElement('div');\n aDiv.id = \"quizAns\" + id + index;\n aDiv.className = 'Answer';\n iDiv.append(aDiv);\n\n //console.log(qa.type);\n\n var num_correct;\n if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n if (\"answer_cols\" in qa) {\n //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n }\n } else if (qa.type == \"numeric\") {\n //console.log(\"numeric\");\n make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n }\n\n\n //Make div for feedback\n var fb = document.createElement(\"div\");\n fb.id = \"fb\" + id;\n //fb.style=\"font-size: 20px;text-align:center;\";\n fb.className = \"Feedback\";\n fb.setAttribute(\"data-answeredcorrect\", 0);\n fb.setAttribute(\"data-numcorrect\", num_correct);\n iDiv.append(fb);\n\n\n });\n var preserveResponses = mydiv.dataset.preserveresponses;\n console.log(preserveResponses);\n console.log(preserveResponses == \"true\");\n if (preserveResponses == \"true\") {\n console.log(preserveResponses);\n // Create Div to contain record of answers\n var iDiv = document.createElement('div');\n iDiv.id = 'responses' + mydiv.id;\n iDiv.className = 'JCResponses';\n // Create a place to store responses as an empty array\n iDiv.setAttribute('data-responses', '[]');\n\n // Dummy Text\n iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n //iDiv.className = 'Quiz';\n mydiv.appendChild(iDiv);\n }\n//console.log(\"At end of show_questions\");\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([mydiv]);\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n return false;\n}\n/* This is to handle asynchrony issues in loading Jupyter notebooks\n where the quiz has been previously run. The Javascript was generally\n being run before the div was added to the DOM. I tried to do this\n more elegantly using Mutation Observer, but I didn't get it to work.\n\n Someone more knowledgeable could make this better ;-) */\n\n function try_show() {\n if(document.getElementById(\"qQLEgurnkwem\")) {\n show_questions(questionsqQLEgurnkwem, qQLEgurnkwem); \n } else {\n setTimeout(try_show, 200);\n }\n };\n \n {\n // console.log(element);\n\n //console.log(\"qQLEgurnkwem\");\n // console.log(document.getElementById(\"qQLEgurnkwem\"));\n\n try_show();\n }\n ", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%deleteInput\n", "from jupyterquiz import display_quiz\n", "\n", "quiz = [{\n", " \"question\": \"Which of the following is an advantage of Newton's Method?\",\n", " \"type\": \"multiple_choice\",\n", " \"answers\": [\n", " {\n", " \"answer\": \"Requires less memory compared to other methods\",\n", " \"correct\": False,\n", " \"feedback\": \"Incorrect, try again\"\n", " },\n", " {\n", " \"answer\": \"Typically converges faster than gradient descent for well-behaved functions\",\n", " \"correct\": True,\n", " \"feedback\": \"Newton's Method can converge more quickly for certain types of functions due to its use of both first and second derivatives.\"\n", " },\n", " {\n", " \"answer\": \"Does not require calculation of second derivatives\",\n", " \"correct\": False,\n", " \"feedback\": \"Incorrect, try again\"\n", " },\n", " {\n", " \"answer\": \"Suitable for very large datasets\",\n", " \"correct\": False,\n", " \"feedback\": \"Incorrect, try again\"\n", " }\n", " ]\n", " }]\n", "\n", "display_quiz(quiz)\n" ] } ], "metadata": { "colab": { "include_colab_link": true, "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.5" } }, "nbformat": 4, "nbformat_minor": 5 }