[pypy-commit] pypy default: clean up ufuncs, fix some behaviors

bdkearns noreply at buildbot.pypy.org
Thu Oct 17 09:13:57 CEST 2013


Author: Brian Kearns <bdkearns at gmail.com>
Branch: 
Changeset: r67454:022ea29b5d2e
Date: 2013-10-17 03:10 -0400
http://bitbucket.org/pypy/pypy/changeset/022ea29b5d2e/

Log:	clean up ufuncs, fix some behaviors

diff --git a/lib_pypy/numpypy/lib/__init__.py b/lib_pypy/numpypy/lib/__init__.py
--- a/lib_pypy/numpypy/lib/__init__.py
+++ b/lib_pypy/numpypy/lib/__init__.py
@@ -5,10 +5,12 @@
 from .function_base import *
 from .shape_base import *
 from .twodim_base import *
+from .ufunclike import *
 from .utils import *
 
 __all__ = ['math']
 __all__ += function_base.__all__
 __all__ += shape_base.__all__
 __all__ += twodim_base.__all__
+__all__ += ufunclike.__all__
 __all__ += utils.__all__
diff --git a/lib_pypy/numpypy/lib/ufunclike.py b/lib_pypy/numpypy/lib/ufunclike.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/numpypy/lib/ufunclike.py
@@ -0,0 +1,177 @@
+"""
+Module of functions that are like ufuncs in acting on arrays and optionally
+storing results in an output array.
+
+"""
+from __future__ import division, absolute_import, print_function
+
+__all__ = ['fix', 'isneginf', 'isposinf']
+
+from ..core import numeric as nx
+
+def fix(x, y=None):
+    """
+    Round to nearest integer towards zero.
+
+    Round an array of floats element-wise to nearest integer towards zero.
+    The rounded values are returned as floats.
+
+    Parameters
+    ----------
+    x : array_like
+        An array of floats to be rounded
+    y : ndarray, optional
+        Output array
+
+    Returns
+    -------
+    out : ndarray of floats
+        The array of rounded numbers
+
+    See Also
+    --------
+    trunc, floor, ceil
+    around : Round to given number of decimals
+
+    Examples
+    --------
+    >>> np.fix(3.14)
+    3.0
+    >>> np.fix(3)
+    3.0
+    >>> np.fix([2.1, 2.9, -2.1, -2.9])
+    array([ 2.,  2., -2., -2.])
+
+    """
+    x = nx.asanyarray(x)
+    y1 = nx.floor(x)
+    y2 = nx.ceil(x)
+    if y is None:
+        y = nx.asanyarray(y1)
+    y[...] = nx.where(x >= 0, y1, y2)
+    return y
+
+def isposinf(x, y=None):
+    """
+    Test element-wise for positive infinity, return result as bool array.
+
+    Parameters
+    ----------
+    x : array_like
+        The input array.
+    y : array_like, optional
+        A boolean array with the same shape as `x` to store the result.
+
+    Returns
+    -------
+    y : ndarray
+        A boolean array with the same dimensions as the input.
+        If second argument is not supplied then a boolean array is returned
+        with values True where the corresponding element of the input is
+        positive infinity and values False where the element of the input is
+        not positive infinity.
+
+        If a second argument is supplied the result is stored there. If the
+        type of that array is a numeric type the result is represented as zeros
+        and ones, if the type is boolean then as False and True.
+        The return value `y` is then a reference to that array.
+
+    See Also
+    --------
+    isinf, isneginf, isfinite, isnan
+
+    Notes
+    -----
+    Numpy uses the IEEE Standard for Binary Floating-Point for Arithmetic
+    (IEEE 754).
+
+    Errors result if the second argument is also supplied when `x` is a
+    scalar input, or if first and second arguments have different shapes.
+
+    Examples
+    --------
+    >>> np.isposinf(np.PINF)
+    array(True, dtype=bool)
+    >>> np.isposinf(np.inf)
+    array(True, dtype=bool)
+    >>> np.isposinf(np.NINF)
+    array(False, dtype=bool)
+    >>> np.isposinf([-np.inf, 0., np.inf])
+    array([False, False,  True], dtype=bool)
+
+    >>> x = np.array([-np.inf, 0., np.inf])
+    >>> y = np.array([2, 2, 2])
+    >>> np.isposinf(x, y)
+    array([0, 0, 1])
+    >>> y
+    array([0, 0, 1])
+
+    """
+    if y is None:
+        x = nx.asarray(x)
+        y = nx.empty(x.shape, dtype=nx.bool_)
+    nx.logical_and(nx.isinf(x), ~nx.signbit(x), y)
+    return y
+
+def isneginf(x, y=None):
+    """
+    Test element-wise for negative infinity, return result as bool array.
+
+    Parameters
+    ----------
+    x : array_like
+        The input array.
+    y : array_like, optional
+        A boolean array with the same shape and type as `x` to store the
+        result.
+
+    Returns
+    -------
+    y : ndarray
+        A boolean array with the same dimensions as the input.
+        If second argument is not supplied then a numpy boolean array is
+        returned with values True where the corresponding element of the
+        input is negative infinity and values False where the element of
+        the input is not negative infinity.
+
+        If a second argument is supplied the result is stored there. If the
+        type of that array is a numeric type the result is represented as
+        zeros and ones, if the type is boolean then as False and True. The
+        return value `y` is then a reference to that array.
+
+    See Also
+    --------
+    isinf, isposinf, isnan, isfinite
+
+    Notes
+    -----
+    Numpy uses the IEEE Standard for Binary Floating-Point for Arithmetic
+    (IEEE 754).
+
+    Errors result if the second argument is also supplied when x is a scalar
+    input, or if first and second arguments have different shapes.
+
+    Examples
+    --------
+    >>> np.isneginf(np.NINF)
+    array(True, dtype=bool)
+    >>> np.isneginf(np.inf)
+    array(False, dtype=bool)
+    >>> np.isneginf(np.PINF)
+    array(False, dtype=bool)
+    >>> np.isneginf([-np.inf, 0., np.inf])
+    array([ True, False, False], dtype=bool)
+
+    >>> x = np.array([-np.inf, 0., np.inf])
+    >>> y = np.array([2, 2, 2])
+    >>> np.isneginf(x, y)
+    array([1, 0, 0])
+    >>> y
+    array([1, 0, 0])
+
+    """
+    if y is None:
+        x = nx.asarray(x)
+        y = nx.empty(x.shape, dtype=nx.bool_)
+    nx.logical_and(nx.isinf(x), nx.signbit(x), y)
+    return y
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -63,6 +63,7 @@
         ("less_equal", "less_equal"),
         ("maximum", "maximum"),
         ("minimum", "minimum"),
