Author: mattip <[email protected]>
Branch: ufuncapi
Changeset: r74825:d5aace1c3418
Date: 2014-12-05 01:54 +0200
http://bitbucket.org/pypy/pypy/changeset/d5aace1c3418/

Log:    refactor, properly allocate via signature dtypes. Still no casting

diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -561,7 +561,8 @@
                  self.name, self.nin, len(args_w))
         for i in range(self.nin):
             inargs[i] = convert_to_array(space, args_w[i])
-            assert isinstance(inargs[i], W_NDimArray)
+        for i in inargs:
+            assert isinstance(i, W_NDimArray)
         outargs = [None] * self.nout
         for i in range(len(args_w)-self.nin):
             out = args_w[i+self.nin]
@@ -574,16 +575,23 @@
                 outargs[i] = out
         if sig is None:
             sig = space.wrap(self.signature)
-        index, dtypes = self.type_resolver(space, inargs, outargs, sig)
-        outargs = self.alloc_outargs(space, index, inargs, outargs, dtypes)
-        for i in range(len(outargs)):
-            assert isinstance(outargs[i], W_NDimArray)
-        outargs0 = outargs[0]
-        assert isinstance(outargs0, W_NDimArray)
-        res_dtype = outargs0.get_dtype()
+        _dtypes = self.dtypes
+        if self.match_dtypes:
+            _dtypes = [i.get_dtype() for i in inargs if isinstance(i, 
W_NDimArray)]
+            for i in outargs:
+                if isinstance(i, W_NDimArray):
+                    _dtypes.append(i.get_dtype())
+                else:
+                    _dtypes.append(_dtypes[0])
+        index, dtypes = self.type_resolver(space, inargs, outargs, sig, 
_dtypes)
         func = self.funcs[index]
         if not self.core_enabled:
             # func is going to do all the work, it must accept W_NDimArray args
+            inargs0 = inargs[0]
+            assert isinstance(inargs0, W_NDimArray)
+            outarg_shapes = [inargs0.get_shape()] * self.nargs
+            inargs, outargs = self.alloc_args(space, inargs, outargs,
+                                              dtypes, outarg_shapes)
             if self.stack_inputs:
                 arglist = space.newlist(list(inargs + outargs))
                 space.call_args(func, Arguments.frompacked(space, arglist))
@@ -594,121 +602,14 @@
             if len(outargs) < 2:
                 return outargs[0]
             return space.newtuple(outargs)
