提交 0781c496 authored 作者: lamblin's avatar lamblin

Merge pull request #1264 from nouiz/sparse

Sparse
...@@ -3152,12 +3152,24 @@ class Dot(gof.op.Op): ...@@ -3152,12 +3152,24 @@ class Dot(gof.op.Op):
if not x_is_sparse_var: if not x_is_sparse_var:
x = tensor.as_tensor_variable(x) x = tensor.as_tensor_variable(x)
if x.ndim not in (1, 2):
raise TypeError(
'theano.sparse.Dot: input 0 (0-indexed) must have ndim of '
'1 or 2, %d given.' % x.ndim)
if not y_is_sparse_var: if not y_is_sparse_var:
y = tensor.as_tensor_variable(y) y = tensor.as_tensor_variable(y)
if y.ndim not in (1, 2):
raise TypeError(
'theano.sparse.Dot: input 1 (1-indexed) must have ndim of '
'1 or 2, %d given.' % y.ndim)
if y.ndim == 1 or x.ndim == 1:
bz = (False,)
else:
bz = (False, False)
return gof.Apply(self, [x, y], [tensor.tensor(dtype=dtype_out, return gof.Apply(self, [x, y], [tensor.tensor(dtype=dtype_out,
broadcastable=(False, False))]) broadcastable=bz)])
def perform(self, node, inputs, out): def perform(self, node, inputs, out):
x, y = inputs x, y = inputs
...@@ -3294,7 +3306,10 @@ usmm = Usmm() ...@@ -3294,7 +3306,10 @@ usmm = Usmm()
class ConstructSparseFromList(gof.Op): class ConstructSparseFromList(gof.Op):
"""Constructs a sparse matrix out of a list of 2-D matrix rows""" """Constructs a sparse matrix out of a list of 2-D matrix rows
:note: The grad implemented is regular, i.e. not structured.
"""
def __hash__(self): def __hash__(self):
return hash((type(self))) return hash((type(self)))
...@@ -3304,33 +3319,59 @@ class ConstructSparseFromList(gof.Op): ...@@ -3304,33 +3319,59 @@ class ConstructSparseFromList(gof.Op):
def __str__(self): def __str__(self):
return self.__class__.__name__ return self.__class__.__name__
def make_node(self, x, y, ilist): def make_node(self, x, values, ilist):
"""
:param x: a dense matrix that specify the output shape.
:param values: a dense matrix with the values to use for output.
:param ilist: a dense vector with the same lenght as the number of rows
then values. It specify where in the output to put
the corresponding rows.
This create a sparse matrix with the same shape as `x`. Its
values are the rows of `values` moved. Pseudo-code::
output = csc_matrix.zeros_like(x, dtype=values.dtype)
for in_idx, out_idx in enumerate(ilist):
output[out_idx] = values[in_idx]
"""
x_ = theano.tensor.as_tensor_variable(x) x_ = theano.tensor.as_tensor_variable(x)
y_ = theano.tensor.as_tensor_variable(y) values_ = theano.tensor.as_tensor_variable(values)
ilist_ = theano.tensor.as_tensor_variable(ilist) ilist_ = theano.tensor.as_tensor_variable(ilist)
if ilist_.type.dtype[:3] not in ('int', 'uin'): if ilist_.type.dtype[:3] not in ('int', 'uin'):
raise TypeError('index must be integers') raise TypeError('index must be integers')
if ilist_.type.ndim != 1: if ilist_.type.ndim != 1:
raise TypeError('index must be vector') raise TypeError('index must be vector')
if x_.type.ndim == 0: if x_.type.ndim != 2:
raise TypeError('cannot index into a scalar') raise TypeError(
if y_.type.ndim > x_.type.ndim: 'cannot create a sparse matrix with %d dimensions' %
raise TypeError('cannot construct sparse matrix as dimensions differ') x_.type.ndim)
return gof.Apply(self, [x_, y_, ilist_], [theano.sparse.csc_matrix(dtype=x.dtype)]) if values_.type.ndim != 2:
raise TypeError(
'cannot create a sparse matrix from values with %d ndim' %
values_.type.ndim)
# We only need the shape of `x` in the perform
# If we keep in the graph the x variable as input of the Apply node,
# this can rise the memory usage. That is why the Apply node
# take `x_.shape` as input and not `x`.
return gof.Apply(self, [x_.shape, values_, ilist_],
[csc_matrix(dtype=x.dtype)])
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
x, values, idx = inp out_shape, values, ilist = inp
out, = out_ out, = out_
rows, cols = values.shape rows, cols = values.shape
assert rows == len(idx) assert rows == len(ilist)
indptr = numpy.arange(cols + 1) * rows indptr = numpy.arange(cols + 1) * rows
indices = as_strided(idx, indices = as_strided(ilist,
strides=(0, idx.strides[0]), strides=(0, ilist.strides[0]),
shape = (cols, idx.shape[0])).flatten() shape=(cols, ilist.shape[0])).flatten()
data = values.T.flatten() data = values.T.flatten()
out[0] = scipy.sparse.csc_matrix((data, indices, indptr), shape=x.shape, out[0] = scipy.sparse.csc_matrix((data, indices, indptr),
dtype=x.dtype) shape=out_shape,
dtype=values.dtype)
def infer_shape(self, node, ishapes): def infer_shape(self, node, ishapes):
x, y, ilist = ishapes x, y, ilist = ishapes
...@@ -3356,3 +3397,5 @@ class ConstructSparseFromList(gof.Op): ...@@ -3356,3 +3397,5 @@ class ConstructSparseFromList(gof.Op):
gy = theano.tensor.advanced_subtensor1(g_output, *idx_list) gy = theano.tensor.advanced_subtensor1(g_output, *idx_list)
return [gx, gy] + [DisconnectedType()()] * len(idx_list) return [gx, gy] + [DisconnectedType()()] * len(idx_list)
construct_sparse_from_list = ConstructSparseFromList()
...@@ -1027,8 +1027,9 @@ class test_structureddot(unittest.TestCase): ...@@ -1027,8 +1027,9 @@ class test_structureddot(unittest.TestCase):
overhead_tol) overhead_tol)
class DotTests(unittest.TestCase): class DotTests(utt.InferShapeTester):
def setUp(self): def setUp(self):
super(DotTests, self).setUp()
x_size = (10, 100) x_size = (10, 100)
y_size = (100, 1000) y_size = (100, 1000)
utt.seed_rng() utt.seed_rng()
...@@ -1043,48 +1044,47 @@ class DotTests(unittest.TestCase): ...@@ -1043,48 +1044,47 @@ class DotTests(unittest.TestCase):
numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX) numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX)
self.y_csc = scipy.sparse.csc_matrix( self.y_csc = scipy.sparse.csc_matrix(
numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX) numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX)
self.v_10 = numpy.asarray(numpy.random.uniform(-1, 1, 10),
dtype=theano.config.floatX)
self.v_100 = numpy.asarray(numpy.random.uniform(-1, 1, 100),
dtype=theano.config.floatX)
def test_csr_dense(self): def test_csr_dense(self):
x = theano.sparse.csr_matrix('x') x = theano.sparse.csr_matrix('x')
y = theano.tensor.matrix('y') y = theano.tensor.matrix('y')
v = theano.tensor.vector('v')
f_a = theano.function([x, y], theano.sparse.dot(x, y)) for (x, y, x_v, y_v) in [(x, y, self.x_csr, self.y),
f_b = lambda x, y: x * y (x, v, self.x_csr, self.v_100),
(v, x, self.v_10, self.x_csr)]:
f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y
assert _allclose(f_a(self.x_csr, self.y), f_b(self.x_csr, self.y)) assert _allclose(f_a(x_v, y_v), f_b(x_v, y_v))
# Test infer_shape # Test infer_shape
f_a = theano.function([x, y], theano.sparse.dot(x, y).shape) self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
f_b = lambda x, y: (x * y).shape [x_v, y_v],
assert numpy.all(f_a(self.x_csr, self.y) == f_b(self.x_csr, self.y)) (Dot, Usmm, UsmmCscDense))
topo = f_a.maker.fgraph.toposort()
if theano.config.mode != 'FAST_COMPILE':
nb = 0
else:
nb = 1
assert sum([isinstance(node.op, (Dot, Usmm, UsmmCscDense))
for node in topo]) == nb
def test_csc_dense(self): def test_csc_dense(self):
x = theano.sparse.csc_matrix('x') x = theano.sparse.csc_matrix('x')
y = theano.tensor.matrix('y') y = theano.tensor.matrix('y')
v = theano.tensor.vector('v')
f_a = theano.function([x, y], theano.sparse.dot(x, y)) for (x, y, x_v, y_v) in [(x, y, self.x_csc, self.y),
f_b = lambda x, y: x * y (x, v, self.x_csc, self.v_100),
(v, x, self.v_10, self.x_csc)]:
assert _allclose(f_a(self.x_csc, self.y), f_b(self.x_csc, self.y)) f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y
# Test infer_shape assert _allclose(f_a(x_v, y_v), f_b(x_v, y_v))
f_a = theano.function([x, y], theano.sparse.dot(x, y).shape)
f_b = lambda x, y: (x * y).shape # Test infer_shape
assert numpy.all(f_a(self.x_csc, self.y) == f_b(self.x_csc, self.y)) self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
topo = f_a.maker.fgraph.toposort() [x_v, y_v],
if theano.config.mode != 'FAST_COMPILE': (Dot, Usmm, UsmmCscDense))
nb = 0
else:
nb = 1
assert sum([isinstance(node.op, (Dot, Usmm, UsmmCscDense))
for node in topo]) == nb
def test_sparse_sparse(self): def test_sparse_sparse(self):
for d1, d2 in [('float32', 'float32'), for d1, d2 in [('float32', 'float32'),
......
...@@ -6929,7 +6929,6 @@ class AdvancedSubtensor1(Op): ...@@ -6929,7 +6929,6 @@ class AdvancedSubtensor1(Op):
out[0] = x.take(i, axis=0, out=o) out[0] = x.take(i, axis=0, out=o)
def connection_pattern(self, node): def connection_pattern(self, node):
rval = [[True]] rval = [[True]]
for ipt in node.inputs[1:]: for ipt in node.inputs[1:]:
...@@ -6939,17 +6938,18 @@ class AdvancedSubtensor1(Op): ...@@ -6939,17 +6938,18 @@ class AdvancedSubtensor1(Op):
def grad(self, inputs, grads): def grad(self, inputs, grads):
global sparse_module_ref global sparse_module_ref
x, ilist = inputs
gz, = grads gz, = grads
assert len(inputs) == 2 assert len(inputs) == 2
if inputs[0].type.sparse_grad:
if x.type.sparse_grad:
if sparse_module_ref is None: if sparse_module_ref is None:
import theano.sparse as sparse_module_ref import theano.sparse as sparse_module_ref
rval1 = [sparse_module_ref.ConstructSparseFromList()( rval1 = [sparse_module_ref.construct_sparse_from_list(x, gz,
(inputs[0]), gz, inputs[1])] ilist)]
else: else:
rval1 = [advanced_inc_subtensor1( rval1 = [advanced_inc_subtensor1(zeros_like(x), gz, ilist)]
zeros_like(inputs[0]), gz, inputs[1])]
return rval1 + [DisconnectedType()()] * (len(inputs) - 1) return rval1 + [DisconnectedType()()] * (len(inputs) - 1)
def R_op(self, inputs, eval_points): def R_op(self, inputs, eval_points):
......
...@@ -9,6 +9,7 @@ _logger = logging.getLogger('theano.tensor.opt') ...@@ -9,6 +9,7 @@ _logger = logging.getLogger('theano.tensor.opt')
import operator import operator
import itertools import itertools
import StringIO
import sys import sys
import traceback import traceback
from itertools import izip from itertools import izip
...@@ -807,11 +808,15 @@ class ShapeFeature(object): ...@@ -807,11 +808,15 @@ class ShapeFeature(object):
else: else:
if not isinstance(s, (tuple, list)): if not isinstance(s, (tuple, list)):
raise TypeError('shapes must be tuple/list', (r, s)) raise TypeError('shapes must be tuple/list', (r, s))
if r.ndim != len(s): if r.ndim != len(s):
sio = StringIO.StringIO()
theano.printing.debugprint(r, file=sio, print_type=True)
raise AssertionError( raise AssertionError(
"Something inferred a shape with %d dimensions " "Something inferred a shape with %d dimensions "
"for a variable with %d dimensions." % ( "for a variable with %d dimensions"
len(s), r.ndim)) " for the variable:\n%s" % (
len(s), r.ndim, sio.getvalue()))
shape_vars = [] shape_vars = []
for i in range(r.ndim): for i in range(r.ndim):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论