+        ("mod", "mod"),
         ("multiply", "multiply"),
         ("negative", "negative"),
         ("not_equal", "not_equal"),
@@ -90,8 +91,6 @@
         ('invert', 'invert'),
         ('isnan', 'isnan'),
         ('isinf', 'isinf'),
-        ('isneginf', 'isneginf'),
-        ('isposinf', 'isposinf'),
         ('isfinite', 'isfinite'),
         ('logical_and', 'logical_and'),
         ('logical_xor', 'logical_xor'),
@@ -105,6 +104,7 @@
         ('floor_divide', 'floor_divide'),
         ('logaddexp', 'logaddexp'),
         ('logaddexp2', 'logaddexp2'),
+        ('ldexp', 'ldexp'),
         ('real', 'real'),
         ('imag', 'imag'),
         ('ones_like', 'ones_like'),
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -16,23 +16,24 @@
 def done_if_false(dtype, val):
     return not dtype.itemtype.bool(val)
 
+
 class W_Ufunc(W_Root):
     _attrs_ = ["name", "promote_to_float", "promote_bools", "identity",
-               "allow_complex", "complex_to_float"]
+               "allow_bool", "allow_complex", "complex_to_float"]
     _immutable_fields_ = ["promote_to_float", "promote_bools", "name",
-            "allow_complex", "complex_to_float"]
+            "allow_bool", "allow_complex", "complex_to_float"]
 
     def __init__(self, name, promote_to_float, promote_bools, identity,
-                 int_only, allow_complex, complex_to_float):
+                 int_only, allow_bool, allow_complex, complex_to_float):
         self.name = name
         self.promote_to_float = promote_to_float
         self.promote_bools = promote_bools
