Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r80605:04570c9524ed
Date: 2015-11-09 08:53 +0100
http://bitbucket.org/pypy/pypy/changeset/04570c9524ed/

Log:    merge heads

diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -18,3 +18,14 @@
 .. branch: int_0/i-need-this-library-to-build-on-ubuntu-1-1446717626227
 
 Document that libgdbm-dev is required for translation/packaging
+
+.. branch: propogate-nans
+
+Ensure that ndarray conversion from int16->float16->float32->float16->int16
+preserves all int16 values, even across nan conversions. Also fix argmax, 
argmin
+for nan comparisons
+
+.. branch: array_interface
+
+Support common use-cases for __array_interface__, passes upstream tests
+
diff --git a/pypy/module/micronumpy/compile.py 
b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -371,6 +371,8 @@
     @specialize.arg(2)
     def call_method(self, w_obj, s, *args):
         # XXX even the hacks have hacks
+        if s == 'size': # used in _array() but never called by tests
+            return IntObject(0)
         return getattr(w_obj, 'descr_' + s)(self, *args)
 
     @specialize.arg(1)
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -2,6 +2,7 @@
 from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
 from rpython.rlib.buffer import SubBuffer
 from rpython.rlib.rstring import strip_spaces
+from rpython.rlib.rawstorage import RAW_STORAGE_PTR
 from rpython.rtyper.lltypesystem import lltype, rffi
 
 from pypy.module.micronumpy import descriptor, loop, support
@@ -45,7 +46,7 @@
     try:
         w_interface = space.getattr(w_object, 
space.wrap("__array_interface__"))
         if w_interface is None:
