[pypy-commit] pypy ufuncapi: handle the difference between the GenericUfuncApi and the GeneralizedUfuncApi
mattip
noreply at buildbot.pypy.org
Sat Nov 29 21:32:01 CET 2014
Author: mattip <matti.picus at gmail.com>
Branch: ufuncapi
Changeset: r74760:8651d4080984
Date: 2014-11-29 22:28 +0200
http://bitbucket.org/pypy/pypy/changeset/8651d4080984/
Log: handle the difference between the GenericUfuncApi and the
GeneralizedUfuncApi
diff --git a/pypy/module/cpyext/ndarrayobject.py b/pypy/module/cpyext/ndarrayobject.py
--- a/pypy/module/cpyext/ndarrayobject.py
+++ b/pypy/module/cpyext/ndarrayobject.py
@@ -265,6 +265,13 @@
rffi.CCHARP], PyObject)
def PyUFunc_FromFuncAndDataAndSignature(space, funcs, data, types, ntypes,
nin, nout, identity, name, doc, check_return, signature):
+ w_signature = rffi.charp2str(signature)
+ return do_ufunc(space, funcs, data, types, ntypes, nin, nout, identity, name, doc,
+ check_return, w_signature)
+
+
+def do_ufunc(space, funcs, data, types, ntypes, nin, nout, identity, name, doc,
+ check_return, w_signature):
funcs_w = [None] * ntypes
dtypes_w = [None] * ntypes * (nin + nout)
for i in range(ntypes):
@@ -273,10 +280,17 @@
dtypes_w[i] = get_dtype_cache(space).dtypes_by_num[ord(types[i])]
w_funcs = space.newlist(funcs_w)
w_dtypes = space.newlist(dtypes_w)
- w_signature = rffi.charp2str(signature)
w_doc = rffi.charp2str(doc)
w_name = rffi.charp2str(name)
w_identity = space.wrap(identity)
ufunc_generic = ufuncs.frompyfunc(space, w_funcs, nin, nout, w_dtypes,
w_signature, w_identity, w_name, w_doc, stack_inputs=True)
return ufunc_generic
+
+ at cpython_api([rffi.CArrayPtr(rffi.CArrayPtr(gufunctype)), rffi.VOIDP, rffi.CCHARP, Py_ssize_t, Py_ssize_t,
+ Py_ssize_t, Py_ssize_t, rffi.CCHARP, rffi.CCHARP, Py_ssize_t], PyObject)
+def PyUFunc_FromFuncAndData(space, funcs, data, types, ntypes,
+ nin, nout, identity, name, doc, check_return):
+ w_signature = ""
+ return do_ufunc(space, funcs, data, types, ntypes, nin, nout, identity,
+ name, doc, check_return, w_signature)
diff --git a/pypy/module/cpyext/test/test_ndarrayobject.py b/pypy/module/cpyext/test/test_ndarrayobject.py
--- a/pypy/module/cpyext/test/test_ndarrayobject.py
+++ b/pypy/module/cpyext/test/test_ndarrayobject.py
@@ -322,9 +322,9 @@
char types[] = { NPY_DOUBLE,NPY_DOUBLE, NPY_INT, NPY_INT };
void *array_data[] = {NULL, NULL};
PyObject * retval;
- retval = PyUFunc_FromFuncAndDataAndSignature(funcs,
+ retval = PyUFunc_FromFuncAndData(funcs,
array_data, types, 2, 1, 1, PyUFunc_None,
- "times2", "times2_docstring", 0, "()->()");
+ "times2", "times2_docstring", 0);
return retval;
"""
),
@@ -336,13 +336,23 @@
PyObject * retval;
retval = PyUFunc_FromFuncAndDataAndSignature(funcs,
array_data, types, 2, 1, 1, PyUFunc_None,
- "times2", "times2_docstring", 0, "(m)->(m)");
+ "times2", "times2_docstring", 0, "()->()");
return retval;
+ """),
+ ("create_float_ufunc_3x3", "METH_NOARGS",
"""
- ),
+ PyUFuncGenericFunction funcs[] = {&float_func_with_sig_3x3};
+ char types[] = { NPY_FLOAT,NPY_FLOAT};
+ void *array_data[] = {NULL, NULL};
+ return PyUFunc_FromFuncAndDataAndSignature(funcs,
+ array_data, types, 1, 1, 1, PyUFunc_None,
+ "float_3x3",
+ "a ufunc that tests a more complicated signature",
+ 0, "(m,m)->(m,m)");
+ """),
], prologue='''
#include "numpy/ndarraytypes.h"
- /*#include <numpy/ufuncobject.h>*/
+ /*#include <numpy/ufuncobject.h> generated by numpy setup.py*/
typedef void (*PyUFuncGenericFunction)
(char **args,
npy_intp *dimensions,
@@ -390,11 +400,34 @@
in += in_step;
out += out_step;
};
- }; ''')
+ };
+ void float_func_with_sig_3x3(char ** args, npy_intp * dimensions,
+ npy_intp* steps, void* data)
+ {
+ int target_dims[] = {1, 3};
+ int target_steps[] = {0, 0, 12, 4, 12, 4};
+ int res = 0;
+ int i;
+ for (i=0; i<sizeof(target_dims)/sizeof(int); i++)
+ if (dimensions[i] != target_dims[i])
+ res += 1;
+ for (i=0; i<sizeof(target_steps)/sizeof(int); i++)
+ if (steps[i] != target_steps[i])
+ res += +10;
+ *((float *)args[1]) = res;
+ };
+
+ ''')
+ sq = arange(18, dtype="float32").reshape(2,3,3)
+ float_ufunc = mod.create_float_ufunc_3x3()
+ out = float_ufunc(sq)
+ assert out[0, 0, 0] == 0
+
times2 = mod.create_ufunc_basic()
arr = arange(12, dtype='i').reshape(3, 4)
- out = times2(arr, sig='(d)->(d)', extobj=[0, 0, None])
+ out = times2(arr, extobj=[0, 0, None])
assert (out == arr * 2).all()
+
times2prime = mod.create_ufunc_signature()
out = times2prime(arr, sig='(d)->(d)', extobj=[0, 0, None])
assert (out == arr * 2).all()
diff --git a/pypy/module/micronumpy/nditer.py b/pypy/module/micronumpy/nditer.py
--- a/pypy/module/micronumpy/nditer.py
+++ b/pypy/module/micronumpy/nditer.py
@@ -256,8 +256,8 @@
_backstride = [(_shape[fastest] - 1) * _stride[0]] + old_iter.slice_backstride
if flat:
_shape = [support.product(_shape)]
- assert len(_stride) == 2
- _stride = [min(_stride[0], _stride[1])]
+ if len(_stride) > 1:
+ _stride = [min(_stride[0], _stride[1])]
_backstride = [(shape[0] - 1) * _stride[0]]
return SliceIter(old_iter.array, old_iter.size / shape[fastest],
new_shape, new_strides, new_backstrides,
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
@@ -150,8 +150,9 @@
ufunc = frompyfunc([int_times2, double_times2], 1, 1,
signature='()->()',
dtypes=[dtype(int), dtype(int),
- dtype(float), dtype(float)
- ]
+ dtype(float), dtype(float)
+ ],
+ stack_inputs=True,
)
ai = arange(10, dtype=int)
ai2 = ufunc(ai)
@@ -167,13 +168,23 @@
out_array[:] = in_array * 2
from numpy import frompyfunc, dtype, arange
ufunc = frompyfunc([times_2], 1, 1,
+ signature='(m,n)->(n,m)',
+ dtypes=[dtype(int), dtype(int)],
+ stack_inputs=True,
+ )
+ ai = arange(18, dtype=int).reshape(2,3,3)
+ ai3 = ufunc(ai[0,:,:])
+ ai2 = ufunc(ai)
+ assert (ai2 == ai * 2).all()
+ ufunc = frompyfunc([times_2], 1, 1,
signature='(m,m)->(m,m)',
dtypes=[dtype(int), dtype(int)],
stack_inputs=True,
)
ai = arange(18, dtype=int).reshape(2,3,3)
exc = raises(ValueError, ufunc, ai[:,:,0])
- assert "mismatch in its core dimension 1" in exc.value.message
+ assert "Operand 0 has a mismatch in its core dimension 1" in exc.value.message
+ ai3 = ufunc(ai[0,:,:])
ai2 = ufunc(ai)
assert (ai2 == ai * 2).all()
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
@@ -581,8 +581,13 @@
func = self.funcs[index]
if not self.core_enabled:
# func is going to do all the work, it must accept W_NDimArray args
- arglist = space.newlist(list(inargs + outargs))
- space.call_args(func, Arguments.frompacked(space, arglist))
+ if self.stack_inputs:
+ arglist = space.newlist(list(inargs + outargs))
+ space.call_args(func, Arguments.frompacked(space, arglist))
+ else:
+ arglist = space.newlist(inargs)
+ outargs = space.call_args(func, Arguments.frompacked(space, arglist))
+ return outargs
if len(outargs) < 2:
return outargs[0]
return space.newtuple(outargs)
@@ -603,12 +608,9 @@
for i in range(self.nin, self.nargs):
iter_ndim += self.core_num_dims[i];
# Validate the core dimensions of all the operands,
- # and collect all of the labeled core dimension sizes
- # into the array 'inner_dimensions[1:]'. Initialize them to
- # 1, for example in the case where the operand broadcasts
- # to a core dimension, it won't be visited.
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)
@@ -706,8 +708,33 @@
w_itershape = space.newlist([space.wrap(i) for i in iter_shape])
w_op_axes = space.w_None
-
if self.stack_inputs:
+ #print '\nsignature', sig
+ #print [(d, getattr(self,d)) for d in dir(self) if 'core' in d or 'broad' in d]
+ #print [(d, locals()[d]) for d in locals() if 'core' in d or 'broad' in d]
+ #print 'shapes',[d.get_shape() for d in inargs + outargs]
+ #print 'steps',[d.implementation.strides for d in inargs + outargs]
+ if isinstance(func, W_GenericUFuncCaller):
+ # Use GeneralizeUfunc interface with signature
+ # 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:]
+ 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:]
+ #print 'set_dims_and_steps with dims, steps',dims,steps
+ func.set_dims_and_steps(space, dims, steps)
+ else:
+ # it is a function, ready to be called by the iterator,
+ # from frompyfunc
+ pass
# mimic NpyIter_AdvancedNew with a nditer
nd_it = W_NDIter(space, space.newlist(inargs + outargs), w_flags,
w_op_flags, w_op_dtypes, w_casting, w_op_axes,
@@ -808,14 +835,6 @@
assert isinstance(outargs[i], W_NDimArray)
return outargs
- def prep_call(self, space, index, inargs, outargs):
- # Use the index and signature to determine
- # dims and steps for function call
- return self.funcs[index], [inargs[0].get_shape()[0]], \
- [inargs[0].implementation.get_strides()[0],
- outargs[0].implementation.get_strides()[0]]
-
-
W_Ufunc.typedef = TypeDef("numpy.ufunc",
__call__ = interp2app(W_Ufunc.descr_call),
__repr__ = interp2app(W_Ufunc.descr_repr),
@@ -1176,7 +1195,7 @@
stack_inputs*: boolean, whether the function is of the form
out = func(*in) False
or
- func(*in_out) True (forces use of a nditer with 'external_loop')
+ func(*[in + out]) True
only one of out_dtype or signature may be specified
@@ -1243,8 +1262,8 @@
'identity must be None or an int')
if len(signature) == 0:
- # cpython compatability, func is of the form (i),(i)->(i)
- signature = ','.join(['(i)'] * nin) + '->' + ','.join(['(i)'] * nout)
+ # cpython compatability, func is of the form (),()->()
+ signature = ','.join(['()'] * nin) + '->' + ','.join(['()'] * nout)
else:
#stack_inputs = True
pass
@@ -1257,18 +1276,21 @@
w_ret.w_doc = space.wrap(doc)
return w_ret
-# Instantiated in cpyext/ndarrayobject
+# Instantiated in cpyext/ndarrayobject. It is here since ufunc calls
+# set_dims_and_steps, otherwise ufunc, ndarrayobject would have circular
+# imports
npy_intpp = rffi.LONGP
LONG_SIZE = LONG_BIT / 8
CCHARP_SIZE = _get_bitsize('P') / 8
class W_GenericUFuncCaller(W_Root):
- _attrs_ = ['func', 'data', 'dims', 'steps']
+ _attrs_ = ['func', 'data', 'dims', 'steps', 'dims_steps_set']
def __init__(self, func, data):
self.func = func
self.data = data
self.dims = alloc_raw_storage(0, track_allocation=False)
self.steps = alloc_raw_storage(0, track_allocation=False)
+ self.dims_steps_set = False
def __del__(self):
free_raw_storage(self.dims, track_allocation=False)
@@ -1276,10 +1298,14 @@
def descr_call(self, space, __args__):
args_w, kwds_w = __args__.unpack()
- # Can be called two ways, as an inner-loop function with boxes,
- # or as an outer-looop functio with ndarrays
+ # Can be called two ways, as a GenericUfunc or a GeneralizedUfunc.
+ # The difference is in the meaning of dims and steps,
+ # a GenericUfunc is a scalar function that flatiters over the array(s).
+ # a GeneralizedUfunc will iterate over dims[0], but will use dims[1...]
+ # and steps[1, ...] to call a function on ndarray(s).
+ # set up via a call to set_dims_and_steps()
dataps = alloc_raw_storage(CCHARP_SIZE * len(args_w), track_allocation=False)
- if isinstance(args_w[0], W_NDimArray):
+ if self.dims_steps_set is False:
self.dims = alloc_raw_storage(LONG_SIZE * len(args_w), track_allocation=False)
self.steps = alloc_raw_storage(LONG_SIZE * len(args_w), track_allocation=False)
for i in range(len(args_w)):
@@ -1294,13 +1320,10 @@
raw_storage_setitem(self.dims, LONG_SIZE * i, rffi.cast(rffi.LONG, arg_i.get_size()))
raw_storage_setitem(self.steps, LONG_SIZE * i, rffi.cast(rffi.LONG, arg_i.get_dtype().elsize))
else:
- if self.dims is None or self.steps is None:
- raise OperationError(space.w_RuntimeError,
- space.wrap("call set_dims_and_steps first"))
for i in range(len(args_w)):
arg_i = args_w[i]
- # raw_storage_setitem(dataps, CCHARP_SIZE * i,
- # rffi.cast(rffi.CCHARP, arg_i.storage))
+ raw_storage_setitem(dataps, CCHARP_SIZE * i,
+ rffi.cast(rffi.CCHARP, arg_i.implementation.get_storage_as_int(space)))
try:
arg1 = rffi.cast(rffi.CArrayPtr(rffi.CCHARP), dataps)
arg2 = rffi.cast(npy_intpp, self.dims)
@@ -1309,29 +1332,24 @@
finally:
free_raw_storage(dataps, track_allocation=False)
+ def set_dims_and_steps(self, space, dims, steps):
+ if not isinstance(dims, list) or not isinstance(steps, list):
+ raise oefmt(space.w_RuntimeError,
+ "set_dims_and_steps called inappropriately")
+ if self.dims_steps_set:
+ raise oefmt(space.w_RuntimeError,
+ "set_dims_and_steps called inappropriately")
+ self.dims = alloc_raw_storage(LONG_SIZE * len(dims), track_allocation=False)
+ self.steps = alloc_raw_storage(LONG_SIZE * len(steps), track_allocation=False)
+ for i in range(len(dims)):
+ raw_storage_setitem(self.dims, LONG_SIZE * i, rffi.cast(rffi.LONG, dims[i]))
+ for i in range(len(steps)):
+ raw_storage_setitem(self.steps, LONG_SIZE * i, rffi.cast(rffi.LONG, steps[i]))
+ self.dims_steps_set = True
+
W_GenericUFuncCaller.typedef = TypeDef("hiddenclass",
__call__ = interp2app(W_GenericUFuncCaller.descr_call),
)
-def set_dims_and_steps(obj, space, dims, steps):
- if not isinstance(obj, W_GenericUFuncCaller):
- raise OperationError(space.w_RuntimeError,
- space.wrap("set_dims_and_steps called inappropriately"))
- if not isinstance(dims, list) or not isinstance(steps, list):
- raise OperationError(space.w_RuntimeError,
- space.wrap("set_dims_and_steps called inappropriately"))
- if len(dims) != len(step):
- raise OperationError(space.w_RuntimeError,
- space.wrap("set_dims_and_steps called inappropriately"))
- if self.dims is not None or self.steps is not None:
- raise OperationError(space.w_RuntimeError,
- space.wrap("set_dims_and_steps called inappropriately"))
- self.dims = alloc_raw_storage(LONG_SIZE * len(dims), track_allocation=False)
- self.steps = alloc_raw_storage(LONG_SIZE * len(dims), track_allocation=False)
- for d in dims:
- raw_storage_setitem(self.dims, LONG_SIZE * i, rffi.cast(rffi.LONG, d))
- for d in steps:
- raw_storage_setitem(self.steps, LONG_SIZE * i, rffi.cast(rffi.LONG, d))
-
GenericUfunc = lltype.FuncType([rffi.CArrayPtr(rffi.CCHARP), npy_intpp, npy_intpp,
rffi.VOIDP], lltype.Void)
More information about the pypy-commit
mailing list