In [1]:
    
from __future__ import print_function, division
import thinkstats2
import thinkplot
import pandas as pd
import numpy as np
from fractions import Fraction
%matplotlib inline
    
In [2]:
    
def scalar_product(x, y):
    x = np.asarray(x)
    y = np.asarray(y)
    return np.sum(x * y)
    
In [3]:
    
scalar_product([1,2,3], (4,5,6))
    
    Out[3]:
In [4]:
    
scalar_product([1,2,3], 2)
    
    Out[4]:
In [5]:
    
scalar_product([1,2,3], [2])
    
    Out[5]:
In [6]:
    
try:
    scalar_product([1,2,3], (4,5,6,7))
except ValueError as e:
    print(e)
    
    
In [7]:
    
class ArrayWrapper:
    def __init__(self, array):
        self.array = np.asarray(array)
        
    def __eq__(self, other):
        return np.array_equal(self.array, other.array)
    
    def __add__(self, other):
        return self.__class__(self.array + other.array)
    
    def __sub__(self, other):
        return self.__class__(self.array - other.array)
    
    def __str__(self):
        return str(self.array)
    
    def __repr__(self):
        return '%s(\n%s)' % (self.__class__.__name__, str(self.array))
    
    def __len__(self):
        return len(self.array)
    
    def __getitem__(self, index):
        return self.array[index]
    
    def __setitem__(self, index, elt):
        self.array[index] = elt
        
    @property
    def t(self):
        return self.__class__(self.array.transpose())
    
class Vector(ArrayWrapper):
    def __mul__(self, other):
        return scalar_product(self.array, other.array)
    
In [8]:
    
def random_array(*shape):
    return np.random.randint(1, 10, shape)
    
In [9]:
    
x = Vector(random_array(3))
x
    
    Out[9]:
In [10]:
    
x[0], x[1], x[2]
    
    Out[10]:
In [11]:
    
x[1] += 1
    
In [12]:
    
for elt in x:
    print(elt)
    
    
In [13]:
    
y = Vector(x.array)
y
    
    Out[13]:
In [14]:
    
x == y
    
    Out[14]:
In [15]:
    
x.t
    
    Out[15]:
In [16]:
    
x == x.t
    
    Out[16]:
In [17]:
    
y = Vector(random_array(3))
y
    
    Out[17]:
In [18]:
    
x == y
    
    Out[18]:
In [19]:
    
x+y
    
    Out[19]:
In [20]:
    
x-y
    
    Out[20]:
In [21]:
    
x*y
    
    Out[21]:
In [31]:
    
def mm_product(array1, array2):
    dtype = np.result_type(array1, array2)
    array = np.zeros((len(array1), len(array2)), dtype=dtype)
    for i, row1 in enumerate(array1):
        for j, row2 in enumerate(array2):
            array[i][j] = scalar_product(row1, row2)
    return array
    
In [32]:
    
class Matrix(ArrayWrapper):
    
    def __mul__(self, other):
        return self.__class__(mm_product(self.array, other.t.array))
    
    def __truediv__(self, other):
        return self.__class__(np.linalg.solve(self.array, other.array.flat))
    
In [33]:
    
A = Matrix(random_array(3, 3))
A
    
    Out[33]:
In [34]:
    
len(A)
    
    Out[34]:
In [35]:
    
for row in A:
    print(row)
    
    
In [36]:
    
B = Matrix(random_array(3, 3))
B
    
    Out[36]:
In [37]:
    
A+B
    
    Out[37]:
In [38]:
    
A-B
    
    Out[38]:
In [39]:
    
A*B
    
    Out[39]:
In [40]:
    
A.array.dot(B.array)
    
    Out[40]:
In [41]:
    
x = Vector(random_array(3))
x
    
    Out[41]:
In [42]:
    
A*x
    
    Out[42]:
In [45]:
    
def mv_product(A, x):
    dtype = np.result_type(A, x)
    array = np.zeros(len(A), dtype=dtype)
    for i, row in enumerate(A):
        array[i] = scalar_product(row, x)
    return Vector(array)
    
In [46]:
    
mv_product(A.array, x.array)
    
    Out[46]:
In [47]:
    
A.array.dot(x.array)
    
    Out[47]:
In [48]:
    
x = Matrix(random_array(3, 1))
x
    
    Out[48]:
In [49]:
    
x == x.t
    
    Out[49]:
In [50]:
    
x.t * x
    
    Out[50]:
In [51]:
    
x * x.t
    
    Out[51]:
In [52]:
    
x * x
    
    Out[52]:
In [53]:
    
A * x
    
    Out[53]:
In [54]:
    
A.array.dot(x.array)
    
    Out[54]:
In [55]:
    
scalar = Matrix([[2]])
scalar
    
    Out[55]:
In [56]:
    
scalar == scalar.t
    
    Out[56]:
In [57]:
    
scalar * scalar
    
    Out[57]:
In [58]:
    
x * scalar
    
    Out[58]:
In [59]:
    
A * scalar
    
    Out[59]:
In [60]:
    
b = A * x
b
    
    Out[60]:
In [61]:
    
b.array
    
    Out[61]:
In [62]:
    
np.linalg.solve(A.array, b.array)
    
    Out[62]:
In [63]:
    
print(A / b)
    
    
In [64]:
    
A.array.shape
    
    Out[64]:
In [65]:
    
b.array.shape
    
    Out[65]:
In [66]:
    
m = np.hstack([A.array, b.array]).astype(Fraction)
print(m)
    
    
In [67]:
    
m[1] -= m[0]
print(m)
    
    
In [68]:
    
m[:, :-1]
    
    Out[68]:
In [69]:
    
m[:, -1]
    
    Out[69]:
In [70]:
    
def solve_augmented(m):
    m = m.astype(float)
    return np.linalg.solve(m[:, :-1], m[:,-1])
    
In [71]:
    
print(solve_augmented(m))
    
    
In [72]:
    
row1 = 0
row2 = 1
col = 0
pivot = m[row1, col]
victim = m[row2, col]
m[row1], pivot, victim, m[row1] * Fraction(victim, pivot)
    
    Out[72]:
In [73]:
    
m[row2] -= m[row1] * Fraction(victim, pivot)
print(m)
    
    
In [74]:
    
def clobber(m, row1, row2, col):
    pivot = m[row1, col]
    victim = m[row2, col]
    m[row2] -= m[row1] * Fraction(victim, pivot)
    
In [75]:
    
clobber(m, 0, 2, 0)
print(m)
    
    
In [76]:
    
clobber(m, 1, 2, 1)
print(m)
    
    
In [77]:
    
m[2] /= m[2,2]
print(m)
    
    
In [ ]: