提交 1bece41f authored 作者: nouiz's avatar nouiz

Merge pull request #795 from bouchnic/monoid

Monoid NEWS: Added sparse Binomial, csr_fbinomial, csc_fbinomial, csr_dbinomial, csc_dbinomial, Moved out of sparse sandbox: SpSum, sp_sum, ColScaleCSC, RowScaleCSC, col_scale, row_scale, Diag, diag, SquareDiagonal, square_diagonal, EnsureSortedIndices, ensure_sorted_indices, clean) added sparse.sqrt,sqr,log1p,floor,ceil,sgn,round_half_to_even,arctanh,tanh,arcsinh,sinh,arctan,arcsin,tan,sin}
...@@ -3138,6 +3138,7 @@ def structured_monoid(tensor_op): ...@@ -3138,6 +3138,7 @@ def structured_monoid(tensor_op):
data = tensor_op(data, *xs) data = tensor_op(data, *xs)
return CSM(x.format)(data, ind, ptr, shape) return CSM(x.format)(data, ind, ptr, shape)
wrapper.__name__ = str(tensor_op.scalar_op)
return wrapper return wrapper
return decorator return decorator
...@@ -3195,6 +3196,112 @@ def structured_add(x): ...@@ -3195,6 +3196,112 @@ def structured_add(x):
# see decorator for function body # see decorator for function body
# Sparse operation (map 0 to 0)
@structured_monoid(tensor.sin)
def sin(x):
"""Elemwise sinus of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.tan)
def tan(x):
"""Elemwise tan of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.arcsin)
def arcsin(x):
"""Elemwise arcsinus of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.arctan)
def arctan(x):
"""Elemwise arctan of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.sinh)
def sinh(x):
"""Elemwise sinh of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.arcsinh)
def arcsinh(x):
"""Elemwise arcsinh of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.tanh)
def tanh(x):
"""Elemwise tanh of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.arctanh)
def arctanh(x):
"""Elemwise arctanh of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.round_half_to_even)
def rint(x):
"""Elemwise round half to even of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.sgn)
def sgn(x):
"""Elemwise signe of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.ceil)
def ceil(x):
"""Elemwise ceiling of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.floor)
def floor(x):
"""Elemwise floor of `x`.
"""
# see decorator for function body
@structured_monoid(tensor.log1p)
def log1p(x):
"""Elemwise log(1 + `x`).
"""
# see decorator for function body
@structured_monoid(tensor.sqr)
def sqr(x):
"""Elemwise `x` * `x`.
"""
# see decorator for function body
@structured_monoid(tensor.sqrt)
def sqrt(x):
"""Elemwise square root of `x`.
"""
# see decorator for function body
# Dot # Dot
class StructuredDot(gof.Op): class StructuredDot(gof.Op):
"""Structured Dot is like dot, except that only the """Structured Dot is like dot, except that only the
......
...@@ -16,6 +16,7 @@ from theano import compile, config, gof ...@@ -16,6 +16,7 @@ from theano import compile, config, gof
from theano.sparse import enable_sparse from theano.sparse import enable_sparse
from theano.gof.python25 import all, any, product from theano.gof.python25 import all, any, product
if enable_sparse == False: if enable_sparse == False:
raise SkipTest('Optional package sparse disabled') raise SkipTest('Optional package sparse disabled')
...@@ -74,7 +75,7 @@ def random_lil(shape, dtype, nnz): ...@@ -74,7 +75,7 @@ def random_lil(shape, dtype, nnz):
return rval return rval
def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5): def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5, gap=None):
"""Return a tuple containing everything needed to """Return a tuple containing everything needed to
perform a test. perform a test.
...@@ -86,6 +87,11 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5): ...@@ -86,6 +87,11 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5):
:param n: Number of variable. :param n: Number of variable.
:param out_dtype: dtype of output. :param out_dtype: dtype of output.
:param p: Sparsity proportion. :param p: Sparsity proportion.
:param gap: Tuple for the range of the random sample. When
length is 1, it is assumed to be the exclusive
max, when `gap` = (`a`, `b`) it provide a sample
from [a, b[. If `None` is used, it provide [0, 1]
for float dtypes and [0, 50[ for integer dtypes.
:return: (variable, data) where both `variable` :return: (variable, data) where both `variable`
and `data` are list. and `data` are list.
...@@ -97,21 +103,34 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5): ...@@ -97,21 +103,34 @@ def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5):
assert 0 <= p and p <= 1 assert 0 <= p and p <= 1
assert len(shape) == 2 assert len(shape) == 2
assert out_dtype in sparse.all_dtypes assert out_dtype in sparse.all_dtypes
assert gap is None or isinstance(gap, (tuple, list))
def _rand(): def _rand():
where = numpy.random.binomial(1, p, size=shape).astype('int8') where = numpy.random.binomial(1, p, size=shape).astype('int8')
if out_dtype in sparse.discrete_dtypes: if out_dtype in sparse.discrete_dtypes:
value = numpy.random.randint(20, size=shape).astype(out_dtype) if not gap:
value = numpy.random.randint(50, size=shape)
elif len(gap) == 2:
value = numpy.random.randint(gap[0], gap[1], size=shape)
else:
value = numpy.random.randint(gap[0], size=shape)
else: else:
value = numpy.random.random(shape).astype(out_dtype) if not gap:
value = numpy.random.random(shape)
elif len(gap) == 2:
a, b = gap
value = a + numpy.random.random(shape) * (b - a)
else:
value = numpy.random.random(shape) * gap[0]
value.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)
for k in range(n)] for k in range(n)]
data = [getattr(scipy.sparse, format + '_matrix')(_rand()) data = [getattr(scipy.sparse, format + '_matrix')(_rand(), dtype=out_dtype)
for k in range(n)] for k in range(n)]
return (variable, data) return (variable, data)
...@@ -2224,73 +2243,261 @@ class MultinomialTester(utt.InferShapeTester): ...@@ -2224,73 +2243,261 @@ class MultinomialTester(utt.InferShapeTester):
self.op_class) self.op_class)
class _StructuredMonoidUnaryTester(unittest.TestCase): def elemwise_checker(op, expected_f, gap=None, test_dtypes=None,
def test_op(self): grad_test=True):
for format in sparse.sparse_formats: """Return the appropriate test class for the elemwise on sparse.
x = getattr(theano.sparse, format + '_matrix')()
spa = getattr(sp, format + '_matrix') :param op: Op to test.
:expected_f: Function use to compare. This function must act
a = spa(numpy.random.random_integers(5, size=(3, 4)) - 1, on dense matrix. If the op is structured
dtype=theano.config.floatX) see the `structure_function` decorator to make
this function structured.
f = theano.function([x], self.op(x)) :param gap: Tuple for the range of the random sample. When
length is 1, it is assumed to be the exclusive
tested = f(a) max, when `gap` = (`a`, `b`) it provide a sample
expected = self.expected_op(a.todense()) from [a, b[. If `None` is used, it provide [0, 1]
expected[a.todense() == 0] = 0 for float dtypes and [0, 50[ for integer dtypes.
:param test_dtypes: Particular dtypes for testing the op.
assert tested.shape == expected.shape If `None`, this is set to the most common
assert tested.dtype == expected.dtype dtypes.
assert numpy.allclose(tested.todense(), expected) :param grad_test: True for testing the grad. False will
skip this test.
class StructuredSigmoidTester(_StructuredMonoidUnaryTester): :return: The class that perform the tests, not an instance
def setUp(self): of the class.
super(StructuredSigmoidTester, self).setUp() """
self.op = structured_sigmoid
self.expected_op = lambda x: 1.0 / (1.0 + numpy.exp(-x))
class StructuredExpTester(_StructuredMonoidUnaryTester):
def setUp(self):
super(StructuredExpTester, self).setUp()
self.op = structured_exp
self.expected_op = numpy.exp
class StructuredLogTester(_StructuredMonoidUnaryTester):
def setUp(self):
super(StructuredLogTester, self).setUp()
self.op = structured_log
self.expected_op = numpy.log
class StructuredPowTester(_StructuredMonoidUnaryTester):
def setUp(self):
super(StructuredPowTester, self).setUp()
self.op = lambda x: structured_pow(x, 2)
self.expected_op = lambda x: numpy.power(x, 2)
class StructuredMinimumTester(_StructuredMonoidUnaryTester):
def setUp(self):
super(StructuredMinimumTester, self).setUp()
self.op = lambda x: structured_minimum(x, 2)
self.expected_op = lambda x: numpy.minimum(x, 2)
class StructuredMaximumTester(_StructuredMonoidUnaryTester):
def setUp(self):
super(StructuredMaximumTester, self).setUp()
self.op = lambda x: structured_maximum(x, 2)
self.expected_op = lambda x: numpy.maximum(x, 2)
if test_dtypes is None:
test_dtypes = sparse.all_dtypes
class Tester(unittest.TestCase):
__name__ = op.__name__.capitalize() + 'Tester'
def setUp(self):
super(Tester, self).setUp()
self.op = op
self.expected_f = expected_f
def test_op(self):
for format in sparse.sparse_formats:
for dtype in test_dtypes:
if dtype == 'int8':
continue
variable, data = sparse_random_inputs(
format,
shape=(4, 7),
out_dtype=dtype,
gap=gap)
f = theano.function(variable, self.op(*variable))
tested = f(*data)
data = [m.toarray() for m in data]
expected = self.expected_f(*data)
assert tested.format == format
tested = tested.toarray()
try:
assert numpy.allclose(tested, expected)
except AssertionError:
raise AssertionError(self.__name__)
# Test with int8 as dtype
# These tests are not in the loop for two reasons.
# First, in recent version of numpy, when a numpy
# function have int8 as input dtype, it returns a
# float16 as output dtype. Since this does not provide
# enough precision, we upcast the data before we apply the
# function.
# Second, the tolerance for the checkup in DebugMode
# is too high.
if 'int8' in test_dtypes:
if gap:
domain = gap
else:
domain = (0, 5)
variable, data = sparse_random_inputs(
format,
shape=(4, 7),
out_dtype='int8',
gap=domain)
f = theano.function(variable, self.op(*variable))
old_value = (tensor.basic.float32_atol,
tensor.basic.float32_rtol,
tensor.basic.float64_atol,
tensor.basic.float64_rtol)
tensor.basic.float32_atol = 1e-4
tensor.basic.float32_rtol = 1e-3
tensor.basic.float64_atol = 1e-3
tensor.basic.float64_rtol = 1e-4
try:
tested = f(*data)
finally:
(tensor.basic.float32_atol,
tensor.basic.float32_rtol,
tensor.basic.float64_atol,
tensor.basic.float64_rtol) = old_value
data = [m.toarray().astype('float32') for m in data]
expected = self.expected_f(*data)
assert tested.format == format
tested = tested.toarray()
try:
assert numpy.allclose(tested, expected, rtol=1e-2)
except AssertionError:
raise AssertionError(self.__name__)
if grad_test:
def test_grad(self):
for format in sparse.sparse_formats:
for dtype in sparse.float_dtypes:
variable, data = sparse_random_inputs(
format,
shape=(4, 7),
out_dtype=dtype)
verify_grad_sparse(self.op,
data,
structured=True)
return Tester
def structure_function(f, index=0):
"""Decorator to structure a function wich
apply on dense matrix.
Here, the inputs of the function must be
dense matrix. The sparse pattern is
determined by finding the zeros.
:param index: The index of the parameter
from wich the function must
be structured.
:return: The structured function for its
`index` parameter.
"""
class StructuredAddTester(_StructuredMonoidUnaryTester): def structured_function(*args):
def setUp(self): pattern = args[index]
super(StructuredAddTester, self).setUp() evaluated = f(*args)
self.op = lambda x: structured_add(x, 2) evaluated[pattern == 0] = 0
self.expected_op = lambda x: numpy.add(x, 2) return evaluated
return structured_function
StructuredSigmoidTester = elemwise_checker(
sparse.structured_sigmoid,
structure_function(lambda x: 1.0 / (1.0 + numpy.exp(-x))),
test_dtypes=[m for m in sparse.all_dtypes
if not m in sparse.complex_dtypes])
StructuredExpTester = elemwise_checker(
sparse.structured_exp,
structure_function(numpy.exp))
StructuredLogTester = elemwise_checker(
sparse.structured_log,
structure_function(numpy.log),
gap=(0.5, 10))
StructuredPowTester = elemwise_checker(
lambda x: sparse.structured_pow(x, 2),
structure_function(lambda x: numpy.power(x, 2)))
StructuredMinimumTester = elemwise_checker(
lambda x: structured_minimum(x, 2),
structure_function(lambda x: numpy.minimum(x, 2)))
StructuredMaximumTester = elemwise_checker(
lambda x: structured_maximum(x, 2),
structure_function(lambda x: numpy.maximum(x, 2)))
StructuredAddTester = elemwise_checker(
lambda x: structured_add(x, 2),
structure_function(lambda x: numpy.add(x, 2)))
SinTester = elemwise_checker(
sparse.sin,
numpy.sin)
TanTester = elemwise_checker(
sparse.tan,
numpy.tan,
gap=(-1, 1))
ArcSinTester = elemwise_checker(
sparse.arcsin,
numpy.arcsin,
gap=(-1, 1))
ArcTanTester = elemwise_checker(
sparse.arctan,
numpy.arctan)
SinhTester = elemwise_checker(
sparse.sinh,
numpy.sinh)
ArcSinhTester = elemwise_checker(
sparse.arcsinh,
numpy.arcsinh,
gap=(-1, 1))
TanhTester = elemwise_checker(
sparse.tanh,
numpy.tanh,
gap=(-1, 1))
ArcTanhTester = elemwise_checker(
sparse.arctanh,
numpy.arctanh,
gap=(-0.9, 1))
RintTester = elemwise_checker(
sparse.rint,
numpy.rint,
grad_test=False,
test_dtypes=sparse.float_dtypes)
SgnTester = elemwise_checker(
sparse.sgn,
numpy.sign,
grad_test=False,
test_dtypes=[m for m in sparse.all_dtypes
if not m in sparse.complex_dtypes])
CeilTester = elemwise_checker(
sparse.ceil,
numpy.ceil,
grad_test=False,
test_dtypes=[m for m in sparse.all_dtypes
if not m in sparse.complex_dtypes])
FloorTester = elemwise_checker(
sparse.floor,
numpy.floor,
grad_test=False,
test_dtypes=[m for m in sparse.all_dtypes
if not m in sparse.complex_dtypes])
Log1pTester = elemwise_checker(
sparse.log1p,
numpy.log1p,
gap=(0.5, 10))
SqrTester = elemwise_checker(
sparse.sqr,
lambda x: x * x)
SqrtTester = elemwise_checker(
sparse.sqrt,
numpy.sqrt,
gap=(0, 10))
class MulSVTester(unittest.TestCase): class MulSVTester(unittest.TestCase):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论