diff --git a/exercises/week06/Recursive least squares.ipynb b/exercises/week06/Recursive least squares.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9e6d2b7a4356501cf6d1dabbba62ba1246378b99 --- /dev/null +++ b/exercises/week06/Recursive least squares.ipynb @@ -0,0 +1,1111 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e3f2b37e", + "metadata": { + "deletable": false, + "editable": false + }, + "outputs": [], + "source": [ + "# Initialize Otter\n", + "import otter\n", + "grader = otter.Notebook(\"Recursive least squares.ipynb\")" + ] + }, + { + "cell_type": "markdown", + "id": "eb88859c-9077-43c0-868b-e5f45289ff06", + "metadata": {}, + "source": [ + "# Matrix Analysis 2025 - EE312\n", + "## Week 6 - Recursive least squares\n", + "[N. Aspert](https://people.epfl.ch/nicolas.aspert) - [LTS2](https://lts2.epfl.ch)\n", + "\n", + "\n", + "## Important\n", + "- The unit tests are here to help you, however they might pass even if your implementation is faulty (and conversely fail even if your implementation is correct), due to platform/environment variability. Feel free to ignore them (after carefully reviewing your answers/results)\n", + "\n", + "\n", + "## Objective\n", + "\n", + "The goal of this notebook is to find the parameters of a system using least squares, and more specifically use a recursive formulation to update those parameters with a reduced amount of computations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4ab0d05-aa5a-4dc7-b2b5-b04ad6dd426e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "d1a159ae-f3e6-4629-bf5b-6d93af1b0b68", + "metadata": {}, + "source": [ + "## Part I - Least-square parameter estimation\n", + "\n", + "In this notebook, we will estimate physical parameters of a moving system. Its $(x,y)$ coordinates are sampled at regular time intervals, but are noisy (e.g. due to measurement artefacts). You can load them from a file using the code below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68883575-5b43-4ae7-8e23-13cdab9b2fa4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "data = np.load('data.npz')" + ] + }, + { + "cell_type": "markdown", + "id": "b82107e6-9019-4598-9e9a-9b123fba4e58", + "metadata": { + "tags": [] + }, + "source": [ + "Once loaded, the `data` variable is a dictionary containing 3 arrays:\n", + "- `time` for the time values corresponding to each sample\n", + "- `tr1` for the first system trajectory\n", + "- `tr2` for the second system trajectory\n", + "\n", + "The trajectories are stored as complex numbers to have a single value representing the $x$ and $y$ components. In the first part of the problem we will only use ther data from `tr1`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "141016a9-b347-4c7b-b76e-13af8ade65e3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plt.scatter(np.real(data['tr1']), np.imag(data['tr1']), marker='+')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')" + ] + }, + { + "cell_type": "markdown", + "id": "575ce09b-fcbd-4442-a8e7-dc2d5e587226", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "Let us now build the model of this system. In this part we will assume that the system is defined by 3 parameters:\n", + "- its initial position $p_0$\n", + "- its initial velocity $v_0$\n", + "- its acceleration $a$ which is constant\n", + "\n", + "Those parameters will be represented as complex values (in order to take into account their $x$ and $y$ components)\n", + "\n", + "1. Express the position of the system $p(t)$ as a function of $t$ and of the 3 above parameters. " + ] + }, + { + "cell_type": "markdown", + "id": "2857a8f4", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "markdown", + "id": "5e93d1f2-82a7-4e1b-b543-d89dd38c1c69", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "Since we have the whole trajectory of the system and given the relatively small number of samples, we can now estimate directly the 3 parameters using a least-square approximation. \n", + "\n", + "2. Using the previous question, write a function that generates the matrix $A$ s.t. \n", + "$$\n", + "A\\begin{pmatrix}p_0 \\\\ v_0 \\\\ a\\end{pmatrix} = \\begin{pmatrix}p(t_0) \\\\ p(t_1) \\\\ \\vdots \\\\ p(t_{N-1}) \\end{pmatrix} \n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbeb5b6b-77ee-4b03-8871-81be21c5cf01", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def computeA(t):\n", + " \"\"\"\n", + " Computes the A matrix \n", + "\n", + " Parameters\n", + " ----------\n", + " t : vector containing values of time samples\n", + " \n", + " Returns\n", + " -------\n", + " The A matrix \n", + " \"\"\"\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f62efb3c", + "metadata": { + "deletable": false, + "editable": false + }, + "outputs": [], + "source": [ + "grader.check(\"q2\")" + ] + }, + { + "cell_type": "markdown", + "id": "9903f3fe-bd77-4e83-880f-5fdddbd450a6", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "3. Estimate the parameters of the system using a least square approximation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8623196-a2cd-47ef-b2c6-30804172868d", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def solve_lsq(t, pos):\n", + " \"\"\"\n", + " Solve the system Ax = pos, where x is the vector (p_0, v_0, a) and pos contains the position values at time t\n", + "\n", + " Parameters\n", + " ----------\n", + " t : vector containing values of time samples\n", + " pos: vector containing the positions of the system corresponding to the values of t (complex)\n", + "\n", + " \n", + " Returns\n", + " -------\n", + " The vector x containing the approximation of the system's parameters in the least-square sense.\n", + " \"\"\"\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8182465f", + "metadata": { + "deletable": false, + "editable": false + }, + "outputs": [], + "source": [ + "grader.check(\"q3\")" + ] + }, + { + "cell_type": "markdown", + "id": "f7edef23-f5d4-4c59-b4c8-ef57ad9d6b8b", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "4. Display the measured trajectory `tr1` and the one reconstructed from the parameters you estimated. If your implementation is correct, they should match. Also plot the euclidean distance between the measured and reconstructed point fo each timestep." + ] + }, + { + "cell_type": "markdown", + "id": "c1867853", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18e792a6-6104-422d-a7b3-c7b53a56b32b", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the observed and predicted data\n", + "..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa59fa59-f036-4906-87d3-52e558e7685c", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the distance between each point\n", + "..." + ] + }, + { + "cell_type": "markdown", + "id": "832f6e06-46c1-4ac9-a01f-6acb138560df", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "## Part II - Recursive least-square estimation\n", + "\n", + "The problem addressed in the first part was fairly easy: we had all measurements available at once (which is fine in an \"offline\" setting), and computing the least-square solution did not require to invert a huge matrix. In a more realistic setting, the measurements would come gradually and it would be nice to update our parameters without recomputing the whole solution at each step. This is where recursive least-squares come into place.\n" + ] + }, + { + "cell_type": "markdown", + "id": "f51903c3-e84d-4352-a4ea-e568bdc3aead", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "We first need to prove a classical result. Let us consider four matrices $A,B,C$ and $D$ such that:\n", + "- $A$ is an invertible $N\\times N$ matrix\n", + "- $B$ is a $N\\times M$ matrix \n", + "- $C$ is $M\\times N$ matrix\n", + "- $D$ is an invertible $M\\times M$ matrix\n", + "\n", + "5. Prove that $(A + BDC)^{-1} = A^{-1} - A^{-1}B(D^{-1}+CA^{-1}B)^{-1}CA^{-1}$\n" + ] + }, + { + "cell_type": "markdown", + "id": "be98a834", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "markdown", + "id": "6768f75a-e59d-42f1-99b7-9c16150ec2a0", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "<!-- BEGIN QUESTION -->\n", + "\n", + "Let us consider the least-square problem $Ax=y$, and suppose we already have received a small (but still keeping the system over-determined) number of measurements $y_0$ and computed $\\hat{x}_0$ that is the least-square solution of $A_0\\hat{x}_0=y_0$. Let us assume we have already computed $Q_0=(A_0^TA_0)^{-1}$.\n", + "\n", + "Now another set of measurements $y_1$ arrives, and the system you want to solve looks like\n", + "$$\n", + "\\begin{pmatrix}A_0\\\\A_1\\end{pmatrix}x = \\begin{pmatrix}y_0\\\\y_1\\end{pmatrix} \n", + "$$\n", + "\n", + "6. Using this block matrix formulation, rewrite the normal equation and express the updated solution $\\hat{x}_1$ using $A_0$, $A_1$, $Q_0^{-1}$, $\\hat{x}_0$, and $y_1$. \n", + "\n", + "Let us denote by $Q_1 = (A_0^TA_0 + A_1^TA_1)^{-1}$. Express $\\hat{x}_1$ as a function of $Q_1$, $A_1$, $y1$ and $\\hat{x}_0$. " + ] + }, + { + "cell_type": "markdown", + "id": "c3516e6e", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "markdown", + "id": "58d88136-aa54-4839-af16-672638af3087", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "<!-- BEGIN QUESTION -->\n", + "\n", + "Using the result shown in question 5, express $Q_1$ as a function of $Q_0$, $A_1$ and $U=Q_0A_1^T$. \n", + "\n", + "Hints:\n", + "- you might consider $Q_1^{-1}$\n", + "- what about $Q_0$ symmetry ?\n", + "\n", + "\n", + "Why is it interesting to update the system's parameters by computing $Q_1$ this way ?" + ] + }, + { + "cell_type": "markdown", + "id": "4368082f", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "markdown", + "id": "29107f5e-92d0-4790-b235-8e1362cd8250", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "8. Using the results of questions 6 and 7, implement a function that computes the updated parameters for a new set of measurements and complete the `recursive_lsq`implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bab14427-11c5-47b9-99b1-04e99fb92571", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def update_lsq(Q0, xhat0, meas_time, y1):\n", + " \"\"\"\n", + " Compute the updated parameters xhat1 from Q0, y1, xhat0\n", + "\n", + " Parameters\n", + " ----------\n", + " Q0 : matrix Q0\n", + " xhat0: previously computed parameters of the system\n", + " meas_time: vector containing the times at which samples were acquired\n", + " y1: vector containing the new positions of the system corresponding to the values of meas_time\n", + " \n", + " Returns\n", + " -------\n", + " xhat1 and Q1 \n", + " \"\"\"\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "371b7525-b48b-43b8-a636-180cbd2e2566", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def recursive_lsq(t, pos, batch_size):\n", + " \"\"\"\n", + " Perform recursive LSQ fit on iput data splitted in batches\n", + "\n", + " Parameters\n", + " ----------\n", + " t: vector containing values of time samples\n", + " pos: vector containing the positions of the system corresponding to the values of t (complex)\n", + " batch_size: size of each batch of measurements\n", + " \n", + " Returns\n", + " -------\n", + " array containing model parameters estimated after each batch\n", + " \"\"\"\n", + " \n", + " if batch_size < 2:\n", + " raise ValueError(\"Invalid batch size\")\n", + " \n", + " nsteps = len(t)//batch_size\n", + "\n", + " # initialization step\n", + " A0 =computeA(t[:batch_size])\n", + " Q0 = np.linalg.inv(A0.T@A0)\n", + " \n", + " xhat0 = ...\n", + " xh_cur = xhat0\n", + " Q_cur = Q0\n", + " x_h = [xhat0] # store the history of parameters in a list\n", + " for k in np.arange(1, nsteps):\n", + " cur_time = t[k*batch_size:(k+1)*batch_size]\n", + " cur_pos = pos[k*batch_size:(k+1)*batch_size]\n", + " xhat1, Q1 = ...\n", + " x_h.append(xhat1)\n", + " xh_cur = xhat1\n", + " Q_cur = Q1\n", + " return np.array(x_h)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebdfb081", + "metadata": { + "deletable": false, + "editable": false + }, + "outputs": [], + "source": [ + "grader.check(\"q8\")" + ] + }, + { + "cell_type": "markdown", + "id": "a9f907da-74a3-4d1c-9892-8d2b9fa9c69f", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "9. Complete the `predict_trajectory` function, and the `tr1` data:\n", + "- display the predicted trajectory of the system when using recursive least-squares\n", + "- display the distance between the predicted and observed trajectory\n", + "Make sure to chose a suitable batch size." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cbfe794-5aa1-4e12-a13c-3af377dfa828", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def predict_trajectory(x_h, t, pos, batch_size):\n", + " pos_pred = np.zeros(pos.shape, dtype='complex')\n", + " # Initialization\n", + " pos_pred[:batch_size] = pos[:batch_size]\n", + " for k in np.arange(1, len(x_h)):\n", + " A1 = computeA(t[k*batch_size:(k+1)*batch_size])\n", + " pos_pred[k*batch_size:(k + 1)*batch_size] = ...\n", + " return pos_pred" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97513eaf-ef5a-43c5-86f9-29cbc9bbd4de", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "batch_size = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cfd4110-2ee6-4e5e-bfdc-e6155745e19e", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "x_h = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5daa05c1-23bf-4f00-a7d4-9849b6ef27f6", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "pred_traj = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1961a97-4a16-4d47-8f0e-df2e106a68a6", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the predicted and observed data\n", + "..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11ec5c11-9ee2-49e0-b4d3-507629f2f71d", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the error between prediction and observation\n", + "..." + ] + }, + { + "cell_type": "markdown", + "id": "31ef60cf-1277-401a-b8d3-32b8e50e5797", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "## Part III - Recursive least square with forgetting factor\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "528dc1bb-b87b-4af9-8247-c718a74fbf7a", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "10. Use the recursive least square on the `tr2` trajectory, whose acceleration varies across time. Display the predicted trajectory and approximation error. Is that what you expected ?" + ] + }, + { + "cell_type": "markdown", + "id": "31abd7d1", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e39bedb4-7c88-4398-a41b-26c8b815640c", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "x2_h = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "341e47da-1516-4a69-a90f-58e227297e10", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "pred_traj2 = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebe4b99f-9f32-47b4-9af6-3086608358f4", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the predicted and observed data\n", + "..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d564e0b0-73f5-4315-b5ea-0986822e1388", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the error between prediction and observation\n", + "..." + ] + }, + { + "cell_type": "markdown", + "id": "70180a33-f723-4ccd-8f09-6c772941865a", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "In order to solve this issue, we could either build a more complex model. However, making the assumption that the acceleration changes infrequently, we can try to update the recursive least-square in a way that will give higher importance to the recent measurements with respect to older ones. In order to do so, we will introduce a **forgetting factor** $\\alpha$, with $0<\\alpha<1$.\n", + "\n", + "We will use this as follows:\n", + "When another set of measurements $y_1$ arrives, and the system defined in question 6 will be rewritten as\n", + "$$\n", + "\\begin{pmatrix}\\alpha A_0\\\\A_1\\end{pmatrix}x = \\begin{pmatrix}\\alpha y_0\\\\y_1\\end{pmatrix} \n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "6275538d-4edd-42d1-aa15-312936c217d9", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "11. Using the new \"forgetful\" system defined above, update the expressions $Q_1$ and $\\hat{x}_1$ derived in questions 6 and 7\n", + "\n", + "Hint: \n", + "- You might want to introduce $U_\\alpha = \\frac{1}{\\alpha} Q_0A_1^T$," + ] + }, + { + "cell_type": "markdown", + "id": "4d5f71ee", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "markdown", + "id": "bc7bc015-18a8-4059-a2cb-cc680f199b1e", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n", + "12. Using the results found at the previous question, implement the recursive least square with a forgetting factor." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6476b120-1e67-43b7-80fa-42eff433fb46", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def update_lsq_ff(Q0, xhat0, meas_time, y1, ff):\n", + " \"\"\"\n", + " Compute the updated parameters xhat1 from Q0, y1, xhat0, ff\n", + "\n", + " Parameters\n", + " ----------\n", + " Q0 : matrix Q0\n", + " xhat0: previously computed parameters of the system\n", + " meas_time: vector containing the times at which samples were acquired\n", + " y1: vector containing the new positions of the system corresponding to the values of meas_time\n", + " ff: forgetting factor \n", + " \n", + " Returns\n", + " -------\n", + " xhat1 and Q1 \n", + " \"\"\"\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70c04c51-59d8-4742-8bba-6c5ebe639c5f", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "def recursive_lsq_ff(t, pos, batch_size, ff):\n", + " \"\"\"\n", + " Perform recursive LSQ fit on iput data splitted in batches\n", + "\n", + " Parameters\n", + " ----------\n", + " t: vector containing values of time samples\n", + " pos: vector containing the positions of the system corresponding to the values of t (complex)\n", + " batch_size: size of each batch of measurements\n", + " ff: forgetting factor (in practice, keep it close to 1)\n", + " \n", + " Returns\n", + " -------\n", + " array containing model parameters estimated after each batch\n", + " \"\"\"\n", + " \n", + " if batch_size < 2:\n", + " raise ValueError(\"Invalid batch size\")\n", + " \n", + " nsteps = len(t)//batch_size\n", + "\n", + " # initialization step\n", + " A0 = computeA(t[:batch_size])\n", + " Q0 = np.linalg.inv(A0.T@A0)\n", + " \n", + " xhat0 = ...\n", + " xh_cur = xhat0\n", + " Q_cur = Q0\n", + " x_h = [xhat0] # store the history of parameters in a list\n", + " for k in np.arange(1, nsteps):\n", + " cur_time = t[k*batch_size:(k+1)*batch_size]\n", + " cur_pos = pos[k*batch_size:(k+1)*batch_size]\n", + " xhat1, Q1 = ...\n", + " x_h.append(xhat1)\n", + " xh_cur = xhat1\n", + " Q_cur = Q1\n", + " return np.array(x_h)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf21410e", + "metadata": { + "deletable": false, + "editable": false + }, + "outputs": [], + "source": [ + "grader.check(\"q12\")" + ] + }, + { + "cell_type": "markdown", + "id": "fb61e06c-cf61-41c0-88c1-2d49c58870ca", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- BEGIN QUESTION -->\n", + "\n", + "13. Use the functions defined at question 12 to predict `tr2` trajectory. Adjust the batch size and forgetting factor (hint: keep it close to 1) to improve results. Display the predicted trajectory and observed one, and also the evolution of the prediction error across time." + ] + }, + { + "cell_type": "markdown", + "id": "d01eed91", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "source": [ + "_Type your answer here, replacing this text._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0de3bc00-2da1-4758-9158-829d3724322a", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "ff = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6adab39d-b558-4b7f-a4d2-9ef44350f312", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "batch_size = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b421fd4d-d7ca-4b7b-8aa5-09280ed01322", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "x2_h_ff = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f4204d6-6da3-4652-a491-aaf95bc6e455", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "pred_traj2_ff = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31cd1cdc-0b4e-4563-a2cd-4a0b1874d4cb", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the predicted and observed data\n", + "..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8414ea3-f39e-4ede-a690-4e3d94ce6b29", + "metadata": { + "tags": [ + "otter_answer_cell" + ] + }, + "outputs": [], + "source": [ + "# Plot the error between prediction and observation\n", + "..." + ] + }, + { + "cell_type": "markdown", + "id": "a84ca578", + "metadata": { + "deletable": false, + "editable": false + }, + "source": [ + "<!-- END QUESTION -->\n", + "\n" + ] + } + ], + "metadata": { + "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.10" + }, + "otter": { + "OK_FORMAT": true, + "tests": { + "q12": { + "name": "q12", + "points": 2, + "suites": [ + { + "cases": [ + { + "code": ">>> def test_recursive_lsq_ff(batch_size):\n... x_h = recursive_lsq_ff(data['time'][:150], data['tr1'][:150], batch_size, 0.95)\n... assert len(x_h) == 3\n... np.testing.assert_array_almost_equal(x_h[0], [9.97 - 2.03j, -2.57 - 2.57j, -4.87 - 4.77j], decimal=2)\n... np.testing.assert_array_almost_equal(x_h[1], [10.02 - 1.99j, -3.33 - 3.33j, -0.04 + 0.06j], decimal=2)\n... np.testing.assert_array_almost_equal(x_h[2], [10.18 - 1.82j, -4.79 - 4.79j, 3.99 + 4.09j], decimal=2)\n>>> batch_size = 50\n>>> test_recursive_lsq_ff(batch_size)\n", + "failure_message": "Check your implementation", + "hidden": false, + "locked": false, + "success_message": "Good, parameters seem to match" + } + ], + "scored": true, + "setup": "", + "teardown": "", + "type": "doctest" + } + ] + }, + "q2": { + "name": "q2", + "points": 2, + "suites": [ + { + "cases": [ + { + "code": ">>> def testA(N):\n... t = np.arange(0, N)\n... A = computeA(t)\n... assert A.shape == (N, 3)\n... np.testing.assert_array_almost_equal(A[:, 0], np.ones(N))\n... np.testing.assert_array_almost_equal(A[:, 1], t)\n... np.testing.assert_array_almost_equal(A[:, 2], 0.5 * t ** 2)\n>>> testA(5)\n", + "failure_message": "Check your implementation", + "hidden": false, + "locked": false, + "success_message": "Good, results seem correct" + } + ], + "scored": true, + "setup": "", + "teardown": "", + "type": "doctest" + } + ] + }, + "q3": { + "name": "q3", + "points": 2, + "suites": [ + { + "cases": [ + { + "code": ">>> def test_lsq():\n... xt = np.array([10.05 - 1.95j, -3.21 + -3.21j, 0.8 + 0.9j])\n... x = solve_lsq(data['time'], data['tr1'])\n... np.testing.assert_array_almost_equal(x, xt, decimal=2)\n>>> test_lsq()\n", + "failure_message": "Check your implementation", + "hidden": false, + "locked": false, + "success_message": "Good, parameters seem to match" + } + ], + "scored": true, + "setup": "", + "teardown": "", + "type": "doctest" + } + ] + }, + "q8": { + "name": "q8", + "points": 4, + "suites": [ + { + "cases": [ + { + "code": ">>> def test_recursive_lsq(batch_size):\n... x_h = recursive_lsq(data['time'][:150], data['tr1'][:150], batch_size)\n... assert len(x_h) == 3\n... np.testing.assert_array_almost_equal(x_h[0], [9.97 - 2.03j, -2.57 - 2.57j, -4.87 - 4.77j], decimal=2)\n... np.testing.assert_array_almost_equal(x_h[1], [10.01 - 1.99j, -3.34 - 3.34j, -0.02 + 0.08j], decimal=2)\n... np.testing.assert_array_almost_equal(x_h[2], [10.17 - 1.83j, -4.75 - 4.75j, 3.91 + 4.01j], decimal=2)\n>>> batch_size = 50\n>>> test_recursive_lsq(batch_size)\n", + "failure_message": "Check your implementation", + "hidden": false, + "locked": false, + "success_message": "Good, parameters seem to match" + } + ], + "scored": true, + "setup": "", + "teardown": "", + "type": "doctest" + } + ] + } + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/exercises/week06/data.npz b/exercises/week06/data.npz new file mode 100644 index 0000000000000000000000000000000000000000..d7d8fed3919fc28db7e27e3baefe5fb6b9c0a88c Binary files /dev/null and b/exercises/week06/data.npz differ