EJERCICIO 1

  • Escribe los metodos __repr__ y __str__ para la clase Array de forma que se imprima legiblemente como en numpy arrays.

In [1]:
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")
    def __repr__(self):
        self.repr = ""
        for i in range((self.shape[0]-1)):
            self.repr += str(self.data[i]) + ',' + '\n'
        self.repr = self.repr + str(self.data[self.shape[0]-1])
        return "([" + self.repr + "])"
    def __str__(self):
        self.stru = ""
        self.str = ""
        for j in range(self.shape[0]-1):
            self.str = ""
            for i in range(self.shape[1]):
                self.str += str(self.data[j][i]) + ' '
            self.stru += self.str + '\n'
            self.str = ""
        for i in range(self.shape[1]):
            self.str += str(self.data[self.shape[0]-1][i]) + ' '
        self.stru = self.stru + self.str

        return "[ " + str(self.stru) +"]"

In [2]:
A = Array([[1,2,3,4],[4,5,6,7]])

In [3]:
A


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

In [4]:
print(A)


[ 1 2 3 4 
4 5 6 7 ]

EJERCICIO 2

  • Escribe el metodo __setitem__ para que el codigo A[i,j] = new_value cambie el valor de la entrada (i,j) del array. El esqueleto de la funcion es
    class Array:
      #
      #
      def __setitem__(self, idx, new_value):
          "Ejercicio"
      #
    

In [5]:
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")
    def __getitem__(self, idx):
        return self.data[idx[0]][idx[1]]
    def __setitem__(self,idx,new_value):
        self.data[idx[0]][idx[1]] = new_value

In [6]:
A = Array([[1,2,3],[4,5,6]])

In [7]:
A[0,1]


Out[7]:
2

In [8]:
A[0,1] = 3

In [9]:
A[0,1]


Out[9]:
3

EJERCICIO 3

  1. Implementa una funcion de zeros para crear arrays "vacios"
    def zeros(shape):
     "Implementame por favor"
    
    Hint, encontraras utiles las listas de comprehension, por ejemplo el codigo [0. for x in range(5)] crea una lista de 5 ceros.
  2. Implementa una funcion eye(n) que crea la matriz identidad de $n\times n$ (es decir, la matriz que tiene puros ceros y unos en la diagonal). El nombre eye es tradicional en software de algebra lineal, aunque no es muy intuitivo.

In [10]:
class Array:
    def __init__(self, list_of_rows):
        "Constructor y validador"
        # obtener dimensiones
        self.data = list_of_rows
        self.shape = (len(list_of_rows), len(list_of_rows[0]))
        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")
    def zeros(self,r,c):
        self.columnas = c
        self.filas = r
        self.zeros = [[0 for col in range(c)] for row in range(r)]
        return self.zeros
    def eye(self,n):
        self.zeros = [[0 for col in range(n)] for row in range(n)]
        for i in range(n):
            self.zeros[i][i] = 1
        return self.zeros

In [11]:
A = Array([[1,2,3],[4,5,6]])

In [12]:
A.zeros(2,3)


Out[12]:
[[0, 0, 0], [0, 0, 0]]

In [13]:
A.eye(2)


Out[13]:
[[1, 0], [0, 1]]

In [14]:
class Zeros:
    def __init__(self,r,c):
        self.columnas = c
        self.filas = r
        self.data = [[0 for col in range(c)] for row in range(r)]
    def __repr__(self):
        self.repr = ""
        for i in range((self.filas-1)):
            self.repr += str(self.data[i]) + ',' + '\n'
        self.repr = self.repr + str(self.data[self.filas-1])
        return "([" + self.repr + "])"
    def __str__(self):
        self.stru = ""
        self.str = ""
        for j in range(self.filas-1):
            self.str = ""
            for i in range(self.columnas):
                self.str += str(self.data[j][i]) + ' '
            self.stru += self.str + '\n'
            self.str = ""
        for i in range(self.columnas):
            self.str += str(self.data[self.filas-1][i]) + ' '
        self.stru = self.stru + self.str
        return "[ " + str(self.stru) +"]"

In [15]:
x = Zeros(5,6)

In [16]:
x


Out[16]:
([[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, 0, 0, 0, 0, 0]])

In [17]:
print(x)


[ 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 0 0 0 0 0 ]

In [18]:
class eye:
    def __init__(self,n):
        self.dim = n
        self.data = [[0 for col in range(n)] for row in range(n)]
        for i in range(n):
            self.data[i][i] = 1
    def __repr__(self):
        self.repr = ""
        for i in range((self.dim-1)):
            self.repr += str(self.data[i]) + ',' + '\n'
        self.repr = self.repr + str(self.data[self.dim-1])
        return "([" + self.repr + "])"
    def __str__(self):
        self.stru = ""
        self.str = ""
        for j in range(self.dim-1):
            self.str = ""
            for i in range(self.dim):
                self.str += str(self.data[j][i]) + ' '
            self.stru += self.str + '\n'
            self.str = ""
        for i in range(self.dim):
            self.str += str(self.data[self.dim-1][i]) + ' '
        self.stru = self.stru + self.str
        return "[ " + str(self.stru) +"]"

