Numpy Lab 1 - MTH309 - Spring 018

Feb. 8, 2018

Outline

  1. Getting Started
  2. Intro to Anaconda, Python and Numpy
  3. Linear Algebra with Numpy
  4. Applications from Ch. 1.6

1. Getting Started

1. Create a new folder called MTH309_NumpyLabs

2. Save the file http://www.acsu.buffalo.edu/~danet/Sp18/MTH309/MTH309_files/NumpyLab1.zip into that folder and uncompress the '.zip' file

* The file NumpyLab1.ipynb is a template for today's lab
* The file mth309.py is a resource file that has specialized functions for our class!

3. Open file 'NumpyLab1.ipynb'

* On a mac, navigate to the folder called NumpyLab1 using the 'cd' command in your terminal application
* When in the correct folder using the terminal application, type: jupyter notebook NumpyLab1.ipynb
* This should launch a jupyter notebook in your web browser

2. Intro to Anaconda, Python and Numpy

Python is a programming language. It is similar to Matlab and R. It is not as low-level as c or c++. It is probably the most widely used programming language for data science and computer science.

Anaconda is a software distribution manager. It is used to install and update software including python itself and python modules, such as NumPy. Installation and update is implemented using conda

* For example, in a terminal write: 'conda update python'

Numpy [num-pie = numerical + python] is a python module designed for linear algebra.

In [1]:
# First thing, you must import the numpy module.

from numpy import *

# Also, did you notice that anything written after '#' is commented out. 
# Write comments. Write lots of comments.

Creating arrays

Numpy arrays can be created from lists of numbers, or lists of lists of numbers, using numpy's array function.

Lists in python are comma-separated sequences of things enclosed by square brackets, like this: [2,4,6,8].

Note that Jupyter Notebook automatically prints the value of the last expression in a cell, but you need to explicitly "print" anything else you want to see. You can also "print" the last expression in a cell, and for a numpy array that gives a cleaner rendering.

In [2]:
x = array([1,20,300])
x
Out[2]:
array([  1,  20, 300])
In [3]:
print(x)
print('This is something')
print(10*x)
[  1  20 300]
This is something
[  10  200 3000]

A two-dimensional numpy array, which we will use as a matrix in this course, can be constructed from a list of its rows:

In [4]:
a = array( [[1,2,-3], [7,8,4]] )
print(a)
[[ 1  2 -3]
 [ 7  8  4]]

There are functions in numpy to create frequently-used special matrices such as the "identity" matrices and zero matrices.

In [5]:
print(eye(3))
#print(5*eye(3))
[[ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 0.  0.  1.]]
In [6]:
zeros(3)
Out[6]:
array([ 0.,  0.,  0.])
In [7]:
zeros((2,5))
Out[7]:
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])

You can make random matrices that contain integers using random.randint

In [8]:
A = random.randint(0,10,(3,9))
print(A)
[[6 1 2 7 2 1 0 7 5]
 [1 3 2 1 6 0 9 2 3]
 [1 0 6 9 5 7 3 0 8]]

Accessing elements and rows of arrays

Elements of arrays are accessed using integer indices.

SUPER-IMPORTANT: In numpy, as throughout Python and most other modern programming languages, counting starts at 0, not 1!

For example, the array [1,20,300] has elements numbers 0, 1, and 2.

In [9]:
a = array( [[1,20,-300], [7,80,900]] )
print(a)
[[   1   20 -300]
 [   7   80  900]]
In [10]:
print(a[0][0])
1
In [11]:
print(a[0])
[   1   20 -300]
In [12]:
print(a[1])
[  7  80 900]
In [13]:
print(a[1][:2])
[ 7 80]

Arithmetic on arrays¶

In [14]:
u = array([1,2,3])
v = array([0,10,100])
print(u)
print(v)
[1 2 3]
[  0  10 100]

Numpy allows us to do arithmetic on arrays very easily.

To add the two arrays above elementwise, as in vector addition, we just write u+v as you'd expect:

In [15]:
print(u+v)
[  1  12 103]

We can also do scalar multiplication easily:

In [16]:
print(u + 100*v)
[    1  1002 10003]

There are 2 ways to change a variable

In [17]:
u = u*10;
v *= 10; # you can also write +=, -=, /=, 
print(u)
print(v)
[10 20 30]
[   0  100 1000]

3. Linear Algebra with Numpy

Let's consider (modified) Exercise 13 from Chapter 1.2, which asks us to find the general solution for a system whose augmented matrix is given by

$ a = \left[\begin{array}{cccccc} 1&-3&0&-1&0&-2 \\ 0&1&0&0&-4&1 \\ 0&0&0&0&0&0 \\ 0&0&0&1&9&4 \end{array}\right] $

We will solve it 2 ways:

  1. Using Numpy arrays and row reductions
  2. Using the rre function from mth309.py

First approach: Using Numpy arrays and row reductions

In [18]:
#first define the augmented matrix encoding the system of equations