+        self.identity = identity
+        self.int_only = int_only
+        self.allow_bool = allow_bool
         self.allow_complex = allow_complex
         self.complex_to_float = complex_to_float
 
-        self.identity = identity
-        self.int_only = int_only
-
     def descr_repr(self, space):
         return space.wrap("<ufunc '%s'>" % self.name)
 
@@ -259,10 +260,10 @@
 
     def __init__(self, func, name, promote_to_float=False, promote_bools=False,
         identity=None, bool_result=False, int_only=False,
-        allow_complex=True, complex_to_float=False):
+        allow_bool=True, allow_complex=True, complex_to_float=False):
 
         W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity,
-                         int_only, allow_complex, complex_to_float)
+                         int_only, allow_bool, allow_complex, complex_to_float)
         self.func = func
         self.bool_result = bool_result
 
@@ -274,17 +275,19 @@
             if space.is_w(out, space.w_None):
                 out = None
         w_obj = convert_to_array(space, w_obj)
-        if w_obj.get_dtype().is_flexible_type():
+        dtype = w_obj.get_dtype()
+        if dtype.is_flexible_type():
             raise OperationError(space.w_TypeError,
                       space.wrap('Not implemented for this type'))
-        if self.int_only and not w_obj.get_dtype().is_int_type():
+        if (self.int_only and not dtype.is_int_type() or
+                not self.allow_bool and dtype.is_bool_type() or
+                not self.allow_complex and dtype.is_complex_type()):
             raise OperationError(space.w_TypeError, space.wrap(
                 "ufunc %s not supported for the input type" % self.name))
         calc_dtype = find_unaryop_result_dtype(space,
                                   w_obj.get_dtype(),
                                   promote_to_float=self.promote_to_float,
-                                  promote_bools=self.promote_bools,
-                                  allow_complex=self.allow_complex)
+                                  promote_bools=self.promote_bools)
         if out is not None:
             if not isinstance(out, W_NDimArray):
                 raise OperationError(space.w_TypeError, space.wrap(
@@ -324,10 +327,10 @@
 
     def __init__(self, func, name, promote_to_float=False, promote_bools=False,
         identity=None, comparison_func=False, int_only=False,
-        allow_complex=True, complex_to_float=False):
+        allow_bool=True, allow_complex=True, complex_to_float=False):
 
         W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity,
-                         int_only, allow_complex, complex_to_float)
+                         int_only, allow_bool, allow_complex, complex_to_float)
         self.func = func
         self.comparison_func = comparison_func
         if name == 'logical_and':
@@ -375,16 +378,14 @@
                 w_rdtype = w_ldtype
             elif w_lhs.is_scalar() and not w_rhs.is_scalar():
                 w_ldtype = w_rdtype
+        if (self.int_only and (not w_ldtype.is_int_type() or not w_rdtype.is_int_type()) or
+                not self.allow_bool and (w_ldtype.is_bool_type() or w_rdtype.is_bool_type()) or
+                not self.allow_complex and (w_ldtype.is_complex_type() or w_rdtype.is_complex_type())):
+            raise OperationError(space.w_TypeError, space.wrap("Unsupported types"))
         calc_dtype = find_binop_result_dtype(space,
             w_ldtype, w_rdtype,
-            int_only=self.int_only,
             promote_to_float=self.promote_to_float,
-            promote_bools=self.promote_bools,
-            allow_complex=self.allow_complex,
-            )
-        if self.int_only and not calc_dtype.is_int_type():
-            raise OperationError(space.w_TypeError, space.wrap(
-                "ufunc '%s' not supported for the input types" % self.name))
+            promote_bools=self.promote_bools)
         if space.is_none(w_out):
             out = None
         elif not isinstance(w_out, W_NDimArray):
@@ -431,14 +432,10 @@
 
 
 def find_binop_result_dtype(space, dt1, dt2, promote_to_float=False,
-    promote_bools=False, int_only=False, allow_complex=True):
+        promote_bools=False):
     # dt1.num should be <= dt2.num
     if dt1.num > dt2.num:
         dt1, dt2 = dt2, dt1
