Skip to content

Commit 56b2df4

Browse files
authored
Merge pull request #366 from kohr-h/issue-365__getitem_none
Issue 365 getitem none
2 parents 78afeb8 + 215f3f3 commit 56b2df4

2 files changed

Lines changed: 138 additions & 7 deletions

File tree

pygpu/gpuarray.pyx

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,83 @@ cdef class GpuArray:
18361836
raise TypeError, "len() of unsized object"
18371837

18381838
def __getitem__(self, key):
1839-
return self.__cgetitem__(key)
1839+
cdef unsigned int i
1840+
1841+
if key is Ellipsis:
1842+
return self.__cgetitem__(key)
1843+
1844+
# A list or a sequence of list should trigger "fancy" indexing.
1845+
# This is not implemented yet.
1846+
# Conversely, if a list contains slice or Ellipsis objects, it behaves
1847+
# the same as a tuple.
1848+
if isinstance(key, list):
1849+
if any(isinstance(k, slice) or k is Ellipsis for k in key):
1850+
return self.__getitem__(tuple(key))
1851+
else:
1852+
raise NotImplementedError, "fancy indexing not supported"
1853+
1854+
try:
1855+
iter(key)
1856+
except TypeError:
1857+
key = (key,)
1858+
else:
1859+
if all(isinstance(k, list) for k in key):
1860+
raise NotImplementedError, "fancy indexing not supported"
1861+
1862+
key = tuple(key)
1863+
1864+
# Need to massage Ellipsis here, to avoid packing it into a tuple.
1865+
if key.count(Ellipsis) > 1:
1866+
raise IndexError, "cannot use more than one Ellipsis"
1867+
1868+
# The following code replaces an Ellipsis found in the key by
1869+
# the corresponding number of slice(None) objects, depending on the
1870+
# number of dimensions. As example, this allows indexing on the last
1871+
# dimension with a[..., 1:] on any array (including 1-dim). This
1872+
# is also required for numpy compat.
1873+
try:
1874+
ell_idx = key.index(Ellipsis)
1875+
except ValueError:
1876+
pass
1877+
else:
1878+
# Need number of axes minus missing dimensions extra slice(None)
1879+
# objects, not counting None entries and the Ellipsis itself
1880+
num_slcs = self.ga.nd - (len(key) - key.count(None) - 1)
1881+
fill_slices = (slice(None),) * num_slcs
1882+
key = key[:ell_idx] + fill_slices + key[ell_idx + 1:]
1883+
1884+
# Remove the None entries for indexing
1885+
getitem_idcs = tuple(k for k in key if k is not None)
1886+
1887+
# For less than 1 index, fill up with slice(None) to the right.
1888+
# This allows indexing a[1:] in multi-dimensional arrays, where the
1889+
# slice is applied along the first axis only. It also allows
1890+
# a[()], which simply is a view in Numpy.
1891+
if len(getitem_idcs) <= 1:
1892+
getitem_idcs = (getitem_idcs +
1893+
(slice(None),) * (self.ga.nd - len(getitem_idcs)))
1894+
1895+
# Slice into array, then reshape, accommodating for None entries in key
1896+
sliced = self.__cgetitem__(getitem_idcs)
1897+
if key.count(None) == 0:
1898+
# Avoid unnecessary reshaping if there was no None
1899+
return sliced
1900+
else:
1901+
new_shape = []
1902+
i = 0
1903+
if sliced.shape:
1904+
for k in key:
1905+
if isinstance(k, int):
1906+
continue
1907+
elif k is None:
1908+
new_shape.append(1)
1909+
else:
1910+
new_shape.append(sliced.shape[i])
1911+
i += 1
1912+
# Add remaining entries from sliced.shape if existing (happens
1913+
# for 1 index or less if ndim >= 2).
1914+
new_shape.extend(sliced.shape[i:])
1915+
return sliced.reshape(new_shape)
18401916

18411917
cdef __cgetitem__(self, key):
18421918
cdef ssize_t *starts
@@ -1896,16 +1972,37 @@ cdef class GpuArray:
18961972
steps[i] = 1
18971973

18981974
return pygpu_index(self, starts, stops, steps)
1975+
18991976
finally:
19001977
free(starts)
19011978
free(stops)
19021979
free(steps)
19031980