a = array([[ 1,-3, 0,-1, 0,-2],
           [ 0, 1, 0, 0,-4, 1],
           [ 0, 0, 0, 0, 0, 0],
           [ 0, 0, 0, 1, 9, 4]],dtype=float)
print(a)
[[ 1. -3.  0. -1.  0. -2.]
 [ 0.  1.  0.  0. -4.  1.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  1.  9.  4.]]

Step 1: Swap row 3 and row 4.

Note that because counting starts at 0,1,2, ... in python, these rows 3 and 4 are indexed 2 and 3.

In [19]:
a[[2,3]] = a[[3,2]]
print(a)
[[ 1. -3.  0. -1.  0. -2.]
 [ 0.  1.  0.  0. -4.  1.]
 [ 0.  0.  0.  1.  9.  4.]
 [ 0.  0.  0.  0.  0.  0.]]

Step 2: Cancel out the -3 in row 1. (i.e., row 0)

In [20]:
a[0] = a[0] + 3*a[1]
print(a)
[[  1.   0.   0.  -1. -12.   1.]
 [  0.   1.   0.   0.  -4.   1.]
 [  0.   0.   0.   1.   9.   4.]
 [  0.   0.   0.   0.   0.   0.]]

Step 3: Cancel out the -1 in row 1. (i.e., row 0)

In [21]:
a[0] = a[0] + a[2]
row_echelon_form = a;
print(row_echelon_form)
[[ 1.  0.  0.  0. -3.  5.]
 [ 0.  1.  0.  0. -4.  1.]
 [ 0.  0.  0.  1.  9.  4.]
 [ 0.  0.  0.  0.  0.  0.]]

The matrix is now in reduced echelon form!

Second approach: Using the 'rre' function from mth309.py

In [22]:
# first load mth309
from mth309 import *

print(a)

print('\n Using the rre function, we can obtain the reduced echelon form with 1 line of code!')
print(rre(a))
[[ 1.  0.  0.  0. -3.  5.]
 [ 0.  1.  0.  0. -4.  1.]
 [ 0.  0.  0.  1.  9.  4.]
 [ 0.  0.  0.  0.  0.  0.]]

 Using the rre function, we can obtain the reduced echelon form with 1 line of code!
 1 0 0 0 -3 5
 0 1 0 0 -4 1
 0 0 0 1  9 4
 0 0 0 0  0 0


Using the function 'Matrix' from

The function matrix allows rational fractions to be depicted as fractions and not decimals.

In [23]:
#Lets create a random matrix

random.seed(777) # This line is just to ensure that the following "random" matrix is the same every time you execute this cell
A = random.randint(0,10,(3,4))
print(A)
[[7 6 7 1]
 [7 4 7 9]
 [8 7 2 0]]
In [24]:
a = Matrix(A)  # create an array whose elements are rational numbers (actually fractions.Fraction)
print(a)
 7 6 7 1
 7 4 7 9
 8 7 2 0


In [25]:
print(a/2)
print(A/2)
 7/2   3 7/2 1/2
 7/2   2 7/2 9/2
   4 7/2   1   0


[[ 3.5  3.   3.5  0.5]
 [ 3.5  2.   3.5  4.5]
 [ 4.   3.5  1.   0. ]]

Let's compute the row-reduced echelon form of A

In [26]:
print(rre(a))
print(rre(A))
 1 0 0 73/21
 0 1 0    -4
 0 0 1  2/21


 1 0 0 73/21
 0 1 0    -4
 0 0 1  2/21


We can compare with the result from the built-in (floating-point) linear solver linalg.solve(). First let's convert our answer to approximate decimal form:

In [27]:
linalg.solve(A[:,:3],A[:,3])
Out[27]:
array([ 3.47619048, -4.        ,  0.0952381 ])

To convert the fractions back to decimals, use the function decimalized

In [28]:
decimalized(rre(A))
Out[28]:
array([[ 1.        ,  0.        ,  0.        ,  3.47619048],
       [ 0.        ,  1.        ,  0.        , -4.        ],
       [ 0.        ,  0.        ,  1.        ,  0.0952381 ]])

Agreement!

4. Applications from Ch. 10.6

Application 1: Balancing Chemical Equations (see page 52 in book)

Balance the following chemical equation:

$ (x_1) C_3H_8 + (x_2)O_2 \rightarrow (x_3)CO_2 + (x_4)*H_2O $

Note that:

  • $C_3H_8$ is propane
  • $O_2$ is oxygen
  • C0_2 is carbon dioxide
  • $H_20$ is water

Balancing the equation means that the carbons, hydrogens and oxygens must be conserved. We set the system up using vectors:

  • row 1 encodes carbon conservation
  • row 2 encodes hydrogen conservation
  • row 3 encodes oxygen conservation

Thus this system is equivalent to:

$ x_1\left[\begin{array}{c}3\\8\\0\end{array}\right] + x_1\left[\begin{array}{c}0\\0\\2\end{array}\right] \rightarrow x_3\left[\begin{array}{c}1\\0\\2\end{array}\right] + x_4\left[\begin{array}{c}0\\2\\1\end{array}\right] $

