Skip to content

[Breaking change]: Tensor operations align equivalent shapes and empty tensors #56305

Description

@tannergooding

Description

Tensor operations now apply consistent shape-alignment rules across broadcasting, elementwise operations, copies, equality, reshaping, axis operations, and views. Default rank-zero empty tensors and spans are treated as having the effective shape [0] for computations, and redundant leading singleton dimensions can be added or removed when aligning shapes.

The change also corrects native-width traversal so tensor spans backed by native storage can process more than int.MaxValue logical elements without narrowing their element counts. No public API was added. For details, see dotnet/runtime#135060.

Version

Other (please put exact version in description textbox)

.NET 11.

Previous behavior

Shape handling was inconsistent across tensor operations. Default rank-zero empty values retained empty metadata, but individual operations did not consistently treat them as vectors of shape [0]. Some operations required exact shape matches while others aligned dimensions differently, so equivalent shapes with redundant leading singleton dimensions could be rejected or interpreted inconsistently. Axis behavior for stack and concatenate could also vary with the input ranks.

For example, a default empty tensor and a tensor with explicit shape [0] could be handled differently by operations that inspected stored rank or lengths directly. Operations could also reject or misalign an input with shape [1, 1, 3] when used with a destination or other input having shape [3].

Native-backed tensor spans with more than int.MaxValue logical elements could also encounter traversal paths that narrowed element counts or offsets to int, preventing operations from correctly covering the full logical range.

The EqualsAny, GreaterThanAny, GreaterThanOrEqualAny, LessThanAny, and LessThanOrEqualAny operations did not consistently traverse the input's logical length, including for empty inputs.

New behavior

Tensor computations treat Tensor<T>.Empty and default tensor spans as having effective shape [0], while preserving their stored Rank, Lengths, and Strides. Explicitly ranked empty shapes retain their specified dimensions.

When an operation aligns shapes, it may add or remove redundant leading singleton dimensions. Thus [1, 1, 3] can align with [3], but [2, 1] and [1, 2] remain distinct, and zero-length dimensions are not discarded. Shape equality ignores only leading singleton padding; it does not broadcast other dimensions. Stack and concatenate interpret their axis using the first input's effective shape, and other inputs and destinations are aligned to that shape.

For example, default empty values can broadcast to [2, 0], while a binary operation between effective shapes [0] and [0, 2] rejects the incompatible trailing dimensions. A source with shape [1, 1, 3] can be copied or used in an elementwise operation with a destination of shape [3]; the source and destination retain their requested shape metadata.

Native-backed tensor spans can now be traversed using native-sized lengths and offsets, including spans with more than int.MaxValue logical elements. Empty views also retain a storage origin within their source.

The EqualsAny, GreaterThanAny, GreaterThanOrEqualAny, LessThanAny, and LessThanOrEqualAny operations now inspect the input's full logical range and handle empty inputs correctly.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

The previous shape rules differed across related tensor operations, making it difficult to predict which dimensions were significant and how empty values would behave. Applying shared alignment rules makes broadcasting, copies, equality, reshaping, axis operations, and views consistent while preserving explicitly requested dimensions. Native-sized traversal avoids prematurely narrowing logical counts and offsets for spans backed by larger storage.

Recommended action

Review code that depends on a particular tensor operation accepting or rejecting shape combinations. Account for default rank-zero empty values as effective shape [0] during computations, and account for leading singleton padding being ignored when shapes are aligned or compared. Explicit zero-length dimensions and non-leading singleton dimensions remain significant.

For stack and concatenate, interpret the axis relative to the first input's effective shape; the first input determines the result's rank. If an application requires exact stored ranks or lengths rather than shape equivalence, validate those metadata explicitly before calling the operation. The stored metadata of default empty values is not changed.

There is no compatibility switch to restore the previous inconsistent shape handling.

Feature area

Core .NET libraries

Affected APIs

  • System.Numerics.Tensors.Tensor.Broadcast, BroadcastTo, and TryBroadcastTo (all overloads).
  • System.Numerics.Tensors.Tensor elementwise operations and copy operations that align tensor shapes or write to a caller-provided destination.
  • System.Numerics.Tensors.Tensor.Concatenate, ConcatenateOnDimension, Stack, and StackAlongDimension (all overloads).
  • System.Numerics.Tensors.Tensor.Reshape, Split, SqueezeDimension, Unsqueeze, PermuteDimensions, SetSlice, SequenceEqual, ResizeTo, Reverse, and ReverseDimension (all changed overloads).
  • System.Numerics.Tensors.Tensor.EqualsAny, GreaterThanAny, GreaterThanOrEqualAny, LessThanAny, and LessThanOrEqualAny (all overloads).
  • System.Numerics.Tensors.Tensor.IndexOfMax, IndexOfMaxMagnitude, IndexOfMin, and IndexOfMinMagnitude (all overloads), for native-backed spans whose logical element count exceeds int.MaxValue.

Note

This issue was generated with AI assistance from GitHub Copilot.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions