提交 60b5ccc2 authored 作者: abergeron's avatar abergeron

Merge pull request #1972 from nouiz/cumfct

Add check on the output of NumPy.
...@@ -10,7 +10,6 @@ tensor = basic ...@@ -10,7 +10,6 @@ tensor = basic
from theano.gradient import DisconnectedType from theano.gradient import DisconnectedType
class CumsumOp(theano.Op): class CumsumOp(theano.Op):
# See function cumsum for docstring # See function cumsum for docstring
def __init__(self, axis=None): def __init__(self, axis=None):
...@@ -45,8 +44,8 @@ class CumsumOp(theano.Op): ...@@ -45,8 +44,8 @@ class CumsumOp(theano.Op):
# We need to reverse the gradients along ``self.axis``, # We need to reverse the gradients along ``self.axis``,
# compute cumsum, then reverse again # compute cumsum, then reverse again
reverse_slicing = [slice(None,None,None)] * gi.ndim reverse_slicing = [slice(None, None, None)] * gi.ndim
reverse_slicing[self.axis] = slice(None,None,-1) reverse_slicing[self.axis] = slice(None, None, -1)
reverse_slicing = tuple(reverse_slicing) reverse_slicing = tuple(reverse_slicing)
return [cumsum(gi[reverse_slicing], self.axis)[reverse_slicing]] return [cumsum(gi[reverse_slicing], self.axis)[reverse_slicing]]
...@@ -74,13 +73,19 @@ class CumsumOp(theano.Op): ...@@ -74,13 +73,19 @@ class CumsumOp(theano.Op):
if (!%(z)s) if (!%(z)s)
%(fail)s; %(fail)s;
{ {
PyArray_CumSum(%(x)s, NPY_MAXDIMS, PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s); PyObject * t = PyArray_CumSum(
Py_XDECREF(%(z)s); // Because PyArray_CumSum returns a newly created reference on %(z)s. %(x)s, NPY_MAXDIMS,
PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s);
if (!t){
%(fail)s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
} }
""" % locals() """ % locals()
else: else:
code = """ code = """
if(!(%(z)s && PyArray_CompareLists(PyArray_DIMS(%(z)s), PyArray_DIMS(%(x)s), PyArray_NDIM(%(x)s)) )) if(!(%(z)s && PyArray_CompareLists(PyArray_DIMS(%(z)s), PyArray_DIMS(%(x)s), PyArray_NDIM(%(x)s))))
{ {
Py_XDECREF(%(z)s); Py_XDECREF(%(z)s);
%(z)s = (PyArrayObject*) PyArray_SimpleNew(PyArray_NDIM(%(x)s), PyArray_DIMS(%(x)s), PyArray_TYPE((PyArrayObject*) py_%(x)s)); %(z)s = (PyArrayObject*) PyArray_SimpleNew(PyArray_NDIM(%(x)s), PyArray_DIMS(%(x)s), PyArray_TYPE((PyArrayObject*) py_%(x)s));
...@@ -89,15 +94,22 @@ class CumsumOp(theano.Op): ...@@ -89,15 +94,22 @@ class CumsumOp(theano.Op):
if (!%(z)s) if (!%(z)s)
%(fail)s; %(fail)s;
{ {
PyArray_CumSum(%(x)s, %(axis)s, PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s);
Py_XDECREF(%(z)s); // Because PyArray_CumSum returns a newly created reference on %(z)s. PyObject * t = PyArray_CumSum(
%(x)s, %(axis)s,
PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s);
if (!t){
%(fail)s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
} }
""" % locals() """ % locals()
return code return code
def c_code_cache_version(self): def c_code_cache_version(self):
return (3,) return (6,)
def __str__(self): def __str__(self):
return "%s{%s}" % (self.__class__.__name__, self.axis) return "%s{%s}" % (self.__class__.__name__, self.axis)
...@@ -183,8 +195,14 @@ class CumprodOp(theano.Op): ...@@ -183,8 +195,14 @@ class CumprodOp(theano.Op):
if (!%(z)s) if (!%(z)s)
%(fail)s; %(fail)s;
{ {
PyArray_CumProd(%(x)s, NPY_MAXDIMS, PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s); PyObject * t = PyArray_CumProd(
Py_XDECREF(%(z)s); // Because PyArray_CumSum returns a newly created reference on %(z)s. %(x)s, NPY_MAXDIMS,
PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s);
if (!t){
%(fail)s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
} }
""" % locals() """ % locals()
else: else:
...@@ -198,15 +216,21 @@ class CumprodOp(theano.Op): ...@@ -198,15 +216,21 @@ class CumprodOp(theano.Op):
if (!%(z)s) if (!%(z)s)
%(fail)s; %(fail)s;
{ {
PyArray_CumProd(%(x)s, %(axis)s, PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s); PyObject * t = PyArray_CumProd(
Py_XDECREF(%(z)s); // Because PyArray_CumSum returns a newly created reference on %(z)s. %(x)s, %(axis)s,
PyArray_TYPE((PyArrayObject*) py_%(x)s), %(z)s);
if (!t){
%(fail)s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
} }
""" % locals() """ % locals()
return code return code
def c_code_cache_version(self): def c_code_cache_version(self):
return (2,) return (4,)
def __str__(self): def __str__(self):
return "%s{%s}" % (self.__class__.__name__, self.axis) return "%s{%s}" % (self.__class__.__name__, self.axis)
...@@ -319,7 +343,7 @@ class BinCountOp(theano.Op): ...@@ -319,7 +343,7 @@ class BinCountOp(theano.Op):
def __eq__(self, other): def __eq__(self, other):
return (type(self) == type(other) and return (type(self) == type(other) and
self.minlength == other.minlength) self.minlength == other.minlength)
def __hash__(self): def __hash__(self):
return hash(type(self)) ^ hash(self.minlength) return hash(type(self)) ^ hash(self.minlength)
...@@ -346,8 +370,8 @@ class BinCountOp(theano.Op): ...@@ -346,8 +370,8 @@ class BinCountOp(theano.Op):
if x.dtype in numpy_unsupported_dtypes: if x.dtype in numpy_unsupported_dtypes:
raise TypeError( raise TypeError(
("Input dtypes %s are not supported by numpy.bincount, " ("Input dtypes %s are not supported by numpy.bincount, "
% numpy_unsupported_dtypes), x.dtype) % numpy_unsupported_dtypes), x.dtype)
if x.ndim != 1: if x.ndim != 1:
raise TypeError("Inputs must be of dimension 1.") raise TypeError("Inputs must be of dimension 1.")
...@@ -473,9 +497,9 @@ class RepeatOp(theano.Op): ...@@ -473,9 +497,9 @@ class RepeatOp(theano.Op):
if repeats.dtype in numpy_unsupported_dtypes: if repeats.dtype in numpy_unsupported_dtypes:
raise TypeError( raise TypeError(
("dtypes %s are not supported by numpy.repeat " ("dtypes %s are not supported by numpy.repeat "
"for the 'repeats' parameter, " "for the 'repeats' parameter, "
% str(numpy_unsupported_dtypes)), repeats.dtype) % str(numpy_unsupported_dtypes)), repeats.dtype)
if self.axis is None: if self.axis is None:
broadcastable = [False] broadcastable = [False]
...@@ -532,7 +556,7 @@ class RepeatOp(theano.Op): ...@@ -532,7 +556,7 @@ class RepeatOp(theano.Op):
repeats = node.inputs[1] repeats = node.inputs[1]
out_shape = list(i0_shapes) out_shape = list(i0_shapes)
#uint64 shape are not supported. # uint64 shape are not supported.
dtype = None dtype = None
if repeats.dtype in ['uint8', 'uint16', 'uint32']: if repeats.dtype in ['uint8', 'uint16', 'uint32']:
dtype = 'int64' dtype = 'int64'
...@@ -597,9 +621,9 @@ class Bartlett(gof.Op): ...@@ -597,9 +621,9 @@ class Bartlett(gof.Op):
if M.ndim != 0: if M.ndim != 0:
raise TypeError('%s only works on scalar input' raise TypeError('%s only works on scalar input'
% self.__class__.__name__) % self.__class__.__name__)
elif (not M.dtype.startswith('int')) and \ elif (not M.dtype.startswith('int') and
(not M.dtype.startswith('uint')): not M.dtype.startswith('uint')):
# dtype is a theano attribute here # dtype is a theano attribute here
raise TypeError('%s only works on integer input' raise TypeError('%s only works on integer input'
% self.__class__.__name__) % self.__class__.__name__)
return gof.Apply(self, [M], [tensor.dvector()]) return gof.Apply(self, [M], [tensor.dvector()])
...@@ -612,7 +636,8 @@ class Bartlett(gof.Op): ...@@ -612,7 +636,8 @@ class Bartlett(gof.Op):
def infer_shape(self, node, in_shapes): def infer_shape(self, node, in_shapes):
temp = node.inputs[0] temp = node.inputs[0]
M = tensor.switch(tensor.lt(temp, 0), M = tensor.switch(tensor.lt(temp, 0),
tensor.cast(0, temp.dtype), temp) tensor.cast(0, temp.dtype),
temp)
return [[M]] return [[M]]
def grad(self, inputs, output_grads): def grad(self, inputs, output_grads):
...@@ -620,7 +645,7 @@ class Bartlett(gof.Op): ...@@ -620,7 +645,7 @@ class Bartlett(gof.Op):
bartlett_ = Bartlett() bartlett_ = Bartlett()
#I create a function only to have the doc show well. # I create a function only to have the doc show well.
def bartlett(M): def bartlett(M):
"""An instance of this class returns the Bartlett spectral window in the """An instance of this class returns the Bartlett spectral window in the
time-domain. The Bartlett window is very similar to a triangular window, time-domain. The Bartlett window is very similar to a triangular window,
...@@ -668,7 +693,7 @@ class FillDiagonal(gof.Op): ...@@ -668,7 +693,7 @@ class FillDiagonal(gof.Op):
val = tensor.cast(val, dtype=scalar.upcast(a.dtype, val.dtype)) val = tensor.cast(val, dtype=scalar.upcast(a.dtype, val.dtype))
if val.dtype != a.dtype: if val.dtype != a.dtype:
raise TypeError('%s: type of second parameter must be the same as' raise TypeError('%s: type of second parameter must be the same as'
' the first\'s' % self.__class__.__name__) ' the first\'s' % self.__class__.__name__)
return gof.Apply(self, [a, val], [a.type()]) return gof.Apply(self, [a, val], [a.type()])
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
...@@ -698,7 +723,8 @@ class FillDiagonal(gof.Op): ...@@ -698,7 +723,8 @@ class FillDiagonal(gof.Op):
return [None, None] return [None, None]
elif a.ndim > 2: elif a.ndim > 2:
raise NotImplementedError('%s: gradient is currently implemented' raise NotImplementedError('%s: gradient is currently implemented'
' for matrices only' % self.__class__.__name__) ' for matrices only' %
self.__class__.__name__)
wr_a = fill_diagonal(grad, 0) # valid for any number of dimensions wr_a = fill_diagonal(grad, 0) # valid for any number of dimensions
# diag is only valid for matrices # diag is only valid for matrices
import theano.sandbox.linalg import theano.sandbox.linalg
...@@ -707,7 +733,7 @@ class FillDiagonal(gof.Op): ...@@ -707,7 +733,7 @@ class FillDiagonal(gof.Op):
fill_diagonal_ = FillDiagonal() fill_diagonal_ = FillDiagonal()
#I create a function only to have the doc show well. # I create a function only to have the doc show well.
def fill_diagonal(a, val): def fill_diagonal(a, val):
""" Returns a copy of an array with all """ Returns a copy of an array with all
elements of the main diagonal set to a specified scalar value. elements of the main diagonal set to a specified scalar value.
...@@ -730,7 +756,6 @@ def fill_diagonal(a, val): ...@@ -730,7 +756,6 @@ def fill_diagonal(a, val):
return fill_diagonal_(a, val) return fill_diagonal_(a, val)
class FillDiagonalOffset(gof.Op): class FillDiagonalOffset(gof.Op):
# See function fill_diagonal_offset for docstring # See function fill_diagonal_offset for docstring
def __eq__(self, other): def __eq__(self, other):
...@@ -753,10 +778,10 @@ class FillDiagonalOffset(gof.Op): ...@@ -753,10 +778,10 @@ class FillDiagonalOffset(gof.Op):
raise TypeError('%s: first parameter must have exactly' raise TypeError('%s: first parameter must have exactly'
' two dimensions' % self.__class__.__name__) ' two dimensions' % self.__class__.__name__)
elif val.ndim != 0: elif val.ndim != 0:
raise TypeError('%s: second parameter must be a scalar'\ raise TypeError('%s: second parameter must be a scalar'
% self.__class__.__name__) % self.__class__.__name__)
elif offset.ndim != 0: elif offset.ndim != 0:
raise TypeError('%s: third parameter must be a scalar'\ raise TypeError('%s: third parameter must be a scalar'
% self.__class__.__name__) % self.__class__.__name__)
val = tensor.cast(val, dtype=scalar.upcast(a.dtype, val.dtype)) val = tensor.cast(val, dtype=scalar.upcast(a.dtype, val.dtype))
if val.dtype != a.dtype: if val.dtype != a.dtype:
...@@ -764,11 +789,9 @@ class FillDiagonalOffset(gof.Op): ...@@ -764,11 +789,9 @@ class FillDiagonalOffset(gof.Op):
' as the first\'s' % self.__class__.__name__) ' as the first\'s' % self.__class__.__name__)
elif offset.dtype[:3] != 'int': elif offset.dtype[:3] != 'int':
raise TypeError('%s: type of third parameter must be as integer' raise TypeError('%s: type of third parameter must be as integer'
' use theano.tensor.cast( input, \'int32/int64\')' \ ' use theano.tensor.cast( input, \'int32/int64\')'
% self.__class__.__name__) % self.__class__.__name__)
return gof.Apply(self, [a, val, offset], [a.type()]) return gof.Apply(self, [a, val, offset], [a.type()])
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
...@@ -780,24 +803,23 @@ class FillDiagonalOffset(gof.Op): ...@@ -780,24 +803,23 @@ class FillDiagonalOffset(gof.Op):
""" """
Note: The fill_diagonal only support rectangular matrix. The output Note: The fill_diagonal only support rectangular matrix. The output
of tall matrix is "wrapped", which is an option in numpy 1.9.0 of tall matrix is "wrapped", which is an option in numpy 1.9.0
but was regarded as a bug in numpy 1.6.2. Here I implement the but was regarded as a bug in numpy 1.6.2. Here I implement the
fill_diagonal_offset with unwrapped output, so fill_diagonal_offset fill_diagonal_offset with unwrapped output, so fill_diagonal_offset
supports tall matrix.(This make a little difference between the output supports tall matrix.(This make a little difference between the output
of fill_diagonal and fill_diagonal_offset only in the case of tall of fill_diagonal and fill_diagonal_offset only in the case of tall
matrix) matrix)
""" """
if offset >= 0: if offset >= 0:
start = offset start = offset
num_of_step = min( min(width,height), width - offset) num_of_step = min(min(width, height), width - offset)
else: else:
start = - offset * a.shape[1] start = - offset * a.shape[1]
num_of_step = min( min(width,height), height + offset) num_of_step = min(min(width, height), height + offset)
step = a.shape[1] + 1 step = a.shape[1] + 1
end = start + step * num_of_step end = start + step * num_of_step
# Write the value out into the diagonal. # Write the value out into the diagonal.
a.flat[start:end:step] = val a.flat[start:end:step] = val
output_storage[0][0] = a output_storage[0][0] = a
def grad(self, inp, cost_grad): def grad(self, inp, cost_grad):
...@@ -812,19 +834,19 @@ class FillDiagonalOffset(gof.Op): ...@@ -812,19 +834,19 @@ class FillDiagonalOffset(gof.Op):
if (a.dtype.startswith('complex')): if (a.dtype.startswith('complex')):
return [None, None] return [None, None]
# only valid for matrices # only valid for matrices
wr_a = fill_diagonal_offset(grad, 0, offset) wr_a = fill_diagonal_offset(grad, 0, offset)
offset_abs = basic.abs_( offset ) offset_abs = basic.abs_(offset)
pos_offset_flag = basic.ge( offset, 0 ) pos_offset_flag = basic.ge(offset, 0)
neg_offset_flag = basic.lt( offset, 0 ) neg_offset_flag = basic.lt(offset, 0)
min_wh = basic.minimum(width,height) min_wh = basic.minimum(width, height)
start = offset * pos_offset_flag + offset_abs * width \ start = offset * pos_offset_flag + offset_abs * width \
* neg_offset_flag * neg_offset_flag
num_of_step = basic.minimum( min_wh, width * pos_offset_flag num_of_step = basic.minimum( min_wh, width * pos_offset_flag
+ height * neg_offset_flag - offset_abs ) + height * neg_offset_flag - offset_abs )
step = a.shape[1] + 1 step = a.shape[1] + 1
end = start + step * num_of_step end = start + step * num_of_step
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论