19041981
def __setitem__(self, idx, v):
1905-
cdef GpuArray tmp = self.__cgetitem__(idx)
1906-
cdef GpuArray gv = carray(v, self.ga.typecode, False, 'A', 0,
1907-
self.context, GpuArray)
1982+
cdef GpuArray tmp, gv
1983+
1984+
if isinstance(idx, list):
1985+
if any(isinstance(i, slice) or i is Ellipsis for i in idx):
1986+
self.__setitem__(tuple(idx), v)
1987+
else:
1988+
raise NotImplementedError, "fancy indexing not supported"
1989+
try:
1990+
iter(idx)
1991+
except TypeError:
1992+
idx = (idx,)
1993+
else:
1994+
if all(isinstance(i, list) for i in idx):
1995+
raise NotImplementedError, "fancy indexing not supported"
1996+
1997+
idx = tuple(idx)
1998+
1999+
if idx.count(Ellipsis) > 1:
2000+
raise IndexError, "cannot use more than one Ellipsis"
19082001

2002+
# Remove None entries, they should be ignored (as in Numpy)
2003+
idx = tuple(i for i in idx if i is not None)
2004+
tmp = self.__cgetitem__(idx)
2005+
gv = carray(v, self.ga.typecode, False, 'A', 0, self.context, GpuArray)
19092006
array_setarray(tmp, gv)
19102007

19112008
def take1(self, GpuArray idx):

pygpu/tests/test_gpu_ndarray.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ def test_empty_no_params():
273273

274274

275275
def test_mapping_getitem_ellipsis():
276-
for shp in [(5,), (6, 7), (4, 8, 9), (1, 8, 9)]:
276+
for shp in [(), (5,), (6, 7), (4, 8, 9), (1, 8, 9)]:
277277
for dtype in dtypes_all:
278278
for offseted in [True, False]:
279279
yield mapping_getitem_ellipsis, shp, dtype, offseted
@@ -289,12 +289,35 @@ def mapping_getitem_ellipsis(shp, dtype, offseted):
289289
assert numpy.allclose(a, b_cpu)
290290

291291

292-
def test_mapping_setitem_ellipsis():
292+
def test_getitem_none():
293+
for shp in [(), (5,), (6, 7), (4, 8, 9), (1, 8, 9)]:
294+
yield getitem_none, shp
295+
296+
297+
def getitem_none(shp):
298+
a, a_gpu = gen_gpuarray(shp, ctx=ctx)
299+
300+
assert numpy.allclose(a_gpu[..., None], a[..., None])
301+
302+
for _ in range(5):
303+
# Choose something to slice with, always works
304+
indcs = tuple(numpy.random.choice([0, slice(None), slice(1, None)],
305+
size=len(shp)))
306+
indcs = indcs[:1] + (None,) + indcs[1:]
307+
assert numpy.allclose(a_gpu[indcs], a[indcs])
308+
309+
if shp:
310+
assert numpy.allclose(a_gpu[1:, None], a[1:, None])
311+
312+
313+
def test_mapping_setitem():
293314
for shp in [(9,), (8, 9), (4, 8, 9), (1, 8, 9)]:
294315
for dtype in dtypes_all:
295316
for offseted in [True, False]:
296317
yield mapping_setitem_ellipsis, shp, dtype, offseted
297318
yield mapping_setitem_ellipsis2, shp, dtype, offseted
319+
yield mapping_setitem_firstaxis, shp, dtype, offseted
320+
298321

299322
@guard_devsup
300323
def mapping_setitem_ellipsis(shp, dtype, offseted):
@@ -303,13 +326,24 @@ def mapping_setitem_ellipsis(shp, dtype, offseted):
303326
a_gpu[...] = 2
304327
assert numpy.allclose(a, numpy.asarray(a_gpu))
305328

329+
306330
@guard_devsup
307331
def mapping_setitem_ellipsis2(shp, dtype, offseted):
308332
a, a_gpu = gen_gpuarray(shp, dtype, offseted, ctx=ctx)
309333
b, b_gpu = gen_gpuarray(shp[1:], dtype, False, ctx=ctx)
310334
a[:] = b
311335
a_gpu[:] = b_gpu
312-
assert numpy.allclose(a, numpy.asarray(b_gpu))
336+
assert numpy.allclose(a, numpy.asarray(a_gpu))
337+
338+
339+
@guard_devsup
340+
def mapping_setitem_firstaxis(shp, dtype, offseted):
341+
a, a_gpu = gen_gpuarray(shp, dtype, offseted, ctx=ctx)
342+
b, b_gpu = gen_gpuarray(shp[1:], dtype, False, ctx=ctx)
343+
a[0] = b
344+
a_gpu[0] = b_gpu
345+
assert numpy.allclose(a, numpy.asarray(a_gpu))
346+
313347

314348
class WriteReadTest(unittest.TestCase):
315349
def setUp(self):

0 commit comments

Comments
 (0)