Debugging

  • Mosiah 4:29
    • And finally, I cannot tell you all the things whereby ye may commit sin; for there are divers ways and means, even so many that I cannot number them.

Key types of bugs (among many):

  • syntax errors
    • Improper python
    • Missing ":" or indentation, or spelling, or invalid expressions.
    • Usually easily found. Python will tell you about them.
    • Often parentheses issues.
  • runtime errors
    • An error that occurs when you run the code.
    • Divide by zero, or an improper value.
    • Bad guess causes issues with a solver
    • Interpolating outside the range of x-data (without specifying extrapolation).
    • etc.
  • logical errors
    • You coded it wrong.
    • But the computer doesn't know that. It tries to solve the problem.
    • You might get an answer but its be wrong (or only sometimes wrong :(
    • You might not get an answer.

The Π Rule

  • "However long you think it should take, multiply by Π."
  • Bugs are often the reason for the Π rule!
In [124]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.integrate import quad
from scipy.interpolate import interp1d
from scipy.optimize import curve_fit, fsolve
import sympy as sp
sp.init_printing()
from IPython.display import display

Example 1

In [125]:
def (κ):
    η = 1
     = 1
    return (np.exp(-5.2*((((κ*η)**4) + **4)**0.25 - ))

I = quad(, 0,1)[0]
  File "<ipython-input-125-db6ae7012ebd>", line 6
    I = quad(fη, 0,1)[0]
    ^
SyntaxError: invalid syntax

Error

  • Parentheses
    • If you get a syntax error on a line that looks fine, the problem is often on the previous line.
    • In this case, we are missing an ending parenthesis (or we have an unnecessary beginning parenthesis) so that Python thinks the command is continuing into the quad statement below.

Fix

  • Above, there is a missing ) at the end.
  • Count the number of left ( and right ) and make sure they match.
  • Delete any parentheses that are not needed (around the whole expression, and around the (κ*η)**4

    return np.exp(-5.2*(((κ*η)**4 + cη**4)**0.25 - cη))

  • Add spaces to make the expression more clear

    return np.exp( -5.2*( ((κ*η)**4 + cη**4)**0.25 - cη ) )

  • Compare carefully to the intended equation. $$f_{\eta}(\kappa) = \exp(-5.2([(\kappa\eta)^4 + c_{\eta}^4]^{1/4}-c_{\eta})).$$

    • Rewrite the code if needed.
    • Try working inside-out.
    • Write () in pairs, then fill in inside
    • DON'T GUESS AND HOPE

Example 2

Use sympy to solve for the root of $$x^3 + 15x^2 = 3x - 10$$ Then numerically evaluate each root.

In [126]:
x = sp.symbols('x')
ex = sp.Eq(x**3 + 15*x*x, 3*x - 10)
roots = sp.solve(ex,x)
display(roots)
roots.evalf()
$$\left [ -5 - \frac{78}{\left(- \frac{1}{2} - \frac{\sqrt{3} i}{2}\right) \sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}} - \frac{\left(- \frac{1}{2} - \frac{\sqrt{3} i}{2}\right) \sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}}{3}, \quad -5 - \frac{\left(- \frac{1}{2} + \frac{\sqrt{3} i}{2}\right) \sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}}{3} - \frac{78}{\left(- \frac{1}{2} + \frac{\sqrt{3} i}{2}\right) \sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}}, \quad - \frac{\sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}}{3} - 5 - \frac{78}{\sqrt[3]{\frac{27 \sqrt{5321}}{2} + \frac{7425}{2}}}\right ]$$
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-126-f9b81b4cfd78> in <module>
      3 roots = sp.solve(ex,x)
      4 display(roots)
----> 5 roots.evalf()

AttributeError: 'list' object has no attribute 'evalf'

Error:

roots.evalf() should be

roots[0].evalf()
roots[1].evalf()
roots[2].evalf()

Fix:

  • Read the output.
  • Python doesn't say "you forgot the [0]"
  • Python is more literal.
    • roots is a list and Python lists don't know anything about sympy's evalf function.
    • That is, you can't do list.evalf().
    • But that is a hint that you did it wrong. A hint that you need the [0], because the elements of the list are sympy expressions and some_expression.evalf() does make sense.

Example 3

Consider this problem and its solution.

Solve the following two equations in the two unknowns $c_L$ and $c_{\eta}$: $$\int_0^{\infty}E(\kappa)d\kappa = K,$$

$$\int_0^{\infty}2\nu\kappa^2E(\kappa)d\kappa = \epsilon.$$

Here, $\nu = 1.5\times 10^{-5}$, $K = 0.008$, and $\epsilon = 0.003$.

You can take $\infty\approx 1E5$.

We also have the following functions and relations: $$L = \frac{K^{3/2}}{\epsilon},$$ $$\eta = \left(\frac{\nu^3}{\epsilon}\right)^{1/4},$$

$$E(\kappa) = 1.5\epsilon^{2/3}\kappa^{-5/3}f_L(\kappa)f_{\eta}(\kappa),$$$$f_L(\kappa) = \left(\frac{\kappa L}{[(\kappa L)^2 + c_L]^{1/2}}\right)^{11/3},$$$$f_{\eta}(\kappa) = \exp(-5.2([(\kappa\eta)^4 + c_{\eta}^4]^{1/4}-c_{\eta})).$$
  • Guesses: $c_L=5$, $c_{\eta}=0.5$.
  • Note: $K$ is different than $\kappa$
