Accessing Tensor Elements

Indexing tensors is broadly similar to indexing arrays. Some of the differences are:

  • Tensor indexers always return a tensor, even if the result is a single value. To get single values, use the GetValue() method.

  • In addition to integers and ranges, tensor indices may be boolean arrays, sets of integers, or certain special values.

  • The number of indices does not have to be equal to the number of dimensions. When some indices are omitted, the remaining dimensions are assumed to be full.

  • Whenever possible, tensor indexers return a view of the data. The main reason is performance: copying elements is often unnecessary.

All these points are expanded on below.

Tensor Indices

There are six different types of tensor indices. Tensors can have 2, 3, 4 or more dimensions. To avoid a combinatorial explosion of overloads, indexing is performed using a TensorIndex structure. There is an implicit conversion to TensorIndex for every value that may serve as a tensor index.

Integers

An integer index selects a single value within a dimension. The presence of an integer index reduces the rank of the resulting tensor by one.

Passing all integer indices results in a scalar (rank 0) tensor:

System.Index

The Index type was introduced in C# 8.0. Indexes work exactly like integers, but allow for counting from the end of a dimension. As with integers, the rank of the resulting tensor is reduced by one.

System.Range

The Range type was also introduced in C# 8.0. The resulting tensor contains only the values along the dimension that fall within the specified range.

A range index does not change the rank of the resulting tensor, regardless of how many elements are in the range. If the range is empty, then the resulting tensor will be empty as well. If the range contains only one value, then the resulting tensor will have length 1 along the dimension.

All variations of ranges may be used:

Extreme.Mathematics.Range and Extreme.Mathematics.Slice

The Range and Slice types were introduced before Index and Range. Their use is similar to the ranges discussed above.

Advanced Indices

Occasionally, it can be useful to have a more complex value as an index. The AdvancedTensorIndex structure enables the use of lists of integer and booleans as a tensor indices.

As with TensorIndex, implicit conversions do the heavy lifting.

When the index is a sequence of integers, the resulting tensor contains the entries whose index in the specified dimension is listed in the sequence in the order in which they appear in the sequence.

The sequence can be an array, an IEnumerable<T>, a Vector<T> or even a Tensor with integer elements. It must have the same number of elements as the length of the dimension.

When the index is a sequence of booleans, the resulting tensor contains only those entries whose value at the corresponding position in the index is true. The length in the dimension is the total number of true values in the index sequence.

Indexers that include an advanced index always return a copy of the data. If the same index occurs two or more times, the same values are copied each time.