flopscope.

arr[key]

Index the array. Basic indexing is free; advanced indexing bills.

Adapted from NumPy docs np.getitem

Areacore
Typecustom
NumPy Refnp.getitem
Cost
per-operation
Flopscope Context

Array indexing. Basic slicing: free (view). Fancy indexing: 4*numel(output); boolean masks add numel(mask).

Basic indexing -- every part of key is an int (Python or numpy scalar), slice, flops.newaxis (None), Ellipsis, or a tuple of those -- returns a view, matching NumPy's zero-copy semantics: 0 FLOPs.

Advanced indexing materializes a gathered copy and is billed under "getitem": 4 FLOPs per gathered output element (matching take), plus one scan FLOP per boolean-mask element (matching compress). A part triggers advanced indexing when it is any of:

This is about a part -- an already-decomposed element of parts -- not the top-level key: m[1:3, ::2] has key as a tuple, but that tuple is the multi-axis split itself (parts = key), and its parts are slices, so it stays basic. Likewise v[(0, 2, 4)] on a 1-D v has key as the 3-tuple; splitting it into parts 0, 2, 4 yields three int parts -- basic, and numpy raises its own "too many indices" error since none of them consumes as an array. A tuple only gathers when it survives the top-level split as one part, e.g. v[(0, 2, 4),] (a 1-tuple containing the 3-tuple) or m[tuple_a, tuple_b] (each axis's index happens to be a tuple).

A numpy/Python integer scalar stays basic (a view) -- only integer arrays (or a sequence/buffer coerced to one) gather. A part numpy cannot turn into an integer/bool array -- an object array (kind O, e.g. a generator), a string/bytes field selector (U/S), a float/complex array, or a ragged sequence flops.asarray rejects -- falls through to super().__getitem__ unbilled, so numpy handles or raises on it exactly as usual. mask_elems counts boolean elements only: a bool scalar contributes 1, a bool array contributes its size; integer operands add nothing beyond the gather.