Step 1 is rewrite the system as a homogeneous equation:

$ x_1\left[\begin{array}{c}3\\8\\0\end{array}\right] + x_1\left[\begin{array}{c}0\\0\\2\end{array}\right] + x_3\left[\begin{array}{c}-1\\0\\-2\end{array}\right] + x_4\left[\begin{array}{c}0\\-2\\-1\end{array}\right] = \left[\begin{array}{c}0\\0\\0\end{array}\right] $

Which is equivalent to $A{\bf x} = {\bf 0}$ where

$ A = \left[\begin{array}{cccc} 3&0&-1&0\\8&0&0&-2\\0&2&-2&-1 \end{array}\right] $.

Step 2 is to create the augmented matrix in numpy

In [29]:
a = array([[ 3,0,-1,0,0],[8,0,0,-2,0],[0,2,-2,-1,0]] )
print(a)
[[ 3  0 -1  0  0]
 [ 8  0  0 -2  0]
 [ 0  2 -2 -1  0]]

Step 3 is to solve the row-reduced echelon form

In [30]:
ra = rre(a)
print(ra)
 1 0 0 -1/4 0
 0 1 0 -5/4 0
 0 0 1 -3/4 0


Note that $x_4$ is free, and the others are basic.

The solution states

$ \begin{eqnarray} x_1 &=& 1/4 x_4\\ x_2 &=& 5/4 x_4\\ x_3 &=& 3/4 x_4\\ x_4 &=& x_4 \end{eqnarray} $

Lets multiply by 4 to make then numbers integers.

Let $x_4=4$ so that

$ \begin{eqnarray} x_1 &=& 1 \\ x_2 &=& 5 \\ x_3 &=& 3 \\ x_4 &=& 4 \end{eqnarray} $

We can now write:

$ C_3H_8 + 5O_2 \rightarrow 3CO_2 + 4 H_2O $

Application 2: Flows on a traffic network

Consider the following traffic flows in a Baltimore neighborhood

Let's solve the traffic at on the road sections: $x_1$, $x_2$, $x_3$, $x_4$ and $x_5$

First, we need to make a table for the flows in and out of each node.

Also, the total flow into the network ($500+300+100+400$) equals the total flow out ($300 + x_3 + 600$), implying

$x_3=400$.

Let's write down the system of equations

$ \begin{eqnarray} x_1 + x_2&=& 800\\ x_2 -x_3 + x_4&=& 300\\ x_4 + x_5&=& 500\\ x_1 + x_5 &=& 600\\ x_3 &=& 400 \end{eqnarray} $.

and the augmented matrix

$ A = \left[\begin{array}{cccccc} 1&1&0&0&0&800\\ 0&1&-1&1&0&300\\ 0&0&0&1&1&500\\ 1&0&0&0&1&600\\ 0&0&1&0&0&400 \end{array}\right] $.

We solve for the unknown flows using the row-reduced echelon form:

In [31]:
A = Matrix([[1,1,0,0,0,800],
     [0,1,-1,1,0,300],
     [0,0,0,1,1,500],
     [1,0,0,0,1,600],
    [0,0,1,0,0,400]])
print(A)
 1 1  0 0 0 800
 0 1 -1 1 0 300
 0 0  0 1 1 500
 1 0  0 0 1 600
 0 0  1 0 0 400


In [32]:
print(rre(A))
 1 0 0 0  1 600
 0 1 0 0 -1 200
 0 0 1 0  0 400
 0 0 0 1  1 500
 0 0 0 0  0   0


It follows that the solution is given by

$ \begin{eqnarray} x_1&=& 600 - x_5\\ x_2 &=& 200 + x_5\\ x_3&=& 400\\ x_4 &=& 500 - x_5\\ x_5 &=& \text{free} \end{eqnarray} $

Application 3: Equilibrium of a 3-sector economy (exercise 2 in Ch. 1.6)

Consider an economy with three sectors: Chemicals, Fuels, and Machinery.

  • Chemicals sells 30% of its output to Fuels and 50% to Machinery, and retains the rest.
  • Fuels sells 80% of its output to Chemicals and 10% to Machinery and retains the rest.
  • Machinery sells 40% to Chemicals and 40% to Fuels and retains the rest.

Find the set of equilibrium prices when the price for Machinery output is 100 units.

First you must write a table for the energy flows and write out the system of equations:

In [33]:
# First define the augmented matrix
a = Matrix( 10* array( [[.8,-.8,-.4,0],[-.3,.9,-.4,0],[-.5,-.1,.8,0]] ) )
a
Out[33]:
  8 -8 -4 0
 -3  9 -4 0
 -5 -1  8 0
In [34]:
ra = rre(a)
ra
Out[34]:
 1 0 -17/12 0
 0 1 -11/12 0
 0 0      0 0

Machine units are a free variable. If the Machine units is 12, then the Chemical units is 17 and the Fuel units is 11.