Scientific Computing Using Python - PHYS:4905 - Fall 2018

Lecture #06 - 9/6/2018 - Prof. Kaaret

These notes borrow from Linear Algebra by Cherney, Denton, Thomas, and Waldron and the WikiPedia pages on Augmented matrix and Gaussian elimination.


Defining your own functions in Python

To define a function in Python, you use the def statement.  The following defines a function that calculated the nth root of a number.

def nthroot(x, n) :
  """
  nthroot returns the n-th root of x.
  arguments: x, n
  x is the value, n specifies which root
  """
  y = x**(1.0/n)
  return y

To call this function, use

nthroot(27.0, 3)

The 'declaration' of the function is the first line with the def statement.  The word after def sets the name of the function, which in this case is nthroot.  After the name, there are parenthesis and inside the parathesis is a list of arguments to the function.  In this case, there are two arguments x and n.  After the parenthesis, there is a colon, just like in for and if statments.

After the declaration, it is customary to have a comment block.  For function definitions, Python will use the comment if someone asks for help on the function by typing help(nthroot).  Probably the most important comments you can make are the ones that describe your functions, since those will be needed by anyone who uses the function (and not only people who look inside at the code within your functions).

The body of the function is indented, just like in for and if statments.  At the end of the body, there is usually a return statement.  For nthroot, we return the calculated nth root.  It is ok to not include a return statement.  In that case, Python returns the value None.

To use the function, run the file that contains it.  If you edit the function, run the file again and the function will be updated with the new definition.  If you build your functions into libraries and then import those libraries, you need to jump through more hoops to update the function definitions.

Note that variables in the function are not the same as the variables in your console or in the code calling the function.  Save the function above, run the file, and then do

nthroot(27.0, 3)

Now look at the Variable explorer pane.  You don't see x, n, or y.  This is because those variables are local to the function.  They are defined only internally to the function.  Indeed, you could even set x = 3.14, before you call nthroot, and the x in the console window would be unchanged, even though the function uses an internal variable with the same name.

Functions can see variables used in the console window or the calling code, as long as their names don't conflict with any variables local to the function.  However, this is bad (very bad) programming practice.  When you define functions you should use the following guidelines:
Python automatically makes a copy of any simple variables when calling a function.  Try changing nthroot so that the last two lines are

  x = x**(1.0/n)
  return x


Run the file and then in the console window do
x = 3.14
nthroot(x, 3)
x


You'll see that the x in the console window is unchanged, even though the function changed x internally.

However, if you change the same two lines to
  x[0] = x[0]**(1.0/n)
  return x[0]


and then in the console pane do

x = [3.14]
nthroot(x, 3)
x


You'll find that the value of x[0] has changed.  Python does not automatically copy lists or numpy arrays when passing them to functions.  Thus, it is possible to write functions that have side effects, meaning they change in the variables in the calling code (or console).  Side effects are to be avoided because they can create bugs that are a huge hassle to figure out.  If you need to modify an array inside of a function, make a copy of the array with the copy function.


Augmented matrices in Python

Recall from the last lecture that we could write our system of linear equation in matrix form and also as an augmented matrix with no variables.
x+y=272x-y=0(112-1)(xy)=(270)(11|272-1|0)\begin{matrix} x + y = 27 \\ 2x - y = 0\\ \end{matrix} \;\;\; ⟺ \;\;\; \begin{pmatrix} 1 & 1 \\ 2 & -1 \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} 27 \\ 0 \end{pmatrix} \;\;\; ⟺ \;\;\; \left(\begin{array}{cc|l} 1 & 1 &| 27 \\ 2 & -1 &| 0 \\ \end{array}\right)
The augmented matrix form can be represented in Python as a matrix,
m = array([[1.0, 1.0, 27],[2, -1, 0]])

We can then write Python functions that carry out our three elementary row operations.

The code is here:

def row_swap(m, i, j) :
  """
  Swap rows i and j of matrix m.
  """
  mp = m.copy() # make a copy of the input matrix
  t = mp[i,:].copy() # copy row i of mp, need to copy since next line changes mp
  mp[i,:] = mp[j,:] # copy row j of mp into row i
  mp[j,:] = t # put the copy of the original row i into row j
  return mp

def scalar_mult(m, i, c) :
  """
  Multiply row i of matrix m by c.
  """
  mp = m.copy() # make a copy of the input matrix
  mp[i,:] = c*mp[i,:] # multiply row i by c and put it back into row i
  return mp

def row_add(m, i, j, c) :
  """
  Multiply row i of matrix m by c, add to row j, and replace row j.
  """
  mp = m.copy() # make a copy of the input matrix
  mp[j,:] = c*mp[i,:]+mp[j,:] # multiply row i by c, add to row j, and put sum back into row j
  return mp


Copy these routines into Spyder and save the file as eros.py.  Run the file.  Create an augmented matrix and try the functions in the file to see if they work properly.  Use the Variable explorer window or print out the contents of your matrices to check.

Assignment

The assignment for today's class involves doing Gaussian elimination in Python.

HW #6 is due at the beginning of class on Tuesday, September 11.
https://homepage.physics.uiowa.edu/~pkaaret/2018f_p4905/hw06.html