Homework 2¶
Due Tuesday, September 28th 2021 at 11:59 PM.¶
Be sure to push the final version of your code to your GitHub repo. Follow the instructions on the course website.
Topics¶
Part 0: Course Workflow¶
Part 1: git
Remotes¶
- Problem 1: Understanding remotes
Part 2: Python Basics¶
- Problem 2: DNA complement
Part 3: Closure and Decorators¶
Part 0 [10 pts]: Course Workflow¶
Once you receive HW1 feedback, you will need to merge your HW1-dev
branch into master
. This counts for points so be sure to do this.
For HW2 and homework going forward you will be rewarded direct points for following all stages of the git
workflow which involves:
- 5pts for completing homework on
HWn-dev
(HW2-dev
for this assignment) - 2pts for making a PR on
HWn-dev
to merge into master (HW2-dev
in this assignment) - 3pts for merging your previous assignment ($\text{HW}_{n-1}$) into
master
($\text{HW}_{1}$ from the previous assignment) AFTER approval by the teaching staff.
Part 1 [15 pts]: git
Remotes¶
Problem 1 [15 pts]: Understanding Remotes¶
Remotes are an important concept in git
. Being a decentralized version control system, git
allows each user to have the entire history of the project locally. This means that you can develop anywhere you want (e.g. an airplane over the ocean!). It also means that you have to be comfortable working with repos from other people; not just a central repository.
The key idea here is that of remote repositories. You have probably noticed by now that git
has names such as origin
and master
. The name origin
refers to the name of the remote repository while the name master
refers to the name of the branch
. Repositories can have many branches. You can create references to other repositories (called remotes) within your current repository and customize the names.
You will complete this problem in your course repo in the HW2/
directory on the HW2-dev
branch.
Complete the following steps:
- Type
git remote -v
and take a screenshot. Save it asP1_1.png
, put it in yourHW2-final/
directory, commit the changes to your local repo, and push the changes toorigin
. - Add your
playground
repository as a remote. Use the commandgit remote add remote_name remote_url
whereremote_name
should be replaced with the alias of the remote repo andremote_url
should be replaced with the url to the remote repo. You can name the remote whatever you want. In the rest of this exercise, we will refer to the playground remote name asmy_name
. - Type
git remote -v
and take a screenshot. Save it asP1_3.png
and put it in yourHW2-final/
directory, commit the changes to your local repo, and push the changes toorigin
. - Now create a remote to the course playground (
cs107-sys-dev/playground
). No need to take a screenshot this time. - Next
fetch
the changes from the remote course playground. - Try out the
git remote show my_name
command. How useful! This is a good thing to check before blindly merging. - Now
merge
the changes into your local cs107 repo on theHW2-dev
branch (this should go smoothly because you shouldn't have any of the files yet so there's really nothing to merge).- Note that Steps 5 and 7 could have been combined with a
git pull remote_name branch_name
command, wherebranch_name
should be replaced with the branch that you want to pull from. It's good to get in the habit of first fetching and then merging; this helps enforce good practices. - Hint: Because your
playground
and course repos are not related, they have differentgit
histories.git
automatically rejects merges of repos with different histories. However, for this problem we will override that behavior. Try using the option--allow-unrelated-histories
. - Hint: You may need to resolve a merge conflict. In case you do, it's best to retain whatever was in your course repo (instead of what was in the playground).
- Note that Steps 5 and 7 could have been combined with a
- Show the status of your repo with the appropriate
git
command. Take a screenshot, save it asP1_8.png
, and put it in yourHW2-final/
directory. - Move the files and directories that were merged from the playground repo into your
HW2-final/
directory. - Commit the changes to your local repo.
- Push the changes to your remote course repo (probably named
origin
). - Optional Rename the remote with the command
git remote rename my_name new_name
, wherenew_name
should be replaced by the new alias that you want to use. Remove the remote withgit remote remove my_name
.
Final Deliverables (expected in your HW2-final/
directory)¶
P1_1.png
P1_3.png
P1_8.png
- From playground merge
src/
tests/
README.md
environment.yml
Part 2 [20 pts]: Python Basics¶
Problem 2 [20 pts]: DNA complement¶
Much of biology and bioinformatics depends on understanding and manipulating long sequences of DNA. A common thing to look at would be the complement of a given sequence.
Recall that DNA has 4 bases: A
,T
,G
,C
. DNA sequences are described by a concatenation of these letters. For example, a DNA sequence might have the form AATGGC
. The complement of the base A
is the base T
and the complement of the base G
is the base C
. The DNA complement of AATGGC
is therefore TTACCG
.
Write a Python function that accepts a string of arbitrary length representing a DNA sequence and returns the corresponding DNA complement, also as a string. Please put this function in a file called P2.py
.
API Design Considerations and Requirements¶
- Name your function
dna_complement
and have it take in a DNA sequence as input. This input should be a string. - You must handle the situation in which the input string is either empty or does not contain characters corresponding to the real DNA bases. If either of these situations arise, please have your function return
None
. - Your function should be case-agnostic. This means that the input string to the function can be any case. However, your function must return the DNA complement in upper case.
- Demo your function. You should do this in the same file in which the function is defined (
P2.py
). The demo should do the following:- Print out an example input string. You can choose whatever example you want.
- Call the function that you just defined
- Print out the output string that you get from your function.
- Repeat the demo with an invalid DNA string (containing characters other than ATGC).
- These two demos will take a total of six lines of code in your
P2.py
file after the definition of the function.
Notes: Assume that the DNA sequence is provided in the file. There is no need to have the user input the string on the command line.
Final deliverables
- The
P2.py
file containing your function and demos.
Part 3 [55 pts]: Closures and Decorators¶
Problem 3 [25 pts]: Simple Bank Account Withdraw System¶
The goal of this problem is to write a simple bank account withdraw system. The problem is based off of a problem in Structure and Interpretation of Computer Programs.
Instructions: Do each part in a different file (naming convention supplied below) and clearly label each with comments and useful naming.
a). Write a closure to make withdrawals from a bank account. You MUST use a nested function for this problem.
- The outer function should accept the initial balance as an argument (we'll refer to this argument as
balance
in this problem statement, but you can call it whatever you want). - The inner function should accept the withdrawal amount as an argument and return the new balance.
Notes:
- For this part, do not try to reassign
balance
in the inner function. Just see what happens when you return a new balance. You can store the new balance in a new name (call itnew_bal
if you want) or just return the new balance directly. - You should check for basic exceptions (e.g. attempting to withdraw more than the current balance).
Once you write your functions, demo them as follows to see if it behaves as expected for two consecutive withdrawls:
wd = make_withdrawal(init_balance)
wd(withdrawal_amount)
wd(new_withdrawal_amount)
The closure and demo should be submitted in a file called P3a.py
.
You should observe that this does not behave correctly. Explain why not. Add this explanation to your P3a.py
file.
Notes:
- The explanation can be entered as a
Python
string in your demo script and printed out to the screen.
b).
Now let's fix P3a. You can fix things up by updating balance
within the inner function. But this won't work. Try it out and explain why it doesn't work. Try to use the language that we used in lecture.
- Hint: Python Execution Model.
Submit this solution in the file P3b.py
. Again, provide an explanation as a string and print to the screen. As before, you can print this string before calling the demo.
c).
Now make just one small change to your code from P3b. Declare balance
as a nonlocal variable using the nonlocal
keyword. That is, after you get to the inner function, write nonlocal balance
. Here's some information on the nonlocal
statement: nonlocal
.
Now test things out like you did in P3a. It should be behave correctly now.
Submit as P3c.py
.
d).
Finally, visualize your code from P3c with Python Tutor. Take a screenshot at the final step and name it P3d.png
Final deliverables¶
P3a.py
P3b.py
P3c.py
P3d.png
Problem 4 [30 pts]: Analogue Clock¶
In this excercise you will write code to simulate an analogue clock. Please save this in a file called P4.py
.
Before presenting the requirements for your code, let's review some clock anatomy.
Clock Anatomy¶
The hands of a clock can be defined by two points. The first point is given by the origin $\left(0, 0\right)$. The second point will be somewhere on a circle. Precisely where on the circle depends on the time of day and the clock hand that we're thinking about (hour, minute, second). The time of day can be converted to the degrees of a circle (or an angle). For example, we're used to looking at a clock with 12 at the very top of the circle. In our minds, this corresponds to $0^{\circ}$. From this perspective, 3:00 in the afternoon corresponds to $90^{\circ}$. A few hours on the clock correspond to the following angles (in degrees):
Time | 12:00 | 3:00 | 6:00 | 9:00 |
$\theta$ (${}^{\circ}$) | 0 | 90 | 180 | 270 |
From here, you should be able to see that each hour represents $30^{\circ}$. And so, in degrees on a clock, we have, $$\theta = 30t_{h}$$ where $t_{h}$ is the hour. Of course, this is at odds with the usual mathematical definition. In mathematics, we would expect 3:00 to be at $0^{\circ}$ and 12:00 to be at $90^{\circ}$. Let's introduce a shift to fix this, $$\theta = 90 - 30t_{h}.$$ Do you understand what happened here? There is one more little modification that can be done, which will help the readability of the hour hand. Since each hour consists of thirty degrees, we can have the hour hand move a little bit each minute to help it slide along between two hours. This can be accomplished by recognizing that there are $60$ minutes in an hour. Hence, at the first minute of an hour, the hour hand should be pointing directly to the hour that just started. At minute $60$, the hour hand should be pointing to the next hour, which means that it will have passed through $30^{\circ}$. Hence, the angle of the hour hand is given by, $$\boxed{\theta = 90 - 30t_{h} - \frac{t_{m}}{2}}.$$ where $t_{m}$ represents the minutes.
Now that you know the angle that the hour hand has moved through, you're just about ready to calculate its $(x,y)$ position. Remember, this is the second point on the line. You just need to do two things:
- Define the length of the line by some parameter $r$. You choose this parameter! It just tells you how big the hour hand is.
- Convert $\theta$ to radians: $\theta_h = \dfrac{\pi}{180}\theta$
Then the $(x,y)$ coordinates are given by, \begin{align*} x &= r\cos\left(\theta_h\right) \\ y &= r\sin\left(\theta_h\right). \end{align*} Now you're ready to plot a line representing the hour hand!
The minute and second hands are much easier. The angles that the minute hand and second hand pass through are $$\boxed{\theta = 90 - 6t_{m}}$$ and $$\boxed{\theta = 90 - 6t_{s}},$$ respectively, where $t_{s}$ is the seconds. You can compute the $\left(x,y\right)$ coordinates of each hand the same way as you did with the hour hand. Just choose a length $r$ and convert to radians.
A Clock Hand Closure¶
Write a closure with the following API:
- The outer function should take in a single float representing the length of the clock hand. This is $r$ in the mathematics above.
- The outer function should return an inner function like all good closures do.
- The inner function should take in a single float representing the angle of the clock hand. This is $\theta$ in degrees in the mathematics above.
- The inner function should return the $\left(x,y\right)$ coordinate of the clock hand on the circle.
- Don't forget to convert to radians!
Demonstrate your closure in P4.py
by doing the following:
- Get the current date and time
- Instantiate the clock hands using your closure. This should look similar to the following pseudo-code:
import numpy
import matplotlib.pyplot
import datetime
### Closure defined up here
currentDT = datetime.datetime.now()
hour = currentDT.hour
minute = currentDT.minute
second = currentDT.second
# Calculate theta in degrees for each hand
# Specify the length of hour, minute and second hands
# hour_hand = name_of_closure(length_of_hour_hand)
# x_hour, y_hour = hour_hand(theta_hour)
# Plot the clock
Submit this as P4.py
. Here's an example clock:
Movement of the clock hands¶
You created an analog clock. Sadly, it only displays the current time. It would be really cool if we could make the hands move. In this portion of the assignment, you get to animate the clock.
- Write a
while
loop and plot a new clock at each iteration. This means you will need to get the current time at each iteration of the while loop. It's up to you how to terminate thewhile
loop. - At the end of each loop we need to wipe the plot and draw a new one with updated coordinates.
- Use
plt.cla()
to wipe the plot. - Use
plt.pause(0.1)
to have some pause so the loop doesn't run faster than a second. - Define your figure with
fig = plt.figure(figsize=(6,6))
and usefig.canvas.draw()
so you can see the plot live while the loop is running. You should determine where to place these commands in your code. - To have a consistent aspect ratio for your axis use
plt.axis([xmin, xmax, ymin, ymax])
. You choose the values ofxmin
,xmax
,ymin
, andymax
.
- Use
Submit this file as P4_animation.py
Final deliverables¶
P4.py
P4_animation.py