提交 f0d1e63e authored 作者: Frédéric Bastien's avatar Frédéric Bastien

Merge pull request #2987 from mbeissinger/master

Added tensor implementation of numpy's allclose function.
......@@ -1197,6 +1197,35 @@ The six usual equality and inequality operators share the same interface.
This is equivalent to ``numpy.isinf``.
.. function:: isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
Returns a symbolic ``'int8'`` tensor representing where two tensors are equal
within a tolerance.
The tolerance values are positive, typically very small numbers.
The relative difference `(rtol * abs(b))` and the absolute difference `atol` are
added together to compare against the absolute difference between `a` and `b`.
For finite values, isclose uses the following equation to test whether two
floating point values are equivalent:
``|a - b| <= (atol + rtol * |b|)``
For infinite values, isclose checks if both values are the same signed inf value.
If equal_nan is True, isclose considers NaN values in the same position to be close.
Otherwise, NaN values are not considered close.
This is equivalent to ``numpy.isclose``.
.. function:: allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
Returns a symbolic ``'int8'`` value representing if all elements in two tensors are equal
within a tolerance.
See notes in `isclose` for determining values equal within a tolerance.
This is equivalent to ``numpy.allclose``.
Condition
---------
......
......@@ -1669,6 +1669,138 @@ def isinf(a):
"""isinf(a)"""
def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
"""
Implements Numpy's ``allclose`` on tensors.
``absolute(a - b) <= (atol + rtol * absolute(b))``
:note: Not a symmetric equation. See Numpy's documentation.
:param a: input to compare
:type a: tensor
:param b: input to compare
:type b: tensor
:param rtol: the relative tolerance parameter
:type rtol: float
:param atol: the absolute tolerance parameter
:type atol: float
:param equal_nan: whether to consider nan's in the same place to be close
:type equal_nan: bool
:returns: a boolean value (of type int8 returned by the tensor
elementwise `all` function) whether all elements in a and b are in
the tolerance range defined above.
:rtype: int8
"""
return all(isclose(a, b, rtol, atol, equal_nan))
def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
"""
Implements Numpy's ``isclose`` on tensors.
The tolerance values are positive, typically very small numbers. The
relative difference (`rtol` * abs(`b`)) and the absolute difference
`atol` are added together to compare against the absolute difference
between `a` and `b`.
``absolute(a - b) <= (atol + rtol * absolute(b))``
:note: Not a symmetric equation. See Numpy's documentation.
:param a: input to compare
:type a: tensor
:param b: input to compare
:type b: tensor
:param rtol: the relative tolerance parameter
:type rtol: float
:param atol: the absolute tolerance parameter
:type atol: float
:param equal_nan: whether to consider nan's in the same place to be close
:type equal_nan: bool
:returns: returns a boolean (int8) array where two arrays are element-wise
equal within a tolerance.
:rtype: int8
>>> import theano
>>> import numpy as np
>>> a = theano._asarray([1e10, 1e-7], dtype="float64")
>>> b = theano._asarray([1.00001e10, 1e-8], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([1, 0], dtype=int8)
>>> a = theano._asarray([1e10, 1e-8], dtype="float64")
>>> b = theano._asarray([1.00001e10, 1e-9], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([1, 1], dtype=int8)
>>> a = theano._asarray([1e10, 1e-8], dtype="float64")
>>> b = theano._asarray([1.0001e10, 1e-9], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([0, 1], dtype=int8)
>>> a = theano._asarray([1.0, np.nan], dtype="float64")
>>> b = theano._asarray([1.0, np.nan], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([1, 0], dtype==int8)
>>> a = theano._asarray([1.0, np.nan], dtype="float64")
>>> b = theano._asarray([1.0, np.nan], dtype="float64")
>>> theano.tensor.isclose(a, b, equal_nan=True).eval()
array([1, 1], dtype==int8)
>>> a = theano._asarray([1.0, np.inf], dtype="float64")
>>> b = theano._asarray([1.0, -np.inf], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([1, 0], dtype==int8)
>>> a = theano._asarray([1.0, np.inf], dtype="float64")
>>> b = theano._asarray([1.0, np.inf], dtype="float64")
>>> theano.tensor.isclose(a, b).eval()
array([1, 1], dtype==int8)
"""
# close will be an int8 array of 1 where within tolerance
# and 0 where not within tolerance or there was a nan or inf value.
diff = abs(a - b)
tolerance = atol + rtol * abs(b)
close_prelim = le(diff, tolerance)
a_nan = isnan(a)
b_nan = isnan(b)
nans = bitwise_or(a_nan, b_nan)
a_inf = isinf(a)
b_inf = isinf(b)
infs = bitwise_or(a_inf, b_inf)
nans_or_infs = bitwise_or(nans, infs)
# close is now an array of 0's except where elements are not nan or inf
# and are withing the tolerance.
close = bitwise_and(close_prelim, bitwise_not(nans_or_infs))
# deal with signed inf values. this will make an array inf_eq of 0's
# except where inf values have the same sign.
both_infs = bitwise_and(a_inf, b_inf)
inf_signs_eq = eq(a_inf * sgn(a), b_inf * sgn(b))
inf_eq = bitwise_and(both_infs, inf_signs_eq)
# now create the potential result combining close and inf_eq
close_with_infs = bitwise_or(close, inf_eq)
# deal with comparing nan's.
if equal_nan:
both_nans = bitwise_and(a_nan, b_nan)
return bitwise_or(close_with_infs, both_nans)
# otherwise nan's aren't considered close.
else:
return close_with_infs
##########################
# Condition
##########################
......
......@@ -47,7 +47,8 @@ from theano.tensor import (_shared, wvector, bvector, autocast_float_as,
itensor3, Tile, switch, Diagonal, Diag,
nonzero, flatnonzero, nonzero_values,
stacklists, DimShuffle, hessian, ptp, power,
swapaxes, choose, Choose, NoneConst, AllocEmpty
swapaxes, choose, Choose, NoneConst, AllocEmpty,
isclose, allclose,
)
from theano.tests import unittest_tools as utt
......@@ -4119,6 +4120,75 @@ class test_comparison(unittest.TestCase):
except TypeError:
assert err
def test_isclose(self):
for dtype in ['float64', 'float32', 'complex64', 'complex128']:
l = numpy.asarray(
[0., 1., -1., 0.,
numpy.nan, numpy.inf, -numpy.inf, numpy.inf],
dtype=dtype)
r = numpy.asarray(
[0., 1.0001, -1.000000000001, numpy.nan,
numpy.nan, numpy.inf, numpy.inf, 0.],
dtype=dtype)
for x, y, err in [
(shared(l.astype(dtype)), shared(r.astype(dtype)), False),
(l, shared(r.astype(dtype)), True),
(constant(l), shared(r.astype(dtype)), False),
(shared(l.astype(dtype)), r, False),
(shared(l.astype(dtype)), constant(r), False),
]:
try:
fn1 = inplace_func([], isclose(x, y, equal_nan=False))
fn2 = inplace_func([], isclose(x, y, equal_nan=True))
v1 = fn1()
v2 = fn2()
self.assertTrue(
numpy.all(
v1 == numpy.asarray(
[True, False, True, False,
False, True, False, False],
dtype="bool"
)
),
numpy.all(
v2 == numpy.asarray(
[True, False, True, False,
True, True, False, False],
dtype="bool"
)
)
)
except TypeError:
if not dtype.startswith('complex'):
assert err
def test_allclose(self):
# equal_nan argument not in current version of numpy allclose,
# force it to False.
for dtype in ['float64', 'float32', 'complex64', 'complex128']:
l = numpy.asarray(
[0., 1., -1., 0.,
numpy.nan, numpy.inf, -numpy.inf, numpy.inf],
dtype=dtype)
r = numpy.asarray(
[0., 1.0001, -1.000000000001, numpy.nan,
numpy.nan, numpy.inf, numpy.inf, 0.],
dtype=dtype)
for x, y, err in [
(shared(l.astype(dtype)), shared(r.astype(dtype)), False),
(l, shared(r.astype(dtype)), True),
(constant(l), shared(r.astype(dtype)), False),
(shared(l.astype(dtype)), r, False),
(shared(l.astype(dtype)), constant(r), False),
]:
try:
fn = inplace_func([], allclose(x, y, equal_nan=False))
v = fn()
self.assertTrue(numpy.all(v == numpy.allclose(l, r)))
except TypeError:
if not dtype.startswith('complex'):
assert err
class test_bitwise(unittest.TestCase):
dtype = ['int8', 'int16', 'int32', 'int64', ]
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论