[pypy-commit] pypy default: (fijal, agaynor review) Merge refactor-signature branch. This is mostly
fijal
noreply at buildbot.pypy.org
Tue Dec 20 23:27:12 CET 2011
Author: Maciej Fijalkowski <fijall at gmail.com>
Branch:
Changeset: r50791:931240eb1ae7
Date: 2011-12-21 00:26 +0200
http://bitbucket.org/pypy/pypy/changeset/931240eb1ae7/
Log: (fijal, agaynor review) Merge refactor-signature branch. This is
mostly a refactor of signature.py, but brings a few new features as
well:
* sharing of iterators within one expression
* specializing on array storage
* virtual views
diff --git a/pypy/module/micronumpy/REVIEW.txt b/pypy/module/micronumpy/REVIEW.txt
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/REVIEW.txt
@@ -0,0 +1,10 @@
+REVIEW NOTES
+============
+
+* VirtualSlice vs. W_NDimSlice?
+* W_NDimSlice.__init__ calls ConcreteArray.__init__ instead of
+ ViewArray.__init__, W_FlatIterator as well.
+* Cleanup of the iterator and array caching/numbering. It's a mess right now:
+ * _creater_iter updates the arraylist
+ * Why do Scalars need an iterator at all?
+ * Do views share storage with concrete arrays or other views?
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -21,7 +21,6 @@
_immutable_fields_ = ["itemtype", "num", "kind"]
def __init__(self, itemtype, num, kind, name, char, w_box_type, alternate_constructors=[]):
- self.signature = signature.BaseSignature()
self.itemtype = itemtype
self.num = num
self.kind = kind
@@ -228,4 +227,4 @@
)
def get_dtype_cache(space):
- return space.fromcache(DtypeCache)
\ No newline at end of file
+ return space.fromcache(DtypeCache)
diff --git a/pypy/module/micronumpy/interp_extras.py b/pypy/module/micronumpy/interp_extras.py
--- a/pypy/module/micronumpy/interp_extras.py
+++ b/pypy/module/micronumpy/interp_extras.py
@@ -4,4 +4,4 @@
@unwrap_spec(array=BaseArray)
def debug_repr(space, array):
- return space.wrap(array.debug_repr())
+ return space.wrap(array.find_sig().debug_repr())
diff --git a/pypy/module/micronumpy/interp_iter.py b/pypy/module/micronumpy/interp_iter.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_iter.py
@@ -0,0 +1,104 @@
+
+from pypy.rlib import jit
+from pypy.rlib.objectmodel import instantiate
+from pypy.module.micronumpy.strides import calculate_broadcast_strides
+
+# Iterators for arrays
+# --------------------
+# all those iterators with the exception of BroadcastIterator iterate over the
+# entire array in C order (the last index changes the fastest). This will
+# yield all elements. Views iterate over indices and look towards strides and
+# backstrides to find the correct position. Notably the offset between
+# x[..., i + 1] and x[..., i] will be strides[-1]. Offset between
+# x[..., k + 1, 0] and x[..., k, i_max] will be backstrides[-2] etc.
+
+# BroadcastIterator works like that, but for indexes that don't change source
+# in the original array, strides[i] == backstrides[i] == 0
+
+class BaseIterator(object):
+ def next(self, shapelen):
+ raise NotImplementedError
+
+ def done(self):
+ raise NotImplementedError
+
+class ArrayIterator(BaseIterator):
+ def __init__(self, size):
+ self.offset = 0
+ self.size = size
+
+ def next(self, shapelen):
+ arr = instantiate(ArrayIterator)
+ arr.size = self.size
+ arr.offset = self.offset + 1
+ return arr
+
+ def done(self):
+ return self.offset >= self.size
+
+class OneDimIterator(BaseIterator):
+ def __init__(self, start, step, stop):
+ self.offset = start
+ self.step = step
+ self.size = stop * step + start
+
+ def next(self, shapelen):
+ arr = instantiate(OneDimIterator)
+ arr.size = self.size
+ arr.step = self.step
+ arr.offset = self.offset + self.step
+ return arr
+
+ def done(self):
+ return self.offset == self.size
+
+def view_iter_from_arr(arr):
+ return ViewIterator(arr.start, arr.strides, arr.backstrides, arr.shape)
+
+class ViewIterator(BaseIterator):
+ def __init__(self, start, strides, backstrides, shape, res_shape=None):
+ self.offset = start
+ self._done = False
+ if res_shape is not None and res_shape != shape:
+ r = calculate_broadcast_strides(strides, backstrides,
+ shape, res_shape)
+ self.strides, self.backstrides = r
+ self.res_shape = res_shape
+ else:
+ self.strides = strides
+ self.backstrides = backstrides
+ self.res_shape = shape
+ self.indices = [0] * len(self.res_shape)
+
+ @jit.unroll_safe
+ def next(self, shapelen):
+ offset = self.offset
+ indices = [0] * shapelen
+ for i in range(shapelen):
+ indices[i] = self.indices[i]
+ done = False
+ for i in range(shapelen - 1, -1, -1):
+ if indices[i] < self.res_shape[i] - 1:
+ indices[i] += 1
+ offset += self.strides[i]
+ break
+ else:
+ indices[i] = 0
+ offset -= self.backstrides[i]
+ else:
+ done = True
+ res = instantiate(ViewIterator)
+ res.offset = offset
+ res.indices = indices
+ res.strides = self.strides
+ res.backstrides = self.backstrides
+ res.res_shape = self.res_shape
+ res._done = done
+ return res
+
+ def done(self):
+ return self._done
+
+class ConstantIterator(BaseIterator):
+ def next(self, shapelen):
+ return self
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -3,28 +3,33 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.module.micronumpy import interp_ufuncs, interp_dtype, signature
+from pypy.module.micronumpy.strides import calculate_slice_strides
from pypy.rlib import jit
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.tool.sourcetools import func_with_new_name
from pypy.rlib.rstring import StringBuilder
-from pypy.rlib.objectmodel import instantiate
-
+from pypy.module.micronumpy.interp_iter import ArrayIterator,\
+ view_iter_from_arr, OneDimIterator
numpy_driver = jit.JitDriver(
- greens=['shapelen', 'signature'],
- reds=['result_size', 'i', 'ri', 'self', 'result']
+ greens=['shapelen', 'sig'],
+ virtualizables=['frame'],
+ reds=['result_size', 'frame', 'ri', 'self', 'result']
)
all_driver = jit.JitDriver(
- greens=['shapelen', 'signature'],
- reds=['i', 'self', 'dtype']
+ greens=['shapelen', 'sig'],
+ virtualizables=['frame'],
+ reds=['frame', 'self', 'dtype']
)
any_driver = jit.JitDriver(
- greens=['shapelen', 'signature'],
- reds=['i', 'self', 'dtype']
+ greens=['shapelen', 'sig'],
+ virtualizables=['frame'],
+ reds=['frame', 'self', 'dtype']
)
slice_driver = jit.JitDriver(
- greens=['shapelen', 'signature'],
- reds=['self', 'source', 'source_iter', 'res_iter']
+ greens=['shapelen', 'sig'],
+ virtualizables=['frame'],
+ reds=['self', 'frame', 'source', 'res_iter']
)
def _find_shape_and_elems(space, w_iterable):
@@ -198,231 +203,17 @@
n_old_elems_to_use *= old_shape[oldI]
return new_strides
-# Iterators for arrays
-# --------------------
-# all those iterators with the exception of BroadcastIterator iterate over the
-# entire array in C order (the last index changes the fastest). This will
-# yield all elements. Views iterate over indices and look towards strides and
-# backstrides to find the correct position. Notably the offset between
-# x[..., i + 1] and x[..., i] will be strides[-1]. Offset between
-# x[..., k + 1, 0] and x[..., k, i_max] will be backstrides[-2] etc.
+class BaseArray(Wrappable):
+ _attrs_ = ["invalidates", "shape", 'size']
-# BroadcastIterator works like that, but for indexes that don't change source
-# in the original array, strides[i] == backstrides[i] == 0
-
-class BaseIterator(object):
- def next(self, shapelen):
- raise NotImplementedError
-
- def done(self):
- raise NotImplementedError
-
- def get_offset(self):
- raise NotImplementedError
-
-class ArrayIterator(BaseIterator):
- def __init__(self, size):
- self.offset = 0
- self.size = size
-
- def next(self, shapelen):
- arr = instantiate(ArrayIterator)
- arr.size = self.size
- arr.offset = self.offset + 1
- return arr
-
- def done(self):
- return self.offset >= self.size
-
- def get_offset(self):
- return self.offset
-
-class OneDimIterator(BaseIterator):
- def __init__(self, start, step, stop):
- self.offset = start
- self.step = step
- self.size = stop * step + start
-
- def next(self, shapelen):
- arr = instantiate(OneDimIterator)
- arr.size = self.size
- arr.step = self.step
- arr.offset = self.offset + self.step
- return arr
-
- def done(self):
- return self.offset == self.size
-
- def get_offset(self):
- return self.offset
-
-class ViewIterator(BaseIterator):
- def __init__(self, arr):
- self.indices = [0] * len(arr.shape)
- self.offset = arr.start
- self.arr = arr
- self._done = False
-
- @jit.unroll_safe
- def next(self, shapelen):
- offset = self.offset
- indices = [0] * shapelen
- for i in range(shapelen):
- indices[i] = self.indices[i]
- done = False
- for i in range(shapelen - 1, -1, -1):
- if indices[i] < self.arr.shape[i] - 1:
- indices[i] += 1
- offset += self.arr.strides[i]
- break
- else:
- indices[i] = 0
- offset -= self.arr.backstrides[i]
- else:
- done = True
- res = instantiate(ViewIterator)
- res.offset = offset
- res.indices = indices
- res.arr = self.arr
- res._done = done
- return res
-
- def done(self):
- return self._done
-
- def get_offset(self):
- return self.offset
-
-class BroadcastIterator(BaseIterator):
- '''Like a view iterator, but will repeatedly access values
- for all iterations across a res_shape, folding the offset
- using mod() arithmetic
- '''
- def __init__(self, arr, res_shape):
- self.indices = [0] * len(res_shape)
- self.offset = arr.start
- #strides are 0 where original shape==1
- self.strides = []
- self.backstrides = []
- for i in range(len(arr.shape)):
- if arr.shape[i] == 1:
- self.strides.append(0)
- self.backstrides.append(0)
- else:
- self.strides.append(arr.strides[i])
- self.backstrides.append(arr.backstrides[i])
- self.res_shape = res_shape
- self.strides = [0] * (len(res_shape) - len(arr.shape)) + self.strides
- self.backstrides = [0] * (len(res_shape) - len(arr.shape)) + self.backstrides
- self._done = False
-
- @jit.unroll_safe
- def next(self, shapelen):
- offset = self.offset
- indices = [0] * shapelen
- _done = False
- for i in range(shapelen):
- indices[i] = self.indices[i]
- for i in range(shapelen - 1, -1, -1):
- if indices[i] < self.res_shape[i] - 1:
- indices[i] += 1
- offset += self.strides[i]
- break
- else:
- indices[i] = 0
- offset -= self.backstrides[i]
- else:
- _done = True
- res = instantiate(BroadcastIterator)
- res.indices = indices
- res.offset = offset
- res._done = _done
- res.strides = self.strides
- res.backstrides = self.backstrides
- res.res_shape = self.res_shape
- return res
-
- def done(self):
- return self._done
-
- def get_offset(self):
- return self.offset
-
-class Call2Iterator(BaseIterator):
- def __init__(self, left, right):
- self.left = left
- self.right = right
-
- def next(self, shapelen):
- return Call2Iterator(self.left.next(shapelen),
- self.right.next(shapelen))
-
- def done(self):
- if isinstance(self.left, ConstantIterator):
- return self.right.done()
- return self.left.done()
-
- def get_offset(self):
- if isinstance(self.left, ConstantIterator):
- return self.right.get_offset()
- return self.left.get_offset()
-
-class Call1Iterator(BaseIterator):
- def __init__(self, child):
- self.child = child
-
- def next(self, shapelen):
- return Call1Iterator(self.child.next(shapelen))
-
- def done(self):
- return self.child.done()
-
- def get_offset(self):
- return self.child.get_offset()
-
-class ConstantIterator(BaseIterator):
- def next(self, shapelen):
- return self
-
- def done(self):
- return False
-
- def get_offset(self):
- return 0
-
-
-class BaseArray(Wrappable):
- _attrs_ = ["invalidates", "signature", "shape", "strides", "backstrides",
- "start", 'order']
-
- _immutable_fields_ = ['start', "order"]
+ _immutable_fields_ = []
strides = None
start = 0
- def __init__(self, shape, order):
+ def __init__(self, shape):
self.invalidates = []
self.shape = shape
- self.order = order
- if self.strides is None:
- self.calc_strides(shape)
-
- def calc_strides(self, shape):
- strides = []
- backstrides = []
- s = 1
- shape_rev = shape[:]
- if self.order == 'C':
- shape_rev.reverse()
- for sh in shape_rev:
- strides.append(s)
- backstrides.append(s * (sh - 1))
- s *= sh
- if self.order == 'C':
- strides.reverse()
- backstrides.reverse()
- self.strides = strides[:]
- self.backstrides = backstrides[:]
def invalidated(self):
if self.invalidates:
@@ -499,33 +290,34 @@
def _reduce_argmax_argmin_impl(op_name):
reduce_driver = jit.JitDriver(
- greens=['shapelen', 'signature'],
- reds=['result', 'idx', 'i', 'self', 'cur_best', 'dtype']
+ greens=['shapelen', 'sig'],
+ reds=['result', 'idx', 'frame', 'self', 'cur_best', 'dtype']
)
def loop(self):
- i = self.start_iter()
- cur_best = self.eval(i)
+ sig = self.find_sig()
+ frame = sig.create_frame(self)
+ cur_best = sig.eval(frame, self)
shapelen = len(self.shape)
- i = i.next(shapelen)
+ frame.next(shapelen)
dtype = self.find_dtype()
result = 0
idx = 1
- while not i.done():
- reduce_driver.jit_merge_point(signature=self.signature,
+ while not frame.done():
+ reduce_driver.jit_merge_point(sig=sig,
shapelen=shapelen,
self=self, dtype=dtype,
- i=i, result=result, idx=idx,
+ frame=frame, result=result,
+ idx=idx,
cur_best=cur_best)
- new_best = getattr(dtype.itemtype, op_name)(cur_best, self.eval(i))
+ new_best = getattr(dtype.itemtype, op_name)(cur_best, sig.eval(frame, self))
if dtype.itemtype.ne(new_best, cur_best):
result = idx
cur_best = new_best
- i = i.next(shapelen)
+ frame.next(shapelen)
idx += 1
return result
def impl(self, space):
- size = self.find_size()
- if size == 0:
+ if self.size == 0:
raise OperationError(space.w_ValueError,
space.wrap("Can't call %s on zero-size arrays" % op_name))
return space.wrap(loop(self))
@@ -533,15 +325,16 @@
def _all(self):
dtype = self.find_dtype()
- i = self.start_iter()
+ sig = self.find_sig()
+ frame = sig.create_frame(self)
shapelen = len(self.shape)
- while not i.done():
- all_driver.jit_merge_point(signature=self.signature,
+ while not frame.done():
+ all_driver.jit_merge_point(sig=sig,
shapelen=shapelen, self=self,
- dtype=dtype, i=i)
- if not dtype.itemtype.bool(self.eval(i)):
+ dtype=dtype, frame=frame)
+ if not dtype.itemtype.bool(sig.eval(frame, self)):
return False
- i = i.next(shapelen)
+ frame.next(shapelen)
return True
def descr_all(self, space):
@@ -549,15 +342,16 @@
def _any(self):
dtype = self.find_dtype()
- i = self.start_iter()
+ sig = self.find_sig()
+ frame = sig.create_frame(self)
shapelen = len(self.shape)
- while not i.done():
- any_driver.jit_merge_point(signature=self.signature,
+ while not frame.done():
+ any_driver.jit_merge_point(sig=sig, frame=frame,
shapelen=shapelen, self=self,
- dtype=dtype, i=i)
- if dtype.itemtype.bool(self.eval(i)):
+ dtype=dtype)
+ if dtype.itemtype.bool(sig.eval(frame, self)):
return True
- i = i.next(shapelen)
+ frame.next(shapelen)
return False
def descr_any(self, space):
@@ -586,26 +380,33 @@
return space.newtuple([space.wrap(i) for i in self.shape])
def descr_set_shape(self, space, w_iterable):
- concrete = self.get_concrete()
new_shape = get_shape_from_iterable(space,
- concrete.find_size(), w_iterable)
- concrete.setshape(space, new_shape)
+ self.size, w_iterable)
+ if isinstance(self, Scalar):
+ return
+ self.get_concrete().setshape(space, new_shape)
def descr_get_size(self, space):
- return space.wrap(self.find_size())
+ return space.wrap(self.size)
def descr_copy(self, space):
+ return self.copy()
+
+ def copy(self):
return self.get_concrete().copy()
def descr_len(self, space):
- return self.get_concrete().descr_len(space)
+ if len(self.shape):
+ return space.wrap(self.shape[0])
+ raise OperationError(space.w_TypeError, space.wrap(
+ "len() of unsized object"))
def descr_repr(self, space):
res = StringBuilder()
res.append("array(")
concrete = self.get_concrete()
dtype = concrete.find_dtype()
- if not concrete.find_size():
+ if not concrete.size:
res.append('[]')
if len(self.shape) > 1:
# An empty slice reports its shape
@@ -617,18 +418,417 @@
concrete.to_str(space, 1, res, indent=' ')
if (dtype is not interp_dtype.get_dtype_cache(space).w_float64dtype and
dtype is not interp_dtype.get_dtype_cache(space).w_int64dtype) or \
- not self.find_size():
+ not self.size:
res.append(", dtype=" + dtype.name)
res.append(")")
return space.wrap(res.build())
+ def descr_str(self, space):
+ ret = StringBuilder()
+ concrete = self.get_concrete_or_scalar()
+ concrete.to_str(space, 0, ret, ' ')
+ return space.wrap(ret.build())
+
+ @jit.unroll_safe
+ def _single_item_result(self, space, w_idx):
+ """ The result of getitem/setitem is a single item if w_idx
+ is a list of scalars that match the size of shape
+ """
+ shape_len = len(self.shape)
+ if shape_len == 0:
+ raise OperationError(space.w_IndexError, space.wrap(
+ "0-d arrays can't be indexed"))
+ if shape_len == 1:
+ if space.isinstance_w(w_idx, space.w_int):
+ return True
+ if space.isinstance_w(w_idx, space.w_slice):
+ return False
+ elif (space.isinstance_w(w_idx, space.w_slice) or
+ space.isinstance_w(w_idx, space.w_int)):
+ return False
+ lgt = space.len_w(w_idx)
+ if lgt > shape_len:
+ raise OperationError(space.w_IndexError,
+ space.wrap("invalid index"))
+ if lgt < shape_len:
+ return False
+ for w_item in space.fixedview(w_idx):
+ if space.isinstance_w(w_item, space.w_slice):
+ return False
+ return True
+
+ @jit.unroll_safe
+ def _prepare_slice_args(self, space, w_idx):
+ if (space.isinstance_w(w_idx, space.w_int) or
+ space.isinstance_w(w_idx, space.w_slice)):
+ return [space.decode_index4(w_idx, self.shape[0])]
+ return [space.decode_index4(w_item, self.shape[i]) for i, w_item in
+ enumerate(space.fixedview(w_idx))]
+
+ def descr_getitem(self, space, w_idx):
+ if self._single_item_result(space, w_idx):
+ concrete = self.get_concrete()
+ item = concrete._index_of_single_item(space, w_idx)
+ return concrete.getitem(item)
+ chunks = self._prepare_slice_args(space, w_idx)
+ return space.wrap(self.create_slice(chunks))
+
+ def descr_setitem(self, space, w_idx, w_value):
+ self.invalidated()
+ if self._single_item_result(space, w_idx):
+ concrete = self.get_concrete()
+ item = concrete._index_of_single_item(space, w_idx)
+ dtype = concrete.find_dtype()
+ concrete.setitem(item, dtype.coerce(space, w_value))
+ return
+ if not isinstance(w_value, BaseArray):
+ w_value = convert_to_array(space, w_value)
+ chunks = self._prepare_slice_args(space, w_idx)
+ view = self.create_slice(chunks).get_concrete()
+ view.setslice(space, w_value)
+
+ @jit.unroll_safe
+ def create_slice(self, chunks):
+ shape = []
+ i = -1
+ for i, (start_, stop, step, lgt) in enumerate(chunks):
+ if step != 0:
+ shape.append(lgt)
+ s = i + 1
+ assert s >= 0
+ shape += self.shape[s:]
+ if not isinstance(self, ConcreteArray):
+ return VirtualSlice(self, chunks, shape)
+ r = calculate_slice_strides(self.shape, self.start, self.strides,
+ self.backstrides, chunks)
+ _, start, strides, backstrides = r
+ return W_NDimSlice(start, strides[:], backstrides[:],
+ shape[:], self)
+
+ def descr_reshape(self, space, args_w):
+ """reshape(...)
+ a.reshape(shape)
+
+ Returns an array containing the same data with a new shape.
+
+ Refer to `numpypy.reshape` for full documentation.
+
+ See Also
+ --------
+ numpypy.reshape : equivalent function
+ """
+ if len(args_w) == 1:
+ w_shape = args_w[0]
+ else:
+ w_shape = space.newtuple(args_w)
+ concrete = self.get_concrete()
+ new_shape = get_shape_from_iterable(space, concrete.size, w_shape)
+ # Since we got to here, prod(new_shape) == self.size
+ new_strides = calc_new_strides(new_shape,
+ concrete.shape, concrete.strides)
+ if new_strides:
+ # We can create a view, strides somehow match up.
+ ndims = len(new_shape)
+ new_backstrides = [0] * ndims
+ for nd in range(ndims):
+ new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
+ arr = W_NDimSlice(concrete.start, new_strides, new_backstrides,
+ new_shape, self)
+ else:
+ # Create copy with contiguous data
+ arr = concrete.copy()
+ arr.setshape(space, new_shape)
+ return arr
+
+ def descr_tolist(self, space):
+ if len(self.shape) == 0:
+ assert isinstance(self, Scalar)
+ return self.value.descr_tolist(space)
+ w_result = space.newlist([])
+ for i in range(self.shape[0]):
+ space.call_method(w_result, "append",
+ space.call_method(self.descr_getitem(space, space.wrap(i)), "tolist")
+ )
+ return w_result
+
+ def descr_mean(self, space):
+ return space.div(self.descr_sum(space), space.wrap(self.size))
+
+ def descr_nonzero(self, space):
+ if self.size > 1:
+ raise OperationError(space.w_ValueError, space.wrap(
+ "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"))
+ concr = self.get_concrete_or_scalar()
+ sig = concr.find_sig()
+ frame = sig.create_frame(self)
+ return space.wrap(space.is_true(
+ sig.eval(frame, concr)))
+
+ def get_concrete_or_scalar(self):
+ return self.get_concrete()
+
+ def descr_get_transpose(self, space):
+ concrete = self.get_concrete()
+ if len(concrete.shape) < 2:
+ return space.wrap(self)
+ strides = []
+ backstrides = []
+ shape = []
+ for i in range(len(concrete.shape) - 1, -1, -1):
+ strides.append(concrete.strides[i])
+ backstrides.append(concrete.backstrides[i])
+ shape.append(concrete.shape[i])
+ return space.wrap(W_NDimSlice(concrete.start, strides[:],
+ backstrides[:], shape[:], concrete))
+
+ def descr_get_flatiter(self, space):
+ return space.wrap(W_FlatIterator(self))
+
+ def getitem(self, item):
+ raise NotImplementedError
+
+ def find_sig(self, res_shape=None):
+ """ find a correct signature for the array
+ """
+ res_shape = res_shape or self.shape
+ return signature.find_sig(self.create_sig(res_shape), self)
+
+ def descr_array_iface(self, space):
+ if not self.shape:
+ raise OperationError(space.w_TypeError,
+ space.wrap("can't get the array data of a 0-d array for now")
+ )
+ concrete = self.get_concrete()
+ storage = concrete.storage
+ addr = rffi.cast(lltype.Signed, storage)
+ w_d = space.newdict()
+ space.setitem_str(w_d, 'data', space.newtuple([space.wrap(addr),
+ space.w_False]))
+ return w_d
+
+def convert_to_array(space, w_obj):
+ if isinstance(w_obj, BaseArray):
+ return w_obj
+ elif space.issequence_w(w_obj):
+ # Convert to array.
+ return array(space, w_obj, w_order=None)
+ else:
+ # If it's a scalar
+ dtype = interp_ufuncs.find_dtype_for_scalar(space, w_obj)
+ return scalar_w(space, dtype, w_obj)
+
+def scalar_w(space, dtype, w_obj):
+ return Scalar(dtype, dtype.coerce(space, w_obj))
+
+class Scalar(BaseArray):
+ """
+ Intermediate class representing a literal.
+ """
+ size = 1
+ _attrs_ = ["dtype", "value", "shape"]
+
+ def __init__(self, dtype, value):
+ self.shape = []
+ BaseArray.__init__(self, [])
+ self.dtype = dtype
+ self.value = value
+
+ def find_dtype(self):
+ return self.dtype
+
+ def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
+ builder.append(self.dtype.itemtype.str_format(self.value))
+
+ def copy(self):
+ return Scalar(self.dtype, self.value)
+
+ def create_sig(self, res_shape):
+ return signature.ScalarSignature(self.dtype)
+
+ def get_concrete_or_scalar(self):
+ return self
+
+
+class VirtualArray(BaseArray):
+ """
+ Class for representing virtual arrays, such as binary ops or ufuncs
+ """
+ def __init__(self, name, shape, res_dtype):
+ BaseArray.__init__(self, shape)
+ self.forced_result = None
+ self.res_dtype = res_dtype
+ self.name = name
+
+ def _del_sources(self):
+ # Function for deleting references to source arrays, to allow garbage-collecting them
+ raise NotImplementedError
+
+ def compute(self):
+ result = W_NDimArray(self.size, self.shape, self.find_dtype())
+ shapelen = len(self.shape)
+ sig = self.find_sig()
+ frame = sig.create_frame(self)
+ ri = ArrayIterator(self.size)
+ while not ri.done():
+ numpy_driver.jit_merge_point(sig=sig,
+ shapelen=shapelen,
+ result_size=self.size,
+ frame=frame,
+ ri=ri,
+ self=self, result=result)
+ result.dtype.setitem(result.storage, ri.offset,
+ sig.eval(frame, self))
+ frame.next(shapelen)
+ ri = ri.next(shapelen)
+ return result
+
+ def force_if_needed(self):
+ if self.forced_result is None:
+ self.forced_result = self.compute()
+ self._del_sources()
+
+ def get_concrete(self):
+ self.force_if_needed()
+ res = self.forced_result
+ assert isinstance(res, ConcreteArray)
+ return res
+
+ def getitem(self, item):
+ return self.get_concrete().getitem(item)
+
+ def setitem(self, item, value):
+ return self.get_concrete().setitem(item, value)
+
+ def find_dtype(self):
+ return self.res_dtype
+
+class VirtualSlice(VirtualArray):
+ def __init__(self, child, chunks, shape):
+ size = 1
+ for sh in shape:
+ size *= sh
+ self.child = child
+ self.chunks = chunks
+ self.size = size
+ VirtualArray.__init__(self, 'slice', shape, child.find_dtype())
+
+ def create_sig(self, res_shape):
+ if self.forced_result is not None:
+ return self.forced_result.create_sig(res_shape)
+ return signature.VirtualSliceSignature(
+ self.child.create_sig(res_shape))
+
+ def force_if_needed(self):
+ if self.forced_result is None:
+ concr = self.child.get_concrete()
+ self.forced_result = concr.create_slice(self.chunks)
+
+ def _del_sources(self):
+ self.child = None
+
+class Call1(VirtualArray):
+ def __init__(self, ufunc, name, shape, res_dtype, values):
+ VirtualArray.__init__(self, name, shape, res_dtype)
+ self.values = values
+ self.size = values.size
+ self.ufunc = ufunc
+
+ def _del_sources(self):
+ self.values = None
+
+ def create_sig(self, res_shape):
+ if self.forced_result is not None:
+ return self.forced_result.create_sig(res_shape)
+ return signature.Call1(self.ufunc, self.name,
+ self.values.create_sig(res_shape))
+
+class Call2(VirtualArray):
+ """
+ Intermediate class for performing binary operations.
+ """
+ def __init__(self, ufunc, name, shape, calc_dtype, res_dtype, left, right):
+ VirtualArray.__init__(self, name, shape, res_dtype)
+ self.ufunc = ufunc
+ self.left = left
+ self.right = right
+ self.calc_dtype = calc_dtype
+ self.size = 1
+ for s in self.shape:
+ self.size *= s
+
+ def _del_sources(self):
+ self.left = None
+ self.right = None
+
+ def create_sig(self, res_shape):
+ if self.forced_result is not None:
+ return self.forced_result.create_sig(res_shape)
+ return signature.Call2(self.ufunc, self.name, self.calc_dtype,
+ self.left.create_sig(res_shape),
+ self.right.create_sig(res_shape))
+
+class ConcreteArray(BaseArray):
+ """ An array that have actual storage, whether owned or not
+ """
+ _immutable_fields_ = ['storage']
+
+ def __init__(self, size, shape, dtype, order='C', parent=None):
+ self.size = size
+ self.parent = parent
+ if parent is not None:
+ self.storage = parent.storage
+ else:
+ self.storage = dtype.malloc(size)
+ self.order = order
+ self.dtype = dtype
+ if self.strides is None:
+ self.calc_strides(shape)
+ BaseArray.__init__(self, shape)
+ if parent is not None:
+ self.invalidates = parent.invalidates
+
+ def get_concrete(self):
+ return self
+
+ def find_dtype(self):
+ return self.dtype
+
+ def getitem(self, item):
+ return self.dtype.getitem(self.storage, item)
+
+ def setitem(self, item, value):
+ self.invalidated()
+ self.dtype.setitem(self.storage, item, value)
+
+ def calc_strides(self, shape):
+ strides = []
+ backstrides = []
+ s = 1
+ shape_rev = shape[:]
+ if self.order == 'C':
+ shape_rev.reverse()
+ for sh in shape_rev:
+ strides.append(s)
+ backstrides.append(s * (sh - 1))
+ s *= sh
+ if self.order == 'C':
+ strides.reverse()
+ backstrides.reverse()
+ self.strides = strides[:]
+ self.backstrides = backstrides[:]
+
+ def array_sig(self, res_shape):
+ if res_shape is not None and self.shape != res_shape:
+ return signature.ViewSignature(self.dtype)
+ return signature.ArraySignature(self.dtype)
+
def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
'''Modifies builder with a representation of the array/slice
The items will be seperated by a comma if comma is 1
Multidimensional arrays/slices will span a number of lines,
each line will begin with indent.
'''
- size = self.find_size()
+ size = self.size
if size < 1:
builder.append('[]')
return
@@ -654,7 +854,7 @@
builder.append(indent)
# create_slice requires len(chunks) > 1 in order to reduce
# shape
- view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+ view = self.create_slice([(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])]).get_concrete()
view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
builder.append('\n' + indent + '..., ')
i = self.shape[0] - 3
@@ -669,7 +869,7 @@
builder.append(indent)
# create_slice requires len(chunks) > 1 in order to reduce
# shape
- view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+ view = self.create_slice([(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])]).get_concrete()
view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
i += 1
elif ndims == 1:
@@ -705,12 +905,6 @@
builder.append('[')
builder.append(']')
- def descr_str(self, space):
- ret = StringBuilder()
- concrete = self.get_concrete()
- concrete.to_str(space, 0, ret, ' ')
- return space.wrap(ret.build())
-
@jit.unroll_safe
def _index_of_single_item(self, space, w_idx):
if space.isinstance_w(w_idx, space.w_int):
@@ -735,456 +929,55 @@
item += v * self.strides[i]
return item
- @jit.unroll_safe
- def _single_item_result(self, space, w_idx):
- """ The result of getitem/setitem is a single item if w_idx
- is a list of scalars that match the size of shape
- """
- shape_len = len(self.shape)
- if shape_len == 0:
- if not space.isinstance_w(w_idx, space.w_int):
- raise OperationError(space.w_IndexError, space.wrap(
- "wrong index"))
- return True
- if shape_len == 1:
- if space.isinstance_w(w_idx, space.w_int):
- return True
- if space.isinstance_w(w_idx, space.w_slice):
- return False
- elif (space.isinstance_w(w_idx, space.w_slice) or
- space.isinstance_w(w_idx, space.w_int)):
- return False
- lgt = space.len_w(w_idx)
- if lgt > shape_len:
- raise OperationError(space.w_IndexError,
- space.wrap("invalid index"))
- if lgt < shape_len:
- return False
- for w_item in space.fixedview(w_idx):
- if space.isinstance_w(w_item, space.w_slice):
- return False
- return True
- @jit.unroll_safe
- def _prepare_slice_args(self, space, w_idx):
- if (space.isinstance_w(w_idx, space.w_int) or
- space.isinstance_w(w_idx, space.w_slice)):
- return [space.decode_index4(w_idx, self.shape[0])]
- return [space.decode_index4(w_item, self.shape[i]) for i, w_item in
- enumerate(space.fixedview(w_idx))]
+class ViewArray(ConcreteArray):
+ def copy(self):
+ array = W_NDimArray(self.size, self.shape[:], self.find_dtype())
+ iter = view_iter_from_arr(self)
+ a_iter = ArrayIterator(array.size)
+ while not iter.done():
+ array.setitem(a_iter.offset, self.getitem(iter.offset))
+ iter = iter.next(len(self.shape))
+ a_iter = a_iter.next(len(array.shape))
+ return array
- def descr_getitem(self, space, w_idx):
- if self._single_item_result(space, w_idx):
- concrete = self.get_concrete()
- if len(concrete.shape) < 1:
- raise OperationError(space.w_IndexError, space.wrap(
- "0-d arrays can't be indexed"))
- item = concrete._index_of_single_item(space, w_idx)
- return concrete.getitem(item)
- chunks = self._prepare_slice_args(space, w_idx)
- return space.wrap(self.create_slice(space, chunks))
+ def create_sig(self, res_shape):
+ return signature.ViewSignature(self.dtype)
- def descr_setitem(self, space, w_idx, w_value):
- self.invalidated()
- if self._single_item_result(space, w_idx):
- concrete = self.get_concrete()
- if len(concrete.shape) < 1:
- raise OperationError(space.w_IndexError, space.wrap(
- "0-d arrays can't be indexed"))
- item = concrete._index_of_single_item(space, w_idx)
- dtype = concrete.find_dtype()
- concrete.setitem(item, dtype.coerce(space, w_value))
- return
- if not isinstance(w_value, BaseArray):
- w_value = convert_to_array(space, w_value)
- chunks = self._prepare_slice_args(space, w_idx)
- view = self.create_slice(space, chunks)
- view.setslice(space, w_value)
- @jit.unroll_safe
- def create_slice(self, space, chunks):
- if len(chunks) == 1:
- start, stop, step, lgt = chunks[0]
- if step == 0:
- shape = self.shape[1:]
- strides = self.strides[1:]
- backstrides = self.backstrides[1:]
- else:
- shape = [lgt] + self.shape[1:]
- strides = [self.strides[0] * step] + self.strides[1:]
- backstrides = [(lgt - 1) * self.strides[0] * step] + self.backstrides[1:]
- start *= self.strides[0]
- start += self.start
- else:
- shape = []
- strides = []
- backstrides = []
- start = self.start
- i = -1
- for i, (start_, stop, step, lgt) in enumerate(chunks):
- if step != 0:
- shape.append(lgt)
- strides.append(self.strides[i] * step)
- backstrides.append(self.strides[i] * (lgt - 1) * step)
- start += self.strides[i] * start_
- # add a reminder
- s = i + 1
- assert s >= 0
- shape += self.shape[s:]
- strides += self.strides[s:]
- backstrides += self.backstrides[s:]
- new_sig = signature.Signature.find_sig([
- W_NDimSlice.signature, self.signature,
- ])
- return W_NDimSlice(self, new_sig, start, strides[:], backstrides[:],
- shape[:])
-
- def descr_reshape(self, space, args_w):
- """reshape(...)
- a.reshape(shape)
-
- Returns an array containing the same data with a new shape.
-
- Refer to `numpypy.reshape` for full documentation.
-
- See Also
- --------
- numpypy.reshape : equivalent function
-"""
- if len(args_w) == 1:
- w_shape = args_w[0]
- else:
- w_shape = space.newtuple(args_w)
- concrete = self.get_concrete()
- new_shape = get_shape_from_iterable(space,
- concrete.find_size(), w_shape)
- # Since we got to here, prod(new_shape) == self.size
- new_strides = calc_new_strides(new_shape,
- concrete.shape, concrete.strides)
- if new_strides:
- # We can create a view, strides somehow match up.
- new_sig = signature.Signature.find_sig([
- W_NDimSlice.signature, self.signature
- ])
- ndims = len(new_shape)
- new_backstrides = [0] * ndims
- for nd in range(ndims):
- new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
- arr = W_NDimSlice(self, new_sig, self.start, new_strides,
- new_backstrides, new_shape)
- else:
- # Create copy with contiguous data
- arr = concrete.copy()
- arr.setshape(space, new_shape)
- return arr
-
- def descr_tolist(self, space):
- if len(self.shape) == 0:
- assert isinstance(self, Scalar)
- return self.value.descr_tolist(space)
- w_result = space.newlist([])
- for i in range(self.shape[0]):
- space.call_method(w_result, "append",
- space.call_method(self.descr_getitem(space, space.wrap(i)), "tolist")
- )
- return w_result
-
- def descr_mean(self, space):
- return space.div(self.descr_sum(space), space.wrap(self.find_size()))
-
- def descr_nonzero(self, space):
- if self.find_size() > 1:
- raise OperationError(space.w_ValueError, space.wrap(
- "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"))
- return space.wrap(space.is_true(
- self.get_concrete().eval(self.start_iter(self.shape))
- ))
-
- def descr_get_transpose(self, space):
- concrete = self.get_concrete()
- if len(concrete.shape) < 2:
- return space.wrap(self)
- new_sig = signature.Signature.find_sig([
- W_NDimSlice.signature, self.signature
- ])
- strides = []
- backstrides = []
- shape = []
- for i in range(len(concrete.shape) - 1, -1, -1):
- strides.append(concrete.strides[i])
- backstrides.append(concrete.backstrides[i])
- shape.append(concrete.shape[i])
- return space.wrap(W_NDimSlice(concrete, new_sig, self.start, strides[:],
- backstrides[:], shape[:]))
-
- def descr_get_flatiter(self, space):
- return space.wrap(W_FlatIterator(self))
-
- def getitem(self, item):
- raise NotImplementedError
-
- def start_iter(self, res_shape=None):
- raise NotImplementedError
-
- def descr_array_iface(self, space):
- concrete = self.get_concrete()
- storage = concrete.get_storage(space)
- addr = rffi.cast(lltype.Signed, storage)
- w_d = space.newdict()
- space.setitem_str(w_d, 'data', space.newtuple([space.wrap(addr),
- space.w_False]))
- return w_d
-
-def convert_to_array(space, w_obj):
- if isinstance(w_obj, BaseArray):
- return w_obj
- elif space.issequence_w(w_obj):
- # Convert to array.
- return array(space, w_obj, w_order=None)
- else:
- # If it's a scalar
- dtype = interp_ufuncs.find_dtype_for_scalar(space, w_obj)
- return scalar_w(space, dtype, w_obj)
-
-def scalar_w(space, dtype, w_obj):
- return Scalar(dtype, dtype.coerce(space, w_obj))
-
-class Scalar(BaseArray):
- """
- Intermediate class representing a literal.
- """
- signature = signature.BaseSignature()
-
- _attrs_ = ["dtype", "value", "shape"]
-
- def __init__(self, dtype, value):
- self.shape = self.strides = []
- BaseArray.__init__(self, [], 'C')
- self.dtype = dtype
- self.value = value
-
- def find_size(self):
- return 1
-
- def get_concrete(self):
- return self
-
- def find_dtype(self):
- return self.dtype
-
- def getitem(self, item):
- raise NotImplementedError
-
- def eval(self, iter):
- return self.value
-
- def start_iter(self, res_shape=None):
- return ConstantIterator()
-
- def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
- builder.append(self.dtype.itemtype.str_format(self.value))
-
- def copy(self):
- return Scalar(self.dtype, self.value)
-
- def debug_repr(self):
- return 'Scalar'
-
- def setshape(self, space, new_shape):
- # In order to get here, we already checked that prod(new_shape) == 1,
- # so in order to have a consistent API, let it go through.
- pass
-
- def get_storage(self, space):
- raise OperationError(space.w_TypeError, space.wrap("Cannot get array interface on scalars in pypy"))
-
-class VirtualArray(BaseArray):
- """
- Class for representing virtual arrays, such as binary ops or ufuncs
- """
- def __init__(self, signature, shape, res_dtype, order):
- BaseArray.__init__(self, shape, order)
- self.forced_result = None
- self.signature = signature
- self.res_dtype = res_dtype
-
- def _del_sources(self):
- # Function for deleting references to source arrays, to allow garbage-collecting them
- raise NotImplementedError
-
- def compute(self):
- i = 0
- signature = self.signature
- result_size = self.find_size()
- result = W_NDimArray(result_size, self.shape, self.find_dtype())
- shapelen = len(self.shape)
- i = self.start_iter()
- ri = result.start_iter()
- while not ri.done():
- numpy_driver.jit_merge_point(signature=signature,
- shapelen=shapelen,
- result_size=result_size, i=i, ri=ri,
- self=self, result=result)
- result.dtype.setitem(result.storage, ri.offset, self.eval(i))
- i = i.next(shapelen)
- ri = ri.next(shapelen)
- return result
-
- def force_if_needed(self):
- if self.forced_result is None:
- self.forced_result = self.compute()
- self._del_sources()
-
- def get_concrete(self):
- self.force_if_needed()
- return self.forced_result
-
- def eval(self, iter):
- if self.forced_result is not None:
- return self.forced_result.eval(iter)
- return self._eval(iter)
-
- def getitem(self, item):
- return self.get_concrete().getitem(item)
-
- def setitem(self, item, value):
- return self.get_concrete().setitem(item, value)
-
- def find_size(self):
- if self.forced_result is not None:
- # The result has been computed and sources may be unavailable
- return self.forced_result.find_size()
- return self._find_size()
-
- def find_dtype(self):
- return self.res_dtype
-
-
-class Call1(VirtualArray):
- def __init__(self, signature, shape, res_dtype, values, order):
- VirtualArray.__init__(self, signature, shape, res_dtype,
- values.order)
- self.values = values
-
- def _del_sources(self):
- self.values = None
-
- def _find_size(self):
- return self.values.find_size()
-
- def _find_dtype(self):
- return self.res_dtype
-
- def _eval(self, iter):
- assert isinstance(iter, Call1Iterator)
- val = self.values.eval(iter.child).convert_to(self.res_dtype)
- sig = jit.promote(self.signature)
- assert isinstance(sig, signature.Signature)
- call_sig = sig.components[0]
- assert isinstance(call_sig, signature.Call1)
- return call_sig.func(self.res_dtype, val)
-
- def start_iter(self, res_shape=None):
- if self.forced_result is not None:
- return self.forced_result.start_iter(res_shape)
- return Call1Iterator(self.values.start_iter(res_shape))
-
- def debug_repr(self):
- sig = self.signature
- assert isinstance(sig, signature.Signature)
- call_sig = sig.components[0]
- assert isinstance(call_sig, signature.Call1)
- if self.forced_result is not None:
- return 'Call1(%s, forced=%s)' % (call_sig.name,
- self.forced_result.debug_repr())
- return 'Call1(%s, %s)' % (call_sig.name,
- self.values.debug_repr())
-
-class Call2(VirtualArray):
- """
- Intermediate class for performing binary operations.
- """
- def __init__(self, signature, shape, calc_dtype, res_dtype, left, right):
- # XXX do something if left.order != right.order
- VirtualArray.__init__(self, signature, shape, res_dtype, left.order)
- self.left = left
- self.right = right
- self.calc_dtype = calc_dtype
- self.size = 1
- for s in self.shape:
- self.size *= s
-
- def _del_sources(self):
- self.left = None
- self.right = None
-
- def _find_size(self):
- return self.size
-
- def start_iter(self, res_shape=None):
- if self.forced_result is not None:
- return self.forced_result.start_iter(res_shape)
- if res_shape is None:
- res_shape = self.shape # we still force the shape on children
- return Call2Iterator(self.left.start_iter(res_shape),
- self.right.start_iter(res_shape))
-
- def _eval(self, iter):
- assert isinstance(iter, Call2Iterator)
- lhs = self.left.eval(iter.left).convert_to(self.calc_dtype)
- rhs = self.right.eval(iter.right).convert_to(self.calc_dtype)
- sig = jit.promote(self.signature)
- assert isinstance(sig, signature.Signature)
- call_sig = sig.components[0]
- assert isinstance(call_sig, signature.Call2)
- return call_sig.func(self.calc_dtype, lhs, rhs)
-
- def debug_repr(self):
- sig = self.signature
- assert isinstance(sig, signature.Signature)
- call_sig = sig.components[0]
- assert isinstance(call_sig, signature.Call2)
- if self.forced_result is not None:
- return 'Call2(%s, forced=%s)' % (call_sig.name,
- self.forced_result.debug_repr())
- return 'Call2(%s, %s, %s)' % (call_sig.name,
- self.left.debug_repr(),
- self.right.debug_repr())
-
-class ViewArray(BaseArray):
- """
- Class for representing views of arrays, they will reflect changes of parent
- arrays. Example: slices
- """
- def __init__(self, parent, signature, strides, backstrides, shape):
+class W_NDimSlice(ViewArray):
+ def __init__(self, start, strides, backstrides, shape, parent):
+ assert isinstance(parent, ConcreteArray)
+ if isinstance(parent, W_NDimSlice):
+ parent = parent.parent
+ size = 1
+ for sh in shape:
+ size *= sh
self.strides = strides
self.backstrides = backstrides
- BaseArray.__init__(self, shape, parent.order)
- self.signature = signature
- self.parent = parent
- self.invalidates = parent.invalidates
+ ViewArray.__init__(self, size, shape, parent.dtype, parent.order,
+ parent)
+ self.start = start
- def get_concrete(self):
- # in fact, ViewArray never gets "concrete" as it never stores data.
- # This implementation is needed for BaseArray getitem/setitem to work,
- # can be refactored.
- self.parent.get_concrete()
- return self
+ def setslice(self, space, w_value):
+ res_shape = shape_agreement(space, self.shape, w_value.shape)
+ self._sliceloop(w_value, res_shape)
- def getitem(self, item):
- return self.parent.getitem(item)
-
- def eval(self, iter):
- return self.parent.getitem(iter.get_offset())
-
- def setitem(self, item, value):
- # This is currently not possible to be called from anywhere.
- raise NotImplementedError
-
- def descr_len(self, space):
- if self.shape:
- return space.wrap(self.shape[0])
- return space.wrap(1)
+ def _sliceloop(self, source, res_shape):
+ sig = source.find_sig(res_shape)
+ frame = sig.create_frame(source, res_shape)
+ res_iter = view_iter_from_arr(self)
+ shapelen = len(res_shape)
+ while not res_iter.done():
+ slice_driver.jit_merge_point(sig=sig,
+ frame=frame,
+ shapelen=shapelen,
+ self=self, source=source,
+ res_iter=res_iter)
+ self.setitem(res_iter.offset, sig.eval(frame, source).convert_to(
+ self.find_dtype()))
+ frame.next(shapelen)
+ res_iter = res_iter.next(shapelen)
def setshape(self, space, new_shape):
if len(self.shape) < 1:
@@ -1220,96 +1013,10 @@
self.backstrides = new_backstrides[:]
self.shape = new_shape[:]
-class W_NDimSlice(ViewArray):
- signature = signature.BaseSignature()
-
- def __init__(self, parent, signature, start, strides, backstrides,
- shape):
- if isinstance(parent, W_NDimSlice):
- parent = parent.parent
- ViewArray.__init__(self, parent, signature, strides, backstrides, shape)
- self.start = start
- self.size = 1
- for sh in shape:
- self.size *= sh
-
- def find_size(self):
- return self.size
-
- def find_dtype(self):
- return self.parent.find_dtype()
-
- def setslice(self, space, w_value):
- res_shape = shape_agreement(space, self.shape, w_value.shape)
- self._sliceloop(w_value, res_shape)
-
- def _sliceloop(self, source, res_shape):
- source_iter = source.start_iter(res_shape)
- res_iter = self.start_iter(res_shape)
- shapelen = len(res_shape)
- while not res_iter.done():
- slice_driver.jit_merge_point(signature=source.signature,
- shapelen=shapelen,
- self=self, source=source,
- res_iter=res_iter,
- source_iter=source_iter)
- self.setitem(res_iter.offset, source.eval(source_iter).convert_to(
- self.find_dtype()))
- source_iter = source_iter.next(shapelen)
- res_iter = res_iter.next(shapelen)
-
- def start_iter(self, res_shape=None):
- if res_shape is not None and res_shape != self.shape:
- return BroadcastIterator(self, res_shape)
- if len(self.shape) == 1:
- return OneDimIterator(self.start, self.strides[0], self.shape[0])
- return ViewIterator(self)
-
- def setitem(self, item, value):
- self.parent.setitem(item, value)
-
- def debug_repr(self):
- return 'Slice(%s)' % self.parent.debug_repr()
-
- def copy(self):
- array = W_NDimArray(self.size, self.shape[:], self.find_dtype())
- iter = self.start_iter()
- a_iter = array.start_iter()
- while not iter.done():
- array.setitem(a_iter.offset, self.getitem(iter.offset))
- iter = iter.next(len(self.shape))
- a_iter = a_iter.next(len(array.shape))
- return array
-
- def get_storage(self, space):
- return self.parent.get_storage(space)
-
-class W_NDimArray(BaseArray):
+class W_NDimArray(ConcreteArray):
""" A class representing contiguous array. We know that each iteration
by say ufunc will increase the data index by one
"""
- def __init__(self, size, shape, dtype, order='C'):
- BaseArray.__init__(self, shape, order)
- self.size = size
- self.dtype = dtype
- self.storage = dtype.malloc(size)
- self.signature = dtype.signature
-
- def get_concrete(self):
- return self
-
- def find_size(self):
- return self.size
-
- def find_dtype(self):
- return self.dtype
-
- def getitem(self, item):
- return self.dtype.getitem(self.storage, item)
-
- def eval(self, iter):
- return self.dtype.getitem(self.storage, iter.get_offset())
-
def copy(self):
array = W_NDimArray(self.size, self.shape[:], self.dtype, self.order)
rffi.c_memcpy(
@@ -1319,32 +1026,16 @@
)
return array
- def descr_len(self, space):
- if len(self.shape):
- return space.wrap(self.shape[0])
- raise OperationError(space.w_TypeError, space.wrap(
- "len() of unsized object"))
-
def setitem(self, item, value):
self.invalidated()
self.dtype.setitem(self.storage, item, value)
- def start_iter(self, res_shape=None):
- if self.order == 'C':
- if res_shape is not None and res_shape != self.shape:
- return BroadcastIterator(self, res_shape)
- return ArrayIterator(self.size)
- raise NotImplementedError # use ViewIterator simply, test it
-
def setshape(self, space, new_shape):
self.shape = new_shape
self.calc_strides(new_shape)
- def debug_repr(self):
- return 'Array'
-
- def get_storage(self, space):
- return self.storage
+ def create_sig(self, res_shape):
+ return self.array_sig(res_shape)
def __del__(self):
lltype.free(self.storage, flavor='raw', track_allocation=False)
@@ -1396,10 +1087,11 @@
)
arr = W_NDimArray(size, shape[:], dtype=dtype, order=order)
shapelen = len(shape)
- arr_iter = arr.start_iter(arr.shape)
+ arr_iter = ArrayIterator(arr.size)
for i in range(len(elems_w)):
w_elem = elems_w[i]
- dtype.setitem(arr.storage, arr_iter.offset, dtype.coerce(space, w_elem))
+ dtype.setitem(arr.storage, arr_iter.offset,
+ dtype.coerce(space, w_elem))
arr_iter = arr_iter.next(shapelen)
return arr
@@ -1492,48 +1184,31 @@
class W_FlatIterator(ViewArray):
- signature = signature.BaseSignature()
@jit.unroll_safe
def __init__(self, arr):
+ arr = arr.get_concrete()
size = 1
for sh in arr.shape:
size *= sh
- new_sig = signature.Signature.find_sig([
- W_FlatIterator.signature, arr.signature
- ])
- ViewArray.__init__(self, arr, new_sig, [arr.strides[-1]],
- [arr.backstrides[-1]], [size])
+ self.strides = [arr.strides[-1]]
+ self.backstrides = [arr.backstrides[-1]]
+ ViewArray.__init__(self, size, [size], arr.dtype, arr.order,
+ arr)
self.shapelen = len(arr.shape)
- self.arr = arr
- self.iter = self.start_iter()
-
- def start_iter(self, res_shape=None):
- if res_shape is not None and res_shape != self.shape:
- return BroadcastIterator(self, res_shape)
- return OneDimIterator(self.arr.start, self.strides[0],
- self.shape[0])
-
- def find_dtype(self):
- return self.arr.find_dtype()
-
- def find_size(self):
- return self.shape[0]
+ self.iter = OneDimIterator(arr.start, self.strides[0],
+ self.shape[0])
def descr_next(self, space):
if self.iter.done():
raise OperationError(space.w_StopIteration, space.w_None)
- result = self.eval(self.iter)
+ result = self.getitem(self.iter.offset)
self.iter = self.iter.next(self.shapelen)
return result
def descr_iter(self):
return self
- def debug_repr(self):
- return 'FlatIter(%s)' % self.arr.debug_repr()
-
-
W_FlatIterator.typedef = TypeDef(
'flatiter',
next = interp2app(W_FlatIterator.descr_next),
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
@@ -2,20 +2,21 @@
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
-from pypy.module.micronumpy import interp_boxes, interp_dtype, signature, types
+from pypy.module.micronumpy import interp_boxes, interp_dtype, types
+from pypy.module.micronumpy.signature import ReduceSignature, ScalarSignature, find_sig
from pypy.rlib import jit
from pypy.rlib.rarithmetic import LONG_BIT
from pypy.tool.sourcetools import func_with_new_name
-
reduce_driver = jit.JitDriver(
- greens = ['shapelen', "signature"],
- reds = ["i", "self", "dtype", "value", "obj"]
+ greens = ['shapelen', "sig"],
+ virtualizables = ["frame"],
+ reds = ["frame", "self", "dtype", "value", "obj"]
)
class W_Ufunc(Wrappable):
_attrs_ = ["name", "promote_to_float", "promote_bools", "identity"]
- _immutable_fields_ = ["promote_to_float", "promote_bools"]
+ _immutable_fields_ = ["promote_to_float", "promote_bools", "name"]
def __init__(self, name, promote_to_float, promote_bools, identity):
self.name = name
@@ -50,6 +51,7 @@
def reduce(self, space, w_obj, multidim):
from pypy.module.micronumpy.interp_numarray import convert_to_array, Scalar
+
if self.argcount != 2:
raise OperationError(space.w_ValueError, space.wrap("reduce only "
"supported for binary functions"))
@@ -60,13 +62,16 @@
raise OperationError(space.w_TypeError, space.wrap("cannot reduce "
"on a scalar"))
- size = obj.find_size()
+ size = obj.size
dtype = find_unaryop_result_dtype(
space, obj.find_dtype(),
promote_to_largest=True
)
- start = obj.start_iter(obj.shape)
shapelen = len(obj.shape)
+ sig = find_sig(ReduceSignature(self.func, self.name, dtype,
+ ScalarSignature(dtype),
+ obj.create_sig(obj.shape)), obj)
+ frame = sig.create_frame(obj)
if shapelen > 1 and not multidim:
raise OperationError(space.w_NotImplementedError,
space.wrap("not implemented yet"))
@@ -74,34 +79,33 @@
if size == 0:
raise operationerrfmt(space.w_ValueError, "zero-size array to "
"%s.reduce without identity", self.name)
- value = obj.eval(start).convert_to(dtype)
- start = start.next(shapelen)
+ value = sig.eval(frame, obj).convert_to(dtype)
+ frame.next(shapelen)
else:
value = self.identity.convert_to(dtype)
- new_sig = signature.Signature.find_sig([
- self.reduce_signature, obj.signature
- ])
- return self.reduce_loop(new_sig, shapelen, start, value, obj, dtype)
+ return self.reduce_loop(shapelen, sig, frame, value, obj, dtype)
- def reduce_loop(self, signature, shapelen, i, value, obj, dtype):
- while not i.done():
- reduce_driver.jit_merge_point(signature=signature,
+ def reduce_loop(self, shapelen, sig, frame, value, obj, dtype):
+ while not frame.done():
+ reduce_driver.jit_merge_point(sig=sig,
shapelen=shapelen, self=self,
- value=value, obj=obj, i=i,
+ value=value, obj=obj, frame=frame,
dtype=dtype)
- value = self.func(dtype, value, obj.eval(i).convert_to(dtype))
- i = i.next(shapelen)
+ assert isinstance(sig, ReduceSignature)
+ value = sig.binfunc(dtype, value, sig.eval(frame, obj).convert_to(dtype))
+ frame.next(shapelen)
return value
class W_Ufunc1(W_Ufunc):
argcount = 1
+ _immutable_fields_ = ["func", "name"]
+
def __init__(self, func, name, promote_to_float=False, promote_bools=False,
identity=None):
W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity)
self.func = func
- self.signature = signature.Call1(func)
def call(self, space, args_w):
from pypy.module.micronumpy.interp_numarray import (Call1,
@@ -117,14 +121,13 @@
if isinstance(w_obj, Scalar):
return self.func(res_dtype, w_obj.value.convert_to(res_dtype))
- new_sig = signature.Signature.find_sig([self.signature, w_obj.signature])
- w_res = Call1(new_sig, w_obj.shape, res_dtype, w_obj, w_obj.order)
+ w_res = Call1(self.func, self.name, w_obj.shape, res_dtype, w_obj)
w_obj.add_invalidates(w_res)
return w_res
class W_Ufunc2(W_Ufunc):
- _immutable_fields_ = ["comparison_func", "func"]
+ _immutable_fields_ = ["comparison_func", "func", "name"]
argcount = 2
def __init__(self, func, name, promote_to_float=False, promote_bools=False,
@@ -133,8 +136,6 @@
W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity)
self.func = func
self.comparison_func = comparison_func
- self.signature = signature.Call2(func)
- self.reduce_signature = signature.BaseSignature()
def call(self, space, args_w):
from pypy.module.micronumpy.interp_numarray import (Call2,
@@ -158,11 +159,9 @@
w_rhs.value.convert_to(calc_dtype)
)
- new_sig = signature.Signature.find_sig([
- self.signature, w_lhs.signature, w_rhs.signature
- ])
new_shape = shape_agreement(space, w_lhs.shape, w_rhs.shape)
- w_res = Call2(new_sig, new_shape, calc_dtype,
+ w_res = Call2(self.func, self.name,
+ new_shape, calc_dtype,
res_dtype, w_lhs, w_rhs)
w_lhs.add_invalidates(w_res)
w_rhs.add_invalidates(w_res)
diff --git a/pypy/module/micronumpy/signature.py b/pypy/module/micronumpy/signature.py
--- a/pypy/module/micronumpy/signature.py
+++ b/pypy/module/micronumpy/signature.py
@@ -1,54 +1,322 @@
-from pypy.rlib.objectmodel import r_dict, compute_identity_hash
+from pypy.rlib.objectmodel import r_dict, compute_identity_hash, compute_hash
from pypy.rlib.rarithmetic import intmask
+from pypy.module.micronumpy.interp_iter import ViewIterator, ArrayIterator, \
+ OneDimIterator, ConstantIterator
+from pypy.module.micronumpy.strides import calculate_slice_strides
+from pypy.rlib.jit import hint, unroll_safe, promote
+def sigeq(one, two):
+ return one.eq(two)
-def components_eq(lhs, rhs):
- if len(lhs) != len(rhs):
- return False
- for i in range(len(lhs)):
- v1, v2 = lhs[i], rhs[i]
- if type(v1) is not type(v2) or not v1.eq(v2):
+def sigeq_no_numbering(one, two):
+ """ Cache for iterator numbering should not compare array numbers
+ """
+ return one.eq(two, compare_array_no=False)
+
+def sighash(sig):
+ return sig.hash()
+
+known_sigs = r_dict(sigeq, sighash)
+
+def find_sig(sig, arr):
+ sig.invent_array_numbering(arr)
+ try:
+ return known_sigs[sig]
+ except KeyError:
+ sig.invent_numbering()
+ known_sigs[sig] = sig
+ return sig
+
+class NumpyEvalFrame(object):
+ _virtualizable2_ = ['iterators[*]', 'final_iter', 'arraylist[*]']
+
+ @unroll_safe
+ def __init__(self, iterators, arrays):
+ self = hint(self, access_directly=True, fresh_virtualizable=True)
+ self.iterators = iterators[:]
+ self.arrays = arrays[:]
+ for i in range(len(self.iterators)):
+ iter = self.iterators[i]
+ if not isinstance(iter, ConstantIterator):
+ self.final_iter = i
+ break
+ else:
+ self.final_iter = -1
+
+ def done(self):
+ final_iter = promote(self.final_iter)
+ if final_iter < 0:
return False
- return True
+ return self.iterators[final_iter].done()
-def components_hash(components):
- res = 0x345678
- for component in components:
- res = intmask((1000003 * res) ^ component.hash())
- return res
+ @unroll_safe
+ def next(self, shapelen):
+ for i in range(len(self.iterators)):
+ self.iterators[i] = self.iterators[i].next(shapelen)
-class BaseSignature(object):
- _attrs_ = []
+def _add_ptr_to_cache(ptr, cache):
+ i = 0
+ for p in cache:
+ if ptr == p:
+ return i
+ i += 1
+ else:
+ res = len(cache)
+ cache.append(ptr)
+ return res
- def eq(self, other):
- return self is other
+class Signature(object):
+ _attrs_ = ['iter_no', 'array_no']
+ _immutable_fields_ = ['iter_no', 'array_no']
+
+ array_no = 0
+ iter_no = 0
+
+ def invent_numbering(self):
+ cache = r_dict(sigeq_no_numbering, sighash)
+ allnumbers = []
+ self._invent_numbering(cache, allnumbers)
+
+ def invent_array_numbering(self, arr):
+ cache = []
+ self._invent_array_numbering(arr, cache)
+
+ def _invent_numbering(self, cache, allnumbers):
+ try:
+ no = cache[self]
+ except KeyError:
+ no = len(allnumbers)
+ cache[self] = no
+ allnumbers.append(no)
+ self.iter_no = no
+
+ def create_frame(self, arr, res_shape=None):
+ res_shape = res_shape or arr.shape
+ iterlist = []
+ arraylist = []
+ self._create_iter(iterlist, arraylist, arr, res_shape, [])
+ return NumpyEvalFrame(iterlist, arraylist)
+
+class ConcreteSignature(Signature):
+ _immutable_fields_ = ['dtype']
+
+ def __init__(self, dtype):
+ self.dtype = dtype
+
+ def eq(self, other, compare_array_no=True):
+ if type(self) is not type(other):
+ return False
+ assert isinstance(other, ConcreteSignature)
+ if compare_array_no:
+ if self.array_no != other.array_no:
+ return False
+ return self.dtype is other.dtype
def hash(self):
- return compute_identity_hash(self)
+ return compute_identity_hash(self.dtype)
-class Signature(BaseSignature):
- _known_sigs = r_dict(components_eq, components_hash)
+ def allocate_view_iter(self, arr, res_shape, chunklist):
+ r = arr.shape, arr.start, arr.strides, arr.backstrides
+ if chunklist:
+ for chunkelem in chunklist:
+ r = calculate_slice_strides(r[0], r[1], r[2], r[3], chunkelem)
+ shape, start, strides, backstrides = r
+ if len(res_shape) == 1:
+ return OneDimIterator(start, strides[0], res_shape[0])
+ return ViewIterator(start, strides, backstrides, shape, res_shape)
- _attrs_ = ["components"]
- _immutable_fields_ = ["components[*]"]
+class ArraySignature(ConcreteSignature):
+ def debug_repr(self):
+ return 'Array'
- def __init__(self, components):
- self.components = components
+ def _invent_array_numbering(self, arr, cache):
+ from pypy.module.micronumpy.interp_numarray import ConcreteArray
+ concr = arr.get_concrete()
+ assert isinstance(concr, ConcreteArray)
+ self.array_no = _add_ptr_to_cache(concr.storage, cache)
- @staticmethod
- def find_sig(components):
- return Signature._known_sigs.setdefault(components, Signature(components))
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ from pypy.module.micronumpy.interp_numarray import ConcreteArray
+ concr = arr.get_concrete()
+ assert isinstance(concr, ConcreteArray)
+ storage = concr.storage
+ if self.iter_no >= len(iterlist):
+ iterlist.append(self.allocate_iter(concr, res_shape, chunklist))
+ if self.array_no >= len(arraylist):
+ arraylist.append(storage)
-class Call1(BaseSignature):
- _immutable_fields_ = ["func", "name"]
+ def allocate_iter(self, arr, res_shape, chunklist):
+ if chunklist:
+ return self.allocate_view_iter(arr, res_shape, chunklist)
+ return ArrayIterator(arr.size)
- def __init__(self, func):
- self.func = func
- self.name = func.func_name
+ def eval(self, frame, arr):
+ iter = frame.iterators[self.iter_no]
+ return self.dtype.getitem(frame.arrays[self.array_no], iter.offset)
-class Call2(BaseSignature):
- _immutable_fields_ = ["func", "name"]
+class ScalarSignature(ConcreteSignature):
+ def debug_repr(self):
+ return 'Scalar'
- def __init__(self, func):
- self.func = func
- self.name = func.func_name
+ def _invent_array_numbering(self, arr, cache):
+ pass
+
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ if self.iter_no >= len(iterlist):
+ iter = ConstantIterator()
+ iterlist.append(iter)
+
+ def eval(self, frame, arr):
+ from pypy.module.micronumpy.interp_numarray import Scalar
+ assert isinstance(arr, Scalar)
+ return arr.value
+
+class ViewSignature(ArraySignature):
+ def debug_repr(self):
+ return 'Slice'
+
+ def _invent_numbering(self, cache, allnumbers):
+ # always invent a new number for view
+ no = len(allnumbers)
+ allnumbers.append(no)
+ self.iter_no = no
+
+ def allocate_iter(self, arr, res_shape, chunklist):
+ return self.allocate_view_iter(arr, res_shape, chunklist)
+
+class VirtualSliceSignature(Signature):
+ def __init__(self, child):
+ self.child = child
+
+ def _invent_array_numbering(self, arr, cache):
+ from pypy.module.micronumpy.interp_numarray import VirtualSlice
+ assert isinstance(arr, VirtualSlice)
+ self.child._invent_array_numbering(arr.child, cache)
+
+ def hash(self):
+ return intmask(self.child.hash() ^ 1234)
+
+ def eq(self, other, compare_array_no=True):
+ if type(self) is not type(other):
+ return False
+ assert isinstance(other, VirtualSliceSignature)
+ return self.child.eq(other.child, compare_array_no)
+
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ from pypy.module.micronumpy.interp_numarray import VirtualSlice
+ assert isinstance(arr, VirtualSlice)
+ chunklist.append(arr.chunks)
+ self.child._create_iter(iterlist, arraylist, arr.child, res_shape,
+ chunklist)
+
+ def eval(self, frame, arr):
+ from pypy.module.micronumpy.interp_numarray import VirtualSlice
+ assert isinstance(arr, VirtualSlice)
+ return self.child.eval(frame, arr.child)
+
+class Call1(Signature):
+ _immutable_fields_ = ['unfunc', 'name', 'child']
+
+ def __init__(self, func, name, child):
+ self.unfunc = func
+ self.child = child
+ self.name = name
+
+ def hash(self):
+ return compute_hash(self.name) ^ intmask(self.child.hash() << 1)
+
+ def eq(self, other, compare_array_no=True):
+ if type(self) is not type(other):
+ return False
+ assert isinstance(other, Call1)
+ return (self.unfunc is other.unfunc and
+ self.child.eq(other.child, compare_array_no))
+
+ def debug_repr(self):
+ return 'Call1(%s, %s)' % (self.name, self.child.debug_repr())
+
+ def _invent_numbering(self, cache, allnumbers):
+ self.child._invent_numbering(cache, allnumbers)
+
+ def _invent_array_numbering(self, arr, cache):
+ from pypy.module.micronumpy.interp_numarray import Call1
+ assert isinstance(arr, Call1)
+ self.child._invent_array_numbering(arr.values, cache)
+
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ from pypy.module.micronumpy.interp_numarray import Call1
+ assert isinstance(arr, Call1)
+ self.child._create_iter(iterlist, arraylist, arr.values, res_shape,
+ chunklist)
+
+ def eval(self, frame, arr):
+ from pypy.module.micronumpy.interp_numarray import Call1
+ assert isinstance(arr, Call1)
+ v = self.child.eval(frame, arr.values).convert_to(arr.res_dtype)
+ return self.unfunc(arr.res_dtype, v)
+
+class Call2(Signature):
+ _immutable_fields_ = ['binfunc', 'name', 'calc_dtype', 'left', 'right']
+
+ def __init__(self, func, name, calc_dtype, left, right):
+ self.binfunc = func
+ self.left = left
+ self.right = right
+ self.name = name
+ self.calc_dtype = calc_dtype
+
+ def hash(self):
+ return (compute_hash(self.name) ^ intmask(self.left.hash() << 1) ^
+ intmask(self.right.hash() << 2))
+
+ def eq(self, other, compare_array_no=True):
+ if type(self) is not type(other):
+ return False
+ assert isinstance(other, Call2)
+ return (self.binfunc is other.binfunc and
+ self.calc_dtype is other.calc_dtype and
+ self.left.eq(other.left, compare_array_no) and
+ self.right.eq(other.right, compare_array_no))
+
+ def _invent_array_numbering(self, arr, cache):
+ from pypy.module.micronumpy.interp_numarray import Call2
+ assert isinstance(arr, Call2)
+ self.left._invent_array_numbering(arr.left, cache)
+ self.right._invent_array_numbering(arr.right, cache)
+
+ def _invent_numbering(self, cache, allnumbers):
+ self.left._invent_numbering(cache, allnumbers)
+ self.right._invent_numbering(cache, allnumbers)
+
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ from pypy.module.micronumpy.interp_numarray import Call2
+
+ assert isinstance(arr, Call2)
+ self.left._create_iter(iterlist, arraylist, arr.left, res_shape,
+ chunklist)
+ self.right._create_iter(iterlist, arraylist, arr.right, res_shape,
+ chunklist)
+
+ def eval(self, frame, arr):
+ from pypy.module.micronumpy.interp_numarray import Call2
+ assert isinstance(arr, Call2)
+ lhs = self.left.eval(frame, arr.left).convert_to(self.calc_dtype)
+ rhs = self.right.eval(frame, arr.right).convert_to(self.calc_dtype)
+ return self.binfunc(self.calc_dtype, lhs, rhs)
+
+ def debug_repr(self):
+ return 'Call2(%s, %s, %s)' % (self.name, self.left.debug_repr(),
+ self.right.debug_repr())
+
+class ReduceSignature(Call2):
+ def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+ self.right._create_iter(iterlist, arraylist, arr, res_shape, chunklist)
+
+ def _invent_numbering(self, cache, allnumbers):
+ self.right._invent_numbering(cache, allnumbers)
+
+ def _invent_array_numbering(self, arr, cache):
+ self.right._invent_array_numbering(arr, cache)
+
+ def eval(self, frame, arr):
+ return self.right.eval(frame, arr)
diff --git a/pypy/module/micronumpy/strides.py b/pypy/module/micronumpy/strides.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/strides.py
@@ -0,0 +1,34 @@
+
+def calculate_slice_strides(shape, start, strides, backstrides, chunks):
+ rstrides = []
+ rbackstrides = []
+ rstart = start
+ rshape = []
+ i = -1
+ for i, (start_, stop, step, lgt) in enumerate(chunks):
+ if step != 0:
+ rstrides.append(strides[i] * step)
+ rbackstrides.append(strides[i] * (lgt - 1) * step)
+ rshape.append(lgt)
+ rstart += strides[i] * start_
+ # add a reminder
+ s = i + 1
+ assert s >= 0
+ rstrides += strides[s:]
+ rbackstrides += backstrides[s:]
+ rshape += shape[s:]
+ return rshape, rstart, rstrides, rbackstrides
+
+def calculate_broadcast_strides(strides, backstrides, orig_shape, res_shape):
+ rstrides = []
+ rbackstrides = []
+ for i in range(len(orig_shape)):
+ if orig_shape[i] == 1:
+ rstrides.append(0)
+ rbackstrides.append(0)
+ else:
+ rstrides.append(strides[i])
+ rbackstrides.append(backstrides[i])
+ rstrides = [0] * (len(res_shape) - len(orig_shape)) + rstrides
+ rbackstrides = [0] * (len(res_shape) - len(orig_shape)) + rbackstrides
+ return rstrides, rbackstrides
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -4,7 +4,6 @@
from pypy.module.micronumpy.interp_ufuncs import (find_binop_result_dtype,
find_unaryop_result_dtype)
-
class BaseNumpyAppTest(object):
def setup_class(cls):
cls.space = gettestobjspace(usemodules=['micronumpy'])
@@ -15,20 +14,37 @@
bool_dtype = get_dtype_cache(space).w_booldtype
ar = W_NDimArray(10, [10], dtype=float64_dtype)
+ ar2 = W_NDimArray(10, [10], dtype=float64_dtype)
v1 = ar.descr_add(space, ar)
v2 = ar.descr_add(space, Scalar(float64_dtype, 2.0))
- assert v1.signature is not v2.signature
+ sig1 = v1.find_sig()
+ sig2 = v2.find_sig()
+ assert v1 is not v2
+ assert sig1.left.iter_no == sig1.right.iter_no
+ assert sig2.left.iter_no != sig2.right.iter_no
+ assert sig1.left.array_no == sig1.right.array_no
+ sig1b = ar2.descr_add(space, ar).find_sig()
+ assert sig1b.left.array_no != sig1b.right.array_no
+ assert sig1b is not sig1
v3 = ar.descr_add(space, Scalar(float64_dtype, 1.0))
- assert v2.signature is v3.signature
+ sig3 = v3.find_sig()
+ assert sig2 is sig3
v4 = ar.descr_add(space, ar)
- assert v1.signature is v4.signature
+ assert v1.find_sig() is v4.find_sig()
bool_ar = W_NDimArray(10, [10], dtype=bool_dtype)
v5 = ar.descr_add(space, bool_ar)
- assert v5.signature is not v1.signature
- assert v5.signature is not v2.signature
+ assert v5.find_sig() is not v1.find_sig()
+ assert v5.find_sig() is not v2.find_sig()
v6 = ar.descr_add(space, bool_ar)
- assert v5.signature is v6.signature
+ assert v5.find_sig() is v6.find_sig()
+ v7 = v6.descr_add(space, v6)
+ sig7 = v7.find_sig()
+ assert sig7.left.left.iter_no == sig7.right.left.iter_no
+ assert sig7.left.left.iter_no != sig7.right.right.iter_no
+ assert sig7.left.right.iter_no == sig7.right.right.iter_no
+ v1.forced_result = ar
+ assert v1.find_sig() is not sig1
def test_slice_signature(self, space):
float64_dtype = get_dtype_cache(space).w_float64dtype
@@ -36,11 +52,14 @@
ar = W_NDimArray(10, [10], dtype=float64_dtype)
v1 = ar.descr_getitem(space, space.wrap(slice(1, 3, 1)))
v2 = ar.descr_getitem(space, space.wrap(slice(4, 6, 1)))
- assert v1.signature is v2.signature
+ assert v1.find_sig() is v2.find_sig()
v3 = v2.descr_add(space, v1)
v4 = v1.descr_add(space, v2)
- assert v3.signature is v4.signature
+ assert v3.find_sig() is v4.find_sig()
+ v5 = ar.descr_add(space, ar).descr_getitem(space, space.wrap(slice(1, 3, 1)))
+ v6 = ar.descr_add(space, ar).descr_getitem(space, space.wrap(slice(1, 4, 1)))
+ assert v5.find_sig() is v6.find_sig()
class TestUfuncCoerscion(object):
def test_binops(self, space):
diff --git a/pypy/module/micronumpy/test/test_compile.py b/pypy/module/micronumpy/test/test_compile.py
--- a/pypy/module/micronumpy/test/test_compile.py
+++ b/pypy/module/micronumpy/test/test_compile.py
@@ -137,6 +137,16 @@
interp = self.run(code)
assert interp.results[0].value.value == 15
+ def test_sum2(self):
+ code = """
+ a = |30|
+ b = a + a
+ sum(b)
+ """
+ interp = self.run(code)
+ assert interp.results[0].value.value == 30 * (30 - 1)
+
+
def test_array_write(self):
code = """
a = [1,2,3,4,5]
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -8,8 +8,6 @@
class MockDtype(object):
- signature = signature.BaseSignature()
-
def malloc(self, size):
return None
@@ -38,92 +36,86 @@
assert a.backstrides == [135, 12, 2]
def test_create_slice_f(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
- s = a.create_slice(space, [(3, 0, 0, 1)])
+ s = a.create_slice([(3, 0, 0, 1)])
assert s.start == 3
assert s.strides == [10, 50]
assert s.backstrides == [40, 100]
- s = a.create_slice(space, [(1, 9, 2, 4)])
+ s = a.create_slice([(1, 9, 2, 4)])
assert s.start == 1
assert s.strides == [2, 10, 50]
assert s.backstrides == [6, 40, 100]
- s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+ s = a.create_slice([(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
assert s.shape == [2, 1]
assert s.strides == [3, 10]
assert s.backstrides == [3, 0]
- s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
assert s.start == 20
assert s.shape == [10, 3]
def test_create_slice_c(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
- s = a.create_slice(space, [(3, 0, 0, 1)])
+ s = a.create_slice([(3, 0, 0, 1)])
assert s.start == 45
assert s.strides == [3, 1]
assert s.backstrides == [12, 2]
- s = a.create_slice(space, [(1, 9, 2, 4)])
+ s = a.create_slice([(1, 9, 2, 4)])
assert s.start == 15
assert s.strides == [30, 3, 1]
assert s.backstrides == [90, 12, 2]
- s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+ s = a.create_slice([(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
assert s.start == 19
assert s.shape == [2, 1]
assert s.strides == [45, 3]
assert s.backstrides == [45, 0]
- s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
assert s.start == 6
assert s.shape == [10, 3]
def test_slice_of_slice_f(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
- s = a.create_slice(space, [(5, 0, 0, 1)])
+ s = a.create_slice([(5, 0, 0, 1)])
assert s.start == 5
- s2 = s.create_slice(space, [(3, 0, 0, 1)])
+ s2 = s.create_slice([(3, 0, 0, 1)])
assert s2.shape == [3]
assert s2.strides == [50]
assert s2.parent is a
assert s2.backstrides == [100]
assert s2.start == 35
- s = a.create_slice(space, [(1, 5, 3, 2)])
- s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+ s = a.create_slice([(1, 5, 3, 2)])
+ s2 = s.create_slice([(0, 2, 1, 2), (2, 0, 0, 1)])
assert s2.shape == [2, 3]
assert s2.strides == [3, 50]
assert s2.backstrides == [3, 100]
assert s2.start == 1 * 15 + 2 * 3
def test_slice_of_slice_c(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
- s = a.create_slice(space, [(5, 0, 0, 1)])
+ s = a.create_slice([(5, 0, 0, 1)])
assert s.start == 15 * 5
- s2 = s.create_slice(space, [(3, 0, 0, 1)])
+ s2 = s.create_slice([(3, 0, 0, 1)])
assert s2.shape == [3]
assert s2.strides == [1]
assert s2.parent is a
assert s2.backstrides == [2]
assert s2.start == 5 * 15 + 3 * 3
- s = a.create_slice(space, [(1, 5, 3, 2)])
- s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+ s = a.create_slice([(1, 5, 3, 2)])
+ s2 = s.create_slice([(0, 2, 1, 2), (2, 0, 0, 1)])
assert s2.shape == [2, 3]
assert s2.strides == [45, 1]
assert s2.backstrides == [45, 2]
assert s2.start == 1 * 15 + 2 * 3
def test_negative_step_f(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
- s = a.create_slice(space, [(9, -1, -2, 5)])
+ s = a.create_slice([(9, -1, -2, 5)])
assert s.start == 9
assert s.strides == [-2, 10, 50]
assert s.backstrides == [-8, 40, 100]
def test_negative_step_c(self):
- space = self.space
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
- s = a.create_slice(space, [(9, -1, -2, 5)])
+ s = a.create_slice([(9, -1, -2, 5)])
assert s.start == 135
assert s.strides == [-30, 3, 1]
assert s.backstrides == [-120, 12, 2]
@@ -132,7 +124,7 @@
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
assert r == 1 + 2 * 10 + 2 * 50
- s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
r = s._index_of_single_item(self.space, self.newtuple(1, 0))
assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
r = s._index_of_single_item(self.space, self.newtuple(1, 1))
@@ -142,7 +134,7 @@
a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
assert r == 1 * 3 * 5 + 2 * 3 + 2
- s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
r = s._index_of_single_item(self.space, self.newtuple(1, 0))
assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
r = s._index_of_single_item(self.space, self.newtuple(1, 1))
@@ -897,13 +889,32 @@
a = zeros(1)
assert debug_repr(a) == 'Array'
assert debug_repr(a + a) == 'Call2(add, Array, Array)'
- assert debug_repr(a[::2]) == 'Slice(Array)'
+ assert debug_repr(a[::2]) == 'Slice'
assert debug_repr(a + 2) == 'Call2(add, Array, Scalar)'
- assert debug_repr(a + a.flat) == 'Call2(add, Array, FlatIter(Array))'
+ assert debug_repr(a + a.flat) == 'Call2(add, Array, Slice)'
assert debug_repr(sin(a)) == 'Call1(sin, Array)'
+
b = a + a
b[0] = 3
- assert debug_repr(b) == 'Call2(add, forced=Array)'
+ assert debug_repr(b) == 'Array'
+
+ def test_virtual_views(self):
+ from numpypy import arange
+ a = arange(15)
+ c = (a + a)
+ d = c[::2]
+ assert d[3] == 12
+ c[6] = 5
+ assert d[3] == 5
+ a = arange(15)
+ c = (a + a)
+ d = c[::2][::2]
+ assert d[1] == 8
+ b = a + a
+ c = b[::2]
+ c[:] = 3
+ assert b[0] == 3
+ assert b[1] == 2
def test_tolist_scalar(self):
from numpypy import int32, bool_
@@ -1075,10 +1086,10 @@
def test_broadcast_setslice(self):
from numpypy import zeros, ones
- a = zeros((100, 100))
- b = ones(100)
+ a = zeros((10, 10))
+ b = ones(10)
a[:, :] = b
- assert a[13, 15] == 1
+ assert a[3, 5] == 1
def test_broadcast_shape_agreement(self):
from numpypy import zeros, array
@@ -1112,6 +1123,14 @@
b[:] = (a + a)
assert (b == zeros((4, 3, 5))).all()
+ def test_broadcast_virtualview(self):
+ from numpypy import arange, zeros
+ a = arange(8).reshape([2, 2, 2])
+ b = (a + a)[1, 1]
+ c = zeros((2, 2, 2))
+ c[:] = b
+ assert (c == [[[12, 14], [12, 14]], [[12, 14], [12, 14]]]).all()
+
def test_argmax(self):
from numpypy import array
a = array([[1, 2], [3, 4], [5, 6]])
@@ -1173,6 +1192,11 @@
a = array([1, 2, 3])
assert dot(a.flat, a.flat) == 14
+ def test_flatiter_varray(self):
+ from numpypy import ones
+ a = ones((2, 2))
+ assert list(((a + a).flat)) == [2, 2, 2, 2]
+
def test_slice_copy(self):
from numpypy import zeros
a = zeros((10, 10))
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -49,10 +49,14 @@
interp.run(space)
w_res = interp.results[-1]
if isinstance(w_res, BaseArray):
- w_res = w_res.eval(w_res.start_iter())
-
+ concr = w_res.get_concrete_or_scalar()
+ sig = concr.find_sig()
+ frame = sig.create_frame(concr)
+ w_res = sig.eval(frame, concr)
if isinstance(w_res, interp_boxes.W_Float64Box):
return w_res.value
+ if isinstance(w_res, interp_boxes.W_Int64Box):
+ return float(w_res.value)
elif isinstance(w_res, interp_boxes.W_BoolBox):
return float(w_res.value)
raise TypeError(w_res)
@@ -78,8 +82,9 @@
def test_add(self):
result = self.run("add")
self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
- 'setinteriorfield_raw': 1, 'int_add': 3,
- 'int_ge': 1, 'guard_false': 1, 'jump': 1})
+ 'setinteriorfield_raw': 1, 'int_add': 2,
+ 'int_ge': 1, 'guard_false': 1, 'jump': 1,
+ 'arraylen_gc': 1})
assert result == 3 + 3
def define_float_add():
@@ -93,7 +98,8 @@
assert result == 3 + 3
self.check_simple_loop({"getinteriorfield_raw": 1, "float_add": 1,
"setinteriorfield_raw": 1, "int_add": 2,
- "int_ge": 1, "guard_false": 1, "jump": 1})
+ "int_ge": 1, "guard_false": 1, "jump": 1,
+ 'arraylen_gc': 1})
def define_sum():
return """
@@ -106,8 +112,8 @@
result = self.run("sum")
assert result == 2 * sum(range(30))
self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 2,
- "int_add": 2, "int_ge": 1, "guard_false": 1,
- "jump": 1})
+ "int_add": 1, "int_ge": 1, "guard_false": 1,
+ "jump": 1, 'arraylen_gc': 1})
def define_prod():
return """
@@ -123,18 +129,22 @@
expected *= i * 2
assert result == expected
self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
- "float_mul": 1, "int_add": 2,
- "int_ge": 1, "guard_false": 1, "jump": 1})
+ "float_mul": 1, "int_add": 1,
+ "int_ge": 1, "guard_false": 1, "jump": 1,
+ 'arraylen_gc': 1})
- def test_max(self):
- py.test.skip("broken, investigate")
- result = self.run("""
+ def define_max():
+ return """
a = |30|
a[13] = 128
b = a + a
max(b)
- """)
+ """
+
+ def test_max(self):
+ result = self.run("max")
assert result == 256
+ py.test.skip("not there yet, getting though")
self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
"float_mul": 1, "int_add": 1,
"int_lt": 1, "guard_true": 1, "jump": 1})
@@ -164,9 +174,9 @@
result = self.run("any")
assert result == 1
self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
- "float_ne": 1, "int_add": 2,
+ "float_ne": 1, "int_add": 1,
"int_ge": 1, "jump": 1,
- "guard_false": 2})
+ "guard_false": 2, 'arraylen_gc': 1})
def define_already_forced():
return """
@@ -183,14 +193,13 @@
# This is the sum of the ops for both loops, however if you remove the
# optimization then you end up with 2 float_adds, so we can still be
# sure it was optimized correctly.
- # XXX the comment above is wrong now. We need preferrably a way to
- # count the two loops separately
- self.check_resops({'setinteriorfield_raw': 4, 'guard_nonnull': 1,
- 'getfield_gc': 35, 'getfield_gc_pure': 6,
- 'guard_class': 22, 'int_add': 8, 'float_mul': 2,
- 'guard_isnull': 2, 'jump': 2, 'int_ge': 4,
- 'getinteriorfield_raw': 4, 'float_add': 2, 'guard_false': 4,
- 'guard_value': 2})
+ self.check_resops({'setinteriorfield_raw': 4, 'getfield_gc': 26,
+ 'getarrayitem_gc': 4, 'getarrayitem_gc_pure': 2,
+ 'getfield_gc_pure': 4,
+ 'guard_class': 8, 'int_add': 8, 'float_mul': 2,
+ 'jump': 2, 'int_ge': 4,
+ 'getinteriorfield_raw': 4, 'float_add': 2,
+ 'guard_false': 4, 'arraylen_gc': 2, 'same_as': 2})
def define_ufunc():
return """
@@ -204,8 +213,9 @@
result = self.run("ufunc")
assert result == -6
self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1, "float_neg": 1,
- "setinteriorfield_raw": 1, "int_add": 3,
- "int_ge": 1, "guard_false": 1, "jump": 1})
+ "setinteriorfield_raw": 1, "int_add": 2,
+ "int_ge": 1, "guard_false": 1, "jump": 1,
+ 'arraylen_gc': 1})
def define_specialization():
return """
@@ -248,7 +258,8 @@
'setinteriorfield_raw': 1,
'int_add': 3,
'int_ge': 1, 'guard_false': 1,
- 'jump': 1})
+ 'jump': 1,
+ 'arraylen_gc': 1})
def define_multidim():
return """
@@ -263,8 +274,9 @@
# int_add might be 1 here if we try slightly harder with
# reusing indexes or some optimization
self.check_simple_loop({'float_add': 1, 'getinteriorfield_raw': 2,
- 'guard_false': 1, 'int_add': 3, 'int_ge': 1,
- 'jump': 1, 'setinteriorfield_raw': 1})
+ 'guard_false': 1, 'int_add': 2, 'int_ge': 1,
+ 'jump': 1, 'setinteriorfield_raw': 1,
+ 'arraylen_gc': 1})
def define_multidim_slice():
return """
@@ -312,7 +324,25 @@
self.check_trace_count(1)
self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add' : 1,
'setinteriorfield_raw': 1, 'int_add': 3,
- 'int_eq': 1, 'guard_false': 1, 'jump': 1})
+ 'int_lt': 1, 'guard_true': 1, 'jump': 1,
+ 'arraylen_gc': 3})
+
+ def define_virtual_slice():
+ return """
+ a = |30|
+ c = a + a
+ d = c -> 1:20
+ d -> 1
+ """
+
+ def test_virtual_slice(self):
+ result = self.run("virtual_slice")
+ assert result == 4
+ self.check_trace_count(1)
+ self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add' : 1,
+ 'setinteriorfield_raw': 1, 'int_add': 2,
+ 'int_ge': 1, 'guard_false': 1, 'jump': 1,
+ 'arraylen_gc': 1})
class TestNumpyOld(LLJitMixin):
def setup_class(cls):
diff --git a/pypy/module/micronumpy/test/test_ztranslation.py b/pypy/module/micronumpy/test/test_ztranslation.py
--- a/pypy/module/micronumpy/test/test_ztranslation.py
+++ b/pypy/module/micronumpy/test/test_ztranslation.py
@@ -1,5 +1,8 @@
-
+from pypy.module.micronumpy import signature
from pypy.objspace.fake.checkmodule import checkmodule
def test_numpy_translates():
+ # XXX: If there are signatures floating around this might explode. This fix
+ # is ugly.
+ signature.known_sigs.clear()
checkmodule('micronumpy')
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -252,7 +252,7 @@
# grow the list
done = 0
while done < len(self._seen_extras):
- print self._seen_extras
+ #print self._seen_extras
ann.build_types(self._seen_extras[done], [],
complete_now=False)
done += 1
More information about the pypy-commit
mailing list