Computational Methods in Physics ASU Physics PHY 494

05 Debugging Primer

Code contains errors (also known as bugs — literally the first bug was a moth stuck in an electromechanical relay of the Mark II).

The first bug: A page from the Harvard Mark II electromechanical computer's log, featuring a dead moth that was removed from the device. Courtesy of the Naval Surface Warfare Center, Dahlgren, VA., 1988. - U.S. Naval Historical. (Image is in the Public Domain)

Bugs can be

Here we provide an introduction to the absolutely necessary business of identifying and fixing ("squashing") software bugs. Work through this page and then see More Resources at the end for more advanced material. Bug-hunting can be difficult and tedious but you have to do it — code that produces wrong results is useless.

General strategy

  1. Be clear about what your code ought to do: what are the inputs, what are the outputs? What do you expect to get?

  2. If you get an exception with a stack trace: look at the code that is referenced.

  3. Output intermediate values (e.g., print() calls). Compare what you get to what you expect. Run the program in your head to understand what values you should see.

  4. Make one change and re-run. Is the bug fixed? If not, repeat.

Helpful strategies

  • Have a terminal and an editor open, side by side, so that you can quickly run the modified code. (Don't forget to save changes before running the code…)

  • Prototype small code pieces interactively in ipython.

  • Modularize code (functions, classes, modules) and test individual pieces of code.

  • Run programs inside ipython using the %run magic function:

  %run hello.py
  

The advantage is that now you can examine variables interactively inside the interpreter.1

Activity: Fix as many bugs as possible!

Code for the examples below comes in a Classroom Activity (set-up link provided in the Canvas LMS). 2

Syntax and language

Bug 1

Print the numbers 0 to 9:

fro num in range(10):
    print(num)

Bug 2

Print the squares of the numbers 0 to 9:

for num in range(10):
    x = num**2
     print(x)
print("Done") 

Bug 3

Print "error" for input 0 or the inverse of the input number:

x = input("Enter non-zero number --> ")
if x = 0:
   print("ERROR: number cannot be 0")
else:
   inverse = 1/x
   print(inverse)

Bug 4

Define the sinc-function \(\mathrm{sinc}(x) = \sin(x)/x\):

def sinc(x):
   return sin(x)/x

print(sinc(3.145))

Slicing and list building

A common problem is getting the indexing wrong, resulting in IndexError or subtle errors in the lists themselves. If faced with slicing problems, try to build and slice lists interactively in ipython and then transfer the working slice recipe to your code. You sometimes may want to create a simpler or shorter list for testing.

Bug 5

Show that my favorite season in Arizona is winter:

seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print('My favorite season is ', seasons[4])

Bug 6

Create a list of values -10, -9.8, -9.6, …, -0.2, 0, 0.2, …, 10.

h = 0.2
x = [-10 + i*h for i in range(100)]

Edge cases

Edge cases occur at the boundaries of the allowed range of input; typically, you have to test them explicitly. (Corner cases occur when multiple edge cases come together.)

Bug 7

The sinc function is defined for all real numbers

import math
def sinc(x):
   return math.sin(x)/x

but this implementation is incomplete.

  1. find values for which our function does not produce the correct result
  2. fix it
  3. BONUS: plot sinc(x) for values from -10 to 10 in steps of 0.2.

Logic errors

Logic errors tend to be the worst because your code runs and might even produce output that looks roughly correct even though it is wrong. You find logic errors by having a good understanding of what your code should produce for a given input and then trying different inputs and following the input through the code.

Bug 8

Create a list of squares of the first 10 natural numbers (0, 1, 2, …, 10) and print their sum 3:

squares = []
s = 0
for n in range(1, 10):
   squares.append(s)
   s = n*n
   print(n, s)

sum_s = sum(squares)
print("sum of squares", sum_s)

# result should be 385

Bug 9

Calculate the position of an object in free fall as a function of time and store time points (in 1-s intervals) and positions in two arrays (for plotting):4

g = -9.81
t, h, tmax = 1., 0., 10.

times, positions = [], []
while t <= tmax:
   x = 0.5 * g * t * t
   times.append(t)
   positions.append(x)
   t += h

print(times)
print(values)

More resources


Footnotes

  1. Inside ipython you can also run the Python debugger using the %pdb magic, but this is a more advanced topic. 

  2. If you do not have the access to the GitHub Classroom (e.g., because you are not taking the PHY494 class) then you can do this exercise by copying and pasting the code given above or by using the PHY494-resources repository: Pull in the latest updates

    cd ~/PHY494-resources
    git pull
    

    then copy the code examples to your work directory for this lesson, ~/PHY494/05_debugging,

    cp -r ~/PHY494-resources/05_debugging ~/PHY494
    cd ~/PHY494/05_debugging    
    

    and work there. 

  3. At least two errors are hidden here. 

  4. Just in case: To stop a running Python program, press CONTROL and C at the same time (control + c).