提交 8ead8d0f authored 作者: Nicolas Bouchard's avatar Nicolas Bouchard

Corrections and tests for Diag and SquareDiagonal.

上级 156ef0a0
...@@ -9,6 +9,7 @@ U{http://www-users.cs.umn.edu/~saad/software/SPARSKIT/paper.ps}. ...@@ -9,6 +9,7 @@ U{http://www-users.cs.umn.edu/~saad/software/SPARSKIT/paper.ps}.
#### COPIED FROM hpu/icml09/sp.py #### COPIED FROM hpu/icml09/sp.py
import numpy import numpy
import scipy
from scipy import sparse as scipy_sparse from scipy import sparse as scipy_sparse
import theano import theano
...@@ -30,70 +31,63 @@ def register_specialize(lopt, *tags, **kwargs): ...@@ -30,70 +31,63 @@ def register_specialize(lopt, *tags, **kwargs):
class Diag(Op): class Diag(Op):
"""Extract the diagonal of a square sparse matrix as a dense
vector.
:param x: A square sparse matrix in csc format.
:return: A dense vector representing the diagonal elements.
:note:
- The grad implemented is regular, i.e. not structured, since
the output is a dense vector.
""" """
Extract the diagonal of a square sparse matrix as a dense vector.
"""
def __eq__(self, other): def __eq__(self, other):
return (type(self) == type(other)) return (type(self) == type(other))
def __hash__(self): def __hash__(self):
return hash(type(self)) return hash(type(self))
def __str__(self):
return "Diag"
def make_node(self, x): def make_node(self, x):
return gof.Apply(self, [x], [tensor.tensor(broadcastable=(False,), return gof.Apply(self, [x], [tensor.tensor(broadcastable=(False,),
dtype=x.dtype)]) dtype=x.dtype)])
def perform(self, node, (x,), (z,)): def perform(self, node, (x,), (z,)):
M, N = x.shape N, M = x.shape
if M != N: if N != M:
raise ValueError("DenseDiag argument not square. Shape:", x.shape) raise ValueError('Diag only apply on square matrix')
z[0] = x.diagonal()
assert x.format == 'csc'
data = x.data
indices = x.indices
indptr = x.indptr
diag = numpy.zeros(N, x.dtype)
#TODO: try using ndarrays and then prune() on the result
# it could be optimized in the case the sparse structure
# does not allow index duplication
for j in xrange(0, N):
for i_idx in xrange(indptr[j], indptr[j + 1]):
if indices[i_idx] == j:
diag[j] += data[i_idx]
z[0] = diag
def grad(self, (diag,), (gz,)): def grad(self, (x,), (gz,)):
return [square_diagonal(gz)] return [square_diagonal(gz)]
def infer_shape(self, nodes, shapes): def infer_shape(self, nodes, shapes):
matrix_shape = shapes[0] return [(tensor.minimum(*shapes[0]), )]
diag_length = matrix_shape[0]
return [(diag_length,)]
def __str__(self):
return self.__class__.__name__
diag = Diag() diag = Diag()
class SquareDiagonal(Op): class SquareDiagonal(Op):
""" """Return a square sparse (csc) matrix whose diagonal
Return a square sparse (csc) matrix whose diagonal
is given by the dense vector argument. is given by the dense vector argument.
:param x: Dense vector for the diagonal.
:return: A sparse matrix having `x` as diagonal.
:note:
- The grad implemented is regular, i.e. not structured.
""" """
def __eq__(self, other): def __eq__(self, other):
return (type(self) == type(other)) return type(self) == type(other)
def __hash__(self): def __hash__(self):
return hash(type(self)) return hash(type(self))
def __str__(self):
return "SquareDiagonal"
def make_node(self, diag): def make_node(self, diag):
diag = tensor.as_tensor_variable(diag) diag = tensor.as_tensor_variable(diag)
if diag.type.ndim != 1: if diag.type.ndim != 1:
...@@ -102,20 +96,25 @@ class SquareDiagonal(Op): ...@@ -102,20 +96,25 @@ class SquareDiagonal(Op):
return gof.Apply(self, [diag], return gof.Apply(self, [diag],
[sparse.SparseType(dtype=diag.dtype, format='csc')()]) [sparse.SparseType(dtype=diag.dtype, format='csc')()])
def perform(self, node, (diag,), (z,)): def perform(self, node, inputs, (z,)):
N, = diag.shape diag, o_shape = inputs[0], inputs[0].shape * 2
N = len(diag)
data = diag[:N]
indices = range(N)
indptr = range(N + 1) indptr = range(N + 1)
indices = indptr[0:N] tup = (data, indices, indptr)
z[0] = scipy_sparse.csc_matrix((diag, indices, indptr),
(N, N), copy=True) z[0] = scipy.sparse.csc_matrix(tup, copy=True)
def grad(self, input, (gz,)): def grad(self, inputs, (gz,)):
return [diag(gz)] return [diag(gz)]
def infer_shape(self, nodes, shapes): def infer_shape(self, nodes, shapes):
diag_length = shapes[0][0] return [(shapes[0][0], shapes[0][0])]
return [(diag_length, diag_length)]
def __str__(self):
return self.__class__.__name__
square_diagonal = SquareDiagonal() square_diagonal = SquareDiagonal()
......
...@@ -18,6 +18,7 @@ from theano.sparse.sandbox import sp ...@@ -18,6 +18,7 @@ from theano.sparse.sandbox import sp
from theano.sparse.tests.test_basic import random_lil from theano.sparse.tests.test_basic import random_lil
from theano.tests import unittest_tools as utt from theano.tests import unittest_tools as utt
from theano.sparse import verify_grad_sparse from theano.sparse import verify_grad_sparse
from theano.sparse.tests.test_basic import sparse_random_inputs
class TestSP(unittest.TestCase): class TestSP(unittest.TestCase):
...@@ -363,30 +364,87 @@ class TestSP(unittest.TestCase): ...@@ -363,30 +364,87 @@ class TestSP(unittest.TestCase):
utt.verify_grad(d, [kvals]) utt.verify_grad(d, [kvals])
def test_diag(): class DiagTester(utt.InferShapeTester):
m = theano.sparse.csc_matrix() def setUp(self):
d = sp.diag(m) super(DiagTester, self).setUp()
f = theano.function([m], d) self.op_class = sp.Diag
f2 = theano.function([m], d.shape) self.op = sp.diag
for K in 1, 5:
np_matrix = numpy.asarray(numpy.reshape(range(K**2),(K,K)), def test_op(self):
dtype=theano.config.floatX) for format in theano.sparse.sparse_formats:
diag = numpy.diagonal(np_matrix) variable, data = sparse_random_inputs(format,
sp_matrix = scipy.sparse.csc_matrix(np_matrix) shape=(10, 10))
assert numpy.all(diag == f(sp_matrix)) z = self.op(*variable)
assert f2(sp_matrix) == diag.shape assert z.type.broadcastable == (False, )
def test_square_diagonal(): f = theano.function(variable, z)
for K in 1, 5: tested = f(*data)
d = tensor.ivector() expected = data[0].toarray().diagonal()
sd = sp.square_diagonal(d)
f = theano.function([d], sd) assert numpy.allclose(tested, expected)
n = numpy.zeros((K,K), dtype='int32')
for i in range(K): def test_infer_shape(self):
n[i,i] = i for format in theano.sparse.sparse_formats:
variable, data = sparse_random_inputs(format,
assert numpy.all(n == f(range(K)).toarray()) shape=(10, 10))
self._compile_and_check(variable,
[self.op(*variable)],
data,
self.op_class)
def test_grad(self):
for format in theano.sparse.sparse_formats:
variable, data = sparse_random_inputs(format,
shape=(10, 10))
verify_grad_sparse(
self.op,
data,
structured=False)
class SquareDiagonalTester(utt.InferShapeTester):
def setUp(self):
super(SquareDiagonalTester, self).setUp()
self.op_class = sp.SquareDiagonal
self.op = sp.square_diagonal
def test_op(self):
for format in theano.sparse.sparse_formats:
for size in range(5, 9):
variable = [tensor.vector()]
data = [numpy.random.random(size)]
f = theano.function(variable, self.op(*variable))
tested = f(*data).toarray()
expected = numpy.diag(*data)
assert numpy.allclose(tested, expected)
assert tested.dtype == expected.dtype
assert tested.shape == expected.shape
def test_infer_shape(self):
for format in theano.sparse.sparse_formats:
for size in range(5, 9):
variable = [tensor.vector()]
data = [numpy.random.random(size)]
self._compile_and_check(variable,
[self.op(*variable)],
data,
self.op_class)
def test_grad(self):
for format in theano.sparse.sparse_formats:
for size in range(5, 9):
variable = [tensor.vector()]
data = [numpy.random.random(size)]
verify_grad_sparse(
self.op,
data,
structured=False)
def test_ensure_sorted_indices(): def test_ensure_sorted_indices():
x = 2000 x = 2000
......
...@@ -102,7 +102,7 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5): ...@@ -102,7 +102,7 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5):
if out_dtype in sparse.discrete_dtypes: if out_dtype in sparse.discrete_dtypes:
value = numpy.random.randint(20, size=shape).astype(out_dtype) value = numpy.random.randint(20, size=shape).astype(out_dtype)
else: else:
value = numpy.random.random(shape) value = numpy.random.random(shape).astype(out_dtype)
return where * value return where * value
variable = [getattr(theano.sparse, format + '_matrix')(dtype=out_dtype) variable = [getattr(theano.sparse, format + '_matrix')(dtype=out_dtype)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论