-        # Figure out the number of iteration dimensions, which
-        # is the broadcast result of all the input non-core
-        # dimensions
-        broadcast_ndim = 0
-        for i in range(len(inargs)):
-            curarg = inargs[i]
-            assert isinstance(curarg, W_NDimArray)
-            n = curarg.ndims() - self.core_num_dims[i]
-            broadcast_ndim = max(broadcast_ndim, n)
-        # Figure out the number of iterator creation dimensions,
-        # which is the broadcast dimensions + all the core dimensions of
-        # the outputs, so that the iterator can allocate those output
-        # dimensions following the rules of order='F', for example.
-        iter_ndim = broadcast_ndim
-        for i in range(self.nin, self.nargs):
-            iter_ndim += self.core_num_dims[i];
-        # Validate the core dimensions of all the operands,
-        inner_dimensions = [1] * (self.core_num_dim_ix + 2)
-        idim = 0
-        core_start_dim = 0
-        for i in range(self.nin):
-            curarg = inargs[i]
-            assert isinstance(curarg, W_NDimArray)
-            dim_offset = self.core_offsets[i]
-            num_dims = self.core_num_dims[i]
-            core_start_dim = curarg.ndims() - num_dims
-            if core_start_dim >=0:
-                idim = 0
-            else:
-                idim = -core_start_dim
-            while(idim < num_dims):
-                core_dim_index = self.core_dim_ixs[dim_offset + idim]
-                op_dim_size = curarg.get_shape()[core_start_dim + idim]
-                if inner_dimensions[core_dim_index + 1] == 1:
-                    inner_dimensions[core_dim_index + 1] = op_dim_size
-                elif op_dim_size != 1 and inner_dimensions[1 + core_dim_index] 
!= op_dim_size:
-                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
-                        "mismatch in its core dimension %d, with gufunc "
-                        "signature %s (size %d is different from %d)",
-                         self.name, i, idim, self.signature, op_dim_size,
-                         inner_dimensions[1 + core_dim_index])
-                idim += 1
-        for i in range(len(outargs)):
-            curarg = outargs[i]
-            assert isinstance(curarg, W_NDimArray)
-            dim_offset = self.core_offsets[i]
-            num_dims = self.core_num_dims[i]
-            core_start_dim = curarg.ndims() - num_dims
-            if core_start_dim < 0:
-                raise oefmt(space.w_ValueError, "%s: Output operand %d does "
-                    "not have enough dimensions (has %d, gufunc with "
-                    "signature %s requires %d)", self.name, i,
-                    curarg.ndims(), self.signature, num_dims)
-            if core_start_dim >=0:
-                idim = 0
-            else:
-                idim = -core_start_dim
-            while(idim < num_dims):
-                core_dim_index = self.core_dim_ixs[dim_offset + idim]
-                op_dim_size = curarg.get_shape()[core_start_dim + idim]
-                if inner_dimensions[core_dim_index + 1] == 1:
-                    inner_dimensions[core_dim_index + 1] = op_dim_size
-                elif inner_dimensions[1 + core_dim_index] != op_dim_size:
-                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
-                        "mismatch in its core dimension %d, with gufunc "
-                        "signature %s (size %d is different from %d)",
-                         self.name, i, idim, self.signature, op_dim_size,
-                         inner_dimensions[1 + core_dim_index])
-                idim += 1
-        iter_shape = [-1] * (broadcast_ndim + (len(outargs) * iter_ndim))
-        j = broadcast_ndim
-        core_dim_ixs_size = 0
-        firstdim = broadcast_ndim
-        for cnd in self.core_num_dims:
-            firstdim  += cnd
-        op_axes_arrays = [[-1,] * (self.nin + len(outargs)),] * firstdim
-        for i in range(self.nin):
-            # Note that n may be negative if broadcasting
-            # extends into the core dimensions.
-            curarg = inargs[i]
-            assert isinstance(curarg, W_NDimArray)
-            n = curarg.ndims() - self.core_num_dims[i]
-            for idim in range(broadcast_ndim):
-                if idim >= broadcast_ndim -n:
-                    op_axes_arrays[idim][i] = idim - (broadcast_ndim -n)
-            core_dim_ixs_size += self.core_num_dims[i];
-        for i in range(len(outargs)):
-            curarg = outargs[i]
-            assert isinstance(curarg, W_NDimArray)
-            iout = i + self.nin
-            n = curarg.ndims() - self.core_num_dims[iout]
-            for idim in range(broadcast_ndim):
-                if idim >= broadcast_ndim -n:
-                    op_axes_arrays[idim][iout] = idim - (broadcast_ndim -n)
-            dim_offset = self.core_offsets[iout]
-            num_dims = self.core_num_dims[iout]
-            for idim in range(num_dims):
-                cdi = self.core_dim_ixs[dim_offset + idim]
-                iter_shape[j] = inner_dimensions[1 + cdi]
-                op_axes_arrays[j][iout] = n + idim
-                j += 1
-            core_dim_ixs_size += self.core_num_dims[iout];
-        # TODO once we support obejct dtypes,
-        # FAIL with NotImplemented if the other object has
-        # the __r<op>__ method and has a higher priority than
-        # the current op (signalling it can handle ndarray's).
-
-        # TODO parse and handle subok
-        # TODO handle flags, op_flags
+        iter_shape, outarg_shapes, matched_dims = self.verify_args(space, 
inargs, outargs)
+        inargs, outargs = self.alloc_args(space, inargs, outargs, dtypes,
+                                          outarg_shapes)
         w_flags = space.w_None # NOT 'external_loop', we do coalescing by 