-            return None
+            return None, False
         version_w = space.finditem(w_interface, space.wrap("version"))
         if version_w is None:
             raise oefmt(space.w_ValueError, "__array_interface__ found without"
@@ -67,19 +68,46 @@
             raise oefmt(space.w_ValueError,
                     "__array_interface__ missing one or more required keys: 
shape, typestr"
                     )
-        raise oefmt(space.w_NotImplementedError,
-                    "creating array from __array_interface__ not supported 
yet")
-        '''
-        data_w = space.listview()
+        if w_descr is not None:
+            raise oefmt(space.w_NotImplementedError,
+                    "__array_interface__ descr not supported yet")
+        if w_strides is None or space.is_w(w_strides, space.w_None):
+            strides = None
+        else:
+            strides = [space.int_w(i) for i in space.listview(w_strides)]
         shape = [space.int_w(i) for i in space.listview(w_shape)]
         dtype = descriptor.decode_w_dtype(space, w_dtype)
-        rw = space.is_true(data_w[1])
-        '''
-        #print 'create view from 
shape',shape,'dtype',dtype,'descr',w_descr,'data',data_w[0],'rw',rw
-        return None
+        if dtype is None:
+            raise oefmt(space.w_ValueError,
+                    "__array_interface__ could not decode dtype %R", w_dtype
+                    )
+        if w_data is not None and (space.isinstance_w(w_data, space.w_tuple) 
or space.isinstance_w(w_data, space.w_list)):
+            data_w = space.listview(w_data)
+            data = rffi.cast(RAW_STORAGE_PTR, space.int_w(data_w[0]))
+            read_only = True # XXX why not space.is_true(data_w[1])
+            offset = 0
+            return W_NDimArray.from_shape_and_storage(space, shape, data, 
+                                    dtype, strides=strides, start=offset), 
read_only
+        if w_data is None:
+            data = w_object
+        else:
+            data = w_data
+        w_offset = space.finditem(w_interface, space.wrap('offset'))
+        if w_offset is None:
+            offset = 0
+        else:
+            offset = space.int_w(w_offset)
+        #print 'create view from shape',shape,'dtype',dtype,'data',data
+        if strides is not None:
+            raise oefmt(space.w_NotImplementedError,
+                   "__array_interface__ strides not fully supported yet") 
+        arr = frombuffer(space, data, dtype, support.product(shape), offset)
+        new_impl = arr.implementation.reshape(arr, shape)
+        return W_NDimArray(new_impl), False
+        
     except OperationError as e:
         if e.match(space, space.w_AttributeError):
-            return None
+            return None, False
         raise
 
 
@@ -103,19 +131,20 @@
     if space.isinstance_w(w_object, space.w_type):
         raise oefmt(space.w_ValueError, "cannot create ndarray from type 
instance")
     # for anything that isn't already an array, try __array__ method first
+    dtype = descriptor.decode_w_dtype(space, w_dtype)
     if not isinstance(w_object, W_NDimArray):
         w_array = try_array_method(space, w_object, w_dtype)
         if w_array is not None:
             # continue with w_array, but do further operations in place
             w_object = w_array
             copy = False
+            dtype = w_object.get_dtype()
     if not isinstance(w_object, W_NDimArray):
-        w_array = try_interface_method(space, w_object)
+        w_array, _copy = try_interface_method(space, w_object)
         if w_array is not None:
             w_object = w_array
-            copy = False
-    dtype = descriptor.decode_w_dtype(space, w_dtype)
-
+            copy = _copy
+            dtype = w_object.get_dtype()
 
     if isinstance(w_object, W_NDimArray):
         npy_order = order_converter(space, w_order, NPY.ANYORDER)
diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py
--- a/pypy/module/micronumpy/loop.py
+++ b/pypy/module/micronumpy/loop.py
@@ -534,10 +534,10 @@
             while not inner_iter.done(inner_state):
                 arg_driver.jit_merge_point(shapelen=shapelen, dtype=dtype)
                 w_val = inner_iter.getitem(inner_state)
-                new_best = getattr(dtype.itemtype, op_name)(cur_best, w_val)
-                if dtype.itemtype.ne(new_best, cur_best):
+                old_best = getattr(dtype.itemtype, op_name)(cur_best, w_val)
+                if not old_best:
                     result = idx
-                    cur_best = new_best
+                    cur_best = w_val
                 inner_state = inner_iter.next(inner_state)
                 idx += 1
             result = get_dtype_cache(space).w_longdtype.box(result)
@@ -557,17 +557,17 @@
         while not iter.done(state):
             arg_flat_driver.jit_merge_point(shapelen=shapelen, dtype=dtype)
             w_val = iter.getitem(state)
-            new_best = getattr(dtype.itemtype, op_name)(cur_best, w_val)
-            if dtype.itemtype.ne(new_best, cur_best):
+            old_best = getattr(dtype.itemtype, op_name)(cur_best, w_val)
+            if not old_best:
                 result = idx
-                cur_best = new_best
+                cur_best = w_val
             state = iter.next(state)
             idx += 1
         return result
 
     return argmin_argmax, argmin_argmax_flat
-argmin, argmin_flat = _new_argmin_argmax('min')
-argmax, argmax_flat = _new_argmin_argmax('max')
+argmin, argmin_flat = _new_argmin_argmax('argmin')
+argmax, argmax_flat = _new_argmin_argmax('argmax')
 
 dot_driver = jit.JitDriver(name = 'numpy_dot',
                            greens = ['dtype'],
diff --git a/pypy/module/micronumpy/test/test_ndarray.py 
b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -1852,6 +1852,24 @@
         a = array([(1, 2)], dtype=[('a', 'int64'), ('b', 'int64')])
         assert a.view('S16')[0] == '\x01' + '\x00' * 7 + '\x02'
 
+    def test_half_conversions(self):
+        from numpy import array, arange
+        from math import isnan, isinf
+        e = array([0, -1, -float('inf'), float('nan'), 6], dtype='float16')
+        assert map(isnan, e) == [False, False, False, True, False]
+        assert map(isinf, e) == [False, False, True, False, False]
+        assert e.argmax() == 3
+        # numpy preserves value for uint16 -> cast_as_float16 -> 
+        #     convert_to_float64 -> convert_to_float16 -> uint16
+        #  even for float16 various float16 nans
+        all_f16 = arange(0xfe00, 0xffff, dtype='uint16')
+        all_f16.dtype = 'float16'
+        all_f32 = array(all_f16, dtype='float32')
+        b = array(all_f32, dtype='float16')
+        c = b.view(dtype='uint16')
+        d = all_f16.view(dtype='uint16')
+        assert (c == d).all()
+
     def test_ndarray_view_empty(self):
         from numpy import array, dtype
         x = array([], dtype=[('a', 'int8'), ('b', 'int8')])
@@ -3052,7 +3070,7 @@
         assert (b == zeros(10)).all()
 
     def test_array_interface(self):
-        from numpy import array
+        from numpy import array, ones
         a = array(2.5)
         i = a.__array_interface__
         assert isinstance(i['data'][0], int)
@@ -3075,7 +3093,7 @@
 
         class Dummy(object):
             def __init__(self, aif=None):
-                if aif:
+                if aif is not None:
                     self.__array_interface__ = aif
 
         a = array(Dummy())
@@ -3084,6 +3102,31 @@
         raises(ValueError, array, Dummy({'version': 0}))
         raises(ValueError, array, Dummy({'version': 'abc'}))
         raises(ValueError, array, Dummy({'version': 3}))
+        raises(TypeError, array, Dummy({'version': 3, 'typestr': 'f8', 
'shape': ('a', 3)}))
+
+        a = array([1, 2, 3])
+        b = array(Dummy(a.__array_interface__))
+        b[1] = 200
+        assert a[1] == 2 # upstream compatibility, is this a bug?
+        interface_a = a.__array_interface__
+        interface_b = b.__array_interface__
+        # only the data[0] value should differ
+        assert interface_a['data'][0] != interface_b['data'][0]
+        assert interface_b['data'][1] == interface_a['data'][1]
+        interface_b.pop('data')
+        interface_a.pop('data')
+        assert interface_a == interface_b
+
+        b = array(Dummy({'version':3, 'shape': (50,), 'typestr': 'u1',
+                         'data': 'a'*100}))
+        assert b.dtype == 'uint8'
+        assert b.shape == (50,)
+
+        a = ones((1,), dtype='float16')
+        b = Dummy(a.__array_interface__)
+        c = array(b)
+        assert c.dtype == 'float16'
+        assert (a == c).all()
 
     def test_array_indexing_one_elem(self):
         from numpy import array, arange
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
@@ -345,6 +345,14 @@
     def min(self, v1, v2):
         return min(v1, v2)
 
+    @raw_binary_op
+    def argmax(self, v1, v2):
+        return v1 >= v2
+
+    @raw_binary_op
+    def argmin(self, v1, v2):
+        return v1 <= v2
+
     @raw_unary_op
     def rint(self, v):
         float64 = Float64(self.space)
@@ -820,6 +828,14 @@
     def min(self, v1, v2):
         return v1 if v1 <= v2 or rfloat.isnan(v1) else v2
 
+    @raw_binary_op
+    def argmax(self, v1, v2):
+        return v1 >= v2 or rfloat.isnan(v1)
+
+    @raw_binary_op
+    def argmin(self, v1, v2):
+        return v1 <= v2 or rfloat.isnan(v1)
+
     @simple_binary_op
     def fmax(self, v1, v2):
         return v1 if v1 >= v2 or rfloat.isnan(v2) else v2
@@ -1407,6 +1423,16 @@
             return v1
         return v2
 
+    def argmin(self, v1, v2):
+        if self.le(v1, v2) or self.isnan(v1):
+            return True
+        return False
+
+    def argmax(self, v1, v2):
+        if self.ge(v1, v2) or self.isnan(v1):
+            return True
+        return False
+
     @complex_binary_op
     def floordiv(self, v1, v2):
         (r1, i1), (r2, i2) = v1, v2
@@ -1927,6 +1953,18 @@
             return v1
         return v2
 
+    @raw_binary_op
+    def argmax(self, v1, v2):
+        if self.space.is_true(self.space.ge(v1, v2)):
+            return True
+        return False
+
+    @raw_binary_op
+    def argmin(self, v1, v2):
+        if self.space.is_true(self.space.le(v1, v2)):
+            return True
+        return False
+
     @raw_unary_op
     def bool(self,v):
         return self._obool(v)
diff --git a/rpython/rlib/rstruct/ieee.py b/rpython/rlib/rstruct/ieee.py
--- a/rpython/rlib/rstruct/ieee.py
+++ b/rpython/rlib/rstruct/ieee.py
@@ -5,7 +5,8 @@
 import math
 
 from rpython.rlib import rarithmetic, rfloat, objectmodel, jit
-from rpython.rlib.rarithmetic import r_ulonglong
+from rpython.rtyper.lltypesystem.rffi import r_ulonglong, r_longlong, 
LONGLONG, ULONGLONG, cast
+from rpython.rlib.longlong2float import longlong2float, float2longlong
 
 def round_to_nearest(x):
     """Python 3 style round:  round a float x to the nearest int, but
@@ -60,7 +61,20 @@
 
     if exp == MAX_EXP - MIN_EXP + 2:
         # nan or infinity
-        result = rfloat.NAN if mant else rfloat.INFINITY
+        if mant == 0:
+            result = rfloat.INFINITY
+        else:
+            # preserve at most 52 bits of mant value, but pad w/zeros
+            exp = r_ulonglong(0x7ff) << 52
+            sign = r_ulonglong(sign) << 63
+            if MANT_DIG < 53:
+                mant = r_ulonglong(mant) << (53 - MANT_DIG) 
+            if mant == 0:
+                result = rfloat.NAN
+            else:
+                uint = exp | mant | sign
+                result =  longlong2float(cast(LONGLONG, uint))
+            return result
     elif exp == 0:
         # subnormal or zero
         result = math.ldexp(mant, MIN_EXP - MANT_DIG)
@@ -72,7 +86,7 @@
 
 def float_unpack80(QQ, size):
     '''Unpack a (mant, exp) tuple of r_ulonglong in 80-bit extended format
-    into a long double float
+    into a python float (a double)
     '''
     if size == 10 or size == 12 or size == 16:
         MIN_EXP = -16381
@@ -100,7 +114,17 @@
 
     if exp == MAX_EXP - MIN_EXP + 2:
         # nan or infinity
-        result = rfloat.NAN if mant &((one << MANT_DIG - 1) - 1) else 
rfloat.INFINITY
+        if mant == 0:
+            result = rfloat.INFINITY
+        else:
+            exp = r_ulonglong(0x7ff) << 52
+            mant = r_ulonglong(mant) >> size + 1
+            if mant == 0:
+                result = rfloat.NAN
+            else:
+                uint = exp | r_ulonglong(mant) | r_ulonglong(sign)
+                result =  longlong2float(cast(LONGLONG, uint))
+            return result
     else:
         # normal
         result = math.ldexp(mant, exp + MIN_EXP - MANT_DIG - 1)
@@ -128,13 +152,19 @@
         raise ValueError("invalid size value")
 
     sign = rfloat.copysign(1.0, x) < 0.0
-    if not rfloat.isfinite(x):
-        if rfloat.isinf(x):
-            mant = r_ulonglong(0)
-            exp = MAX_EXP - MIN_EXP + 2
-        else:  # rfloat.isnan(x):
-            mant = r_ulonglong(1) << (MANT_DIG-2) # other values possible
-            exp = MAX_EXP - MIN_EXP + 2
+    if rfloat.isinf(x):
+        mant = r_ulonglong(0)
+        exp = MAX_EXP - MIN_EXP + 2
+    elif rfloat.isnan(x):
+        asint = cast(ULONGLONG, float2longlong(x))
+        sign = asint >> 63
+        # shift off lower bits, perhaps losing data
+        mant = asint & ((r_ulonglong(1) << 52) - 1)
+        if MANT_DIG < 53:
+            mant = mant >> (53 - MANT_DIG)
+        if mant == 0:
+            mant = r_ulonglong(1) << (MANT_DIG - 1) - 1
+        exp = MAX_EXP - MIN_EXP + 2
     elif x == 0.0:
         mant = r_ulonglong(0)
         exp = 0
@@ -167,7 +197,7 @@
 
     # check constraints
     if not objectmodel.we_are_translated():
-        assert 0 <= mant < 1 << MANT_DIG - 1
+        assert 0 <= mant <= (1 << MANT_DIG) - 1
         assert 0 <= exp <= MAX_EXP - MIN_EXP + 2
         assert 0 <= sign <= 1
     exp = r_ulonglong(exp)
@@ -187,13 +217,16 @@
         raise ValueError("invalid size value")
 
     sign = rfloat.copysign(1.0, x) < 0.0
-    if not rfloat.isfinite(x):
-        if rfloat.isinf(x):
-            mant = r_ulonglong(0)
-            exp = MAX_EXP - MIN_EXP + 2
-        else:  # rfloat.isnan(x):
-            mant = (r_ulonglong(1) << (MANT_DIG-2)) - 1 # other values possible
-            exp = MAX_EXP - MIN_EXP + 2
+    if rfloat.isinf(x):
+        mant = r_ulonglong(0)
+        exp = MAX_EXP - MIN_EXP + 2
+    elif rfloat.isnan(x):  # rfloat.isnan(x):
+        asint = cast(ULONGLONG, float2longlong(x))
+        mant = asint & ((r_ulonglong(1) << 51) - 1)
+        if mant == 0:
+            mant = r_ulonglong(1) << (MANT_DIG - 1) - 1
+        sign = asint < 0
+        exp = MAX_EXP - MIN_EXP + 2
     elif x == 0.0:
         mant = r_ulonglong(0)
         exp = 0
@@ -221,12 +254,12 @@
         if exp >= MAX_EXP - MIN_EXP + 2:
             raise OverflowError("float too large to pack in this format")
 
+        mant = mant << 1
     # check constraints
     if not objectmodel.we_are_translated():
-        assert 0 <= mant < 1 << MANT_DIG - 1
+        assert 0 <= mant <= (1 << MANT_DIG) - 1
         assert 0 <= exp <= MAX_EXP - MIN_EXP + 2
         assert 0 <= sign <= 1
-    mant = mant << 1
     exp = r_ulonglong(exp)
     sign = r_ulonglong(sign)
     return (mant, (sign << BITS - MANT_DIG - 1) | exp)
diff --git a/rpython/rlib/rstruct/test/test_ieee.py 
b/rpython/rlib/rstruct/test/test_ieee.py
--- a/rpython/rlib/rstruct/test/test_ieee.py
+++ b/rpython/rlib/rstruct/test/test_ieee.py
@@ -168,15 +168,31 @@
 
     def test_random(self):
         # construct a Python float from random integer, using struct
+        mantissa_mask = (1 << 53) - 1
         for _ in xrange(10000):
             Q = random.randrange(2**64)
             x = struct.unpack('<d', struct.pack('<Q', Q))[0]
             # nans are tricky:  we can't hope to reproduce the bit
-            # pattern exactly, so check_float will fail for a random nan.
-            if isnan(x):
+            # pattern exactly, so check_float will fail for a nan
+            # whose mantissa does not fit into float16's mantissa.
+            if isnan(x) and (Q & mantissa_mask) >=  1 << 11:
                 continue
             self.check_float(x)
 
+    def test_various_nans(self):
+        # check patterns that should preserve the mantissa across nan 
conversions
+        maxmant64 = (1 << 52) - 1 # maximum double mantissa
+        maxmant16 = (1 << 10) - 1 # maximum float16 mantissa
+        assert maxmant64 >> 42 == maxmant16
+        exp = 0xfff << 52
+        for i in range(20):
+            val_to_preserve = exp | ((maxmant16 - i) << 42)
+            a = ieee.float_unpack(val_to_preserve, 8)
+            assert isnan(a), 'i %d, maxmant %s' % (i, hex(val_to_preserve))
+            b = ieee.float_pack(a, 8)
+            assert b == val_to_preserve, 'i %d, val %s b %s' % (i, 
hex(val_to_preserve), hex(b)) 
+            b = ieee.float_pack(a, 2)
+            assert b == 0xffff - i, 'i %d, b%s' % (i, hex(b))
 
 class TestCompiled:
     def test_pack_float(self):
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to