[pypy-commit] pypy ufuncapi: add and start to test _parse_signature
mattip
noreply at buildbot.pypy.org
Wed Aug 20 23:27:56 CEST 2014
Author: mattip <matti.picus at gmail.com>
Branch: ufuncapi
Changeset: r72939:ddd4e75d91b8
Date: 2014-08-20 22:59 +0300
http://bitbucket.org/pypy/pypy/changeset/ddd4e75d91b8/
Log: add and start to test _parse_signature
diff --git a/pypy/module/micronumpy/support.py b/pypy/module/micronumpy/support.py
--- a/pypy/module/micronumpy/support.py
+++ b/pypy/module/micronumpy/support.py
@@ -40,3 +40,110 @@
if index < 0:
index += size
return index
+
+def _next_non_white_space(s, offset):
+ ret = offset
+ while ret < len(s) and (s[ret] == ' ' or s[ret] == '\t'):
+ ret += 1
+ if ret >= len(s):
+ break
+ return ret
+
+def _is_alpha_underscore(ch):
+ return (ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z') or ch == '_'
+
+def _is_alnum_underscore(ch):
+ return _is_alpha_underscore(ch) or (ch >= '0' and ch <='9')
+
+def _parse_signature(space, ufunc, signature):
+ '''
+ rewritten from _parse_signature in numpy/core/src/umath/ufunc_object.c
+ it takes a signature like '(),()->()' or '(i)->(i)' or '(i,j),(j,k)->(i,k)'
+ and sets up the ufunc to handle the actual call appropriately
+
+ cpython numpy chops the dim names i,j,k out of the signature using pointers with
+ no copying, while faster than this code it seems like a marginally useful optimization.
+ We copy them out into var_names
+ '''
+ i = _next_non_white_space(signature, 0)
+ cur_arg = 0
+ cur_core_dim = 0 # index into ufunc.cor_dim_ixs
+ nd = 0 # number of dims of the current argument
+ var_names = {}
+ while i < len(signature):
+ # loop over input/output arguments
+ if cur_arg == ufunc.nin:
+ if signature[i:i+2] != '->':
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "expect '->'", i, signature)
+ i = _next_non_white_space(signature, i+2)
+ # parse core dimensions of one argument,
+ # e.g. "()", "(i)", or "(i,j)"
+ if signature[i] != '(':
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "expect '('", i, signature)
+ i = _next_non_white_space(signature, i+1)
+ end_of_arg = signature.find(')', i)
+ if end_of_arg < 0:
+ raise oefmt(space.w_ValueError, '%s %d in "%s"',
+ "could not find ')' after", i, signature)
+ if end_of_arg == i:
+ # no named arg, skip the next loop
+ next_comma = -1
+ i += 1
+ else:
+ next_comma = signature.find(',', i, end_of_arg)
+ if next_comma < 0:
+ next_comma = end_of_arg
+ while next_comma > 0 and next_comma <= end_of_arg:
+ # loop over core dimensions
+ name_end = next_comma - 1
+ while signature[name_end] == ' ' or signature[name_end] == '\t':
+ name_end -= 1
+ var_name = signature[i:name_end + 1]
+ if not all([_is_alpha_underscore(s) for s in var_name]):
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "expect dimension name", i, signature)
+ if var_name not in var_names:
+ var_names[var_name] = ufunc.core_num_dim_ix
+ ufunc.core_num_dim_ix += 1
+ ufunc.core_dim_ixs[cur_core_dim] = var_names[var_name]
+ cur_core_dim += 1
+ nd += 1
+ i = next_comma
+ i = _next_non_white_space(signature, i + 1)
+ if signature[i] != ',' and signature[i] != ')' and signature[i] != '-':
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "expect ',' or ')' or '-'", i, signature)
+ if signature[i] == ',':
+ i = _next_non_white_space(signature, i + 1);
+ if signature[i] == ')':
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "',' must not be followed by ')'", i, signature)
+ if end_of_arg <= i:
+ next_comma = -1
+ i = end_of_arg + 1
+ else:
+ next_comma = signature.find(',', i, end_of_arg)
+ if next_comma < 0:
+ next_comma = end_of_arg
+ ufunc.core_num_dims[cur_arg] = nd
+ ufunc.core_offsets[cur_arg] = cur_core_dim - nd
+ cur_arg += 1
+ nd = 0
+ if i < len(signature):
+ i = _next_non_white_space(signature, i)
+ if cur_arg != ufunc.nin and cur_arg != ufunc.nargs:
+ # The list of input arguments (or output arguments) was
+ # only read partially
+ if signature[i] != ',':
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "expect ','", i, signature)
+ i = _next_non_white_space(signature, i + 1);
+ if cur_arg != ufunc.nargs:
+ raise oefmt(space.w_ValueError, '%s at %d in "%s"',
+ "incomplete signature: not all arguments found", i, signature)
+ ufunc.core_dim_ixs = ufunc.core_dim_ixs[:cur_core_dim]
+ if cur_core_dim == 0:
+ ufunc.core_enabled = 0
+ return 0 # for historical reasons, any failures will raise
diff --git a/pypy/module/micronumpy/test/test_support.py b/pypy/module/micronumpy/test/test_support.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_support.py
@@ -0,0 +1,30 @@
+from pypy.module.micronumpy import support
+from pypy.module.micronumpy.ufuncs import W_UfuncGeneric
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+class TestParseSignatureDirect(BaseNumpyAppTest):
+ def test_signature_basic(self):
+ space = self.space
+ funcs = [None]
+ name = 'dummy ufunc'
+ identity = None
+ dtypes = [int, int, int]
+
+ nin = 2
+ nout = 1
+ signature = '(), () -> ( ) '
+ ufunc = W_UfuncGeneric(space, funcs, name, identity, nin, nout, dtypes, signature)
+ # make sure no attributes are added
+ attribs = set(ufunc.__dict__.keys())
+ support._parse_signature(space, ufunc, ufunc.signature)
+ new_attribs = set(ufunc.__dict__.keys())
+ assert attribs == new_attribs
+ assert sum(ufunc.core_num_dims) == 0
+ assert ufunc.core_enabled == 0
+
+ nin = 2
+ nout = 1
+ signature = '(i),(i)->()'
+ ufunc = W_UfuncGeneric(space, funcs, name, identity, nin, nout, dtypes, signature)
+ support._parse_signature(space, ufunc, ufunc.signature)
+ assert ufunc.core_enabled == 1
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
@@ -10,6 +10,7 @@
from pypy.module.micronumpy.base import convert_to_array, W_NDimArray
from pypy.module.micronumpy.ctors import numpify
from pypy.module.micronumpy.strides import shape_agreement
+from pypy.module.micronumpy.support import _parse_signature
def done_if_true(dtype, val):
@@ -490,8 +491,10 @@
If dtypes == 'match', only one argument is provided and the output dtypes
will match the input dtype (not cpython numpy compatible)
+
+ This is the parallel to PyUFuncOjbect, see include/numpy/ufuncobject.h
'''
- _immutable_fields_ = ["funcs", "dtypes", "data"]
+ _immutable_fields_ = ["funcs", "dtypes", "data", "match_dtypes"]
def __init__(self, space, funcs, name, identity, nin, nout, dtypes, signature, match_dtypes=False):
# XXX make sure funcs, signature, dtypes, nin, nout are consistent
@@ -515,6 +518,12 @@
"generic ufunc with %d functions, %d arguments, but %d dtypes",
len(funcs), self.nargs, len(dtypes))
self.signature = signature
+ #These will be filled in by _parse_signature
+ self.core_enabled = True # False for scalar ufunc, True for generalized ufunc
+ self.core_num_dim_ix = 0 # number of distinct dimention names in signature
+ self.core_num_dims = [0] * self.nargs # number of core dimensions of each nargs
+ self.core_offsets = [0] * self.nargs
+ self.core_dim_ixs = [0] * len(signature)
def reduce(self, space, w_obj, w_axis, keepdims=False, out=None, dtype=None,
cumulative=False):
@@ -1027,6 +1036,7 @@
w_ret = W_UfuncGeneric(space, func, name, identity, nin, nout, dtypes, signature,
match_dtypes=match_dtypes)
+ _parse_signature(space, w_ret, w_ret.signature)
if doc:
w_ret.w_doc = space.wrap(doc)
return w_ret
More information about the pypy-commit
mailing list