Tarea 1:

Alumno: Arturo Gonzalez Bencomo

Clase Array completa

La clase array completa se implemento en los ejercicios 1-6 con comentarios en el codigo indicando que metodo se implemento en que ejercicio, posteriormente se comprueba cada seccion correspondiente.

Los ejercicios 7-10 no implicaron modificacion de clase array sino que se agregaron funciones nuevas externas a la clase


In [80]:
class Array:
    "Una clase minima para algebra lineal"    
    def __init__(self, list_of_rows): 
        "Constructor y validador"
        # obtener dimensiones
        self.data = list_of_rows
        nrow = len(list_of_rows)
        
        #  ___caso vector: redimensionar correctamente
        if not isinstance(list_of_rows[0], list):
            nrow = 1
            self.data = [[x] for x in list_of_rows]
        
        # ahora las columnas deben estar bien aunque sea un vector
        ncol = len(self.data[0])
        self.shape = (nrow, ncol)
        
        # validar tamano correcto de filas
        if any([len(r) != ncol for r in self.data]):
            raise Exception("Las filas deben ser del mismo tamano")
     
        for a in range(nrow):
            for b in range(len(list_of_rows[a])):
                if not type(list_of_rows[a][b]) == int and not type(list_of_rows[a][b]) == float:
                    raise Exception("Hay datos no numericos")
    
    
    
    def __repr__(self): #Ejercicio1
        "Funcion que se manda llamar cuando solo pones el dato en terminal >> dato"
        return self.printdata(self.data)
    
    def printdata(self, data):   #Ejercicio1
        "Funcion que da formato a la impresion de matrices y vectores"
        rows = len(data)
        cadena = "["
        for a in range(rows):
            if a>0: 
                cadena+=str("\n ")                            
            cadena+=str(data[a])
            
        cadena+="]"
        return cadena
    
    def __str__(self): #Ejercicio1
        "Funcion que se manda llamar cuando solo pones llamas a imprimir el dato en terminal >>print(dato)"
        return self.printdata(self.data)    
    
    def __getitem__(self, idx):   #Ejercicio2
        "Metodo para poder obtener datos del objeto mediante un indice"
        return self.data[idx[0]][idx[1]]
    
    def __setitem__(self, idx, new_value):   #Ejercicio2
        "Metodo por el cual podemos asignar valores al objeto mediante indices"
        self.data[idx[0]][idx[1]] = new_value        
    
    def transpose(self):   #Ejercicio4
        "Metodo para obtener la matriz transpuesta del objeto Array"
        rows = self.shape[0]
        cols = self.shape[1]
        transpuesta = [[self.data[x][y] for x in range(rows)] for y in range(cols)]
        return Array(transpuesta)
        #return self.printdata(transpuesta)
    
    
    def __add__(self, other):   #Ejercicio5
        "Hora de sumar"
        if isinstance(other, Array):
            if self.shape != other.shape:
                raise Exception("Las dimensiones son distintas!")
            rows, cols = self.shape
            newArray = Array([[0. for c in range(cols)] for r in range(rows)])
            for r in range(rows):
                for c in range(cols):
                    newArray.data[r][c] = self.data[r][c] + other.data[r][c]
            return newArray
        elif isinstance(other, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
            rows, cols = self.shape
            newArray = Array([[0. for c in range(cols)] for r in range(rows)])
            for r in range(rows):
                for c in range(cols):
                    newArray.data[r][c] = self.data[r][c] + other
            return newArray
        else:
            raise Exception("Data must be either Array class or scalar") # es un tipo de error particular usado en estos metodos

        
    __radd__ = __add__     #Ejercicio5
    
    def __sub__(self, other):   #Ejercicio5
        "Hora de restar"
        "Hora de sumar"
        if isinstance(other, Array):
            if self.shape != other.shape:
                raise Exception("Las dimensiones son distintas!")
            rows, cols = self.shape
            newArray = Array([[0. for c in range(cols)] for r in range(rows)])
            for r in range(rows):
                for c in range(cols):
                    newArray.data[r][c] = self.data[r][c] - other.data[r][c]
            return newArray
        elif isinstance(other, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
            rows, cols = self.shape
            newArray = Array([[0. for c in range(cols)] for r in range(rows)])
            for r in range(rows):
                for c in range(cols):
                    newArray.data[r][c] = self.data[r][c] - other
            return newArray
        else:
            raise Exception("Data must be either Array class or scalar") # es un tipo de error particular usado en estos metodos

    __rsub__ = __sub__     #Ejercicio5


    def __mul__(self,other):   #Ejercicio6
        "Metodo para multiplicar una matriz ya sea con un escalar, u otra matriz(vectores tambien ya que son matrices de 1xn o nx1"
        #if len(other[0])
        if isinstance(other, Array):
            compatible = False #
            if self.shape[1] == other.shape[0]:
                compatible = True            
            else:
                print("Incompatible matrices")
                        
            if compatible:
                rows, cols = self.shape
                matmult = []
                for a in range(rows):                
                    vec = []
                    for b in range(other.shape[1]):                  
                        vec.append(self.multAdd(self.data[a],[other.data[i][b] for i in range(other.shape[0])])) 
                    matmult.append(vec)
                
                return self.printdata(matmult)
            
        elif isinstance(other, (int, float)):            
            rows, cols = self.shape
            newArray = Array([[0. for c in range(cols)] for r in range(rows)])
            newArray.data = [[self.data[y][x]*other for x in range(cols)] for y in range(rows)]
            return newArray
    
    def multAdd(self,parama, paramb):  #Ejercicio6 (auxiliar)
        "Metodo para calcular producto punto"
        sum = 0
        for x in range(len(parama)):
            sum+=(parama[x]*paramb[x])
        return sum
        
    
    __rmul__ = __mul__    #Ejercicio6

Ejercicio1

Escribe los metodos repr y str para la clase Array de forma que se imprima legiblemente como en numpy arrays.


In [81]:
dato = Array([[1,2,3],[4,5,6],[8,9,10]])

In [82]:
print(dato)


[[1, 2, 3]
 [4, 5, 6]
 [8, 9, 10]]

In [83]:
dato


Out[83]:
[[1, 2, 3]
 [4, 5, 6]
 [8, 9, 10]]

Ejercicio2

Escribe el metodo setitem para que el codigo A[i,j] = new_value cambie el valor de la entrada (i,j) del array.


In [84]:
dato = Array([[1,2,3],[4,5,6],[8,9,10]])
print("Original: ")
print(dato)
print("Acceder a elemento por indices dato[1,2]")
print(dato[1,2])
dato[0,0] = 10
print("Asignamos elemento por indices dato[0,0] = 10")
print(dato)


Original: 
[[1, 2, 3]
 [4, 5, 6]
 [8, 9, 10]]
Acceder a elemento por indices dato[1,2]
6
Asignamos elemento por indices dato[0,0] = 10
[[10, 2, 3]
 [4, 5, 6]
 [8, 9, 10]]

Ejercicio3

Implementa una funcion de zeros para crear arrays "vacios"


In [85]:
def zeros(filas, columnas):
    "Clase que crea un objeto de clase Array inicializado con valores en ceros sintaxis matriz_ceros(2,3)"
    return Array([[0 for x in range(columnas)] for y in range(filas)])

In [101]:
zeros(5,5)


Out[101]:
[[0, 0, 0, 0, 0]
 [0, 0, 0, 0, 0]
 [0, 0, 0, 0, 0]
 [0, 0, 0, 0, 0]
 [0, 0, 0, 0, 0]]

Implementa una funcion eye(n) que crea la matriz identidad de nxn


In [87]:
def evaluate(a,b):
    "Evalua si son iguales y regresa 1, si no 0"
    if a == b:
        return 1
    else:
        return 0

def eye(n):
    "Crea matriz diagonal de dim = n x n"
    return Array([[evaluate(x,y) for x in range(n)] for y in range(n)])

In [88]:
eye(5)


Out[88]:
[[1, 0, 0, 0, 0]
 [0, 1, 0, 0, 0]
 [0, 0, 1, 0, 0]
 [0, 0, 0, 1, 0]
 [0, 0, 0, 0, 1]]

Ejercicio4

Implementa la funcion de transposicion


In [89]:
dato = Array([[1,2,3,6],[4,5,6,11],[8,9,10,49],[9,12,67,1]])
print("Original: ")
print(dato)
print("Transpuesta")
print(dato.transpose())
dato1 = Array([[1,2,3,6],[4,5,6,11],[8,9,10,49],[9,12,67,1]]).transpose()
print("Transpuesta: ")
print(dato1)


Original: 
[[1, 2, 3, 6]
 [4, 5, 6, 11]
 [8, 9, 10, 49]
 [9, 12, 67, 1]]
Transpuesta
[[1, 4, 8, 9]
 [2, 5, 9, 12]
 [3, 6, 10, 67]
 [6, 11, 49, 1]]
Transpuesta: 
[[1, 4, 8, 9]
 [2, 5, 9, 12]
 [3, 6, 10, 67]
 [6, 11, 49, 1]]

Ejercicio5

Investiga como implementar el metodo de clase radd


In [90]:
dato = Array([[1,2,3],[4,5,6],[8,9,10]])
dato1 = Array([[1,2,71],[14,15,16],[28,29,20]])    
print("elemento1" )
print(dato)
print("elemento2" )
print(dato1)
print("suma de ambos")
print(dato+dato1)
print("suma de Array y escalar")
print(dato+1)
print("suma de escalar y Array")
print(1+dato)


elemento1
[[1, 2, 3]
 [4, 5, 6]
 [8, 9, 10]]
elemento2
[[1, 2, 71]
 [14, 15, 16]
 [28, 29, 20]]
suma de ambos
[[2, 4, 74]
 [18, 20, 22]
 [36, 38, 30]]
suma de Array y escalar
[[2, 3, 4]
 [5, 6, 7]
 [9, 10, 11]]
suma de escalar y Array
[[2, 3, 4]
 [5, 6, 7]
 [9, 10, 11]]

Implementa el metodo de clase sub similar a la suma para poder calcular expresiones como A - B para A y B arrays o numeros.


In [91]:
print("Resta de ambos")
print(dato-dato1)
print("Resta de Array y escalar")
print(dato-1)
print("Resta de escalar y Array")
print(1-dato)


Resta de ambos
[[0, 0, -68]
 [-10, -10, -10]
 [-20, -20, -10]]
Resta de Array y escalar
[[0, 1, 2]
 [3, 4, 5]
 [7, 8, 9]]
Resta de escalar y Array
[[0, 1, 2]
 [3, 4, 5]
 [7, 8, 9]]

Ejercicio6

Implementa las funciones mul y rmul para hacer multiplicacion matricial (y por un escalar).


In [92]:
dato = Array([[1,2,3],[4,5,6],[1,2,3],[4,5,6]])
dato1 = Array([[1,2,8,78],[14,15,12,21],[28,29,78,90]])    
print("primer elemento")
print(dato)
print("Segundo elemento")
print(dato1)
print ("multiplicacion")
print(dato*dato1)
print("multiplicacion de primer elemento por escalar 5")
print(dato * 5)
print("multiplicacion de escalar 5 por primer elemento")
print(5*dato)


primer elemento
[[1, 2, 3]
 [4, 5, 6]
 [1, 2, 3]
 [4, 5, 6]]
Segundo elemento
[[1, 2, 8, 78]
 [14, 15, 12, 21]
 [28, 29, 78, 90]]
multiplicacion
[[113, 119, 266, 390]
 [242, 257, 560, 957]
 [113, 119, 266, 390]
 [242, 257, 560, 957]]
multiplicacion de primer elemento por escalar 5
[[5, 10, 15]
 [20, 25, 30]
 [5, 10, 15]
 [20, 25, 30]]
multiplicacion de escalar 5 por primer elemento
[[5, 10, 15]
 [20, 25, 30]
 [5, 10, 15]
 [20, 25, 30]]

Ejercicio7

Implementa una funcion forward_subs que resuelva sistemas de ecuaciones de la forma $Lx = y$ con $L$ triangular inferior y $y$ cualquier Vector o Array de una columna.


In [93]:
def forward_subs(matrix,vector):
    "Resuelve matrices de la forma Lx = b"
    

    """ Codigo de validacion, tamanios, matriz L"""
    flag = False    
    y = None
    if type(matrix) == Array and type(vector) == Array:        
        if matrix.shape[0] == vector.shape[0]:
           if matrix.shape[0] == matrix.shape[1]: 
               for y in range(matrix.shape[0]):
                   for x in range(matrix.shape[1]):
                       if x>y:
                           if not matrix[y,x] == 0:
                               raise Exception("No es matriz L")
                           else:
                               flag = True
           else:
                print("No es matriz cuadrada")
        else:
            print("No concuerdan las dimensiones de la matriz y del vector")
    
    if flag:
        print("Es matriz L")
        y = []
        for a in range(vector.shape[0]):
            y.append(vector[a,0])
            for b in range(a):
                y[a]-=matrix[a,b]*y[b]                    
            y[a] /= matrix[a,a]
                
    return y

In [94]:
dato = Array([[1,0,0,0],[4,2,0,0],[1,2,1,0],[3,4,5,2]])    
print("Matriz: ")
print(dato)
print("vector")
vector = Array([[1],[2],[3],[6]])
print(vector)
print("solucion de resolucion de Lx = b")
print(forward_subs(dato,vector))


Matriz: 
[[1, 0, 0, 0]
 [4, 2, 0, 0]
 [1, 2, 1, 0]
 [3, 4, 5, 2]]
vector
[[1]
 [2]
 [3]
 [6]]
solucion de resolucion de Lx = b
Es matriz L
[1.0, -1.0, 4.0, -6.5]

Ejercicio8

Implementa una funcion backward_subs que resuelva sistemas $Ux = y$ con $U$ triangular superior y y Vector o Array de una columna.


In [95]:
def backward_subs(matrix, vector):
    "Resuelve matrices de la forma Lx = b"
    """ Codigo de validacion, tamanios, matriz L"""
    flag = False    
    y = None
    if type(matrix) == Array and type(vector) == Array:        
        if matrix.shape[0] == vector.shape[0]:
           if matrix.shape[0] == matrix.shape[1]: 
               for y in range(matrix.shape[0]):
                   for x in range(matrix.shape[1]):
                       if x<y:
                           if not matrix[y,x] == 0:
                               raise Exception("No es matriz U")
                           else:
                               flag = True
           else:
                print("No es matriz cuadrada")
        else:
            print("No concuerdan las dimensiones de la matriz y del vector")
    
    if flag:
        print("Es matriz U")
        y = [0] * vector.shape[0]
        for a in range(vector.shape[0]-1,-1,-1):
            y[a] = vector[a,0]
            for b in range(a+1,vector.shape[0]):
                y[a]-=matrix[a,b]*y[b]                    
            y[a] /= matrix[a,a]                
            
    return y

In [96]:
dato = Array([[1,1],[0,2]])
print("Matriz: ")
print(dato)
print("vector")
vector = Array([[12],[20]])
print(vector)
print("solucion de resolucion de Ux = b")
print(backward_subs(dato,vector))


Matriz: 
[[1, 1]
 [0, 2]]
vector
[[12]
 [20]]
solucion de resolucion de Ux = b
Es matriz U
[2.0, 10.0]

Ejercicio9

Implementa una funcion LU que reciba un Array $A$ y devuelva 3 arrays $L$,$U$ y $P$ tales que $PA = LU$ con $L$ trangular inferior, $U$ triangular superior y $P$ matriz de permutacion.


In [97]:
def LU(A):    
    "Funcion para realizar descomposicion de matriz en PA = LU"
    if type(A == Array):
        
        L = [[0 for x in range(A.shape[1])] for y in range(A.shape[0])]        
        for a in range(A.shape[0]):
            L[a][a] = 1
         
        L = Array(L)
        
        """Matriz de permutacion"""
        list_tuples = [(0,0) for x in range(A.shape[0])]
                    
        for a in range(A.shape[0]):            
            list_tuples[a] = (a,A.data[a])
        
        data = []
        data.append((0,A.data[0]))
        del list_tuples[0]            
        
        if len(list_tuples) == 1:
            starting = 0
        else:
            starting = 1
            
        for row in range(starting,len(list_tuples)):     
            temp = sorted(list_tuples, key=lambda value: value[1][row], reverse=True)
            data += temp
                                        
        U = []
        for row in data:
            U.append(row[1])
        
        U = Array(U)
            
        permutation_matrix = [[0 for x in range(A.shape[0])] for y in range(A.shape[0])]        
        permutation_matrix = Array(permutation_matrix)                
        
        for row in range(len(data)):    
            permutation_matrix[row, data[row][0]] = 1   
                       
        n = A.shape[0]
        for a in range(n):
            for b in range(a+1,n):    
                multiplicador = float((U[b,a]/U[a,a]))
                L[b,a] = multiplicador
                multiplicador= multiplicador*(-1)
                for c in range(n):						
                    U[b,c] += U[a,c]*multiplicador
        return [L,U,permutation_matrix]

In [98]:
A = Array([[1,1,-1],[1,-2,3],[2,3,1]])
#A = Array([[1,7,-1],[1,6,8],[2,3,7]])
print("Matriz original")
print(A)    
L,U,P = LU(A)
print("Matrices de Lower, Upper y Permutacion son: ")
print("L")
print(L)
print("U")
print(U)
print("P")
print(P)


Matriz original
[[1, 1, -1]
 [1, -2, 3]
 [2, 3, 1]]
Matrices de Lower, Upper y Permutacion son: 
L
[[1, 0, 0]
 [2.0, 1, 0]
 [1.0, -3.0, 1]]
U
[[1, 1, -1]
 [0.0, 1.0, 3.0]
 [0.0, 0.0, 13.0]]
P
[[1, 0, 0]
 [0, 0, 1]
 [0, 1, 0]]

Ejercicio10

Implementa una funcion lu_linsolve que resuelva cualquier sistema de ecuaciones Ax = y con A un Array y y un Vector o Array de una columna.


In [99]:
def lu_linsolve(A,y):
    "Funcion para resolver sistemas de ecuaciones basado en la descomposicion LU"
    solucion = None
    if type(A) == Array and type(y) == Array:
        if A.shape[0] == y.shape[0]:
            L,U,P = LU(A)
            new_vector = []
            for x in range(P.shape[0]):
                for z in range(P.shape[1]):
                    if(P[x,z] == 1):
                        new_vector.append([y[z,0]])
                        
            new_vector = Array(new_vector)                        
            x = Array([forward_subs(L,new_vector)])
            solucion = backward_subs(U, x.transpose())
                        
        else:
            print("No son compatibles Array y vector")
            print("Forma de matriz ")
            print(A.shape)
            print("Forma de vector")
            print(y.shape)
    else:
        print("No son estructuras de datos Array")
        
    return solucion

In [100]:
matriz = Array([[1,1,3],[2,4,4],[2,2,2]])
print("La matriz es: ")
print(matriz)
vector = Array([[30],[68],[36]])    
print("El vector es: ")
print(vector)
solucion = lu_linsolve(matriz,vector)
print("La solucion al sistema de ecuaciones es")
print(solucion)


La matriz es: 
[[1, 1, 3]
 [2, 4, 4]
 [2, 2, 2]]
El vector es: 
[[30]
 [68]
 [36]]
Es matriz L
Es matriz U
La solucion al sistema de ecuaciones es
[2.0, 10.0, 6.0]

In [ ]:


In [ ]: