Goals for this homework

  • Familiarize yourself with Python, including its syntax and style. Learn how to code and run Python programs.
  • Write a Python class with custom state variables and methods.
  • Practice writing test code.

You are expected to complete this assignment individually. If you need help, you are invited to come to virtual office hours and/or ask questions on Piazza. Clarification questions about the assignments may be asked publicly. Once you have specific bugs related to your code, make the posts private. Piazza is your community resource - please use those as discussions amongst yourselves. We are not monitoring it all day, rather we each have check-in times once a day. Therefore, you need to start early enough to wait for feedback and/or build a vibrant, supportive community that helps each other while we are completing other necessary tasks (research, developing assignments, preparing for lecture, performing advising tasks, etc.).

In this homework, you will implement a qubit. The first half of the course builds upon this code week by week.

You will submit several files for this assignment, including files from the warmup and the homework (qubit.py, testharness ). You will submit your work in a zip file to Gradescope. More detailed submission instructions will come later.

Error Handling

Code inevitably breaks: users may provide incorrect inputs, existing data may be incorrect, or the method may encounter an unexpected edge case. When errors occur, the question becomes, "What should we do?" and "How can we notify the caller that an error occured?" Different situations necessitate different responses. Sometimes, we'll need to just give a warning to the user, othertimes we'll need to completely halt execution.

Error handling capabilities vary by language; in Python, the keyword throw is reserved to throw exceptions. In this course, we will print the error message in a special way (see below). If there is an opportunity, we will designate a specific return value for an error condition. If there is no available return value, then we will exit from within the method.

  print("error: too many widgets for the number of grommets");
  print("error: need ten boondoggles, but only have "+ num_bds);
  return False

Set Up

When completing assignments, the first task is to create a skeleton project that will minimally execute. This week, I provided it for you. In the future, you will be expected to create it for yourself. A skeleton is created through the following steps (which you do not need to do this week but will need to in the future):
  • Step 1: In the main file, put in the method declarations and the input/output/modifications of the method. In the body, print the fact that it is not yet implemented and return with the right type. For example:
    def surface_area_cylinder( height, radius):
    	print("surface_area_cylinder not yet implemented")
    	return 0.0
    
  • Step 2: Create the test file with minimal test code, consisting of a single call to each function.
    val = surface_area_cylinder(3.5, 7.9)
    

First get this compiling and running. It won't do anything useful, but this will mean that your code will compile and execute with our infrastructure. This must work in order to get any points in this course. Do this first, not last.

Why are skeletons important? This code guides your thinking about the key methods / classes that need to be implemented.

From a global perspective, skeletons help you better structure code. For example, if you'll need to sort arrays to be both ascending and descending, it may be easier to have a single def sort_arrays with an ascending=False variable.

From a more granular perspective, skeletons help you think about the key variables that need to be stored / input / output. For example, we could have implemented def surface_area_cylinder(height, radius, circumference). But, a skeleton would likely help us recognize that circumference is redundant if we are given the radius.

Qubit Class

Object-oriented design involves two steps: Identifying the elements of the state to store and identifying the actions (which are the methods) to implement.

The Qubit holds the state of a single qubit. As you know, a single qubit has two values, alpha and beta.

Along with the state, we implement a number of methods. First, we need constructor to initialize the state to some default value (|0>).

__init__(self): # constructor to initialize values

Next, we need to implement any setters / getters that are not yet implemented. For any values we want to provide the programmer, instead of making them public, we provide the setter / getter. This is an object-oriented programming convention that makes it easier to make changes to your implementation down the road - change the variable name, the variable type, etc. You only need to make sure that a conversion is provided in the method to support those who wanted direct access.

  • setvalue(self,v) # accepts a string (bra-ket notation) or list of floats (vector) 
    			    # to set qubit state
    			    # strings are limited to |0> and |1>
    			    
  • getvaluebraket(self) # return qubit state in bra-ket notation as a string 
    getvaluevector(self) # return qubit state in vector notation as a list

We will implement a few gates and then a measurement function.

  • notgate(self) hgate(self) zgate(self) measure(self)
In addition, there are two methods I have provided to you that are critical for grading, so DO NOT CHANGE THEM!
  • compare(obj1, obj2) 
    Compare two objects so that you and we can check your calculations in test functions

Testing

Place a robust sequence of tests in testlab4.py or provide a reasonable write up that describes how you would test the code. This should include tests that test your puppy method as well as all of the qubit methods.

Submit

Submit both files; the autograder should evaluate it.