core_num_dims
         w_op_flags = space.newtuple([space.wrap(r) for r in ['readonly'] * 
len(inargs)] + \
                                     [space.wrap(r) for r in ['readwrite'] * 
len(outargs)])
         w_op_dtypes = space.w_None
         w_casting = space.w_None
-        w_itershape = space.newlist([space.wrap(i) for i in iter_shape]) 
         w_op_axes = space.w_None
 
         if self.stack_inputs:
@@ -722,16 +623,17 @@
                 # Unlike numpy, we will not broadcast dims before
                 # the core_ndims rather we use nditer iteration
                 # so dims[0] == 1
-                dims = [1] + inner_dimensions[1:]
+                dims = [1] + matched_dims
                 steps = []
                 allargs = inargs + outargs
-                assert core_start_dim >= 0
                 for i in range(len(allargs)):
                     steps.append(0)
                 for i in range(len(allargs)):
                     _arg = allargs[i]
                     assert isinstance(_arg, W_NDimArray)
-                    steps += _arg.implementation.strides[core_start_dim:]
+                    start_dim = len(_arg.get_shape()) - len(iter_shape)
+                    assert start_dim >= 0
+                    steps += _arg.implementation.strides[start_dim:]
                 #print 'set_dims_and_steps with dims, steps',dims,steps
                 func.set_dims_and_steps(space, dims, steps)
             else:
@@ -739,6 +641,7 @@
                 # from frompyfunc
                 pass
             # mimic NpyIter_AdvancedNew with a nditer
+            w_itershape = space.newlist([space.wrap(i) for i in iter_shape]) 
             nd_it = W_NDIter(space, space.newlist(inargs + outargs), w_flags,
                           w_op_flags, w_op_dtypes, w_casting, w_op_axes,
                           w_itershape)
@@ -770,7 +673,10 @@
                 return space.newtuple([convert_to_array(space, o) for o in 
outargs])
             return outargs[0]
         inargs0 = inargs[0]
+        outargs0 = outargs[0]
         assert isinstance(inargs0, W_NDimArray)
+        assert isinstance(outargs0, W_NDimArray)
+        res_dtype = outargs0.get_dtype()
         new_shape = inargs0.get_shape()
         if len(outargs) < 2:
             return loop.call_many_to_one(space, new_shape, func,
@@ -808,8 +714,8 @@
             kwargs_w.pop(kw)
         return w_subok, w_out, sig, casting, extobj
 
-    def type_resolver(self, space, inargs, outargs, type_tup):
-        # Find a match for the inargs.dtype in self.dtypes, like
+    def type_resolver(self, space, inargs, outargs, type_tup, _dtypes):
+        # Find a match for the inargs.dtype in _dtypes, like
         # linear_search_type_resolver in numpy ufunc_type_resolutions.c
         # type_tup can be '', a tuple of dtypes, or a string
         # of the form d,t -> D where the letters are dtype specs
@@ -831,7 +737,7 @@
                     " after the -> sign, not '%s'", self.name, self.nin, 
                     self.nout, type_tup)
         else:
-            # XXX does not pass translation
+            # XXX why does the next line not pass translation?
             # dtypes = [i.get_dtype() for i in inargs]
             for i in inargs:
                 if isinstance(i, W_NDimArray):
@@ -843,39 +749,164 @@
                     dtypes.append(i.get_dtype())
                 else:
                     dtypes.append(None)
-        #Find the first matchup of dtypes with self.dtypes
-        for i in range(0, len(self.dtypes), self.nargs):
+        #Find the first matchup of dtypes with _dtypes
+        for i in range(0, len(_dtypes), self.nargs):
             allok = True
             for j in range(self.nargs):
