Homework 2

Due Thursday, September 26th 2019 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

Part 2: Python Basics

Part 3: Closure and Decorators


Part 0 [10 pts]: Course Workflow

Once you receive HW1 feedback (no later than Friday Sep 20) 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 (HW1 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:

  1. Type git remote -v and take a screenshot. Save it as P1_1.png, put it in your HW2-final/ directory, commit the changes to your local repo, and push the changes to origin.
  2. Add your playground repository as a remote. Use the command git remote add remote_name remote_url where remote_name should be replaced with the alias of the remote repo and remote_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 as my_name.
  3. Type git remote -v and take a screenshot. Save it as P1_3.png and put it in your HW2-final/ directory, commit the changes to your local repo, and push the changes to origin.
  4. Now create a remote to the course playground (dsondak/playground). No need to take a screenshot this time.
  5. Next fetch the changes from the remote course playground.
  6. Try out the git remote show my_name command. How useful! This is a good thing to check before blindly merging.
  7. Now merge the changes into your local copy (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, where branch_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 different git 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 probably best to retain whatever was in your course repo (instead of what was in the playground).
  8. Show the status of your repo with the appropriate git command. Take a screen shot, save it as P1_7.png, and put it in your HW2-final/ directory.
  9. Commit the changes to your local repo.
  10. Push the changes to your remote course repo (probably named origin).
  11. Optional Rename the remote with the command git remote rename my_name new_name, where new_name should be replaced by the new alias that you want to use. Remove the remote with git remote remove my_name.

Final Deliverables

  • The P1_1.png, P1_3.png and P1_7.png

Part 2 [30 pts]: Python Basics

Problem 2 [10 pts]: Count and Plot

In this problem, you will make a bar plot of the computer languages that students who take CS207 know. The file languages.txt contains all the languages that students listed as their primary language in the course survey from a previous iteration of the course.

Do the following:

  1. Write a function called languages that accepts a string representing the languages filename.
  2. Load the language strings from the file into a list (you may assume the file is in the current directory).
  3. Use the Counter function from the collections library to count the number of occurrences of each element of the list. Print out your answer to the screen.
  4. The languages function should return the frequency and label of each language (this can be extracted from the dictionary returned by the Counter function).
  5. In the same file where you defined the function (P2.py), call the function with languages.txt and get the labels and frequency.
  6. Create a bar plot to display the frequency of each language. Be sure to label the x-axis!
    • Be sure to import matplotlib: import matplotlib.pyplot as plt.
    • To generate the bar plot write plt.bar(x_coords, freqs). You need to define x_coords and freqs.
    • Hint: You may want to use the numpy arange function to create x_coords. Remember, x_coords is the x-axis and it should have points for each distinct language.
    • Hint: To get freqs, you may want to use the values() method on your result from step 2. That is, freqs = result_from_2.values().
    • Hint: To label the x-axis you should use plt.xticks(x_coords, labels) where labels can be accessed through the keys() method on your result from step 2.

Final Deliverables

  • The P2.py file containing your code
  • The bar plot (in .png or .pdf format)
  • Please include the languages.txt file in your HW2-final/ directory.

Problem 3 [10 pts]: Average Area of the Circles (Part 1)

The file circles.txt contains measurements of circle radii. Your task is to write a script that reports the average area of the circles. You will not use the numpy mean function. Here are the requirements:

  1. Open circles.txt (your function will assume the file is in the current directory) , read in the data, and convert the data to floats. Store in a list.
  2. Write a function that computes the area of a circle. The argument should be a single float.
  3. Write a function, called my_ave, that computes the average of the areas of the circles.
    • At the very least, my_ave should accept the list of radii as one argument and the circle function that you wrote in step 2 as another argument.
    • Note: There are other ways of doing this task, but I want you to do it this way.
  4. Print out the result. You should format the result Myavg : YOUR_RESULT. Hence if you're average area was $10$, the result should look like Myavg : 10

Final Deliverables

  • P3.py
  • Please include the circles.txt file in your HW2-final/ directory.

Problem 4 [10 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.

You must 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 P4.py.

API Design Considerations and Requirements

  • Name your function dna_complement and have it take in a DNA sequence (string) as input.
  • You must handle the situation in which the input string does not contain characters corresponding to the real DNA bases. If this situation arises, please have your function return None.
  • Your function should be case-agnostic in the sense 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 in the same Python file. Print out an example input string, call the function, and print out the output string. Assume the original DNA sequence definition already exists in the file. That is, there is no need to have the user input the string on the command line. You should also demo the case where the input is an invalid DNA string (containing characters other than ATGC).

Final deliverables

  • The P4.py file containing your function.

Part 3 [45 pts]: Closures and Decorators

Problem 5 [15 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. 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.

NOTE 1: 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 it new_bal if you want) or just return the new balance directly.

NOTE 2: 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:

wd = make_withdrawal(init_balance)
wd(withdrawal_amount)
wd(new_withdrawal_amount)

This demo should be submitted as P5a.py.

You should observe that this does not behave correctly. Explain why not.

Note: The explanation can be entered as a Python string in your demo script and printed out to the screen.

Note: Feel free to put the explanation above the demo. Otherwise the Python exception handler will kill the script before your explanation gets printed out. For example, your string could start with "The following code does not work because...". When code is run, the warning string will print out followed by the Python exception.

b). 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.

Submit this solution in the file P5b.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 Part b. Declare balance as a nonlocal variable using the nonlocal keyword. That is, after you get to the inner function, say nonlocal balance. Here's some information on the nonlocal statement: nonlocal.

Now test things out like you did in Part a. It should be behaving correctly now.

Submit as P5c.py.

d). Finally, visualize your code from Part 5c with Python Tutor. Take a screen shot and name it P5d.png

Summary of Deliverables

  • P5a.py
  • P5b.py
  • P5c.py
  • P5d.png

Problem 6 [15pts]: Average Area of the Circles (part 2)

Let's return to the data from Problem 3.

Write two functions:

  1. The first function should return the average circle area (you can re-use the my_ave function you already wrote if you'd like, but you might need to update it slightly for this problem).
  2. The second function np_ave should just use numpy to compute the average.

Write a decorator to time the evaluation of each function. You can use the timing decorator from lecture.

Notes and Hints

  1. Include the functions in a file called P6.py.
  2. Be fair!
  3. To be as fair as possible, do the following:
    1. Create an areas list/array outside of your averaging functions. This means that you should do a loop over the radii you read in from circles.txt, compute the area from each point, and store that area in an array. Do you know why this is more fair? If not, think about it make sure you understand. Also, do not use append. Instead, preallocate space for your area list/array.
    2. Your my_ave function should accept your areas data as a list. Remember, to allocate a list you should do [0.0]*N: if you use such a construct your list will be filled in with $N$ zeros.
    3. Your np_ave function should accept your areas data as a numpy array. To allocate a numpy array do areas_np = np.zeros(len(radii)).
    4. Now both functions are using the best data types possible for their tasks.

Final deliverables

  • P6.py containing your functions and speed test.

Problem 7 [15 pts]: Positivity

Write a decorator to check if a quantity returned from a function is positive. An exception should be raised if the quantity is not positive.

Write three functions and decorate them with your decorator:

  1. A function that returns the discriminant $\displaystyle d = b^{2} - 4ac$
    • Note: This function should accept a, b, and c as inputs and return the discriminant.
    • Note: You may assume that all three coefficients are real numbers.
  2. A function that computes the absolute value.
    • Note: You must write this yourself. Do not use built-ins functions.
    • Note: This function should accept a scalar x as an input and return the absolute value of x.
    • Note: You may assume that x is a real number.
  3. A function of your own choosing.

Put these functions in a file called P7.py.

Show that your decorator behaves correctly. For each function, show two cases (where applicable):

  1. A case where positivity is not violated
  2. A case where positivity is violated

You should demo your functions in the same P7.py file in which they are defined. For example, your demo might look something like (but not exactly the same):

print("Trying to compute the discriminant of {0:4.2f}x^2 + {1:4.2f}x + {2:4.2f}\n".format(a, b, c))
print(disc(a, b, c)) # Positivity not violated

print("Trying to calculate the absolute value of {0:8.6f}".format(x))
print(new_abs(x)) # Positivity not violated

and the output might look like:

Trying to compute the discriminant of 2x^2 + 0x + -1
8.0

Trying to compute the discriminant of absolute value of -1
1.0

We also want to make sure we test that exceptions occur when we want them to occur. Provided below is an example of how you might want to test whether your exceptions are working.

In [1]:
def dummy():
    '''Simple function that raises a ValueError'''
    raise ValueError("Test ValueError")

def dummy2():
    '''Simple function that doesn't return an error'''
    return "hello"

def test(f):
    '''Checks to make sure ValueErrors are being thrown correctly'''
    try:
        f()
        '''If you reached this part this means you didn't get an exception!'''
        print("Didn't catch an error - this is not expected!")
    except ValueError:
        '''Caught the exception type you want. Let the user know.'''
        print("Caught ValueError as expected!")
    except Exception:
        '''Some other exception happened. Let the user know something went wack.'''
        print("Caught some Unexpected Error")

test(dummy) #This one raises ValueError
test(dummy2) # This one doesn't
Caught ValueError as expected!
Didn't catch an error - this is not expected!

If you're not used to testing, this might seem strange to you. Why do we expect an error? The reason is that we're making sure we get the correct behavior under certain situations. We expect an error when the user inputs something illegal (however we define that).

Note that the example tests above aren't exactly complete. You may need to tweak them a little bit.

Final deliverables

  • P7.py
    • Including the functions, the function demos, and their tests.