-    if int_only and (not dt1.is_int_type() or not dt2.is_int_type()):
-        raise OperationError(space.w_TypeError, space.wrap("Unsupported types"))
-    if not allow_complex and (dt1.is_complex_type() or dt2.is_complex_type()):
-        raise OperationError(space.w_TypeError, space.wrap("Unsupported types"))
     # Some operations promote op(bool, bool) to return int8, rather than bool
     if promote_bools and (dt1.kind == dt2.kind == interp_dtype.BOOLLTR):
         return interp_dtype.get_dtype_cache(space).w_int8dtype
@@ -507,14 +504,11 @@
         dtypenum += 2
         return interp_dtype.get_dtype_cache(space).dtypes_by_num[dtypenum]
 
-
 @jit.unroll_safe
 def find_unaryop_result_dtype(space, dt, promote_to_float=False,
-    promote_bools=False, promote_to_largest=False, allow_complex=True):
+        promote_bools=False, promote_to_largest=False):
     if promote_bools and (dt.kind == interp_dtype.BOOLLTR):
         return interp_dtype.get_dtype_cache(space).w_int8dtype
-    if not allow_complex and (dt.is_complex_type()):
-        raise OperationError(space.w_TypeError, space.wrap("Unsupported types"))
     if promote_to_float:
         if dt.kind == interp_dtype.FLOATINGLTR or dt.kind==interp_dtype.COMPLEXLTR:
             return dt
@@ -535,7 +529,6 @@
             assert False
     return dt
 
-
 def find_dtype_for_scalar(space, w_obj, current_guess=None):
     bool_dtype = interp_dtype.get_dtype_cache(space).w_booldtype
     long_dtype = interp_dtype.get_dtype_cache(space).w_longdtype
@@ -588,7 +581,6 @@
         'unable to create dtype from objects, ' '"%T" instance not supported',
         w_obj)
 
-
 def ufunc_dtype_caller(space, ufunc_name, op_name, argcount, comparison_func,
                        bool_result):
     dtype_cache = interp_dtype.get_dtype_cache(space)
@@ -606,6 +598,7 @@
             return res
     return func_with_new_name(impl, ufunc_name)
 
+
 class UfuncState(object):
     def __init__(self, space):
         "NOT_RPYTHON"
@@ -635,10 +628,6 @@
             ("greater_equal", "ge", 2, {"comparison_func": True}),
             ("isnan", "isnan", 1, {"bool_result": True}),
             ("isinf", "isinf", 1, {"bool_result": True}),
-            ("isneginf", "isneginf", 1, {"bool_result": True,
-                                         "allow_complex": False}),
-            ("isposinf", "isposinf", 1, {"bool_result": True,
-                                         "allow_complex": False}),
             ("isfinite", "isfinite", 1, {"bool_result": True}),
 
             ('logical_and', 'logical_and', 2, {'comparison_func': True,
@@ -658,7 +647,7 @@
             ("negative", "neg", 1),
             ("absolute", "abs", 1, {"complex_to_float": True}),
             ("rint", "rint", 1),
-            ("sign", "sign", 1, {"promote_bools": True}),
+            ("sign", "sign", 1, {"allow_bool": False}),
             ("signbit", "signbit", 1, {"bool_result": True,
                                        "allow_complex": False}),
             ("reciprocal", "reciprocal", 1),
@@ -713,6 +702,8 @@
                                        "allow_complex": False}),
             ("logaddexp2", "logaddexp2", 2, {"promote_to_float": True,
                                        "allow_complex": False}),
+            ("ldexp", "ldexp", 2, {"int_only": True}),
+
             ("ones_like", "ones_like", 1),
             ("zeros_like", "zeros_like", 1),
         ]:
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -91,14 +91,14 @@
                         uncallable.add(s)
             return uncallable
         assert find_uncallable_ufuncs('int') == set()