-                if dtypes[j] is not None and dtypes[j] != self.dtypes[i+j]:
+                if dtypes[j] is not None and dtypes[j] != _dtypes[i+j]:
                     allok = False
             if allok:    
                 break
         else:
-            if len(self.funcs) < 2:
-                return 0, dtypes
-            raise oefmt(space.w_TypeError,
-                         'input dtype did not match any known dtypes',
-                       )
+            if len(self.funcs) > 1:
+                raise oefmt(space.w_TypeError,
+                            'input dtype did not match any known dtypes',
+                           )
+            i = 0
         # Fill in empty dtypes
         for j in range(self.nargs):
             if dtypes[j] is None:
-                dtypes[j] = self.dtypes[i+j]
+                dtypes[j] = _dtypes[i+j]
         return i / self.nargs, dtypes
 
-    def alloc_outargs(self, space, index, inargs, outargs, dtypes):
-        # Any None outarg should be allocated here
+    def alloc_args(self, space, inargs, outargs, dtypes, outarg_shapes):
+        # Any None outarg are allocated, and inargs, outargs may need casting
         inargs0 = inargs[0]
         assert isinstance(inargs0, W_NDimArray)
-        temp_shape = inargs0.get_shape() # XXX wrong!!!
-        dtype = inargs0.get_dtype() # XXX wrong!!!
         order = inargs0.get_order()
         for i in range(len(outargs)):
             if outargs[i] is None:
-                outargs[i] = W_NDimArray.from_shape(space, temp_shape, dtype, 
order)
+                j = self.nin + i
+                outargs[i] = W_NDimArray.from_shape(space, outarg_shapes[i], 
dtypes[j], order)
+        for i in outargs:
+            assert isinstance(i, W_NDimArray)
+        return inargs, outargs
+
+    def verify_args(self, space, inargs, outargs):
+        # Figure out the number of iteration dimensions, which
+        # is the broadcast result of all the input non-core
+        # dimensions
+        iter_shape = []
+        max_matched_dims = 0
+        for i in self.core_dim_ixs:
+            if i > max_matched_dims:
+                max_matched_dims = i
+        matched_dims = [-1] * (1 + max_matched_dims)
+        for i in range(self.nin):
+            curarg = inargs[i]
+            assert isinstance(curarg, W_NDimArray)
+            dim_offset = self.core_offsets[i] # index into XXX_ixs
+            num_dims = self.core_num_dims[i]
+            # Make sure the last num_dims shape of curarg match the signature
+            n = len(curarg.get_shape()) - num_dims
+            if n < 0:
+                raise oefmt(space.w_ValueError, "%s: Input operand %d does "
+                    "not have enough dimensions (has %d, gufunc with "
+                    "signature %s requires %d)", self.name, i,
+                    num_dims+n, self.signature, num_dims)
+            dims_to_match = curarg.get_shape()[n:]
+            dims_to_broadcast = curarg.get_shape()[:n]
+            offset = len(dims_to_broadcast) - len(iter_shape) 
+            if offset >= 0:
+                # Prepend extra dimensions to iter_shape
+                iter_shape = dims_to_broadcast[:offset] + iter_shape
+                offset = 0
+            # Make sure iter_shape[offset:] matches dims_to_broadcast
+            offset = abs(offset) # for translation
+            for j in range(offset, len(iter_shape)):
+                x = iter_shape[j + offset]
+                y = dims_to_broadcast[j]
+                if (x > y and x % y) or y %x:
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its broadcast dimension %d "
+                        "(size %d is different from %d)",
+                         self.name, i, j, x, y)
+                iter_shape[offset + j] = max(x, y)
+            # Find or verify signature ixs
+            for j in range(num_dims):
+                core_dim_index = self.core_dim_ixs[dim_offset + j]
+                if core_dim_index > len(dims_to_match):
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its core dimension %d, with gufunc "
+                        "signature %s (index is larger than input shape)",
+                         self.name, i, j, self.signature, core_dim_index)
+                if matched_dims[core_dim_index] < 0:
+                    matched_dims[core_dim_index] = dims_to_match[j]
+                elif matched_dims[core_dim_index] != dims_to_match[j]:
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its core dimension %d, with gufunc "
+                        "signature %s (expected %d, got %d)",
+                         self.name, i, j, 
+                         self.signature, matched_dims[core_dim_index],
+                         dims_to_match[j])
+        outarg_shapes = []
         for i in range(len(outargs)):
