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

Merge pull request #1264 from nouiz/sparse

Sparse
......@@ -3152,12 +3152,24 @@ class Dot(gof.op.Op):
if not x_is_sparse_var:
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:
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,
broadcastable=(False, False))])
broadcastable=bz)])
def perform(self, node, inputs, out):
x, y = inputs
......@@ -3294,7 +3306,10 @@ usmm = Usmm()
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):
return hash((type(self)))
......@@ -3304,33 +3319,59 @@ class ConstructSparseFromList(gof.Op):
def __str__(self):
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)
y_ = theano.tensor.as_tensor_variable(y)
values_ = theano.tensor.as_tensor_variable(values)
ilist_ = theano.tensor.as_tensor_variable(ilist)
if ilist_.type.dtype[:3] not in ('int', 'uin'):
raise TypeError('index must be integers')
if ilist_.type.ndim != 1:
raise TypeError('index must be vector')
if x_.type.ndim == 0:
raise TypeError('cannot index into a scalar')
if y_.type.ndim > x_.type.ndim:
raise TypeError('cannot construct sparse matrix as dimensions differ')
return gof.Apply(self, [x_, y_, ilist_], [theano.sparse.csc_matrix(dtype=x.dtype)])
if x_.type.ndim != 2:
raise TypeError(
'cannot create a sparse matrix with %d dimensions' %
x_.type.ndim)
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_):
x, values, idx = inp
out_shape, values, ilist = inp
out, = out_
rows, cols = values.shape
assert rows == len(idx)
assert rows == len(ilist)
indptr = numpy.arange(cols + 1) * rows
indices = as_strided(idx,
strides=(0, idx.strides[0]),
shape = (cols, idx.shape[0])).flatten()
indices = as_strided(ilist,
strides=(0, ilist.strides[0]),
shape=(cols, ilist.shape[0])).flatten()
data = values.T.flatten()
out[0] = scipy.sparse.csc_matrix((data, indices, indptr), shape=x.shape,
dtype=x.dtype)
out[0] = scipy.sparse.csc_matrix((data, indices, indptr),
shape=out_shape,
dtype=values.dtype)
def infer_shape(self, node, ishapes):
x, y, ilist = ishapes
......@@ -3356,3 +3397,5 @@ class ConstructSparseFromList(gof.Op):
gy = theano.tensor.advanced_subtensor1(g_output, *idx_list)
return [gx, gy] + [DisconnectedType()()] * len(idx_list)
construct_sparse_from_list = ConstructSparseFromList()
......@@ -1027,8 +1027,9 @@ class test_structureddot(unittest.TestCase):
overhead_tol)
class DotTests(unittest.TestCase):
class DotTests(utt.InferShapeTester):
def setUp(self):
super(DotTests, self).setUp()
x_size = (10, 100)
y_size = (100, 1000)
utt.seed_rng()
......@@ -1043,48 +1044,47 @@ class DotTests(unittest.TestCase):
numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX)
self.y_csc = scipy.sparse.csc_matrix(
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):
x = theano.sparse.csr_matrix('x')
y = theano.tensor.matrix('y')
v = theano.tensor.vector('v')
f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y
for (x, y, x_v, y_v) in [(x, y, self.x_csr, self.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
f_a = theano.function([x, y], theano.sparse.dot(x, y).shape)
f_b = lambda x, y: (x * y).shape
assert numpy.all(f_a(self.x_csr, self.y) == f_b(self.x_csr, self.y))
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
# Test infer_shape
self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
[x_v, y_v],
(Dot, Usmm, UsmmCscDense))
def test_csc_dense(self):
x = theano.sparse.csc_matrix('x')
y = theano.tensor.matrix('y')
v = theano.tensor.vector('v')
f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y
for (x, y, x_v, y_v) in [(x, y, self.x_csc, self.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
f_a = theano.function([x, y], theano.sparse.dot(x, y).shape)
f_b = lambda x, y: (x * y).shape
assert numpy.all(f_a(self.x_csc, self.y) == f_b(self.x_csc, self.y))
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
assert _allclose(f_a(x_v, y_v), f_b(x_v, y_v))
# Test infer_shape
self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
[x_v, y_v],
(Dot, Usmm, UsmmCscDense))
def test_sparse_sparse(self):
for d1, d2 in [('float32', 'float32'),
......
......@@ -6929,7 +6929,6 @@ class AdvancedSubtensor1(Op):
out[0] = x.take(i, axis=0, out=o)
def connection_pattern(self, node):
rval = [[True]]
for ipt in node.inputs[1:]:
......@@ -6939,17 +6938,18 @@ class AdvancedSubtensor1(Op):
def grad(self, inputs, grads):
global sparse_module_ref
x, ilist = inputs
gz, = grads
assert len(inputs) == 2
if inputs[0].type.sparse_grad:
if x.type.sparse_grad:
if sparse_module_ref is None:
import theano.sparse as sparse_module_ref
rval1 = [sparse_module_ref.ConstructSparseFromList()(
(inputs[0]), gz, inputs[1])]
rval1 = [sparse_module_ref.construct_sparse_from_list(x, gz,
ilist)]
else:
rval1 = [advanced_inc_subtensor1(
zeros_like(inputs[0]), gz, inputs[1])]
rval1 = [advanced_inc_subtensor1(zeros_like(x), gz, ilist)]
return rval1 + [DisconnectedType()()] * (len(inputs) - 1)
def R_op(self, inputs, eval_points):
......
......@@ -9,6 +9,7 @@ _logger = logging.getLogger('theano.tensor.opt')
import operator
import itertools
import StringIO
import sys
import traceback
from itertools import izip
......@@ -807,11 +808,15 @@ class ShapeFeature(object):
else:
if not isinstance(s, (tuple, list)):
raise TypeError('shapes must be tuple/list', (r, s))
if r.ndim != len(s):
sio = StringIO.StringIO()
theano.printing.debugprint(r, file=sio, print_type=True)
raise AssertionError(
"Something inferred a shape with %d dimensions "
"for a variable with %d dimensions." % (
len(s), r.ndim))
"Something inferred a shape with %d dimensions "
"for a variable with %d dimensions"
" for the variable:\n%s" % (
len(s), r.ndim, sio.getvalue()))
shape_vars = []
for i in range(r.ndim):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论