Skip to content

[release/10.0-preview4] Rewrite of System.Numerics.Tensors to allow for more code sharing, various correctness fixes, and stability improvements #114979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fa35a89
Refactor Tensor to be more reusable and validate appropriate state
tannergooding Mar 7, 2025
4bc491e
Handle Equals, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrE…
tannergooding Apr 11, 2025
47348fe
Many implementations correctly swapped to new form. (#25)
michaelgsharp Apr 16, 2025
6400ad6
more tensors updates (#26)
michaelgsharp Apr 17, 2025
80dd6da
Resolve a few build failures
tannergooding Apr 17, 2025
96d683b
Ensure SetSlice and ToString are working as expected
tannergooding Apr 17, 2025
28e655f
Tensors lastfew (#27)
michaelgsharp Apr 21, 2025
cbae3a1
Minor cleanup of the Tensor files
tannergooding Apr 21, 2025
5ce0d05
Ensure that tensor tests are building
tannergooding Apr 21, 2025
daeaa58
Resolving various build failures due to API compatibility
tannergooding Apr 21, 2025
0b3476e
Ensure flattendLength is adjusted after the stride is set for that di…
tannergooding Apr 21, 2025
5afc632
Ensure that we set linearLength if -1 is passed in when strides is empty
tannergooding Apr 21, 2025
5e8d924
Ensure that the first index is correct
tannergooding Apr 21, 2025
52ac342
Cleanup to ensure iteration and construction initializes correctly
tannergooding Apr 21, 2025
f9c29f9
Ensure that broadcasting is allowed to be in any stride position
tannergooding Apr 21, 2025
aa745bb
Have AreCompatible handle empty shapes
tannergooding Apr 21, 2025
d757a6c
Ensure IndexOutOfRangeException is thrown for invalid indexes
tannergooding Apr 21, 2025
455a42a
Ensure that the stride is set to 0 when the length of a dimension is …
tannergooding Apr 21, 2025
aea4bf5
Fixing Broadcasting Loop (#29)
michaelgsharp Apr 22, 2025
4668101
Ensure that minimumLinearLength is actually the minimum
tannergooding Apr 22, 2025
f846271
Ensure the rented buffer is cleared
tannergooding Apr 22, 2025
7dd1669
Fix the AreCompatible checks
tannergooding Apr 22, 2025
2d5f2d8
Tensor finishing (#30)
michaelgsharp Apr 22, 2025
6a7456e
only2 tests left
michaelgsharp Apr 22, 2025
a6b4621
Update src/libraries/System.Numerics.Tensors/src/System/Numerics/Tens…
tannergooding Apr 23, 2025
07d7653
Update compatibility suppressions
tannergooding Apr 23, 2025
5dcebe3
transpose working
michaelgsharp Apr 23, 2025
1eb7021
reverse working
michaelgsharp Apr 23, 2025
4fa8d34
Revert the unnecessary sln changes
tannergooding Apr 23, 2025
5a110c4
Remove an unnecessary using
tannergooding Apr 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@
<data name="ThrowArgument_SetSliceNoRange" xml:space="preserve">
<value>When no ranges are specified the values tensor must be equal in size as the input tensor.</value>
</data>
<data name="ThrowArgument_LengthsNotBroadcastCompatible" xml:space="preserve">
<value>Lengths are not broadcast compatible.</value>
<data name="ThrowArgument_LengthsNotCompatible" xml:space="preserve">
<value>Lengths are not compatible with each other.</value>
</data>
<data name="ThrowArgument_SplitNotSplitEvenly" xml:space="preserve">
<value>The number of splits must perfectly divide the dimension.</value>
Expand Down Expand Up @@ -240,4 +240,7 @@
<data name="Arithmetic_NaN" xml:space="preserve">
<value>Function does not accept floating point Not-a-Number values.</value>
</data>
</root>
<data name="ThrowArgument_LengthIsNonZeroForNullReference" xml:space="preserve">
<value>The provided length is non zero while the data reference is null.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,8 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="System\Numerics\Tensors\netcore\TensorShape.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorHelpers.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorExtensions.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor.Factory.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ITensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\IReadOnlyTensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanDebugView.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ReadOnlyTensorSpan.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanHelpers.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanHelpers.T.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpan.cs" />
<Compile Include="System\NIndex.cs" />
<Compile Include="System\NRange.cs" />
<Compile Include="System\Buffers\NIndex.cs" />
<Compile Include="System\Buffers\NRange.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IAggregationOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IBinaryOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IBooleanUnaryOperator.cs" />
Expand All @@ -44,6 +32,14 @@
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IUnaryOneToFourOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IUnaryOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IUnaryTwoToOneOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\IReadOnlyTensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\IReadOnlyTensor_1.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ITensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ITensor_1.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ReadOnlyTensorSpan_1.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor_1.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorOperation.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.Abs.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.Acos.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.Acosh.cs" />
Expand Down Expand Up @@ -170,6 +166,9 @@
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.TrailingZeroCount.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.Truncate.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorPrimitives.Xor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorShape.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpan.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanDebugView.cs" />
</ItemGroup>

<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,181 +2,50 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Numerics.Tensors
{

/// <summary>
/// Represents a read-only tensor.
/// </summary>
/// <summary>Represents a read-only tensor.</summary>
[Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public interface IReadOnlyTensor
{
/// <summary>
/// Gets a value that indicates whether the collection is currently empty.
/// </summary>
/// <summary>Gets the specified element of the tensor.</summary>
/// <param name="indexes">The index of the element for which to get.</param>
/// <returns>The element that exists at <paramref name="indexes" />.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the following conditions is met:
/// * <paramref name="indexes" /> does not contain <see cref="Rank" /> elements
/// * <paramref name="indexes" /> contains an element that is negative or greater than or equal to the corresponding dimension length
/// </exception>
object? this[params scoped ReadOnlySpan<nint> indexes] { get; }

/// <inheritdoc cref="this[ReadOnlySpan{nint}]" />
object? this[params scoped ReadOnlySpan<NIndex> indexes] { get; }

/// <summary>Gets the total number of items in the tensor.</summary>
nint FlattenedLength { get; }

/// <summary>Gets a value indicating whether this tensor is empty.</summary>
/// <value><see langword="true"/> if this tensor is empty; otherwise, <see langword="false"/>.</value>
bool IsEmpty { get; }

/// <summary>
/// Gets a value that indicates whether the underlying buffer is pinned.
/// </summary>
/// <summary>Gets a value that indicates whether the underlying buffer is pinned.</summary>
bool IsPinned { get; }

/// <summary>
/// Gets the number of elements in the tensor.
/// </summary>
nint FlattenedLength { get; }

/// <summary>
/// Gets the number of dimensions in the tensor.
/// </summary>
int Rank { get; }

/// <summary>
/// Gets the length of each dimension in the tensor.
/// </summary>
/// <summary>Gets the length of each dimension in the tensor.</summary>
[UnscopedRef]
ReadOnlySpan<nint> Lengths { get; }

/// <summary>
/// Gets the stride of each dimension in the tensor.
/// </summary>
/// <summary>Gets the rank, or number of dimensions, in the tensor.</summary>
int Rank { get; }

/// <summary>Gets the stride of each dimension in the tensor.</summary>
[UnscopedRef]
ReadOnlySpan<nint> Strides { get; }

/// <summary>
/// Gets the value at the specified indexes.
/// </summary>
/// <param name="indexes">The indexes to be used.</param>
object this[params scoped ReadOnlySpan<nint> indexes] { get; }

/// <summary>
/// Gets the value at the specified indexes.
/// </summary>
/// <param name="indexes">The indexes to be used.</param>
object this[params scoped ReadOnlySpan<NIndex> indexes] { get; }

/// <summary>
/// Pins and gets a <see cref="MemoryHandle"/> to the backing memory.
/// </summary>
/// <summary>Pins and gets a <see cref="MemoryHandle"/> to the backing memory.</summary>
/// <returns><see cref="MemoryHandle"/></returns>
MemoryHandle GetPinnedHandle();
}

/// <summary>
/// Represents a read-only tensor.
/// </summary>
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
/// <typeparam name="T">The element type.</typeparam>
[Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public interface IReadOnlyTensor<TSelf, T> : IReadOnlyTensor, IEnumerable<T>
where TSelf : IReadOnlyTensor<TSelf, T>
{
/// <summary>
/// Gets an empty tensor.
/// </summary>
static abstract TSelf? Empty { get; }

/// <summary>
/// Gets the value at the specified indexes.
/// </summary>
/// <param name="indexes">The indexes to be used.</param>
new T this[params scoped ReadOnlySpan<nint> indexes] { get; }

/// <summary>
/// Gets the value at the specified indexes.
/// </summary>
/// <param name="indexes">The indexes to be used.</param>
new T this[params scoped ReadOnlySpan<NIndex> indexes] { get; }

/// <summary>
/// Gets the values at the specified ranges.
/// </summary>
/// <param name="ranges">The ranges to be used.</param>
TSelf this[params scoped ReadOnlySpan<NRange> ranges] { get; }

/// <summary>
/// Creates a read-only tensor span for the entire underlying buffer.
/// </summary>
/// <returns>The converted <see cref="ReadOnlyTensorSpan{T}"/>.</returns>
ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan();

/// <summary>
/// Creates a read-only tensor span for the specified start indexes.
/// </summary>
/// <param name="start">The start locations to be used.</param>
/// <returns>The converted <see cref="ReadOnlyTensorSpan{T}"/>.</returns>
ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<nint> start);

/// <summary>
/// Creates a read-only tensor span for the specified start indexes.
/// </summary>
/// <param name="startIndex">The started indexes to be used.</param>
/// <returns>The converted <see cref="ReadOnlyTensorSpan{T}"/>.</returns>
ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<NIndex> startIndex);

/// <summary>
/// Creates a read-only tensor span for the specified ranges.
/// </summary>
/// <param name="range">The ranges to be used.</param>
/// <returns>The converted <see cref="ReadOnlyTensorSpan{T}"/>.</returns>
ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<NRange> range);

/// <summary>
/// Copies the tensor to the specified destination. The destination tensor must be equal to or larger than the source tensor.
/// </summary>
/// <param name="destination">The destination span where the data should be copied to.</param>
void CopyTo(scoped TensorSpan<T> destination);

/// <summary>
/// Flattens the tensor to the specified destination. The destination span must be equal to or larger than the number of elements in the source tensor.
/// </summary>
/// <param name="destination">The destination span where the data should be flattened to.</param>
void FlattenTo(scoped Span<T> destination);

/// <summary>
/// Returns a reference to the 0th element of the tensor. If the tensor is empty, returns <see langword="null"/>.
/// </summary>
/// <remarks>
/// This method can be used for pinning and is required to support the use of the tensor within a fixed statement.
/// </remarks>
ref readonly T GetPinnableReference();

/// <summary>
/// Slices the tensor using the specified start indexes.
/// </summary>
/// <param name="start">The start locations to be used.</param>
/// <returns>The sliced tensor.</returns>
TSelf Slice(params scoped ReadOnlySpan<nint> start);

/// <summary>
/// Slices the tensor using the specified start indexes.
/// </summary>
/// <param name="startIndex">The start indexes to be used.</param>
/// <returns>The sliced tensor.</returns>
TSelf Slice(params scoped ReadOnlySpan<NIndex> startIndex);

/// <summary>
/// Slices the tensor using the specified ranges.
/// </summary>
/// <param name="range">The ranges to be used.</param>
/// <returns>The sliced tensor.</returns>
TSelf Slice(params scoped ReadOnlySpan<NRange> range);

/// <summary>
/// Tries to copy the tensor to the specified destination. The destination tensor must be equal to or larger than the source tensor.
/// </summary>
/// <param name="destination">The destination span where the data should be copied to.</param>
/// <returns><see langword="true" /> if the copy succeeded, <see langword="false" /> otherwise.</returns>
bool TryCopyTo(scoped TensorSpan<T> destination);

/// <summary>
/// Tries to flatten the tensor to the specified destination. The destination span must be equal to or larger than the number of elements in the source tensor.
/// </summary>
/// <param name="destination">The destination span where the data should be flattened to.</param>
/// <returns><see langword="true" /> if the flatten succeeded, <see langword="false" /> otherwise.</returns>
bool TryFlattenTo(scoped Span<T> destination);
}
}
Loading
Loading