LAB 03 Matrices dispersas
Contents
!wget --no-cache -O init.py -q https://raw.githubusercontent.com/rramosp/20192.L3/master/init.py
import init; init.init(force_download=False); init.get_weblink()
import init
from local.lib.rlxmoocapi import submit, session
import inspect
student = session.Session(init.endpoint).login( course_id=init.course_id,
session_id="UDEA",
lab_id="lab_03" )
vamos a implementar una matriz dispersa con una estructura de datos que nos permita realizar operaciones más eficientes. Representaremos una matriz como un diccionario de diccionarios. Es decir, usaremos un diccionario que, por cada fila, mantenga a su vez un diccionario indexado por columnas de valores de esa fila.
LAB 03 Matrices dispersas¶
Ejercicio 1.¶
Completa la función siguiente para que el diccionario d
opere como un diccionario de diccionarios. Es decir, que cada key1
del diccionario sea a su vez un diccionario de forma que al añadir un item
con una key2
concreta:
si
d
no tiene ningún elemento en esakey1
se cree un nuevo elemento con una diccionario vacíose añada el
item
al diccionario referenciado cond[key1][key2]
observa que, dado un diccionario d
:
d.keys()
devuelve la lista de claveskey in d.keys()
devuelveTrue
si el diccionario ya contiente la clavekey
Ejemplo de ejecución:
> d = {}
> add_to_dict(d, 10, 3, "20")
> add_to_dict(d, 1, 5, "4")
> add_to_dict(d, 1, 2, "14")
> print d
> print d[1][5]
{1: {2: '14', 5: '4'}, 10: {3: '20'}}
4
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
return d
comprueba manualmente tu código
d = {}
add_to_dict(d, 10, 3, "20")
add_to_dict(d, 1, 5, "4")
add_to_dict(d, 1, 2, "14")
print(d)
observa que si los índices son números de files y columnas, accedemos a un elemento con una notación matricial muy limpia
print (d[1][5])
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_01");
Ejercicio 2¶
implementa el constructor de la siguiente clase y el método sparseness_metric
Observa que el constructor deberá:
establecer el valor de
self.shape
al tamaño de la matrixm
. Sim
esNone
el tamaño ha de ser(0,0)
rellenar el diccionario
self.rows
como un diccionario de diccionarios con los valores dem
que sean distintos de 0. Tendrás que recorrer toda la matrizm
para esto.
Ejemplo de ejecución:
> m = array([[1, 0, 9],
> [0, 0, 7],
> [3, 5, 0],
> [5, 0, 0],
> [0, 3, 0]])
>
> s = SparseMatrix(m)
> print "rows", s.rows
> print "length of each row", [len(s.rows[i]) for i in s.rows.keys()]
> print "sparseness metric", s.sparseness_metric()
rows {0: {0: 1, 2: 9}, 1: {2: 7}, 2: {0: 3, 1: 5}, 3: {0: 5}, 4: {1: 3}}
length of each row [2, 1, 2, 1, 1]
sparseness metric 0.3333333
def SparseMatrix2(m=None, **kwargs):
import itertools
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
return d
class SparseMatrix2_class:
def __init__(self, m=m, **kwargs):
self.rows = {}
self.shape = ...
<... TU CODIGO AQUI ...>
def sparseness_metric(self):
metric = <... TU CODIGO AQUI ...>
return metric
return SparseMatrix2_class(**kwargs)
comprueba manualmente tu código
import numpy as np
def random_sparse_matrix(size):
m = np.random.randint(2, size=size)
m = m * np.random.randint(10,size=size)
return m
m = random_sparse_matrix((5,3))
sm = SparseMatrix2(m)
print(m.shape, sm.sparseness_metric())
m
s = SparseMatrix2(m)
print("rows", s.rows)
print("length of each row", [len(s.rows[i]) for i in list(s.rows.keys())])
s = SparseMatrix2(None)
s.shape
registra tu solución en linea¶
student.submit_task(globals(), task_id="task_02");
Ejercicio 3¶
implementa el método to_dense
y el método T
(por transpuesta).
to_dense
: debe de devolver una matriznumpy
equivalente a la propia matriz dispersaT
ha de devolver una matriz dispersa nueva (una instancia nueva de la misma clase) con los índices deself.rows
invertidos.
def SparseMatrix3(m=None, **kwargs):
import itertools
import numpy as np
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
return d
class SparseMatrix3_class:
def __init__(self, m=m, **kwargs):
<... TU CODIGO AQUI ...>
def to_dense(self):
r = np.zeros(self.shape)
<... TU CODIGO AQUI ...>
return r
def T(self):
r = self.__class__(m=None)
<... TU CODIGO AQUI ...>
return r
return SparseMatrix3_class(**kwargs)
comprueba manualmente tu código
import numpy as np
def random_sparse_matrix(size):
m = np.random.randint(2, size=size)
m = m * np.random.randint(10,size=size)
return m
m = random_sparse_matrix((5,3))
s = SparseMatrix3(m)
print(m)
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_03");
Ejercicio 4¶
Implementa el método __getitem__
para que devuelva un elemento de la matriz dispersa con la misma notación que una matriz numpy
. Ten en cuanta que:
si
i
oj
están fuera del tamaño de la matriz has de emitir unAssertionError
si el elemento
(i,j)
existe en el diccionario interno (self.rows
) se ha de devolver el valor almacenadosi no existe, se ha de devolver el valor 0
def SparseMatrix4(m=None, **kwargs):
import itertools
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
return d
class SparseMatrix4_class:
def __init__(self, m=m, **kwargs):
<... TU CODIGO AQUI ...>
def __getitem__(self, pos):
i,j = pos
assert <... TU CODIGO AQUI ...>
return <... TU CODIGO AQUI ...>
return SparseMatrix4_class (**kwargs)
comprueba tu código manualmente
def random_sparse_matrix(size):
m = np.random.randint(2, size=size)
m = m * np.random.randint(10,size=size)
return m
m = random_sparse_matrix((5,3))
print("original matrix shape", m.shape)
print(m)
import itertools
s = SparseMatrix4(m)
print("inner dict", s.rows)
print("\ncoord value_in_m value_in_s")
for i,j in itertools.product(list(range(m.shape[0])), list(range(m.shape[1]))):
print("(%d,%d) %5d %5d"%(i,j, m[i,j], s[i,j]))
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_04");
Ejercicio 5¶
Implementa el método __setitem__
de forma que:
si el valor del elemento es 0, no se haga nada
si las coordenadas
i,j
exceden el tamaño actual de la matriz, se inserte el elemento, pero que se actualice el tamaño (self.shape
) de manera acorde.
def SparseMatrix5(m=None,**kwargs):
import itertools
import numpy as np
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
class SparseMatrix5_class:
def __init__(self, m=m, **kwargs):
<... TU CODIGO AQUI ...>
def __setitem__(self, pos, val):
<... TU CODIGO AQUI ...>
def __getitem__(self, pos):
<... TU CODIGO AQUI ...>
def to_dense(self):
<... TU CODIGO AQUI ...>
return SparseMatrix5_class(**kwargs)
comprueba manualmente tu código
def random_sparse_matrix(size):
m = np.random.randint(2, size=size)
m = m * np.random.randint(10,size=size)
return m
m = random_sparse_matrix((5,3))
s = SparseMatrix5(m)
print("initial")
print(s.rows)
print(s.to_dense().astype(int))
s[4,2] = m[4,2]*2+3
print("\nafter __setitem__")
print(s.rows)
print(s.to_dense().astype(int))
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_05");
Ejercicio 6¶
Implementa el método __add__
Sugerencia de implementación:
verifique con un
assert
que ambas matrices son del mismo tamañocrea una nueva matriz vacía
copie
self.shape
en elshape
de la matriz nuevarecorra todos los elementos de
self.rows
y llame al__setitem__
de la matriz nueva por cada uno de ellosrecorra todos los elementos de
q
y haga un get/set sobre la nueva matriz para añadirlos
def SparseMatrix6(m=None,**kwargs):
import itertools
import numpy as np
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
class SparseMatrix6_class:
def __init__(self, m=m, **kwargs):
<... TU CODIGO AQUI ...>
def to_dense(self):
<... TU CODIGO AQUI ...>
def __getitem__(self, pos):
<... TU CODIGO AQUI ...>
def __setitem__(self, pos, val):
<... TU CODIGO AQUI ...>
def __add__ (self, q):
assert <... TU CODIGO AQUI ...>
r = self.__class__(m=None)
<... TU CODIGO AQUI ...>
return r
return SparseMatrix6_class(**kwargs)
import numpy as np
m1, m2 = random_sparse_matrix((5,3)), random_sparse_matrix((5,3))
sm1 = SparseMatrix6(m1)
sm2 = SparseMatrix6(m2)
print(sm1.to_dense().astype(int))
print("--")
print(sm2.to_dense().astype(int))
print((sm1+sm2).to_dense().astype(int))
print(m1+m2)
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_06");
Ejercicio 7¶
Implementa dot
en nuestra clase de matrices dispersas representadas con un diccionario de diccionarios.
def SparseMatrix7(m=None,**kwargs):
import itertools
import numpy as np
def add_to_dict(d, key1, key2, val):
<... TU CODIGO AQUI ...>
class SparseMatrix7_class:
def __init__(self, m=m. **kwargs):
<... TU CODIGO AQUI ...>
def to_dense(self):
<... TU CODIGO AQUI ...>
def __getitem__(self, pos):
<... TU CODIGO AQUI ...>
def __setitem__(self, pos, val):
<... TU CODIGO AQUI ...>
def __add__ (self, q):
<... TU CODIGO AQUI ...>
def dot(self, q):
r = self.__class__(m=None)
<... TU CODIGO AQUI ...>
return r
def sparseness_metric(self):
<... TU CODIGO AQUI ...>
def T(self):
<... TU CODIGO AQUI ...>
return SparseMatrix7_class(**kwargs)
comprueba tu código manualmente
import numpy as np
m1 = np.random.randint(10, size=(3,2))
print (m1)
m2 = np.random.randint(10, size=(2,3))
print (m2)
print ("-- ")
print (m1.dot(m2))
s1 = SparseMatrix7(m1)
s2 = SparseMatrix7(m2)
print (s1.dot(s2).to_dense().astype(int))
print ("--")
print (m2.dot(m1))
print (s2.dot(s1).to_dense().astype(int))
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_07");
Ejercicio 8¶
Realiza un algoritmo que devuelva el valor máximo de cada fila. Deberá de devolver una lista de tuplas (row_nb, max_value)
solamente de las filas que contengan algún elemento. P.ej. de la siguiente matriz:
[[4 0 0 0]
[0 2 0 5]
[0 2 0 0]
[0 0 0 0]
[0 6 0 0]
[0 0 0 0]
[0 0 0 6]
[0 0 7 7]
[0 0 2 7]
[4 8 0 2]]
debe de devolver:
[(0, 4), (1, 5), (2, 2), (4, 6), (6, 6), (7, 7), (8, 7), (9, 8)]
Este ejercicio debe de funcionar con SparseMatrix
de tu solución del ejercicio 3 anterior
def max_per_row(m):
import numpy as np
return <... TU CODIGO AQUI ...>
m = random_sparse_matrix((10,4))
s = SparseMatrix3(m)
print(m)
print(max_per_row(s))
mm = m.max(axis=1)
w = np.argwhere(mm!=0)[:,0]
np.allclose(np.r_[[(i,j) for i,j in zip(w, mm[w])]], np.r_[max_per_row(s)])
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_08");
Ejercicio 9¶
Realiza un algoritmo que devuelva el valor máximo de cada columna. Deberá de devolver una lista de tuplas (col_nb, max_value)
solamente de las columnas que contengan algún elemento. P.ej. de la siguiente matriz:
[[0 0 2 0 0 1 0 0 0 1]
[5 3 0 4 0 0 0 5 0 2]
[9 0 0 4 0 0 0 0 0 0]]
deberá devolver el siguiente resultado:
[(0, 9), (1, 3), (2, 2), (3, 4), (5, 1), (7, 5), (9, 2)]
Este ejercicio debe de funcionar con SparseMatrix
de tu solución del ejercicio 3 anterior
def max_per_col(m):
import numpy as np
return <... TU CODIGO AQUI ...>
m = SparseMatrix3(random_sparse_matrix((3,10)))
print (m.to_dense().astype(int))
print (max_per_col(m))
registra tu solución en línea¶
student.submit_task(globals(), task_id="task_09");
Análisis de complejidad computacional¶
def xrandom_sparse_matrix(max_rows, max_cols, n_items):
import numpy as np
m = SparseMatrix7()
for _ in range(n_items):
i = np.random.randint(max_rows)
j = np.random.randint(max_cols)
v = np.random.randint(100)
m[i,j] = v
return m
k=xrandom_sparse_matrix(1000,1000,1000)
k.shape, k.sparseness_metric()
import matplotlib.pyplot as plt
%matplotlib inline
def plot_times(n_set, r, xlabel, ylabel):
from scipy.optimize import minimize
n_set = np.array(n_set)
# fit to scaled for numerical stability
x_ticks = n_set/1e3
r = np.array(r)
cfunc = lambda k, x: k[0]+k[1]*x+k[2]*x**2+k[3]*x**3
qfunc = lambda k, x: k[0]+k[1]*x+k[2]*x**2
lfunc = lambda k, x: k[0]+k[1]*x
ccost = lambda k: np.mean( (r-cfunc(k, x_ticks))**2)
qcost = lambda k: np.mean( (r-qfunc(k, x_ticks))**2)
lcost = lambda k: np.mean( (r-lfunc(k, x_ticks))**2)
cx = minimize(ccost, [0,0,0,0], method="BFGS").x
qx = minimize(qcost, [0,0,0], method="BFGS").x
lx = minimize(lcost, [0,0], method="BFGS").x
plt.plot(x_ticks*1e3, cfunc(cx, x_ticks), label="cubic fit", color="black")
plt.plot(x_ticks*1e3, qfunc(qx, x_ticks), label="quadratic fit", color="black", ls="--")
plt.plot(x_ticks*1e3, lfunc(lx, x_ticks), label="linear fit", color="black", ls=":")
plt.plot(x_ticks*1e3, r, label="data", lw=10, alpha=.5, color="red")
plt.legend()
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.grid()
__setitem__
¶
import numpy as np
r2 = []
n2 = range(50,3000,100)
for n in n2:
print (n)
k = %timeit -o -q -r 3 -n 3 xrandom_sparse_matrix(200,200,n)
r2.append(k.best)
plot_times(n2, r2, xlabel="number of triplets", ylabel="time for 200x200 matrix")
__getitem__
¶
r1 = []
n1 = range(50,3000,100)
def getn(m,n):
for i in m.rows.keys():
for j in m.rows[i].keys():
m[i,j]
for n in n1:
print (n)
m = xrandom_sparse_matrix(200,200,n)
k = %timeit -o -q -r 3 -n 3 getn(m,n)
r1.append(k.best)
plot_times(n1, r1, xlabel="number of triplets", ylabel="time for 200x200 matrix")
__add__
¶
r3 = []
n3 = range(50,3000,100)
for n in n3:
print (n)
m1 = xrandom_sparse_matrix(200,200,n)
m2 = xrandom_sparse_matrix(200,200,n)
m1[200,200]=1
m2[200,200]=1
k = %timeit -o -q -r 3 -n 3 m1+m2
r3.append(k.best)
plot_times(n3, r3,xlabel="number of triplets", ylabel="time for 200x200 matrix")
dot
¶
r4 = []
n4 = range(50,3000,200)
for n in n4:
print (n)
m = xrandom_sparse_matrix(200,200,n)
k = %timeit -o -q -r 3 -n 3 m.dot(m.T())
r4.append(k.best)
plot_times(n4, r4,xlabel="number of triplets", ylabel="time for 200x200 matrix")