提交 aead18c5 authored 作者: Frederic Bastien's avatar Frederic Bastien

removed tensor.round but added tensor.round_half_{away_from_zero,to_even}.

Their is many different rounding algo. So we don't create a round fct, but one for a 2 differents version(c round fct() and numpy.round)
上级 f9b12d69
...@@ -1152,17 +1152,90 @@ class IRound(UnaryScalarOp): ...@@ -1152,17 +1152,90 @@ class IRound(UnaryScalarOp):
return "%(z)s = round(%(x)s);" % locals() return "%(z)s = round(%(x)s);" % locals()
iround = IRound(int_out_nocomplex) iround = IRound(int_out_nocomplex)
class Round(UnaryScalarOp): class RoundHalfToEven(UnaryScalarOp):
"""
This function implement the same rounding than numpy: Round half to even
c/c++ round fct IS DIFFERENT!
See http://en.wikipedia.org/wiki/Rounding for more detail
"""
def impl(self, x): def impl(self, x):
return theano._asarray(numpy.round(x), dtype = 'int64') return numpy.round(x)
def c_code___(self, node, name, (x, ), (z, ), sub):
typ = node.outputs[0].type.dtype
if not node.outputs[0].type.dtype in ['float32', 'float64']:
Exception("The output should be float32 or float64")
return """
#ifndef ROUNDING_EPSILON
#define ROUNDING_EPSILON 0.0000001
#endif
if (%(x)s < 0.0){
// We implement the else part like that: -else( -%(x)s);
%(typ)s i;
std::modf( -%(x)s, &i );
// If %(x)s is exactly halfway between two integers
if ((-%(x)s -(i +0.5)) < epsilon){
// If 'i' is even then return 'i'
if (std::fmod( i, 2.0 ) < epsilon){
%(z)s = - i;
}else{
// Else return the nearest even integer
%(z)s = - ceil( i +0.5 );
}
}else{
// round to closest
%(z)s = - round(%(x)s+5);
}
}else{
%(typ)s i;
std::modf( %(x)s, &i );
// If %(x)s is exactly halfway between two integers
if ((%(x)s -(i +0.5)) < epsilon){
// If 'i' is even then return 'i'
if (std::fmod( i, 2.0 ) < epsilon){
%(z)s = i;
}else{
// Else return the nearest even integer
%(z)s = ceil( i +0.5 );
}
}else{
// round to closest
%(z)s = round(%(x)s+5);
}
}
#undef ROUNDING_EPSILON
"""
round_half_to_even = RoundHalfToEven(same_out_float_only)
def round_half_away_from_zero_(a):
if a>0:
return numpy.floor(a + 0.5)
else:
return numpy.ceil(a - 0.5)
round_half_away_from_zero_vec = numpy.vectorize(round_half_away_from_zero_)
class RoundHalfAwayFromZero(UnaryScalarOp):
"""
Implement the same rounding algo as c round() fct.
numpy.round fct IS DIFFERENT!
See http://en.wikipedia.org/wiki/Rounding for more detail
"""
def impl(self, x):
return vec(x)
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.outputs[0].type.dtype == 'float32': if node.outputs[0].type.dtype in ['float32', 'float64']:
return "%(z)s = fround(%(x)s);" % locals()
elif node.outputs[0].type.dtype == 'float64':
return "%(z)s = round(%(x)s);" % locals() return "%(z)s = round(%(x)s);" % locals()
else: else:
Exception("The output should be float32 or float64") Exception("The output should be float32 or float64")
round = Round(same_out_float_only) round_half_away_from_zero = RoundHalfAwayFromZero(same_out_float_only)
class Neg(UnaryScalarOp): class Neg(UnaryScalarOp):
def impl(self, x): def impl(self, x):
......
...@@ -1579,8 +1579,12 @@ def iround(a): ...@@ -1579,8 +1579,12 @@ def iround(a):
"""int(round(a))""" """int(round(a))"""
@_scal_elemwise @_scal_elemwise
def round(a): def round_half_to_even(a):
"""round(a)""" """round_half_to_even(a)"""
@_scal_elemwise
def round_half_away_from_zero(a):
"""round_half_away_from_zero(a)"""
@_scal_elemwise @_scal_elemwise
def sqr(a): def sqr(a):
......
...@@ -129,8 +129,12 @@ def iround_inplace(a): ...@@ -129,8 +129,12 @@ def iround_inplace(a):
"""int(round(a)) (inplace on `a`)""" """int(round(a)) (inplace on `a`)"""
@_scal_inplace @_scal_inplace
def round_inplace(a): def round_half_to_even_inplace(a):
"""round(a) (inplace on `a`)""" """round_half_to_even_inplace(a) (inplace on `a`)"""
@_scal_inplace
def round_half_away_from_zero_inplace(a):
"""round_half_away_from_zero_inplace(a) (inplace on `a`)"""
@_scal_inplace @_scal_inplace
def sqr_inplace(a): def sqr_inplace(a):
......
...@@ -383,14 +383,19 @@ PowInplaceTester = makeBroadcastTester(op = inplace.pow_inplace, ...@@ -383,14 +383,19 @@ PowInplaceTester = makeBroadcastTester(op = inplace.pow_inplace,
column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1)))), column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1)))),
inplace = True) inplace = True)
#Those are corner case when rounding. Their is many rounding algo.
#c round() fct and numpy round are not the same!
_good_broadcast_unary_normal_float = dict(normal = (rand_ranged(-5, 5, (2, 3)),)) corner_case = numpy.asarray([-2.5, -2., -1.5, -1., -0.5, -.51, -.49, 0., 0.49, 0.5, 0.9, 1, 1.5, 2, 2.5])
_good_broadcast_unary_normal_float = dict(normal = (rand_ranged(-5, 5, (2, 3)),),
corner_case = (corner_case,))
_good_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),), _good_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),),
integers = (randint_ranged(-5, 5, (2, 3)),)) integers = (randint_ranged(-5, 5, (2, 3)),),
corner_case = (corner_case,))
_grad_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),),
corner_case = (corner_case,))
_grad_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),))
AbsTester = makeBroadcastTester(op = tensor.abs_, AbsTester = makeBroadcastTester(op = tensor.abs_,
...@@ -448,14 +453,21 @@ IRoundInplaceTester = makeBroadcastTester(op = inplace.iround_inplace, ...@@ -448,14 +453,21 @@ IRoundInplaceTester = makeBroadcastTester(op = inplace.iround_inplace,
good = _good_broadcast_unary_normal, good = _good_broadcast_unary_normal,
inplace = True) inplace = True)
RoundTester = makeBroadcastTester(op = round, RoundHalfToEvenTester = makeBroadcastTester(op = round_half_to_even,
expected = numpy.round, expected = numpy.round,
good = _good_broadcast_unary_normal_float) good = _good_broadcast_unary_normal_float)
RoundInplaceTester = makeBroadcastTester(op = inplace.round_inplace, RoundHalfToEvenInplaceTester = makeBroadcastTester(op = inplace.round_half_to_even_inplace,
expected = numpy.round, expected = numpy.round,
good = _good_broadcast_unary_normal_float, good = _good_broadcast_unary_normal_float,
inplace = True) inplace = True)
RoundHalfAwayFromZeroTester = makeBroadcastTester(op = round_half_away_from_zero,
expected = theano.scalar.basic.round_half_away_from_zero_vec,
good = _good_broadcast_unary_normal_float)
RoundHalfAwayFromZeroInplaceTester = makeBroadcastTester(op = inplace.round_half_away_from_zero_inplace,
expected = theano.scalar.basic.round_half_away_from_zero_vec,
good = _good_broadcast_unary_normal_float,
inplace = True)
SqrTester = makeBroadcastTester(op = sqr, SqrTester = makeBroadcastTester(op = sqr,
expected = numpy.square, expected = numpy.square,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论