In [127]:
def F(cLcη):
    cL = cLcη[0]
     = cLcη[1]
    
    ν = 1.5E-5
    K = 0.008
    ϵ = 0.003
    inf = 1E5
    
    L = K**1.5/ϵ
    η = (ν**3/ϵ)**0.25
    
    def fL(κ):
        return (κ*L/np.sqrt((κ*L)**2 + cL))**(11/3)
    
    def (κ):
        return np.exp(-5.2*(((κ*η)**4 + **4)**0.25) - )
    
    def E(κ):
        return 1.5*ϵ**(2/3)*κ**(-5/3)*fL(κ)*(κ)
    
    def twoNuK2E(κ):
        return 2*ν*κ*2*E(κ)
    
    eq0 = quad(E,        0, inf)    - K
    eq1 = quad(twoNuK2E, 0, inf)[0] - ϵ
    
    return np.array([eq0, eq1])

#-----------------------------------------------------------

guess = np.array([5, 0.5])

cLcη = fsolve(F, guess)

print(f'cL = {cLcη[0]:.4f}')
print(f'cη = {cLcη[1]:.4f}')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-127-871980a29428> in <module>
     32 guess = np.array([5, 0.5])
     33 
---> 34 cLcη = fsolve(F, guess)
     35 
     36 print(f'cL = {cLcη[0]:.4f}')

/usr/local/lib/python3.7/site-packages/scipy/optimize/minpack.py in fsolve(func, x0, args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag)
    146                'diag': diag}
    147 
--> 148     res = _root_hybr(func, x0, args, jac=fprime, **options)
    149     if full_output:
    150         x = res['x']

/usr/local/lib/python3.7/site-packages/scipy/optimize/minpack.py in _root_hybr(func, x0, args, jac, col_deriv, xtol, maxfev, band, eps, factor, diag, **unknown_options)
    212     if not isinstance(args, tuple):
    213         args = (args,)
--> 214     shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
    215     if epsfcn is None:
    216         epsfcn = finfo(dtype).eps

/usr/local/lib/python3.7/site-packages/scipy/optimize/minpack.py in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
     25 def _check_func(checker, argname, thefunc, x0, args, numinputs,
     26                 output_shape=None):
---> 27     res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
     28     if (output_shape is not None) and (shape(res) != output_shape):
     29         if (output_shape[0] != 1):

<ipython-input-127-871980a29428> in F(cLcη)
     23         return 2*ν*κ*2*E(κ)
     24 
---> 25     eq0 = quad(E,        0, inf)    - K
     26     eq1 = quad(twoNuK2E, 0, inf)[0] - ϵ
     27 

TypeError: unsupported operand type(s) for -: 'tuple' and 'float'

Errors:

eq0 = quad(E, 0, inf) - K should be

eq0 = quad(E, 0, inf)[0] - K

Fix:

  • Read the error message.
    • Often just scroll to the bottom. Don't read the whole thing.
    • The line number is listed. Look there.
    • Python doesn't say "you forgot the [0]. It is more literal. It says "you are trying to subtract a tuple and a float.
      • Look for the "-". The left side is a tuple (quad), the right is a float (a number, K).
      • This is a big hint. You don't want a tuple you want the integral. Then we remember...

Error:

return np.exp(-5.2*(((κ*η)**4 + cη**4)**0.25) - cη) should be

return np.exp(-5.2*(((κ*η)**4 + cη**4)**0.25 - cη))

Fix:

  • Here we just see an invalid value encountered in sqrt.
    • This often means that we either
      • Typed the equations wrong
      • Gave bad guesses
      • (Got unlucky)
  • We look carefully at the equation and fix our problem.

Error:

return 2*ν*κ*2*E(κ) should be

return 2*ν*κ**2*E(κ)

Fix:

  • This is a really bad error.
  • The code runs fine. But we get the wrong answer.
  • You have to use engineering intuition and other checks to find these bugs.
  • The best thing is to be very careful with your work. Check it twice.
  • Invert the problem.
    • Once you have the solution, you can run it through the equations to see if you recover the desired answer.
      • This may or may not help.
      • In the above case, it won't if we use the same twoNuK2E function to check, since that is where the bug is.
    • One test is to assume the answer (use the guesses), then compute K and ϵ, then work the problem and see if you recover your assumed answer.
    • This is useful when it is easy to find K and ϵ if you know cL and cη, but hard to get cL and cη from K and ϵ.
  • Sometimes it is worth starting over rather than tracking down a bug. You might not make the same typo again, and might actually save time.

Other examples?

  • Exceeding array bounds, or not being careful about indexing.
  • Using ^ instead of **
  • Using 6.02*10**23 instead of 6.02E23 (more of a computational sin than a bug).
  • Using conflicting names; like using f for both a function and a variable.
  • Passing a function call as an argument instead of passing the function itself as the argument:
    • Like using this: I = quad(f(x), a, b)[0]
    • instead of this: I = quad(f, a, b)[0]
  • Not being careful with units. Make sure everything is dimensionally consistent.
In [ ]: