[pypy-commit] pypy indexing: implement ellipsis indexing

rlamy noreply at buildbot.pypy.org
Fri Jul 17 21:02:17 CEST 2015


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: indexing
Changeset: r78588:5663f55d1d87
Date: 2015-07-17 20:02 +0100
http://bitbucket.org/pypy/pypy/changeset/5663f55d1d87/

Log:	implement ellipsis indexing

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
@@ -222,18 +222,18 @@
         if space.isinstance_w(w_idx, space.w_slice):
             if len(self.get_shape()) == 0:
                 raise oefmt(space.w_ValueError, "cannot slice a 0-d array")
-            return [SliceChunk(w_idx)]
+            return [SliceChunk(w_idx), EllipsisChunk()]
         elif space.isinstance_w(w_idx, space.w_int):
-            return [IntegerChunk(w_idx)]
+            return [IntegerChunk(w_idx), EllipsisChunk()]
         elif isinstance(w_idx, W_NDimArray) and w_idx.is_scalar():
             w_idx = w_idx.get_scalar_value().item(space)
             if not space.isinstance_w(w_idx, space.w_int) and \
                     not space.isinstance_w(w_idx, space.w_bool):
                 raise OperationError(space.w_IndexError, space.wrap(
                     "arrays used as indices must be of integer (or boolean) type"))
-            return [IntegerChunk(w_idx)]
+            return [IntegerChunk(w_idx), EllipsisChunk()]
         elif space.is_w(w_idx, space.w_None):
-            return [NewAxisChunk()]
+            return [NewAxisChunk(), EllipsisChunk()]
         result = []
         i = 0
         has_ellipsis = False
@@ -254,6 +254,8 @@
             else:
                 result.append(IntegerChunk(w_item))
                 i += 1
+        if not has_ellipsis:
+            result.append(EllipsisChunk())
         return result
 
     def descr_getitem(self, space, orig_arr, w_index):
diff --git a/pypy/module/micronumpy/strides.py b/pypy/module/micronumpy/strides.py
--- a/pypy/module/micronumpy/strides.py
+++ b/pypy/module/micronumpy/strides.py
@@ -59,12 +59,7 @@
         backstride = base_stride * max(0, length - 1) * step
         return start, length, stride, backstride
 
-
 class NewAxisChunk(Chunk):
-    start = 0
-    stop = 1
-    step = 1
-    lgt = 1
     input_dim = 0
     out_dim = 1
 
@@ -75,9 +70,15 @@
         return 0, 1, 0, 0
 
 class EllipsisChunk(BaseChunk):
+    input_dim = 0
+    out_dim = 0
     def __init__(self):
         pass
 
+    def compute(self, space, base_length, base_stride):
+        backstride = base_stride * max(0, base_length - 1)
+        return 0, base_length, base_stride, backstride
+
 
 def new_view(space, w_arr, chunks):
     arr = w_arr.implementation
@@ -127,38 +128,48 @@
 @jit.look_inside_iff(lambda space, shape, start, strides, backstrides, chunks:
                      jit.isconstant(len(chunks)))
 def calculate_slice_strides(space, shape, start, strides, backstrides, chunks):
+    """
+    Note: `chunks` must contain exactly one EllipsisChunk object.
+    """
     size = 0
+    used_dims = 0
     for chunk in chunks:
+        used_dims += chunk.input_dim
         size += chunk.out_dim
-    rstrides = [0] * size
-    rbackstrides = [0] * size
+    if used_dims > len(shape):
+        raise oefmt(space.w_IndexError, "too many indices for array")
+    else:
+        extra_dims = len(shape) - used_dims
+    rstrides = [0] * (size + extra_dims)
+    rbackstrides = [0] * (size + extra_dims)
     rstart = start
-    rshape = [0] * size
-    i = -1  # index of the current dimension in the input array
+    rshape = [0] * (size + extra_dims)
+    rstart = start
+    i = 0  # index of the current dimension in the input array
     j = 0  # index of the current dimension in the result view
     for chunk in chunks:
-        i += chunk.input_dim
         if isinstance(chunk, NewAxisChunk):
             rshape[j] = 1
             j += 1
             continue
-        try:
-            s_i = strides[i]
-        except IndexError:
+        elif isinstance(chunk, EllipsisChunk):
+            for k in range(extra_dims):
+                start, length, stride, backstride = chunk.compute(
+                        space, shape[i], strides[i])
+                rshape[j] = length
+                rstrides[j] = stride
+                rbackstrides[j] = backstride
+                j += 1
+                i += 1
             continue
         start, length, stride, backstride = chunk.compute(space, shape[i], strides[i])
         if chunk.out_dim == 1:
+            rshape[j] = length
             rstrides[j] = stride
             rbackstrides[j] = backstride
-            rshape[j] = length
             j += chunk.out_dim
-        rstart += s_i * start
-    # add a reminder
-    s = i + 1
-    assert s >= 0
-    rstrides += strides[s:]
-    rbackstrides += backstrides[s:]
-    rshape += shape[s:]
+        rstart += strides[i] * start
+        i += chunk.input_dim
     return rshape, rstart, rstrides, rbackstrides
 
 
diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -4,7 +4,7 @@
 
 from pypy.conftest import option
 from pypy.module.micronumpy.appbridge import get_appbridge_cache
-from pypy.module.micronumpy.strides import Chunk, new_view
+from pypy.module.micronumpy.strides import Chunk, new_view, EllipsisChunk
 from pypy.module.micronumpy.ndarray import W_NDimArray
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
 
@@ -22,6 +22,8 @@
 
 
 def create_slice(space, a, chunks):
+    if not any(isinstance(c, EllipsisChunk) for c in chunks):
+        chunks.append(EllipsisChunk())
     return new_view(space, W_NDimArray(a), chunks).implementation
 
 
@@ -2490,6 +2492,9 @@
         a = np.arange(6).reshape(2, 3)
         if '__pypy__' in sys.builtin_module_names:
             raises(ValueError, "a[..., ...]")
+        b = a [..., 0]
+        assert (b == [0, 3]).all()
+        assert b.base is a
 
     def test_empty_indexing(self):
         import numpy as np


More information about the pypy-commit mailing list