-            assert isinstance(outargs[i], W_NDimArray)
-        return outargs
+            curarg = outargs[i]
+            dim_offset = self.core_offsets[i]
+            num_dims = self.core_num_dims[i]
+            if not isinstance(curarg, W_NDimArray):
+                arg_shape = iter_shape
+                for j in range(num_dims):
+                    core_dim_index = self.core_dim_ixs[dim_offset + j]
+                    if matched_dims[core_dim_index] < 0:
+                        raise oefmt(space.w_ValueError, "%s: Output operand %d 
"
+                            "is empty but unique core dimension %d in 
signature "
+                            "%s of gufunc was not specified",
+                             self.name, i, core_dim_index, self.signature)
+                    arg_shape.append(matched_dims[core_dim_index])
+                outarg_shapes.append(arg_shape) 
+                continue
+            n = len(curarg.get_shape()) - num_dims
+            if n < 0:
+                raise oefmt(space.w_ValueError, "%s: Output operand %d does "
+                    "not have enough dimensions (has %d, gufunc with "
+                    "signature %s requires %d)", self.name, i,
+                    num_dims+n, self.signature, num_dims)
+            dims_to_match = curarg.get_shape()[n:]
+            dims_to_broadcast = curarg.get_shape()[:n]
+            offset = len(dims_to_broadcast) - len(iter_shape)
+            if offset >= 0:
+                # Prepend extra dimensions to iter_shape, matched_dims
+                iter_shape = dims_to_broadcast[:offset] + iter_shape
+                outarg_shapes = [dims_to_broadcast[:offset] + asp for asp in 
outarg_shapes]
+                offset = 0
+            # Make sure iter_shape[offset:] matches dims_to_broadcast
+            offset = abs(offset) # for translation
+            for j in range(offset, len(iter_shape)):
+                x = iter_shape[j + offset]
+                y = dims_to_broadcast[j]
+                if (x > y and x % y) or y %x:
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its broadcast dimension %d "
+                        "(size %d is different from %d)",
+                         self.name, i, j, x, y)
+                iter_shape[offset + j] = max(x, y)
+            # Find or verify signature ixs
+            for j in range(num_dims):
+                core_dim_index = self.core_dim_ixs[dim_offset + j]
+                if core_dim_index > len(dims_to_match):
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its core dimension %d, with gufunc "
+                        "signature %s (index is larger than input shape)",
+                         self.name, i, j, self.signature, core_dim_index)
+                if matched_dims[core_dim_index] < 0:
+                    matched_dims[core_dim_index] = 
dims_to_match[core_dim_index]
+                elif matched_dims[core_dim_index] != 
dims_to_match[core_dim_index]:
+                    raise oefmt(space.w_ValueError, "%s: Operand %d has a "
+                        "mismatch in its core dimension %d, with gufunc "
+                        "signature %s (expected %d, got %d)",
+                         self.name, i, core_dim_index + num_dims, 
+                         self.signature, matched_dims[core_dim_index],
+                         dims_to_match[core_dim_index])
+            outarg_shapes.append(iter_shape + dims_to_match)
+        # TODO once we support obejct dtypes,
+        # FAIL with NotImplemented if the other object has
+        # the __r<op>__ method and has a higher priority than
+        # the current op (signalling it can handle ndarray's).
+
+        # TODO parse and handle subok
+        # TODO handle flags, op_flags
+        return iter_shape, outarg_shapes, matched_dims
 
 W_Ufunc.typedef = TypeDef("numpy.ufunc",
     __call__ = interp2app(W_Ufunc.descr_call),
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to