mrpro.utils.Indexer

class mrpro.utils.Indexer[source]

Bases: object

Custom Indexing with broadcasting.

This class is used to index tensors in a way that is consistent with the shape invariants of the data objects.

On creation, an index and a shape are required. When calling the Indexer with a tensor, the tensor is first broadcasted to the shape, then the index is applied to the tensor.

After indexing, remaining broadcasted dimensions are reduced to singleton dimensions again. Thus, using the same Indexer on tensors with different singleton dimensions will result in tensors with different shapes. All resulting tensors can be broadcasted to the shape that would result in indexing a full tensor already having the desired shape.

Indexing never removes dimensions, and can only add new dimensions at the beginning of the tensor.

The index can contain slices, integers, boolean masks, sequences of integers, and integer tensors: - Indexing with a slice

Behaves like in numpy, always returns a view. Negative step sizes are not supported and will raise an IndexError. slice(None), i.e., means selecting the whole axis.

  • Indexing with an integer

    If the index is within the bounds of the broadcasted shape, indexing behaves like slicing with index:index+1. Otherwise, an IndexError is raised. Always returns a view.

  • Indexing with a boolean mask

    Singleton dimensions in the mask are interpreted as full slices. This matches broadcasting of the mask to the size of the respective axes of the tensor. If the mask has more than one non-singleton dimension, a new dimension is added at the beginning of the tensor, with length equal to the number of True values in the mask. At the indexed axes, singleton dimensions are kept. If the mask has only one non-singleton dimension, only the size of the indexed axes is changed. Only a single boolean mask is allowed, otherwise an IndexError is raised.

  • Indexing with a sequence of integers

    If a single indexer is a sequence of integers, the result is as if each value of the sequence was used as an integer index and the results were concatenated along the indexed dimension. If more than one sequence of integers is used, a new dimension at the beginning of the tensor, with the length equal to the shape of the sequences, is added. Indexed dimensions are kept as singleton. The different sequences must have the same shape, otherwise an IndexError is raised. Note that, as in numpy and torch, vectorized indexing is performed, not outer indexing.

  • None

    New axes can be added to the front of tensor by using None in the index. This is only allowed at the beginning of the index.

  • Ellipsis

    An indexing expression can contain a single ellipsis, which will be expanded to slice(None) for all axes that are not indexed.

Implementation details: - On creation, the indexing expression is parsed and split into two parts: normal_index and fancy_index. - normal_index contains only indexing expressions that can be represented as view. - fancy_index contains all other indexing expressions. - On call

  • the tensor is broadcasted to the desired shape

  • the normal_index is applied.

  • if required, the fancy_index is applied.

  • remaining broadcasted dimensions are reduced to singleton dimensions.

__init__(broadcast_shape: tuple[int, ...], index: tuple[Any, ...]) None[source]

Initialize the Indexer.

Parameters:
  • broadcast_shape (tuple[int, ...]) – broadcasted shape of the tensor to index. The tensor will be broadcasted to this shape.

  • index (tuple[Any, ...]) – The index to apply to the tensor.

move_axes: tuple[tuple[int, ...], tuple[int, ...]]

final move-axes operation to move the vectorized indices to the beginning of the tensor

more_than_one_vectorized_index

there is more than one vectorized index, thus a new axis will be added

__call__(tensor: Tensor) Tensor[source]

Apply the index to a tensor.

__eq__(value, /)

Return self==value.

__new__(**kwargs)