Fix Tensor stride computation for single-element (length <= 1) tensors#125403
Merged
tannergooding merged 3 commits intomainfrom Mar 13, 2026
Merged
Fix Tensor stride computation for single-element (length <= 1) tensors#125403tannergooding merged 3 commits intomainfrom
tannergooding merged 3 commits intomainfrom
Conversation
Two shortcut TensorShape.Create overloads hardcoded strides: [1] instead of correctly using stride 0 when linearLength <= 1, causing: - Tensor.Create(T[]) with single-element arrays had stride 1 instead of 0 - CreateFromShapeUninitialized with explicit strides [1] for length 1 threw - CopyTo between tensors with mismatched strides failed Fixed TensorShape.Create<T>(T[]?) and TensorShape.Create<T>(ref readonly T, nint, bool) to use stride 0 and set IsBroadcast flag when linearLength <= 1. Updated existing tests that expected incorrect stride values. Added regression test covering the reported scenarios. Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix Tensor.CreateFromShape for FlattenedLength of one
Fix Tensor stride computation for single-element (length <= 1) tensors
Mar 10, 2026
Contributor
|
Tagging subscribers to this area: @dotnet/area-system-numerics-tensors |
src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
tannergooding
approved these changes
Mar 12, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes an inconsistency in System.Numerics.Tensors where certain TensorShape.Create fast paths hardcoded a stride of 1, bypassing the library’s invariant that dimensions with length <= 1 must have stride == 0 (broadcast semantics). This unblocks creating and copying single-element tensors via Tensor.Create, TensorSpan, and ReadOnlyTensorSpan.
Changes:
- Fix
TensorShape.Create(T[]?)andTensorShape.Create(ref readonly T, nint, bool)to setstride = 0and includeIsBroadcastwhenlinearLength <= 1. - Update existing tests to expect
stride == 0for empty-array tensor spans. - Add a regression test covering single-element tensor creation, shape-based creation (with/without explicit strides), and
CopyTo.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs |
Aligns shortcut shape creation with the core stride/broadcast invariants for linearLength <= 1. |
src/libraries/System.Numerics.Tensors/tests/TensorTests.cs |
Adds regression coverage for single-element tensors across Create* APIs and CopyTo. |
src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs |
Corrects empty-array stride expectation to 0 to match computed strides. |
src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs |
Corrects empty-array stride expectation to 0 to match computed strides. |
You can also share your feedback on Copilot code review. Take the survey.
tarekgh
approved these changes
Mar 12, 2026
Copilot AI
added a commit
that referenced
this pull request
Mar 13, 2026
#125403) ## Description Two shortcut `TensorShape.Create` overloads hardcoded `strides: [1]` instead of correctly using stride 0 when `linearLength <= 1`, bypassing the special case that the normal stride computation path handles correctly. This made it impossible to work with single-element tensors: ```csharp var src = Tensor.Create([1.0]); // stride incorrectly set to 1 var dst = Tensor.CreateFromShapeUninitialized<double>(src.Lengths); // stride correctly 0 src.CopyTo(dst); // throws: "Destination is too short" // Passing explicit strides also fails var dst2 = Tensor.CreateFromShapeUninitialized<double>(src.Lengths, src.Strides); // throws: "The provided lengths and strides would allow you to access elements outside the provided memory." ``` ### Changes - **`TensorShape.Create<T>(T[]? array)`** — Default stride to 1, then set stride to 0 and add `IsBroadcast` flag when `linearLength <= 1`. Affects `Tensor.Create(T[])`, `TensorSpan<T>(T[])`, `ReadOnlyTensorSpan<T>(T[])`. - **`TensorShape.Create<T>(ref readonly T, nint, bool)`** — Same fix. Affects `TensorSpan<T>(Span<T>)` and `ReadOnlyTensorSpan<T>(ReadOnlySpan<T>)`. - **Existing test corrections** — Two tests in `TensorSpanTests.cs` and `ReadOnlyTensorSpanTests.cs` asserted `stride == 1` for empty arrays; corrected to expect 0 (consistent with auto-computed strides). - **Regression test** — `TensorCreateSingleElementTests` covers `Create`, `CreateFromShape`, `CreateFromShapeUninitialized` (with and without explicit strides), `CopyTo`, and span-based construction for single-element tensors. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Tensor.CreateFromShape and Tensor.CreateFromShapeUninitialized do not work correctly when FlattenedLength == 1</issue_title> > <issue_description>### Description > > It is currently not possible to create a tensor with a single value (`FlattenedLength == 1`), because it results in an empty backing array. Explicitly specifying the strides causes an exception (`The provided lengths and strides would allow you to access elements outside the provided memory.`), but it should not, as no memory was provided and it should be allocated correctly by the method itself. > > ### Reproduction Steps > > To reproduce, compile and run the code for each scenario. Using `CreateFromShape` instead of `CreateFromShapeUninitialized` yields the same results. > > #### A (without explicit strides) > > ```cs > var src = Tensor.Create([1.0]); > Console.WriteLine($"src lengths: {String.Join(", ", src.Lengths.ToArray())}; strides: {String.Join(", ", src.Strides.ToArray())}; flattened length: {src.FlattenedLength}"); > > var dst = Tensor.CreateFromShapeUninitialized<double>(src.Lengths); > Console.WriteLine($"dst lengths: {String.Join(", ", dst.Lengths.ToArray())}; strides: {String.Join(", ", dst.Strides.ToArray())}; flattened length: {src.FlattenedLength}"); > > src.CopyTo(dst); > ``` > > #### B (with explicit strides) > > ```cs > var src = Tensor.Create([1.0]); > Console.WriteLine($"src lengths: {String.Join(", ", src.Lengths.ToArray())}; strides: {String.Join(", ", src.Strides.ToArray())}; flattened length: {src.FlattenedLength}"); > > var dst = Tensor.CreateFromShapeUninitialized<double>(src.Lengths, src.Strides); // <- notice the difference here > Console.WriteLine($"dst lengths: {String.Join(", ", dst.Lengths.ToArray())}; strides: {String.Join(", ", dst.Strides.ToArray())}; flattened length: {src.FlattenedLength}"); > > src.CopyTo(dst); > ``` > > ### Expected behavior > > In both scenarios, the values in `src` are successfully copied to `dst`. > > ### Actual behavior > > In both scenarios, an `ArgumentException` is thrown, but for different reasons. > > #### A > > ##### Output > > ``` > src lengths: 1; strides: 1; flattened length: 1 > dst lengths: 1; strides: 0; flattened length: 1 > ``` > > ##### Exception > > ``` > System.ArgumentException: 'Destination is too short. (Parameter 'destination')' > ``` > > #### B > > ##### Output > > ``` > src lengths: 1; strides: 1; flattened length: 1 > ``` > > ##### Exception > > ``` > System.ArgumentException: 'The provided lengths and strides would allow you to access elements outside the provided memory.' > ``` > > ### Regression? > > _No response_ > > ### Known Workarounds > > _No response_ > > ### Configuration > > - **.NET:** 10.0.3 > - **.NET SDK:** 10.0.200-preview.0.26103.119 > - **OS:** Windows 11 Home 25H2 26200.7922 > - **Architecture:** x64 > > ### Other information > > _No response_</issue_description> > > <agent_instructions>Ensure that Tensor.Create(T[]) correctly initializes the stride to 0 if length is 1. Add a corresponding regression test and check if any other Create APIs have a similar issue where they short circuit the normal strides computation logic and so fail to account for the length == 1 special case.</agent_instructions> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@tannergooding</author><body> > Looks like this is rather a bug in `Tensor.Create(T[])` where it fails to do `stride = (length > 1) ? length : 0` and only does `stride = length` > > Fix is simple, will have it up momentarily.</body></comment_new> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #125366 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Two shortcut
TensorShape.Createoverloads hardcodedstrides: [1]instead of correctly using stride 0 whenlinearLength <= 1, bypassing the special case that the normal stride computation path handles correctly. This made it impossible to work with single-element tensors:Changes
TensorShape.Create<T>(T[]? array)— Default stride to 1, then set stride to 0 and addIsBroadcastflag whenlinearLength <= 1. AffectsTensor.Create(T[]),TensorSpan<T>(T[]),ReadOnlyTensorSpan<T>(T[]).TensorShape.Create<T>(ref readonly T, nint, bool)— Same fix. AffectsTensorSpan<T>(Span<T>)andReadOnlyTensorSpan<T>(ReadOnlySpan<T>).TensorSpanTests.csandReadOnlyTensorSpanTests.csassertedstride == 1for empty arrays; corrected to expect 0 (consistent with auto-computed strides).TensorCreateSingleElementTestscoversCreate,CreateFromShape,CreateFromShapeUninitialized(with and without explicit strides),CopyTo, and span-based construction for single-element tensors.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.