# Using IPython for interactive work¶

You can work interactively with Python using:

• Spyder3
• Jupyter (IPython)

See this tutorial Tutorial for an introduction to interactive Python. This is a syntetic tutorial only about the tools that can be useful for the digital tests during the course.

## Preamble for working with Arrays and Fractions¶

Copy this preamble in your script to load the needed modules and implement a function printm and verbatim for pretty printing. (You can also put the code in a file, for example, utils.py and import that file from your script from utils import * or, if you are using Jupyter, using the magic function: %run utils.py.

In :
from numpy import *
from fractions import Fraction as f
set_printoptions(precision=3,suppress=True)

def printm(a):
"""Prints the array as strings
:a: numpy array
:returns: prints the array
"""
def p(x):
return str(x)
p = vectorize(p,otypes=[str])
print(p(a))

def tableau(a,W=7):
"""Returns a string for verbatim printing
:a: numpy array
:returns: a string
"""
if len(a.shape) != 2:
raise ValueError('verbatim displays two dimensions')
rv = []
rv+=[r'|'+'+'.join('{:-^{width}}'.format('',width=W) for i in range(a.shape))+"+"]
rv+=[r'|'+'|'.join(map(lambda i: '{0:>{width}}'.format("x"+str(i+1)+" ",width=W), range(a.shape-2)) )+"|"+
'{0:>{width}}'.format("-z ",width=W)+"|"
'{0:>{width}}'.format("b ",width=W)+"|"]
rv+=[r'|'+'+'.join('{:-^{width}}'.format('',width=W) for i in range(a.shape))+"+"]
for i in range(a.shape-1):
rv += [r'| '+' | '.join(['{0:>{width}}'.format(str(a[i,j]),width=W-2) for j in range(a.shape)])+" |"]
rv+=[r'|'+'+'.join('{:-^{width}}'.format('',width=W) for i in range(a.shape))+"+"]
i = a.shape-1
rv += [r'| '+' | '.join(['{0:>{width}}'.format(str(a[i,j]),width=W-2) for j in range(a.shape)])+" |"]
rv+=[r'|'+'+'.join('{:-^{width}}'.format('',width=W) for i in range(a.shape))+"+"]
print('\n'.join(rv))


## Elementary row operations¶

To do elementary row operations on a matrix one can do as follows:

In :
A = array( [[1, f(9,4), 2, f(4,5)],
[f(3,4), 1, f(5,4), 0]])
A[0,:] = f(4,9) * A[0,:]
printm(A)

[['4/9' '1' '8/9' '16/45']
['3/4' '1' '5/4' '0']]

In :
tableau(A)

|-------+-------+-------+-------+
|    x1 |    x2 |    -z |     b |
|-------+-------+-------+-------+
|   4/9 |     1 |   8/9 | 16/45 |
|-------+-------+-------+-------+
|   3/4 |     1 |   5/4 |     0 |
|-------+-------+-------+-------+


Note that indices start from 0, hence a vector of size $n$ has indices from $0$ to $n-1$.

## Matrix Operations¶

#### Construct an array of zeros¶

In :
zeros((3,1))

Out:
array([[0.],
[0.],
[0.]])

#### Concatenate an identity matrix to a matrix¶

In :
A_1 = concatenate([A,identity(2)],axis=1)
printm(A_1)

[['4/9' '1' '8/9' '16/45' '1.0' '0.0']
['3/4' '1' '5/4' '0' '0.0' '1.0']]


#### Insert a row in between other two:¶

In :
A_2 = concatenate([ A[0:1,:], array( [[0,2,1,0]] ), A[1:2,:] ],axis=0)
printm(A_2)

[['4/9' '1' '8/9' '16/45']
['0' '2' '1' '0']
['3/4' '1' '5/4' '0']]


#### Ask for the dimensions of an array:¶

In :
A.shape

Out:
(2, 4)

#### Multiply matrices¶

The operations +,*,/,- are elementwise. For the operations of scalar multiplication and matrix multiplication the dot method must be used. Compare the following two cells:

In :
A = ones((4,2)) # a matrix of 1s of size 4x2
b = array([1,2])
A*b

Out:
array([[1., 2.],
[1., 2.],
[1., 2.],
[1., 2.]])

which is element-wise and hence not matrix-vector multiplication. The correct way to do matrix-vector and matrix-matrix multiplication is:

In :
dot(A,b)

Out:
array([3., 3., 3., 3.])

#### Inverse¶

In :
A = random.randint(1,10,size=(4,4))
A_i = linalg.inv(A)
around( dot(A,A_i), decimals=2) # let's verify

Out:
array([[ 1., -0.,  0.,  0.],
[ 0.,  1.,  0.,  0.],
[ 0., -0.,  1.,  0.],
[-0., -0.,  0.,  1.]])

#### Transpose¶

In :
A.T

Out:
array([[5, 2, 7, 6],
[6, 1, 8, 1],
[6, 3, 7, 5],
[7, 7, 3, 5]])

#### Rank¶

In :
linalg.matrix_rank(A)

Out:
4

#### Reduced row echelon form¶

You should calculate the reduced row echelon form by applying elementary row operations

In :
A=array([[f(3,1),1,1,1],
[2,4,1,1],
[2,1,2,2]])
b=array([2,2,1])

AA=column_stack([A,b])
AA[0,:] = f(1,3) * AA[0,:]  # remember indices start from 0
AA[1,:] = -2 * AA[0,:] + AA[1,:]
AA[2,:] = -2 * AA[0,:] + AA[2,:]
printm(AA)
AA[1,:] = f(3,10)* AA[1,:]
AA[2,:] += -f(1,3) * AA[1,:]
printm(AA)
AA[2,:] = f(10,13) * AA[2,:]
printm(AA)
AA[1,:] += -f(1,10) * AA[2,:]
AA[0,:] += -f(1,3) * AA[2,:]
printm(AA)
AA[0,:] += -f(1,3) * AA[1,:]
printm(AA)

[['1' '1/3' '1/3' '1/3' '2/3']
['0' '10/3' '1/3' '1/3' '2/3']
['0' '1/3' '4/3' '4/3' '-1/3']]
[['1' '1/3' '1/3' '1/3' '2/3']
['0' '1' '1/10' '1/10' '1/5']
['0' '0' '13/10' '13/10' '-2/5']]
[['1' '1/3' '1/3' '1/3' '2/3']
['0' '1' '1/10' '1/10' '1/5']
['0' '0' '1' '1' '-4/13']]
[['1' '1/3' '0' '0' '10/13']
['0' '1' '0' '0' '3/13']
['0' '0' '1' '1' '-4/13']]
[['1' '0' '0' '0' '9/13']
['0' '1' '0' '0' '3/13']
['0' '0' '1' '1' '-4/13']]


You can check your result by importing the module for symbolic computation sympy. You are however discouraged to use this for your calculations:

In :
import sympy as sy
# np.linalg.solve(A,b)

sy.Matrix(AA).rref()

Out:
(Matrix([
[1, 0, 0, 0,  9/13],
[0, 1, 0, 0,  3/13],
[0, 0, 1, 1, -4/13]]), (0, 1, 2))