Author: mattip <matti.pi...@gmail.com> Branch: nditer-external_loop Changeset: r74290:d1e691b381c8 Date: 2014-10-29 23:25 +0200 http://bitbucket.org/pypy/pypy/changeset/d1e691b381c8/
Log: hack at strides and shapes till external loops can be used diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -449,7 +449,7 @@ strides.reverse() backstrides.reverse() new_shape.reverse() - return SliceArray(self.start, strides, backstrides, new_shape, + return type(self)(self.start, strides, backstrides, new_shape, self, orig_array) new_strides = calc_new_strides(new_shape, self.get_shape(), self.get_strides(), @@ -460,10 +460,16 @@ new_backstrides = [0] * len(new_shape) for nd in range(len(new_shape)): new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd] - return SliceArray(self.start, new_strides, new_backstrides, new_shape, + return type(self)(self.start, new_strides, new_backstrides, new_shape, self, orig_array) +class NonWritableSliceArray(SliceArray): + def descr_setitem(self, space, orig_array, w_index, w_value): + raise OperationError(space.w_ValueError, space.wrap( + "assignment destination is read-only")) + + class VoidBoxStorage(BaseConcreteArray): def __init__(self, size, dtype): self.storage = alloc_raw_storage(size) diff --git a/pypy/module/micronumpy/iterators.py b/pypy/module/micronumpy/iterators.py --- a/pypy/module/micronumpy/iterators.py +++ b/pypy/module/micronumpy/iterators.py @@ -8,8 +8,8 @@ At which byte in x.data does the item x[3,4] begin? if x.strides==[1,5]: pData = x.pData + (x.start + 3*1 + 4*5)*sizeof(x.pData[0]) - pData = x.pData + (x.start + 24) * sizeof(x.pData[0]) -so the offset of the element is 24 elements after the first + pData = x.pData + (x.start + 23) * sizeof(x.pData[0]) +so the offset of the element is 23 elements after the first What is the next element in x after coordinates [3,4]? if x.order =='C': @@ -33,7 +33,7 @@ which is x.strides[1] * (x.shape[1] - 1) + x.strides[0] so if we precalculate the overflow backstride as [x.strides[i] * (x.shape[i] - 1) for i in range(len(x.shape))] -we can go faster. +we can do only addition while iterating All the calculations happen in next() """ from rpython.rlib import jit @@ -208,6 +208,12 @@ assert state.iterator is self self.array.setitem(state.offset, elem) + def getoperand(self, st, base): + impl = self.operand_type + res = impl([], self.array.dtype, self.array.order, [], [], + self.array.storage, base) + res.start = st.offset + return res def AxisIter(array, shape, axis, cumulative): strides = array.get_strides() @@ -238,18 +244,26 @@ view into the original array ''' - def __init__(self, array, size, shape, strides, backstrides, op_flags): + def __init__(self, array, size, shape, strides, backstrides, slice_shape, + slice_stride, slice_backstride, op_flags, base): + from pypy.module.micronumpy import concrete ArrayIter.__init__(self, array, size, shape, strides, backstrides, op_flags) - self.slice_shape = array.get_shape()[len(shape):] - self.slice_strides = array.strides[len(shape):] - self.slice_backstrides = array.backstrides[len(shape):] + self.shape = shape[:] + self.slice_shape = slice_shape + self.slice_stride = slice_stride + self.slice_backstride = slice_backstride + self.base = base + if op_flags.rw == 'r': + self.operand_type = concrete.NonWritableSliceArray + else: + self.operand_type = concrete.SliceArray def getitem(self, state): - from pypy.module.micronumpy.concrete import SliceArray assert state.iterator is self - return SliceArray(state.offset, self.slice_strides, - self.slice_backstrides, self.slice_shape, self.array, - self.array) + impl = self.operand_type + arr = impl(state.offset, [self.slice_stride], [self.slice_backstride], + [self.slice_shape], self.array, self.base) + return arr def getitem_bool(self, state): # XXX cannot be called @@ -257,8 +271,11 @@ def setitem(self, state, elem): assert state.iterator is self - slice = SliceArray(state.offset, self.slice_strides, - self.slice_backstrides, self.slice_shape, self.array, - self.array) + impl = self.operand_type + slice = impl(state.offset, [self.slice_stride], [self.slice_backstride], + [self.shape], self.array, self.base) # TODO: implement assert False + + def getoperand(self, state, base): + return self.getitem(state) 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 @@ -179,20 +179,20 @@ # Copy logic from npyiter_coalesce_axes, used in ufunc iterators # and in nditer's with 'external_loop' flag out_shape = it.shape[:] + can_coalesce = True + if it.order == 'F': + fastest = 0 + else: + fastest = -1 for idim in range(it.ndim - 1): - can_coalesce = True for op_it, _ in it.iters: if op_it is None: continue assert isinstance(op_it, ArrayIter) - if len(op_it.shape_m1) < 2: - can_coalesce = False - continue - if len(op_it.shape_m1) != len(it.shape): + indx = len(op_it.strides) + if op_it.array.strides[:indx] != op_it.strides: can_coalesce = False break - if op_it.strides[-1] * op_it.shape_m1[-1] != op_it.backstrides[-1]: - can_coalesce = False if can_coalesce: if it.order == 'F': last = out_shape[0] @@ -203,13 +203,60 @@ for i in range(len(it.iters)): old_iter = it.iters[i][0] shape = [s+1 for s in old_iter.shape_m1] - new_iter = SliceIter(old_iter.array, old_iter.size / last, - shape[:-1], old_iter.strides[:-1], - old_iter.backstrides[:-1], it.op_flags[i]) + strides = old_iter.strides + backstrides = old_iter.backstrides + new_shape = shape[:-1] + new_strides = strides[:-1] + new_backstrides = backstrides[:-1] + _shape = shape[-1] + _stride = strides[fastest] + _backstride = backstrides[-1] + if isinstance(old_iter, SliceIter): + _shape *= old_iter.slice_shape + _stride = old_iter.slice_stride + _backstride = (_shape - 1) * _stride + new_iter = SliceIter(old_iter.array, old_iter.size / shape[-1], + new_shape, new_strides, new_backstrides, + _shape, _stride, _backstride, + it.op_flags[i], it) + if len(shape) > 1: + it.shape = out_shape + else: + it.shape = [1] it.iters[i] = (new_iter, new_iter.reset()) + else: + break + # Always coalesce at least one + if it.order == 'F': + last = out_shape[0] + out_shape = out_shape[1:] + else: + last = out_shape[-1] + out_shape = out_shape[:-1] + for i in range(len(it.iters)): + old_iter = it.iters[i][0] + shape = [s+1 for s in old_iter.shape_m1] + strides = old_iter.strides + backstrides = old_iter.backstrides + new_shape = shape[:-1] + new_strides = strides[:-1] + new_backstrides = backstrides[:-1] + _shape = shape[-1] + _stride = strides[-1] + _backstride = backstrides[-1] + if isinstance(old_iter, SliceIter): + _shape *= old_iter.slice_shape + _stride = old_iter.slice_stride + _backstride = (_shape - 1) * _stride + new_iter = SliceIter(old_iter.array, old_iter.size / shape[-1], + new_shape, new_strides, new_backstrides, + _shape, _stride, _backstride, + it.op_flags[i], it) + if len(shape) > 1: it.shape = out_shape else: - return + it.shape = [1] + it.iters[i] = (new_iter, new_iter.reset()) class IndexIterator(object): def __init__(self, shape, backward=False): @@ -377,7 +424,7 @@ raise oefmt(space.w_ValueError, "If op_axes is provided, at least one list of axes " "must be contained within it") - raise Exception('xxx TODO') + raise oefmt(space.w_NotImplementedError, "op_axis not finished yet") # Check that values make sense: # - in bounds for each operand # ValueError: Iterator input op_axes[0][3] (==3) is not a valid axis of op[0], which has 2 dimensions @@ -389,10 +436,7 @@ return space.wrap(self) def getitem(self, it, st): - impl = it.operand_type - res = impl([], it.array.dtype, it.array.order, [], [], - it.array.storage, self) - res.start = st.offset + res = it.getoperand(st, self) return W_NDimArray(res) def descr_getitem(self, space, w_idx): @@ -411,7 +455,6 @@ space.wrap(len(self.iters)) def descr_next(self, space): - import pdb;pdb.set_trace() for it, st in self.iters: if not it.done(st): break diff --git a/pypy/module/micronumpy/test/test_nditer.py b/pypy/module/micronumpy/test/test_nditer.py --- a/pypy/module/micronumpy/test/test_nditer.py +++ b/pypy/module/micronumpy/test/test_nditer.py @@ -76,7 +76,9 @@ r.append(x) n += 1 assert n == 12 - assert (array(r) == [[ 0, 12], [ 4, 16], [ 8, 20], [ 1, 13], [ 5, 17], [ 9, 21], [ 2, 14], [ 6, 18], [10, 22], [ 3, 15], [ 7, 19], [11, 23]]).all() + assert (array(r) == [[ 0, 12], [ 4, 16], [ 8, 20], [ 1, 13], [ 5, 17], [ 9, 21], + [ 2, 14], [ 6, 18], [10, 22], [ 3, 15], [ 7, 19], [11, 23], + ]).all() e = raises(ValueError, 'r[0][0] = 0') assert str(e.value) == 'assignment destination is read-only' r = [] @@ -250,6 +252,10 @@ a = arange(3) import sys b = arange(8).reshape(2,4) + if '__pypy__' in sys.builtin_module_names: + raises(NotImplementedError, nditer, [a, b, None], flags=['external_loop'], + op_axes=[[0, -1, -1], [-1, 0, 1], None]) + skip('nditer op_axes not implemented yet') it = nditer([a, b, None], flags=['external_loop'], op_axes=[[0, -1, -1], [-1, 0, 1], None]) for x, y, z in it: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit