提交 34836892 authored 作者: Yann N. Dauphin's avatar Yann N. Dauphin

Merge branch 'ynd_sparse' of https://github.com/dwf/Theano into sparse

Conflicts: theano/sparse/basic.py
...@@ -91,5 +91,17 @@ if sys.version_info[:2] < (2,6): ...@@ -91,5 +91,17 @@ if sys.version_info[:2] < (2,6):
for j in range(i+1, r): for j in range(i+1, r):
indices[j] = indices[j-1] + 1 indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices) yield tuple(pool[i] for i in indices)
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = map(tuple, args) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
else: else:
from itertools import combinations from itertools import combinations, product
...@@ -1451,8 +1451,11 @@ class StructuredDotGradCSR(gof.Op): ...@@ -1451,8 +1451,11 @@ class StructuredDotGradCSR(gof.Op):
} }
"""% dict(locals(), **sub) """% dict(locals(), **sub)
sdg_csr = StructuredDotGradCSR() sdg_csr = StructuredDotGradCSR()
class Dot(gof.op.Op): class Dot(gof.op.Op):
""" """
Operation for efficiently calculating the dot product when Operation for efficiently calculating the dot product when
...@@ -1490,7 +1493,9 @@ class Dot(gof.op.Op): ...@@ -1490,7 +1493,9 @@ class Dot(gof.op.Op):
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=(False, False))])
def perform(self, node, (x, y), (out, )): def perform(self, node, inputs, out):
x, y = inputs
out = out[0]
x_is_sparse = _is_sparse(x) x_is_sparse = _is_sparse(x)
y_is_sparse = _is_sparse(y) y_is_sparse = _is_sparse(y)
...@@ -1506,23 +1511,31 @@ class Dot(gof.op.Op): ...@@ -1506,23 +1511,31 @@ class Dot(gof.op.Op):
def grad(self, (x, y), (gz,)): def grad(self, (x, y), (gz,)):
assert _is_sparse_variable(x) or _is_sparse_variable(y) assert _is_sparse_variable(x) or _is_sparse_variable(y)
rval = []
rval = [ if _is_dense_variable(y):
tensor.dot(gz, y.T) if _is_dense_variable(y) else dot(gz, y.T), rval.append(tensor.dot(gz, y.T))
tensor.dot(x.T, gz) if _is_dense_variable(x) else dot(x.T, gz) else:
] rval.append(dot(gz, y.T))
if _is_dense_variable(x):
rval.append(tensor.dot(x.T, gz))
else:
rval.append(dot(x.T, gz))
return rval return rval
_dot = Dot() _dot = Dot()
def dot(x, y): def dot(x, y):
""" """
Operation for efficiently calculating the dot product when Operation for efficiently calculating the dot product when
one or all operands is sparse. Supported format are CSC and CSR. one or all operands is sparse. Supported format are CSC and CSR.
The output of the operation is dense. The output of the operation is dense.
""" """
if hasattr(x, 'getnnz'): x = as_sparse_variable(x) if hasattr(x, 'getnnz'):
if hasattr(y, 'getnnz'): y = as_sparse_variable(y) x = as_sparse_variable(x)
if hasattr(y, 'getnnz'):
y = as_sparse_variable(y)
x_is_sparse_variable = _is_sparse_variable(x) x_is_sparse_variable = _is_sparse_variable(x)
y_is_sparse_variable = _is_sparse_variable(y) y_is_sparse_variable = _is_sparse_variable(y)
...@@ -1572,12 +1585,13 @@ class Usmm(gof.op.Op): ...@@ -1572,12 +1585,13 @@ class Usmm(gof.op.Op):
# We should use Dot22 and Gemm in that case. # We should use Dot22 and Gemm in that case.
raise TypeError(x) raise TypeError(x)
dtype_out = scalar.upcast(alpha.type.dtype, x.type.dtype, y.type.dtype, z.type.dtype) dtype_out = scalar.upcast(alpha.type.dtype, x.type.dtype,
y.type.dtype, z.type.dtype)
alpha = tensor.as_tensor_variable(alpha) alpha = tensor.as_tensor_variable(alpha)
z = tensor.as_tensor_variable(z) z = tensor.as_tensor_variable(z)
assert z.ndim == 2 assert z.ndim == 2
assert alpha.type.broadcastable == (True,)* alpha.ndim assert alpha.type.broadcastable == (True,) * alpha.ndim
if not _is_sparse_variable(x): if not _is_sparse_variable(x):
x = tensor.as_tensor_variable(x) x = tensor.as_tensor_variable(x)
assert x.ndim == 2 assert x.ndim == 2
...@@ -1585,7 +1599,9 @@ class Usmm(gof.op.Op): ...@@ -1585,7 +1599,9 @@ class Usmm(gof.op.Op):
y = tensor.as_tensor_variable(y) y = tensor.as_tensor_variable(y)
assert y.ndim == 2 assert y.ndim == 2
return gof.Apply(self, [alpha, x, y, z], [tensor.tensor(dtype=dtype_out, broadcastable=(False, False))]) return gof.Apply(self, [alpha, x, y, z],
[tensor.tensor(dtype=dtype_out,
broadcastable=(False, False))])
def perform(self, node, (alpha, x, y, z), (out, )): def perform(self, node, (alpha, x, y, z), (out, )):
x_is_sparse = _is_sparse(x) x_is_sparse = _is_sparse(x)
...@@ -1609,6 +1625,7 @@ class Usmm(gof.op.Op): ...@@ -1609,6 +1625,7 @@ class Usmm(gof.op.Op):
out[0] = rval out[0] = rval
usmm = Usmm() usmm = Usmm()
class UsmmCscDense(gof.Op): class UsmmCscDense(gof.Op):
""" """
Performs the expression is alpha * x y + z Performs the expression is alpha * x y + z
...@@ -1621,16 +1638,20 @@ class UsmmCscDense(gof.Op): ...@@ -1621,16 +1638,20 @@ class UsmmCscDense(gof.Op):
def __init__(self, inplace): def __init__(self, inplace):
self.inplace = inplace self.inplace = inplace
if inplace: if inplace:
self.destroy_map={ 0 : [6] } self.destroy_map = {0: [6]}
def __str__(self): def __str__(self):
if self.inplace: if self.inplace:
return 'UsmmCscDense{inplace}' return 'UsmmCscDense{inplace}'
else: else:
return 'UsmmCscDense{no_inplace}' return 'UsmmCscDense{no_inplace}'
def __eq__(self, other): def __eq__(self, other):
return (type(self) == type(other)) and self.inplace == other.inplace return (type(self) == type(other)) and self.inplace == other.inplace
def __hash__(self): def __hash__(self):
return hash(type(self)) ^ self.inplace return hash(type(self)) ^ self.inplace
def infer_shape(self, node, shapes): def infer_shape(self, node, shapes):
xshp, yshp = shapes xshp, yshp = shapes
x, y = node.inputs x, y = node.inputs
...@@ -1643,6 +1664,7 @@ class UsmmCscDense(gof.Op): ...@@ -1643,6 +1664,7 @@ class UsmmCscDense(gof.Op):
if x.ndim == 1 and y.ndim == 1: if x.ndim == 1 and y.ndim == 1:
return [()] return [()]
raise NotImplementedError() raise NotImplementedError()
def make_node(self, alpha, x_val, x_ind, x_ptr, x_nrows, y, z): def make_node(self, alpha, x_val, x_ind, x_ptr, x_nrows, y, z):
alpha = tensor.as_tensor_variable(alpha) alpha = tensor.as_tensor_variable(alpha)
x_val = tensor.as_tensor_variable(x_val) x_val = tensor.as_tensor_variable(x_val)
...@@ -1682,9 +1704,6 @@ class UsmmCscDense(gof.Op): ...@@ -1682,9 +1704,6 @@ class UsmmCscDense(gof.Op):
[tensor.tensor(dtype_out, (False, y.type.broadcastable[1]))]) [tensor.tensor(dtype_out, (False, y.type.broadcastable[1]))])
return r return r
#def perform(self, node, (alpha, x_val, x_ind, x_ptr, x_nrows, y, z), (out,)):
# raise NotImplemented()
def c_support_code(self): def c_support_code(self):
return blas.blas_header_text() return blas.blas_header_text()
...@@ -1700,9 +1719,12 @@ class UsmmCscDense(gof.Op): ...@@ -1700,9 +1719,12 @@ class UsmmCscDense(gof.Op):
def c_header_dirs(self): def c_header_dirs(self):
return blas.ldflags(libs=False, include_dir=True) return blas.ldflags(libs=False, include_dir=True)
def c_code(self, node, name, (alpha, x_val, x_ind, x_ptr, x_nrows, y, z), (zn,), sub): def c_code(self, node, name, inputs, outputs, sub):
alpha, x_val, x_ind, x_ptr, x_nrows, y, z = inputs
zn = outputs[0]
if node.inputs[1].type.dtype in ('complex64', 'complex128'): if node.inputs[1].type.dtype in ('complex64', 'complex128'):
raise NotImplementedError('Complex types are not supported for x_val') raise NotImplementedError('Complex types are not supported for '
'x_val')
if node.inputs[5].type.dtype in ('complex64', 'complex128'): if node.inputs[5].type.dtype in ('complex64', 'complex128'):
raise NotImplementedError('Complex types are not supported for y') raise NotImplementedError('Complex types are not supported for y')
if node.inputs[6].type.dtype != node.outputs[0].type.dtype: if node.inputs[6].type.dtype != node.outputs[0].type.dtype:
...@@ -1714,12 +1736,12 @@ class UsmmCscDense(gof.Op): ...@@ -1714,12 +1736,12 @@ class UsmmCscDense(gof.Op):
else: else:
conv_type = "double" conv_type = "double"
axpy = "daxpy_" axpy = "daxpy_"
# retrieve dtype numbers
typenum_alpha = node.inputs[0].type.dtype_specs()[-1] # retrieve dtype number typenum_alpha = node.inputs[0].type.dtype_specs()[-1]
typenum_x_val = node.inputs[1].type.dtype_specs()[-1] # retrieve dtype number typenum_x_val = node.inputs[1].type.dtype_specs()[-1]
typenum_y = node.inputs[5].type.dtype_specs()[-1] # retrieve dtype number typenum_y = node.inputs[5].type.dtype_specs()[-1]
typenum_z = node.inputs[6].type.dtype_specs()[-1] # retrieve dtype number typenum_z = node.inputs[6].type.dtype_specs()[-1]
typenum_zn = node.outputs[0].type.dtype_specs()[-1] # retrieve dtype number typenum_zn = node.outputs[0].type.dtype_specs()[-1]
inplace = int(self.inplace) inplace = int(self.inplace)
...@@ -1839,15 +1861,24 @@ class UsmmCscDense(gof.Op): ...@@ -1839,15 +1861,24 @@ class UsmmCscDense(gof.Op):
} }
} }
} }
"""% dict(locals(), **sub) """ % dict(locals(), **sub)
return rval return rval
usmm_csc_dense = UsmmCscDense(inplace=False) usmm_csc_dense = UsmmCscDense(inplace=False)
usmm_csc_dense_inplace = UsmmCscDense(inplace=True) usmm_csc_dense_inplace = UsmmCscDense(inplace=True)
local_usmm = gof.opt.PatternSub((tensor.sub, 'z', (tensor.mul, {'pattern' : 'alpha', 'constraint' : lambda expr: numpy.all(expr.type.broadcastable) },
local_usmm = gof.opt.PatternSub(
(tensor.sub, 'z',
(tensor.mul,
{'pattern': 'alpha',
'constraint': lambda expr: numpy.all(expr.type.broadcastable)},
(_dot, 'x', 'y'))), (_dot, 'x', 'y'))),
(usmm, (tensor.neg, 'alpha'), 'x', 'y', 'z')) (usmm, (tensor.neg, 'alpha'), 'x', 'y', 'z'))
register_specialize(local_usmm, name="local_usmm") register_specialize(local_usmm, name="local_usmm")
...@@ -1863,15 +1894,18 @@ def local_usmm_csx(node): ...@@ -1863,15 +1894,18 @@ def local_usmm_csx(node):
if x.type.format == 'csc': if x.type.format == 'csc':
x_val, x_ind, x_ptr, x_shape = csm_properties(x) x_val, x_ind, x_ptr, x_shape = csm_properties(x)
x_nsparse = x_shape[0] x_nsparse = x_shape[0]
dtype_out = scalar.upcast(alpha.type.dtype, x.type.dtype, y.type.dtype, z.type.dtype) dtype_out = scalar.upcast(alpha.type.dtype, x.type.dtype,
y.type.dtype, z.type.dtype)
# Sparse cast is not implemented. # Sparse cast is not implemented.
if y.type.dtype != dtype_out: if y.type.dtype != dtype_out:
return False return False
return [usmm_csc_dense(alpha, x_val, x_ind, x_ptr, x_nsparse, y, z)] return [usmm_csc_dense(alpha, x_val, x_ind, x_ptr,
x_nsparse, y, z)]
return False return False
register_specialize(local_usmm_csx) register_specialize(local_usmm_csx)
@gof.local_optimizer([usmm_csc_dense]) @gof.local_optimizer([usmm_csc_dense])
def local_usmm_csc_dense_inplace(node): def local_usmm_csc_dense_inplace(node):
if node.op == usmm_csc_dense: if node.op == usmm_csc_dense:
......
...@@ -12,6 +12,8 @@ except ImportError: ...@@ -12,6 +12,8 @@ except ImportError:
import theano import theano
from theano import compile, config from theano import compile, config
from theano.sparse import enable_sparse from theano.sparse import enable_sparse
from theano.gof.python25 import product
if enable_sparse == False: if enable_sparse == False:
raise SkipTest('Optional package sparse disabled') raise SkipTest('Optional package sparse disabled')
...@@ -534,7 +536,6 @@ class DotTests(unittest.TestCase): ...@@ -534,7 +536,6 @@ class DotTests(unittest.TestCase):
self.y_csr = scipy.sparse.csr_matrix(numpy.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX) self.y_csr = scipy.sparse.csr_matrix(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.y_csc = scipy.sparse.csc_matrix(numpy.random.binomial(1, 0.5, y_size), 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')
...@@ -542,7 +543,7 @@ class DotTests(unittest.TestCase): ...@@ -542,7 +543,7 @@ class DotTests(unittest.TestCase):
f_a = theano.function([x, y], theano.sparse.dot(x, y)) f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y f_b = lambda x, y: x * y
assert abs(f_a(self.x_csr, self.y) - f_b(self.x_csr, self.y)).max() < 10**-4 assert abs(f_a(self.x_csr, self.y) - f_b(self.x_csr, self.y)).max() < 1e-4
def test_csc_dense(self): def test_csc_dense(self):
x = theano.sparse.csc_matrix('x') x = theano.sparse.csc_matrix('x')
...@@ -551,7 +552,9 @@ class DotTests(unittest.TestCase): ...@@ -551,7 +552,9 @@ class DotTests(unittest.TestCase):
f_a = theano.function([x, y], theano.sparse.dot(x, y)) f_a = theano.function([x, y], theano.sparse.dot(x, y))
f_b = lambda x, y: x * y f_b = lambda x, y: x * y
assert abs(f_a(self.x_csc, self.y) - f_b(self.x_csc, self.y)).max() < 10**-4 assert (abs(f_a(self.x_csc, self.y) - f_b(self.x_csc, self.y)).max()
< 1e-4)
def test_sparse_sparse(self): def test_sparse_sparse(self):
for d1, d2 in [('float32', 'float32'), for d1, d2 in [('float32', 'float32'),
('float32', 'float64'), ('float32', 'float64'),
...@@ -571,7 +574,7 @@ class DotTests(unittest.TestCase): ...@@ -571,7 +574,7 @@ class DotTests(unittest.TestCase):
vx = getattr(self,'x_'+x_f).astype(d1) vx = getattr(self,'x_'+x_f).astype(d1)
vy = getattr(self,'y_'+y_f).astype(d2) vy = getattr(self,'y_'+y_f).astype(d2)
assert abs(f_a(vx, vy) - f_b(vx, vy)).max() < 10**-4 assert abs(f_a(vx, vy) - f_b(vx, vy)).max() < 1e-4
class UsmmTests(unittest.TestCase): class UsmmTests(unittest.TestCase):
...@@ -591,28 +594,28 @@ class UsmmTests(unittest.TestCase): ...@@ -591,28 +594,28 @@ class UsmmTests(unittest.TestCase):
else: else:
return theano.sparse.matrix(format, name, dtype=dtype) return theano.sparse.matrix(format, name, dtype=dtype)
for dtype1 in ['float32', 'float64']: params = product(*([['float32', 'float64']] * 4 +
for dtype2 in ['float32', 'float64']: [['dense', 'csc', 'csr']] * 2))
for dtype3 in ['float32', 'float64']:
for dtype4 in ['float32', 'float64']: for dtype1, dtype2, dtype3, dtype4, format1, format2 in params:
for format1 in ['dense', 'csc','csr']:
for format2 in ['dense', 'csc','csr']:
if format1 == 'dense' and format2 == 'dense': if format1 == 'dense' and format2 == 'dense':
# Usmm won't be used! # Usmm won't be used!
continue continue
x = mat(format1, 'x', dtype1) x = mat(format1, 'x', dtype1)
y = mat(format2, 'y', dtype2) y = mat(format2, 'y', dtype2)
a = theano.tensor.scalar('a', dtype=dtype3) a = theano.tensor.scalar('a', dtype=dtype3)
z = theano.tensor.shared(numpy.asarray(self.z,dtype=dtype4).copy()) z = theano.tensor.shared(
numpy.asarray(self.z, dtype=dtype4).copy()
)
f_b = lambda z, a, x, y: z - a * (x * y) f_b = lambda z, a, x, y: z - a * (x * y)
x_data = numpy.asarray(self.x, dtype = dtype1) x_data = numpy.asarray(self.x, dtype=dtype1)
if format1 != 'dense': if format1 != 'dense':
x_data = as_sparse_format(x_data, format1) x_data = as_sparse_format(x_data, format1)
y_data = numpy.asarray(self.y, dtype = dtype2) y_data = numpy.asarray(self.y, dtype=dtype2)
if format2 != 'dense': if format2 != 'dense':
y_data = as_sparse_format(y_data, format2) y_data = as_sparse_format(y_data, format2)
z_data = numpy.asarray(self.z, dtype = dtype3) z_data = numpy.asarray(self.z, dtype=dtype3)
f_b_out = f_b(z_data, 1, x_data, y_data) f_b_out = f_b(z_data, 1, x_data, y_data)
...@@ -623,26 +626,37 @@ class UsmmTests(unittest.TestCase): ...@@ -623,26 +626,37 @@ class UsmmTests(unittest.TestCase):
mode = theano.compile.mode.get_default_mode().excluding('fusion') mode = theano.compile.mode.get_default_mode().excluding('fusion')
if inplace: if inplace:
updates = {z: z - a * theano.sparse.dot(x, y)}
f_a = theano.function([a, x, y], [], f_a = theano.function([a, x, y], [],
updates={ z : z - a * theano.sparse.dot(x, y)}, updates=updates,
mode = mode) mode=mode)
f_a(1, x_data, y_data) f_a(1, x_data, y_data)
assert abs(z.get_value(borrow=True) - f_b_out).max() < 10**-4 assert abs(z.get_value(borrow=True) - f_b_out).max() < 1e-4
else: else:
f_a = theano.function([a, x, y], z - a * theano.sparse.dot(x, y), f_a = theano.function([a, x, y],
mode = mode) z - a * theano.sparse.dot(x, y),
mode=mode)
f_a_out = f_a(1, x_data, y_data) f_a_out = f_a(1, x_data, y_data)
assert abs(f_a_out - f_b_out).max() < 10**-4 assert abs(f_a_out - f_b_out).max() < 1e-4
topo = f_a.maker.env.toposort() topo = f_a.maker.env.toposort()
if (y.type.dtype == theano.scalar.upcast(dtype1, dtype2, dtype3, dtype4) up = theano.scalar.upcast(dtype1, dtype2, dtype3, dtype4)
and format1=='csc' and format2=='dense'): if y.type.dtype == up and format1 == 'csc' and format2 == 'dense':
assert (sum([isinstance(node.op, tensor.Elemwise) and
assert sum([isinstance(node.op, tensor.Elemwise) and isinstance(node.op.scalar_op, theano.scalar.basic.Cast) for node in topo])==len(topo)-5 isinstance(node.op.scalar_op,
topo = [node for node in topo if not(isinstance(node.op, tensor.Elemwise) and isinstance(node.op.scalar_op, theano.scalar.basic.Cast))] theano.scalar.basic.Cast)
assert len(topo)==5, topo for node in topo]) == len(topo) - 5)
new_topo = []
for node in topo:
if not isinstance(node.op, tensor.Elemwise) and \
isinstance(node.op.scalar_op, theano.scalar.basic.Cast):
new_topo.append(node)
topo = new_topo
assert len(topo) == 5, topo
# Usmm is tested at the same time in debugmode # Usmm is tested at the same time in debugmode
# Check if the optimization local_usmm and local_usmm_csx is applied # Check if the optimization local_usmm and local_usmm_csx is
assert isinstance(topo[0].op, theano.sparse.basic.CSMProperties) # applied
assert isinstance(topo[0].op,
theano.sparse.basic.CSMProperties)
assert isinstance(topo[1].op, theano.tensor.DimShuffle) assert isinstance(topo[1].op, theano.tensor.DimShuffle)
assert isinstance(topo[2].op, theano.tensor.Subtensor) assert isinstance(topo[2].op, theano.tensor.Subtensor)
assert topo[3].op == theano.tensor.neg assert topo[3].op == theano.tensor.neg
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论