-        assert find_uncallable_ufuncs('bool') == set()
+        assert find_uncallable_ufuncs('bool') == set(['sign'])
         assert find_uncallable_ufuncs('float') == set(
                 ['bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor',
-                 'left_shift', 'right_shift', 'invert'])
+                 'left_shift', 'right_shift', 'invert', 'ldexp'])
         assert find_uncallable_ufuncs('complex') == set(
                 ['bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor',
                  'arctan2', 'deg2rad', 'degrees', 'rad2deg', 'radians',
-                 'fabs', 'fmod', 'invert', 'isneginf', 'isposinf',
+                 'fabs', 'fmod', 'invert', 'ldexp', 'mod',
                  'logaddexp', 'logaddexp2', 'left_shift', 'right_shift',
                  'copysign', 'signbit', 'ceil', 'floor', 'trunc'])
 
@@ -174,7 +174,6 @@
         assert fabs(float('-inf')) == float('inf')
         assert isnan(fabs(float('nan')))
 
-
     def test_fmax(self):
         from numpypy import fmax, array
         import math
@@ -194,7 +193,6 @@
         # on Microsoft win32
         assert math.copysign(1., fmax(nnan, nan)) == math.copysign(1., nnan)
 
-
     def test_fmin(self):
         from numpypy import fmin, array
         import math
@@ -213,7 +211,6 @@
         # on Microsoft win32
         assert math.copysign(1., fmin(nnan, nan)) == math.copysign(1., nnan)
 
-
     def test_fmod(self):
         from numpypy import fmod
         import math
@@ -368,7 +365,6 @@
         c = array([10.5+11.5j, -15.2-100.3456j, 0.2343+11.123456j])
         assert (c.round(0) == [10.+12.j, -15-100j, 0+11j]).all()
 
-
     def test_copysign(self):
         from numpypy import array, copysign
 
@@ -436,7 +432,6 @@
 
         assert expm1(1e-50) == 1e-50
 
-
     def test_sin(self):
         import math
         from numpypy import array, sin
@@ -704,6 +699,8 @@
         assert (~a == [-2, -3, -4, -5]).all()
         assert (bitwise_not(a) == ~a).all()
         assert (invert(a) == ~a).all()
+        assert invert(True) == False
+        assert invert(False) == True
 
     def test_shift(self):
         from numpypy import left_shift, right_shift, bool
@@ -964,6 +961,11 @@
         assert logaddexp2(float('inf'), float('-inf')) == float('inf')
         assert logaddexp2(float('inf'), float('inf')) == float('inf')
 
+    def test_ldexp(self):
+        import numpypy as np
+        a = np.ldexp(2, 3)
+        assert type(a) is np.float64 and a == 16.0
+
     def test_ones_like(self):
         from numpypy import array, ones_like
 
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -248,14 +248,6 @@
     def isinf(self, v):
         return False
 
-    @raw_unary_op
-    def isneginf(self, v):
-        return False
-
-    @raw_unary_op
-    def isposinf(self, v):
-        return False
-
     @raw_binary_op
     def eq(self, v1, v2):
         return v1 == v2
@@ -320,6 +312,10 @@
         float64 = Float64()
         return float64.rint(float64.box(v))
 
+    @raw_binary_op
+    def ldexp(self, v1, v2):
+        return Float64().box(v1 * 2**v2)
+
 class NonNativePrimitive(Primitive):
     _mixin_ = True
 
@@ -401,7 +397,7 @@
 
     @simple_unary_op
     def invert(self, v):
-        return ~v
+        return not v
 
     @raw_unary_op
     def isfinite(self, v):
@@ -497,14 +493,6 @@
     def isinf(self, v):
         return False
 
-    @raw_unary_op
-    def isposinf(self, v):
-        return False
-
-    @raw_unary_op
-    def isneginf(self, v):
-        return False
-
     @simple_binary_op
     def bitwise_and(self, v1, v2):
         return v1 & v2
@@ -947,14 +935,6 @@
         return rfloat.isinf(v)
 
     @raw_unary_op
-    def isneginf(self, v):
-        return rfloat.isinf(v) and v < 0
-
-    @raw_unary_op
-    def isposinf(self, v):
-        return rfloat.isinf(v) and v > 0
-
-    @raw_unary_op
     def isfinite(self, v):
         return not (rfloat.isinf(v) or rfloat.isnan(v))
 


More information about the pypy-commit mailing list