In [19]:
y = eye(2)

In [20]:
y


Out[20]:
([[1, 0],
[0, 1]])

In [21]:
print(y)


[ 1 0 
0 1 ]

EJERCICIO 4

  • Implementa la funcion de transposicion
    class Array:
      ###
      ###
      ###
      def transpose(self):
          "Implementame :)"
          ###
    

In [22]:
class Array:
    "Una clase minima para algebra lineal"    
    def __init__(self, list_of_rows):
        "Constructor y validador"
        # obtener dimensiones
        self.data = list_of_rows
        self.shape = (len(list_of_rows), len(list_of_rows[0]))
        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")

    def transpose(self):
        self.fila = self.shape[0]
        self.columna = self.shape[1]
        self.row = [[0 for col in range(self.fila)] for row in range(self.columna)]
        for j in range(self.columna):
            for i in range(self.fila):
                self.row[j][i] = self.data[i][j]
        return self.row

In [23]:
A = Array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])

In [24]:
A.transpose()


Out[24]:
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

EJERCICIO 5

  1. (dificil) En nuestra clase Array la expresion A + 1 tiene sentido para un Array A, sin embargo la expresion inversa falla, por ejemplo
    1 + Array([[1,2], [3,4]])
    
    entrega un error. Investiga como implementar el metodo de clase __radd__ para resolver este problema.
  2. Nuestro metodo de suma no sabe restar, 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 [25]:
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")
        
    def __add__(self, other):
        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(2, (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:
            return NotImplemented # es un tipo de error particular usado en estos metodos
    "Parte 1"
    __radd__ = __add__
    
    "Parte 2"
    def __sub__(self, other):
        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(2, (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:
            return NotImplemented # es un tipo de error particular usado en estos metodos

    def __rsub__(self, other):
        if isinstance(2, (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] = other - self.data[r][c]
            return newArray
        else:
            return NotImplemented # es un tipo de error particular usado en estos metodos

In [26]:
A = Array([[1,2,3],[4,5,6]])

In [27]:
B = Array([[4,5,6],[7,8,9]])

In [28]:
C = A + B

In [29]:
C.data


Out[29]:
[[5, 7, 9], [11, 13, 15]]

In [30]:
D = A + 2

In [31]:
D.data


Out[31]:
[[3, 4, 5], [6, 7, 8]]

In [32]:
E = 3 + B

In [33]:
E.data


Out[33]:
[[7, 8, 9], [10, 11, 12]]

In [34]:
F = A - B

In [35]:
F.data


Out[35]:
[[-3, -3, -3], [-3, -3, -3]]

In [36]:
G = B - 3

In [37]:
G.data


Out[37]:
[[1, 2, 3], [4, 5, 6]]

In [38]:
H = 10 - A

In [39]:
H.data


Out[39]:
[[9, 8, 7], [6, 5, 4]]

EJERCICIO 6

Implementa las funciones __mul__ y __rmul__ para hacer multiplicacion matricial (y por un escalar). Hint. Entiende y modifica el codigo de suma del punto anterior


In [40]:
class Array:
    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")
        
    def __mul__(self, other):
        if isinstance(other, Array):
            if self.shape[1] != other.shape[0]:
                raise Exception("Las dimensiones no coinciden!")
            rowsarr = self.shape[0]
            coincidir = self.shape[1]
            colsoth = other.shape[1]
            val = 0
            newArray = Array([[0. for c in range(colsoth)] for r in range(rowsarr)])
            for r in range(rowsarr):
                for c in range(colsoth):
                    val = 0
                    for i in range(coincidir):
                        val += self.data[r][i] * other.data[i][c]
                    newArray.data[r][c] = val
            return newArray
        elif isinstance(2, (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:
            return NotImplemented # es un tipo de error particular usado en estos metodos
    def __rmul__(self, other):
        if isinstance(other, Array):
            if self.shape[1] != other.shape[0]:
                raise Exception("Las dimensiones no coinciden!")
            rowsarr = self.shape[0]
            coincidir = self.shape[1]
            colsoth = other.shape[1]
            val = 0
            newArray = Array([[0. for c in range(colsoth)] for r in range(rowsarr)])
            for r in range(rowsarr):
                for c in range(colsoth):
                    val = 0
                    for i in range(coincidir):
                        val += other.data[i][c] * self.data[r][i]
                    newArray.data[r][c] = val
            return newArray
        elif isinstance(2, (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:
            return NotImplemented # es un tipo de error particular usado en estos metodos

In [41]:
A = Array([[1,2],[4,5],[7,8]])

In [42]:
B = Array([[4,5,6],[7,8,9]])

In [43]:
C = B * A

In [44]:
C.data


Out[44]:
[[66, 81], [102, 126]]

In [45]:
D = 2 * A

In [46]:
D.data


Out[46]:
[[2, 4], [8, 10], [14, 16]]

In [47]:
E = B * (-1)

In [48]:
E.data


Out[48]:
[[-4, -5, -6], [-7, -8, -9]]

In [ ]: