assessment2 2025 2026(2)
English review edition prepared on 4 October 2026 from a preserved source copy. It is a later presentation, not the historical study interface. Source checks and difficulty judgements describe the original material author's own process; they do not indicate Imperial College London endorsement.
Source SHA-256: 28459fa41c77769668f1490b39f67c4c6e6ab3a3358f34324c37dc5e3d3857f2Source date: 2026-07-31
Notes — cell 1
# MATH40006 In-Course Assessment # 13 February 2026, 09.10-10.10 am ## One Hour ### Answer all questions, submitting your answers as a single Jupyter notebook.
Notes — cell 2
To carry out this assignment, you may find useful, possibly amongst others: - from the `numpy` module, the constant `pi` and the functions `cos`, `sin`, `linspace`, `arange`, `meshgrid`, `tile`, `flatnonzero`,`array`, `zeros` and `append`; - from the `pyplot` submodule of `matplotlib`, the functions `imshow`, `contour` and `quiver`; - from the `Axes3D` submodule of `mpl_toolkits`, the function `plot_wireframe`; - from `sympy`, the function `Rational`. We recommend that you execute the following cell:
Code — cell 3
import numpy as np import sympy as sp import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D
Notes — cell 4
<b>Important</b>: this test is open-book, in the sense that you have access to all the materials on Blackboard, and to documentation websites for Python, NumPy, SymPy and matplotlib. The use of other websites, and of AI assistance, is strictly prohibited. If your code should hang: - As a first instance try to interrupt the kernel. - If that fails, try to restart the kernel. - If that fails, close all Python and Jupyter windows and restart Python. The notebook will be autosaved, but it's a good idea to save manually too, especially before executing a `while` loop or a recursion.
Notes — cell 5
## Question 1 (20 marks) (a) Using `linspace` or `arange`, define the variable `x_values1` to be a one-dimensional array of 121 elements, equally spaced between $-\pi$ and $\pi$ inclusive. Also, define `y_values1` to be a one-dimensional array of 121 elements, equally spaced between $-3$ and $3$ inclusive.
Code — cell 6
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 7
(b) Using `meshgrid`, define `x1` and `y1` to be $121\times121$ arrays consisting, respectively, of the $x$- and $y$-coordinates of the points of the lattice formed from `x_values1` and `y_values1`.
Code — cell 8
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 9
(c) Using `imshow` with appropriate settings for the keyword arguments `origin` and `extent`, generate a colour-map visualisation of the function
$$u(x,\,y) = \frac{y^2}{2}-\cos(x).$$
Your values for $x$ and $y$ should come from `x1` and `y1` respectively.Code — cell 10
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 11
(d) Again using `x1` and `y1`, create a contour plot of the function $u(x,\,y)$ for $-\pi\le x\le\pi$, $-3\le y\le 3$.
Code — cell 12
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 13
(e) Using `linspace` or `arange`, define the variable `x_values2` to be a one-dimensional array of 21 elements, equally spaced between $-\pi$ and $\pi$ inclusive. Also, define `y_values2` to be a one-dimensional array of 21 elements, equally spaced between $-3$ and $3$ inclusive.
Code — cell 14
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 15
(f) Using `meshgrid`, define `x2` and `y2` to be $21\times21$ arrays consisting, respectively, of the $x$- and $y$-coordinates of the points of the lattice formed from `x_values2` and `y_values2`.
Code — cell 16
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 17
(g) The partial derivatives of $u(x,\,y)$ are
$$\begin{align}
\frac{\partial u}{\partial x} &= \sin x,\\
\frac{\partial u}{\partial y} &= y.
\end{align}$$
Using `quiver`, with coordinates drawn from `x2` and `y2`, create a vector field plot of the field
$$\left(\begin{array}{c}
\partial u/\partial y\\
-\partial u/\partial x
\end{array}\right)$$
for $-\pi\le x \le \pi$, $-3\le y \le 3$.Code — cell 18
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 19
(h) Create an image consisting of your vector field plot, in blue, superimposed on your contour plot, in red.
Code — cell 20
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 21
(i) Using `plot_wireframe`, create a 3D plot of the surface $$z=u(x,\,y).$$ Use coordinates drawn from `x1` and `y1`.
Code — cell 22
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 23
## Question 2 (20 marks) This question involves two NumPy functions that you may not have studied: `tile` and `flatnonzero`. (a) Try the code ```python np.tile([1, 0, 7, 0, 0, 2], 3) ``` Experiment further. Write a short description of what `tile` does.
Code — cell 24
# space for code
Code — cell 25
# space for code
Code — cell 26
# space for code
Notes — cell 27
YOUR ANSWER HERE
Notes — cell 28
(b) Try the code ```python np.flatnonzero([1, 0, 7, 0, 0, 2]) ``` Experiment further. Write a short description of what `flatnonzero` does.
Code — cell 29
# space for code
Code — cell 30
# space for code
Code — cell 31
# space for code
Notes — cell 32
YOUR ANSWER HERE
Notes — cell 33
The rest of this question is about a method called the <b>Sieve of Pritchard</b>, which is a little like the Sieve of Eratosthenes but different. We'll begin by using it to sieve the integers between $1$ and $29$. (c) Set the variable `n` equal to 29, and set `sieving_array` equal to a NumPy Boolean array of length $1$, consisting of a single `True`.
Code — cell 34
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 35
(d) Using the NumPy `zeros` function, with `dtype` set to `'bool'`, or otherwise, set `discards` equal to a NumPy Boolean array all of whose elements are `False`. The length of this array should be `n + 1`; that is, it should have length $30$.
Code — cell 36
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 37
(e) Initialize the variables `l` and `p`, giving them the values $1$ and $2$ respectively.
Code — cell 38
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 39
(f) Repeat the following steps for as long as $\min(p^2,l+1)\le n$. You may do this either as a `while` loop or simply by repeated evaluation (there won't be many iterations!) - Set `discards[p]` equal to `True`. - If $l<n$: - set `sieving_array` equal to `np.tile(sieving_array, p)[:n]` (that is, the elements of `np.tile(sieving_array, p)`, up to a maximum of `n` elements); - set `l` equal to `l * p`; - print the new value of `l`. - If $p\le l$, set `sieving_array[p-1]` to `False`. - Set every $p$th element of `sieving_array`, beginning with the element with index `p**2-1`, to `False`. (You should be able to do this with a single line, without using a loop or comprehension.) - Set `nonzeropositions` equal to `np.flatnonzero(sieving_array)`. - If `nonzeropositions` has length $1$, then set `p` equal to `l + 1`. Otherwise, set `p` equal to element $1$ of `nonzeropositions`, plus $1$. - Print the value of `p`; if the algorithm is working, this should always be a prime. Remember to stop when $\min(p^2,l+1) > n$.
Code — cell 40
# YOUR CODE HERE raise NotImplementedError()
Code — cell 41
# YOUR CODE HERE raise NotImplementedError()
Code — cell 42
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 43
(g) Print the final values of `np.flatnonzero(discards)` and `np.flatnonzero(sieving_array[1:]) + 2`.
Code — cell 44
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 45
(h) Write a function called `pritchard_sieve`, which takes an argument `n`, assumed to be a positive integer, and uses the above algorithm to calculate and return an array of the primes between $2$ and $n$ inclusive. Your code need not include any `print` statements.
Code — cell 46
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 47
(i) Test your function, including typical cases and edge cases. Include at least one value of `n` with five or more digits.
Code — cell 48
# YOUR CODE HERE raise NotImplementedError()
Code — cell 49
# YOUR CODE HERE raise NotImplementedError()
Code — cell 50
# YOUR CODE HERE raise NotImplementedError()
Code — cell 51
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 52
## Question 3 (10 marks)
(a) Write a function `neighbor_sums`, which takes a single argument `lis`, assumed to be a list of the form $[a_0, a_1, a_2, a_3, \dots, a_{n-1}, a_n]$, and returns the list
$$[a_0 + a_1,\, a_1 + a_2,\, a_2 + a_3,\, \dots,\, a_{n-1} + a_n].$$Code — cell 53
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 54
(b) Test your function: ```python neighbor_sums([1, 3, 5, 7, 9]) ``` should return ``` [4, 8, 12, 16] ```
Code — cell 55
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 56
For part (c) of this question, you may wish to use the function `riffle`, listed below, which takes arguments `l1` and `l2`, assumed to be lists which are either of the same length, or such that `l1` contains one more element than `l2`, and generates a list consisting of the elements of the two lists, alternating with one another.
Code — cell 57
def riffle(l1, l2):
l3 = l1 + l2
l3[::2] = l1
l3[1::2] = l2
return l3Notes — cell 58
Execute the following cell to see what `riffle` does.
Code — cell 59
print(riffle([0,2,4],[1,3,5])) print(riffle([0,2,4],[1,3]))
Notes — cell 60
(c) Write a function `riffled_neighbor_sums`, which takes a single argument `lis`, assumed to be a list of the form $[a_0, a_1, a_2, a_3, \dots, a_{n-1}, a_n]$, and returns the "riffled" list
$$[a_0,\, a_0 + a_1,\, a_1,\, a_1 + a_2,\, a_2,\, a_2 + a_3,\, a_3,\, \dots,\, a_{n-1},\, a_{n-1} + a_n].$$Code — cell 61
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 62
(d) Test your function: ```python riffled_neighbor_sums([1, 3, 5, 7, 9]) ``` should return ``` [1, 4, 3, 8, 5, 12, 7, 16, 9] ```
Code — cell 63
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 64
The <b>Stern-Brocot tree</b> is an infinite tree of fractions that is known to contain every positive rational number exactly once. The $n$th <b>generation</b> of the Stern-Brocot tree may be generated as follows. Starting with the list `lis = [np.array([0, 1]), np.array([1, 1]), np.array([1, 0])]`, iterate ```python lis = riffled_neighbor_sums(lis) ``` $n$ times. Then, using the final value of `lis`, take the odd-indexed elements, with indexes $1$, $3$, $5$ etc, of `lis`, and convert each element `el` into a SymPy Rational whose numerator is `el[0]` and whose denominator is `el[1]`. Return the resulting list of Rationals. (e) Write a function `sb_generation`, which takes a non-negative int `n`, and returns the $n$th Stern-Brocot generation as a list of SymPy Rationals.
Code — cell 65
# YOUR CODE HERE raise NotImplementedError()
Notes — cell 66
(f) Test it: ```python sb_generation(3) ``` should return ``` [1/4, 2/5, 3/5, 3/4, 4/3, 5/3, 5/2, 4] ```
Code — cell 67
# YOUR CODE HERE raise NotImplementedError()