Exercise 0: Python warmup (not assessed)¶

This notebook has some warmup questions, for getting used to Python. It also shows you how to use the automated ticker.

This notebook also serves as a guide on how to structure your own notebooks. It's all too easy to create spaghetti code in Jupyter, and if you follow the structure here you'll make life easier for yourself. (For the IA Scientific Computing course you will be assessed ONLY on your answers and on whether you are able to explain your code, NOT on how well structured your code is. Nevertheless, if your code is tangled then you may have trouble explaining it!

  1. Initialization
  2. Run-once setup
  3. Answer the questions
  4. Submit your answers
  • Model solutions

1. Initialization¶

Start by importing any required modules.

In [ ]:
import math
Note. If you're using hub.cl.cam.ac.uk then it has all the necessary libraries installed. On other systems you might get an error message like
----> 1 import pandas
ModuleNotFoundError: No module named 'pandas'
This means you need to install pandas before you are able to import it. Install it from within Jupyter by running
!pip install pandas
If you're running on your own machine, you only need to do this once. If you're running on Google Colab, for example, you need to do it every time you get a new server.

2. Run-once setup¶

Load in any datasets you are using. (For these warmup questions, there aren't any datasets needed.)

If there are any general-purpose functions that you've defined, which you plan to use to answer several questions, then place them here. (For these warmup questions, you don't need any such general-purpose functions.)

3. Answer the questions¶

If a question asks you for a value, just compute it. If a question asks you to define a function, define that function. (For now, just prepare your answers. Submitting them to the automated ticker will come later, in step 4.)

When you're asked for a function, you should run one or more tests. Work out by hand the answer you expect, then check your code gives that answer. For these warmup questions, I've suggested testsl; and you can also check your code by looking at the model solutions at the bottom of this notebook. For the assessed ticks, it's up to you to invent the tests, and there's no checking of your code — the ticker only looks at the answers you compute.

Organize your code so that it will work correctly if you run it all top-to-bottom. You can check it using "Kernel | Restart & Run All". If your code relies on running the cells in a screwy order, you will confuse yourself and your readers.

Question (q0). Write a function repeat(x,n) that, given an integer $n$ and a value $x$, produces a list consisting of $n$ copies of $x$.

In [35]:
def repeat(x, n):
    ... # ???
In [ ]:
# Test it works
repeat('me', 3)

Question (q1). Produce a triple consisting of

  • the base 10 logarithm of 1200
  • the tangent of 60 degrees
  • the square root of 20
In [ ]:
q1ans = ... # ???

Question (q2). Let $x$ be a list of strings. Write a one-line function to sort $x$ by length, breaking ties alphabetically.

In [ ]:
def q2f(x):
    ... # ???  
In [ ]:
# Test it works
names = ['adrian', 'chloe', 'guarav', 'shay', 'alexis', 'rebecca', 'zubin']
q2f(names)
# Expect the answer: ['shay', 'chloe', 'zubin', 'adrian', 'alexis', 'guarav', 'rebecca']

Question (q3). Let $x$ be a list of numbers. Write a one-line function to find the number of unique elements in $x$.

In [ ]:
def q3f(x):
    ... # ???
In [ ]:
# Test it works
q3f([1, 4, 9, 6, 1, 5, 5, 1, 5, 7, 2, 9, 9, 5, 9, 3, 7, 10, 7, 10])
# Expect the answer: 9

Question (q4). A simple queue can be simulated by the following equations. Let $q_t$ be the queue size just before timestep $t$, let the service rate be $C$, and let $a_t$ be the amount of work arriving at timestep $t$. Then $$ q_{t+1} = \max(q_t + a_t - C, 0). $$ This is called Lindley's Recursion. Write a function sim(q0,C,a) to compute the queue sizes. It should accept an initial queue size q0 and a list a consisting of $[a_0,a_1,\dots,a_{t-1}]$, and it should return a list $[q_1,\dots,q_t]$.

In [ ]:
def sim(q0, C, a):
    ... # ???
In [ ]:
# Test it works
sim(1, 3, [4, 1, 2, 8, 2, 3, 1])
# Expect the answer: [2, 0, 0, 5, 4, 4, 2]

Question (q5). We can represent a tree as a nested list, for example

x = [1,[[2,4,3],9],[5,[6,7],8]]

Define a function maptree(x, f) which returns a tree of the same shape but with the function f applied to to every leaf.

In [ ]:
def maptree(x, f):
    ... # ???
In [ ]:
# Test it works
x = [1,[[2,4,3],9],[5,[6,7],8]]
maptree(x, lambda leaf: leaf + 1)
# Expect the answer: [2,[[3,5,4],10],[6,[7,8],9]]
In [ ]:
# Test it works
all_leaves = []
maptree(x, lambda leaf: all_leaves.append(leaf))
all_leaves
# Expect the answer: [1,2,4,3,9,5,6,7,8]

4. Submit your answers¶

Run the following two lines to set up the automatic ticker. (The questions are grouped by section, and here we're working on section ex0. For the ticks, you'll be told which section to use.)

It will ask you to log in via Raven, and show you your progress so far.

In [1]:
import ucamcl
GRADER = ucamcl.autograder('https://markmy.solutions', course='scicomp').subsection('ex0')
Waiting for you to log in ... done.
Note. If you get the error message
----> 1 import ucamcl
ModuleNotFoundError: No module named 'ucamcl'
it means you need to install ucamcl. See the comment at the top of this notebook.

For each question, you'll be told how to submit your answers. In brief: you call GRADER.fetch_question to get a question-object with paramaters, you compute your answer on those parameters, then you call GRADER.submit_answer to submit your answer.

The reason you're asked to call fetch_question is so that different students work with different parameters. You also get different parameters each time you make a fresh attempt. If you print the question-object, it'll show what parameters it has.

In [ ]:
# Question q0
q = GRADER.fetch_question('q0')
ans = repeat(q.x, q.n)
GRADER.submit_answer(q, ans)
In [ ]:
# Question q1
GRADER.submit_answer(GRADER.fetch_question('q1'), q1ans)
In [ ]:
# Question q2
q = GRADER.fetch_question('q2')
ans = q2f(q.x)
GRADER.submit_answer(q, ans)
In [ ]:
# Question q3
q = GRADER.fetch_question('q3')
ans = q3f(q.x)
GRADER.submit_answer(q, ans)
In [ ]:
# Question q4
q = GRADER.fetch_question('q4')
ans = sim(q0=q.q0, C=q.C, a=q.a)
GRADER.submit_answer(q, ans)
In [ ]:
# Question q5
q = GRADER.fetch_question('q5')
ans = maptree(q.x, lambda z: f"leaf({z})")
GRADER.submit_answer(q, ans)

Model solutions to the warmup exercises¶

In [39]:
def repeat(x, n):
    return [x]*n
In [40]:
import math

q1ans = (
    math.log10(1200), 
    math.tan(60/360*2*math.pi),
    math.sqrt(20)
)
In [41]:
def q2f(x):
    return [s for _,s in sorted([(len(y),y) for y in x])]
In [42]:
def q3f(x):
    return len({y:True for y in x})
In [43]:
# Question q4
def sim(q0,C,a):
    q = [q0]
    for at in a:
        q.append(max(q[-1] + at - C,0))
    return q[1:]
In [44]:
# Question q5
def maptree(x, f):
    try:
        return [maptree(xx,f) for xx in x]
    except TypeError:
        return f(x)