From fa35a89c3a20f1aee3e404977e3bc570f1910dff Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 7 Mar 2025 11:25:48 -0800 Subject: [PATCH 01/30] Refactor Tensor to be more reusable and validate appropriate state --- .../src/Resources/Strings.resx | 58 +- .../src/System.Numerics.Tensors.csproj | 27 +- .../src/System/{ => Buffers}/NIndex.cs | 0 .../src/System/{ => Buffers}/NRange.cs | 0 .../Tensors/netcore/IReadOnlyTensor.cs | 183 +- .../Tensors/netcore/IReadOnlyTensor_1.cs | 103 + .../Numerics/Tensors/netcore/ITensor.cs | 162 +- .../Numerics/Tensors/netcore/ITensor_1.cs | 93 + .../Tensors/netcore/ReadOnlyTensorSpan.cs | 828 -- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 413 + .../Tensors/netcore/Tensor.Factory.cs | 216 - .../System/Numerics/Tensors/netcore/Tensor.cs | 7596 +++++++++++++++-- .../Tensors/netcore/TensorExtensions.cs | 7183 ---------------- .../Numerics/Tensors/netcore/TensorHelpers.cs | 124 - .../Tensors/netcore/TensorOperation.cs | 285 + .../Numerics/Tensors/netcore/TensorShape.cs | 913 +- .../Numerics/Tensors/netcore/TensorSpan.cs | 882 +- .../Tensors/netcore/TensorSpanHelpers.T.cs | 53 - .../Tensors/netcore/TensorSpanHelpers.cs | 306 - .../Numerics/Tensors/netcore/Tensor_1.cs | 391 + .../src/System/ThrowHelper.cs | 30 +- 21 files changed, 9531 insertions(+), 10315 deletions(-) rename src/libraries/System.Numerics.Tensors/src/System/{ => Buffers}/NIndex.cs (100%) rename src/libraries/System.Numerics.Tensors/src/System/{ => Buffers}/NRange.cs (100%) create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.T.cs delete mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs diff --git a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx index 1039f4e0e286f0..272f6d9db8708c 100644 --- a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx +++ b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -240,4 +240,8 @@ Function does not accept floating point Not-a-Number values. + + The provided length is non zero while the data reference is null. + + diff --git a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj index 807aaf15ff873d..7202c69d6ab762 100644 --- a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj @@ -19,20 +19,8 @@ - - - - - - - - - - - - - - + + @@ -44,6 +32,14 @@ + + + + + + + + @@ -170,6 +166,9 @@ + + + diff --git a/src/libraries/System.Numerics.Tensors/src/System/NIndex.cs b/src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.cs similarity index 100% rename from src/libraries/System.Numerics.Tensors/src/System/NIndex.cs rename to src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.cs diff --git a/src/libraries/System.Numerics.Tensors/src/System/NRange.cs b/src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.cs similarity index 100% rename from src/libraries/System.Numerics.Tensors/src/System/NRange.cs rename to src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.cs diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor.cs index ce52ab8c279b91..9a90a17cb1640c 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor.cs @@ -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 { - - /// - /// Represents a read-only tensor. - /// + /// Represents a read-only tensor. [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] public interface IReadOnlyTensor { - /// - /// Gets a value that indicates whether the collection is currently empty. - /// + /// Gets the specified element of the tensor. + /// The index of the element for which to get. + /// The element that exists at . + /// + /// Thrown when one of the following conditions is met: + /// * does not contain elements + /// * contains an element that is negative or greater than or equal to the corresponding dimension length + /// + object? this[params scoped ReadOnlySpan indexes] { get; } + + /// + object? this[params scoped ReadOnlySpan indexes] { get; } + + /// Gets the total number of items in the tensor. + nint FlattenedLength { get; } + + /// Gets a value indicating whether this tensor is empty. + /// if this tensor is empty; otherwise, . bool IsEmpty { get; } - /// - /// Gets a value that indicates whether the underlying buffer is pinned. - /// + /// Gets a value that indicates whether the underlying buffer is pinned. bool IsPinned { get; } - /// - /// Gets the number of elements in the tensor. - /// - nint FlattenedLength { get; } - - /// - /// Gets the number of dimensions in the tensor. - /// - int Rank { get; } - - /// - /// Gets the length of each dimension in the tensor. - /// + /// Gets the length of each dimension in the tensor. [UnscopedRef] ReadOnlySpan Lengths { get; } - /// - /// Gets the stride of each dimension in the tensor. - /// + /// Gets the rank, or number of dimensions, in the tensor. + int Rank { get; } + + /// Gets the stride of each dimension in the tensor. [UnscopedRef] ReadOnlySpan Strides { get; } - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - object this[params scoped ReadOnlySpan indexes] { get; } - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - object this[params scoped ReadOnlySpan indexes] { get; } - - /// - /// Pins and gets a to the backing memory. - /// + /// Pins and gets a to the backing memory. /// MemoryHandle GetPinnedHandle(); } - - /// - /// Represents a read-only tensor. - /// - /// The type that implements this interface. - /// The element type. - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - public interface IReadOnlyTensor : IReadOnlyTensor, IEnumerable - where TSelf : IReadOnlyTensor - { - /// - /// Gets an empty tensor. - /// - static abstract TSelf? Empty { get; } - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - new T this[params scoped ReadOnlySpan indexes] { get; } - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - new T this[params scoped ReadOnlySpan indexes] { get; } - - /// - /// Gets the values at the specified ranges. - /// - /// The ranges to be used. - TSelf this[params scoped ReadOnlySpan ranges] { get; } - - /// - /// Creates a read-only tensor span for the entire underlying buffer. - /// - /// The converted . - ReadOnlyTensorSpan AsReadOnlyTensorSpan(); - - /// - /// Creates a read-only tensor span for the specified start indexes. - /// - /// The start locations to be used. - /// The converted . - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start); - - /// - /// Creates a read-only tensor span for the specified start indexes. - /// - /// The started indexes to be used. - /// The converted . - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex); - - /// - /// Creates a read-only tensor span for the specified ranges. - /// - /// The ranges to be used. - /// The converted . - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range); - - /// - /// Copies the tensor to the specified destination. The destination tensor must be equal to or larger than the source tensor. - /// - /// The destination span where the data should be copied to. - void CopyTo(scoped TensorSpan destination); - - /// - /// 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. - /// - /// The destination span where the data should be flattened to. - void FlattenTo(scoped Span destination); - - /// - /// Returns a reference to the 0th element of the tensor. If the tensor is empty, returns . - /// - /// - /// This method can be used for pinning and is required to support the use of the tensor within a fixed statement. - /// - ref readonly T GetPinnableReference(); - - /// - /// Slices the tensor using the specified start indexes. - /// - /// The start locations to be used. - /// The sliced tensor. - TSelf Slice(params scoped ReadOnlySpan start); - - /// - /// Slices the tensor using the specified start indexes. - /// - /// The start indexes to be used. - /// The sliced tensor. - TSelf Slice(params scoped ReadOnlySpan startIndex); - - /// - /// Slices the tensor using the specified ranges. - /// - /// The ranges to be used. - /// The sliced tensor. - TSelf Slice(params scoped ReadOnlySpan range); - - /// - /// Tries to copy the tensor to the specified destination. The destination tensor must be equal to or larger than the source tensor. - /// - /// The destination span where the data should be copied to. - /// if the copy succeeded, otherwise. - bool TryCopyTo(scoped TensorSpan destination); - - /// - /// 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. - /// - /// The destination span where the data should be flattened to. - /// if the flatten succeeded, otherwise. - bool TryFlattenTo(scoped Span destination); - } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs new file mode 100644 index 00000000000000..bd1024bcb74aea --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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 +{ + /// Represents a read-only tensor. + /// The type that implements this interface. + /// The element type. + [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public interface IReadOnlyTensor : IReadOnlyTensor, IEnumerable + where TSelf : IReadOnlyTensor + { + /// Gets an empty tensor. + static abstract TSelf Empty { get; } + + /// Gets a reference to the specified element of the tensor. + /// The index of the element for which to get a reference. + /// A reference to the element that exists at . + /// + /// Thrown when one of the following conditions is met: + /// * does not contain elements + /// * contains an element that is negative or greater than or equal to the corresponding dimension length + /// + new ref readonly T this[params scoped ReadOnlySpan indexes] { get; } + + /// + new ref readonly T this[params scoped ReadOnlySpan indexes] { get; } + + /// + TSelf this[params scoped ReadOnlySpan ranges] { get; } + + /// Creates a new readonly tensor span over the tensor. + /// The readonly tensor span representation of the tensor. + ReadOnlyTensorSpan AsReadOnlyTensorSpan(); + + /// Creates a new readonly tensor span over a portion of the tensor starting at a specified position to the end of the tensor. + /// The initial index from which the tensor will be converted. + /// The readonly tensor span representation of the tensor. + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start); + + /// + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex); + + /// Creates a new readonly tensor span over a portion of the tensor defined by the specified range. + /// The range of the tensor to convert. + /// The readonly tensor span representation of the tensor. + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range); + + /// Copies the contents of the tensor into a destination tensor span. + /// The destination tensor span. + /// is shorter than the source tensor. + /// This method copies all of the source tensor to even if they overlap. + void CopyTo(scoped in TensorSpan destination); + + /// Flattens the contents of the tensor into a destination span. + /// The destination span. + /// is shorter than the source tensor. + /// This method copies all of the source tensor to even if they overlap. + void FlattenTo(scoped Span destination); + + /// Returns a reference to an object of type that can be used for pinning. + /// A reference to the element of the tensor at index 0, or null if the tensor is empty. + /// This method is intended to support .NET compilers and is not intended to be called by user code. + ref readonly T GetPinnableReference(); + + /// Forms a slice out of the current tensor that begins at a specified index. + /// The indexes at which to begin the slice. + /// A tensor that consists of all elements of the current tensor from to the end of the tensor. + /// is greater than the number of items in the tensor. + TSelf Slice(params scoped ReadOnlySpan start); + + /// + TSelf Slice(params scoped ReadOnlySpan startIndex); + + /// Gets a slice out of the current tensor that contains a specified range. + /// The range of which to slice. + /// A tensor that consists of all elements of the current tensor in . + /// is larger than the tensor. + TSelf Slice(params scoped ReadOnlySpan range); + + /// Attempts to copy the contents of this tensor into a destination tensor span and returns a value to indicate whether or not the operation succeeded. + /// The target of the copy operation. + /// if the copy operation succeeded; otherwise, false. + /// + /// If the source and overlap, the entirety of the source is handled as if it was copied to a temporary location before it is copied to . + /// If the length is shorter than the source, no items are copied and the method returns false. + /// + bool TryCopyTo(scoped in TensorSpan destination); + + /// Attempts to flatten the contents of this tensor into a destination span and returns a value to indicate whether or not the operation succeeded. + /// The target of the copy operation. + /// if the copy operation succeeded; otherwise, false. + /// + /// If the source and overlap, the entirety of the source is handled as if it was flattened to a temporary location before it is copied to . + /// If the length is shorter than the source, no items are copied and the method returns false. + /// + bool TryFlattenTo(scoped Span destination); + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor.cs index 4a1cbd2b1bf800..f1adace43cb916 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor.cs @@ -6,156 +6,32 @@ namespace System.Numerics.Tensors { - /// - /// Represents a tensor. - /// + /// Represents a tensor. [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] public interface ITensor : IReadOnlyTensor { - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - new object this[params scoped ReadOnlySpan indexes] { get; set; } - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to be used. - new object this[params scoped ReadOnlySpan indexes] { get; set; } - - /// - /// Gets a value that indicates whether the collection is read-only. - /// + /// Gets or sets the specified element of the tensor. + /// The index of the element for which to get. + /// The element that exists at . + /// + /// Thrown when one of the following conditions is met: + /// * does not contain elements + /// * contains an element that is negative or greater than or equal to the corresponding dimension length + /// + new object? this[params scoped ReadOnlySpan indexes] { get; set; } + + /// + new object? this[params scoped ReadOnlySpan indexes] { get; set; } + + /// Gets a value that indicates whether the tensor is read-only. bool IsReadOnly { get; } - /// - /// Clears the tensor. - /// + /// Clears the contents of the tensor span. + /// This method sets the items in the tensor span to their default values. It does not remove items from the tensor span. void Clear(); - /// - /// Fills the contents of this tensor with the given value. - /// + /// Fills the elements of this tensor with a specified value. + /// The value to assign to each element of the tensor. void Fill(object value); } - - /// - /// Represents a tensor. - /// - /// The type that implements this interface. - /// The element type. - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - public interface ITensor : ITensor, IReadOnlyTensor - where TSelf : ITensor - { - // TODO: Determine if we can implement `IEqualityOperators`. - // It looks like C#/.NET currently hits limitations here as it believes TSelf and T could be the same type - // Ideally we could annotate it such that they cannot be the same type and no conflicts would exist - - /// - /// Creates a new tensor with the specified lengths. - /// - /// The lengths of each dimension. - /// to pin the underlying buffer. The default is . - /// - /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. - /// The underlying buffer is initialized to default values. - /// - static abstract TSelf Create(scoped ReadOnlySpan lengths, bool pinned = false); - - /// - /// Creates a new tensor with the specified lengths and strides. - /// - /// The lengths of each dimension. - /// The strides of each dimension. - /// to pin the underlying buffer. The default is . - /// - /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. - /// The underlying buffer is initialized to default values. - /// - static abstract TSelf Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false); - - /// - /// Creates a new tensor with the specified lengths and strides. - /// - /// The lengths of each dimension. - /// to pin the underlying buffer. The default is . - /// - /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. - /// The underlying buffer is not initialized. - /// - static abstract TSelf CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned = false); - - /// - /// Creates a new tensor with the specified lengths and strides. If is true the underlying buffer is - /// created permanently pinned, otherwise the underlying buffer is not pinned. The underlying buffer is not initialized. - /// - /// The lengths of each dimension. - /// The strides of each dimension. - /// to pin the underlying buffer. The default is . - /// - /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. - /// The underlying buffer is not initialized. - /// - static abstract TSelf CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false); - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to use. - new T this[params scoped ReadOnlySpan indexes] { get; set; } - - /// - /// Gets the value at the specified indexes. - /// - /// The indexes to use. - new T this[params scoped ReadOnlySpan indexes] { get; set; } - - /// - /// Gets the values at the specified ranges. - /// - /// The ranges to be used. - new TSelf this[params scoped ReadOnlySpan ranges] { get; set; } - - /// - /// Creates a tensor span for the entire underlying buffer. - /// - /// The converted . - TensorSpan AsTensorSpan(); - - /// - /// Creates a tensor span for the specified start indexes. - /// - /// The start locations to be used. - /// The converted . - TensorSpan AsTensorSpan(params scoped ReadOnlySpan start); - - /// - /// Creates a tensor span for the specified start indexes. - /// - /// The start indexes to be used. - /// The converted . - TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex); - - /// - /// Creates a tensor span for the specified ranges. - /// - /// The ranges to be used. - /// The converted . - TensorSpan AsTensorSpan(params scoped ReadOnlySpan range); - - /// - /// Fills the contents of this tensor with the given value. - /// - void Fill(T value); - - /// - /// Returns a reference to the 0th element of the tensor. If the tensor is empty, returns . - /// - /// - /// This method can be used for pinning and is required to support the use of the tensor within a fixed statement. - /// - new ref T GetPinnableReference(); - } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs new file mode 100644 index 00000000000000..998bfb138bacca --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; + +namespace System.Numerics.Tensors +{ + /// Represents a tensor. + /// The type that implements this interface. + /// The element type. + [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public interface ITensor : ITensor, IReadOnlyTensor + where TSelf : ITensor + { + // TODO: Determine if we can implement `IEqualityOperators`. + // It looks like C#/.NET currently hits limitations here as it believes TSelf and T could be the same type + // Ideally we could annotate it such that they cannot be the same type and no conflicts would exist + + /// Creates a new tensor with the specified lengths. + /// The lengths of each dimension. + /// to pin the underlying buffer. The default is . + /// + /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. + /// The underlying buffer is initialized to default values. + /// + static abstract TSelf Create(scoped ReadOnlySpan lengths, bool pinned = false); + + /// Creates a new tensor with the specified lengths and strides. + /// The lengths of each dimension. + /// The strides of each dimension. + /// to pin the underlying buffer. The default is . + /// + /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. + /// The underlying buffer is initialized to default values. + /// + static abstract TSelf Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false); + + /// Creates a new tensor with the specified lengths and strides. + /// The lengths of each dimension. + /// to pin the underlying buffer. The default is . + /// + /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. + /// The underlying buffer is not initialized. + /// + static abstract TSelf CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned = false); + + /// Creates a new tensor with the specified lengths and strides. If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. The underlying buffer is not initialized. + /// The lengths of each dimension. + /// The strides of each dimension. + /// to pin the underlying buffer. The default is . + /// + /// If is true the underlying buffer is created permanently pinned, otherwise the underlying buffer is not pinned. + /// The underlying buffer is not initialized. + /// + static abstract TSelf CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false); + + /// + new ref T this[params scoped ReadOnlySpan indexes] { get; } + + /// + new ref T this[params scoped ReadOnlySpan indexes] { get; } + + /// Gets or sets a slice out of the current tensor that contains a specified range. + /// The range of which to slice. + /// A tensor that consists of all elements of the current tensor in . + /// is larger than the tensor. + new TSelf this[params scoped ReadOnlySpan range] { get; set; } + + /// Creates a new tensor span over the tensor. + /// The tensor span representation of the tensor. + TensorSpan AsTensorSpan(); + + /// Creates a new tensor span over a portion of the tensor starting at a specified position to the end of the tensor. + /// The initial index from which the tensor will be converted. + /// The tensor span representation of the tensor. + TensorSpan AsTensorSpan(params scoped ReadOnlySpan start); + + /// + TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex); + + /// Creates a new tensor span over a portion of the tensor defined by the specified range. + /// The range of the tensor to convert. + /// The tensor span representation of the tensor. + TensorSpan AsTensorSpan(params scoped ReadOnlySpan range); + + /// + void Fill(T value); + + /// + new ref T GetPinnableReference(); + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs deleted file mode 100644 index c4bdb876dd627d..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs +++ /dev/null @@ -1,828 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; - -#pragma warning disable 0809 //warning CS0809: Obsolete member 'ReadOnlyTensorSpan.Equals(object)' overrides non-obsolete member 'object.Equals(object)' - -namespace System.Numerics.Tensors -{ - /// - /// Represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed - /// or native memory, or to memory allocated on the stack. It is type-safe and memory-safe. - /// - [DebuggerTypeProxy(typeof(TensorSpanDebugView<>))] - [DebuggerDisplay("{ToString(),raw}")] - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - public readonly ref struct ReadOnlyTensorSpan - { - /// A byref or a native ptr. - internal readonly ref T _reference; - internal readonly TensorShape _shape; - - - /// - /// Creates a new span over the entirety of the target array. - /// - /// The target array. - /// Returns default when is null. - /// is covariant and its type is not exactly T[]. - public ReadOnlyTensorSpan(T[]? array) : this(array, 0, [array?.Length ?? 0], []) - { - } - - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the span. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The strides of each dimension. If default or span of length 0 is provided, then strides will be automatically calculated. - /// Returns default when is null. - /// is covariant and its type is not exactly T[]. - /// The specified or end index is not in the range (<0 or >FlattenedLength). - /// - public ReadOnlyTensorSpan(T[]? array, Index startIndex, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - : this(array, startIndex.GetOffset(array?.Length ?? 0), lengths, strides) - { - } - - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the span. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The strides of each dimension. If default or span of length 0 is provided, then strides will be automatically calculated. - /// Returns default when is null. - /// is covariant and its type is not exactly T[]. - /// - /// Thrown when the specified or end index is not in the range (<0 or >FlattenedLength). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyTensorSpan(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - { - if (lengths.IsEmpty && array != null) - lengths = [array.Length]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - if (array == null) - { - if (start != 0 || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - - if (!typeof(T).IsValueType && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)maxElements >= (ulong)(uint)array.Length && array.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } - else - { - if (((uint)start > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - start)) && array.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } - - _shape = new TensorShape(array.Length - start, lengths, strides); - _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */); - } - - /// - /// Creates a new over the provided . The new will - /// have a rank of 1 and a length equal to the length of the provided . - /// - /// The target span. - public ReadOnlyTensorSpan(ReadOnlySpan span) : this(span, [span.Length], []) { } - - /// - /// Creates a new over the provided using the specified lengths and strides. - /// - /// The target span. - /// The lengths of each dimension. - /// The strides for each dimension. The strides will be automatically calculated if not provided. - public ReadOnlyTensorSpan(ReadOnlySpan span, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - { - if (lengths.IsEmpty) - lengths = [span.Length]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (span.IsEmpty ? maxElements != 0 : maxElements >= span.Length) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - - _shape = new TensorShape(span.Length, lengths, strides); - _reference = ref MemoryMarshal.GetReference(span); - } - - /// - /// Creates a new over the provided . The new will - /// have a rank of 1 and a length equal to the length of the provided . - /// - /// The target array. - public ReadOnlyTensorSpan(Array? array) : - this(array, - ReadOnlySpan.Empty, - array == null ? - [0] : - TensorSpanHelpers.FillLengths(array.Rank <= TensorShape.MaxInlineRank ? - stackalloc nint[array.Rank] : - new nint[array.Rank], array), - []) - { - } - - /// - /// Creates a new over the provided using the specified start offsets, lengths, and strides. - /// - /// The target array. - /// The starting offset for each dimension. - /// The lengths of each dimension. - /// The strides for each dimension. This strides will be automatically calculated if not provided. - public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - { - if (lengths.IsEmpty && array != null) - { - lengths = TensorSpanHelpers.FillLengths( - array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], - array); - } - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - if (array == null) - { - if (!start.IsEmpty || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - if (array.GetType().GetElementType() != typeof(T)) - ThrowHelper.ThrowArrayTypeMismatchException(); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - - nint startOffset = TensorSpanHelpers.ComputeStartOffsetSystemArray(array, start); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)startOffset + (ulong)(uint)maxElements >= (ulong)(uint)array.Length && array.Length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - else - { - if (((uint)startOffset > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - startOffset)) && array.Length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - - _shape = new TensorShape(array.Length - startOffset, lengths, strides); - _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - } - - /// - /// Creates a new over the provided using the specified start offsets, lengths, and strides. - /// - /// The target array. - /// The starting offset for each dimension. - /// The lengths of each dimension. - /// The strides for each dimension. The strides will be automatically calculated if not provided. - public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan startIndex, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - { - if (lengths.IsEmpty && array != null) - { - lengths = TensorSpanHelpers.FillLengths( - array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], - array); - } - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - if (array == null) - { - if (!startIndex.IsEmpty || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - if (array.GetType().GetElementType() != typeof(T)) - ThrowHelper.ThrowArrayTypeMismatchException(); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - - nint startOffset = TensorSpanHelpers.ComputeStartOffsetSystemArray(array, startIndex); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)startOffset + (ulong)(uint)maxElements > (ulong)(uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - else - { - if ((uint)startOffset > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - startOffset)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - - _shape = new TensorShape(array.Length - startOffset, lengths, strides); - _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - } - - /// - /// Creates a new span over the target unmanaged buffer. - /// - /// An unmanaged data that points to memory. - /// The number of elements the unmanaged memory can hold. - /// - /// This constructor is quite dangerous, because the length is not checked. - /// But if this creation is correct, then all subsequent uses are correct. - /// - [CLSCompliant(false)] - public unsafe ReadOnlyTensorSpan(T* data, nint dataLength) : this(data, dataLength, [dataLength], []) { } - - /// - /// Creates a new span over the target unmanaged buffer. - /// - /// An unmanaged data that points to memory. - /// The number of elements the unmanaged memory can hold. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The lengths of the strides. If nothing is provided, it figures out the default stride configuration. - /// - /// is a reference type or contains pointers and hence cannot be stored in unmanaged memory. - /// - /// - /// The specified length is negative. - /// - /// - /// This constructor is quite dangerous, because the length is not checked. - /// But if this creation is correct, then all subsequent uses are correct. - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - { - if (dataLength < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); - - if (lengths.IsEmpty) - lengths = [dataLength]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (maxElements >= dataLength && dataLength != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - - _shape = new TensorShape(dataLength, lengths, strides); - _reference = ref *data; - } - - // Constructor for internal use only. It is not safe to expose publicly. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlyTensorSpan(ref T reference, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, nint memoryLength) - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - _shape = new TensorShape(memoryLength, lengths, strides); - _reference = ref reference; - } - - /// - /// Returns a reference to specified element of the ReadOnlyTensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or any index is greater than or equal to FlattenedLength. - /// - public ref readonly T this[params scoped ReadOnlySpan indexes] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (indexes.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _shape._memoryLength || index < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return ref Unsafe.Add(ref _reference, index /* force zero-extension */); - } - } - - /// - /// Returns a reference to specified element of the ReadOnlyTensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or any index is greater than or equal to FlattenedLength. - /// - public ref readonly T this[params scoped ReadOnlySpan indexes] - { - get - { - if (indexes.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _shape._memoryLength || index < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return ref Unsafe.Add(ref _reference, index /* force zero-extension */); - } - } - - /// - /// Returns a slice of the ReadOnlyTensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or any index is greater than or equal to FlattenedLength. - /// - public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] - { - get - { - if (ranges.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return Slice(ranges); - } - } - - /// - /// Gets the number of items in the span. - /// - public nint FlattenedLength => _shape.FlattenedLength; - - /// - /// Gets a value indicating whether this is empty. - /// - /// if this span is empty; otherwise, . - public bool IsEmpty => _shape.IsEmpty; - - /// - /// Gets the length of each dimension in this . - /// - [UnscopedRef] - public ReadOnlySpan Lengths => _shape.Lengths; - - /// - /// Gets the rank, or number of dimensions, of this . - /// - public int Rank => Lengths.Length; - - /// - /// Gets the strides of this . - /// - [UnscopedRef] - public ReadOnlySpan Strides => _shape.Strides; - - /// - /// Compares the given spans and returns false if left and right point at the same memory and have the same length. - /// This opertor does *not* check to see if the *contents* are equal. - /// - public static bool operator !=(ReadOnlyTensorSpan left, ReadOnlyTensorSpan right) => !(left == right); - - /// - /// Compares the given spans and returns true if left and right point at the same memory and have the same length. - /// This operator does *not* check to see if the *contents* are equal. - /// - public static bool operator ==(ReadOnlyTensorSpan left, ReadOnlyTensorSpan right) => - left._shape.FlattenedLength == right._shape.FlattenedLength && - left.Rank == right.Rank && - left._shape.Lengths.SequenceEqual(right._shape.Lengths )&& - Unsafe.AreSame(ref left._reference, ref right._reference); - - /// - /// This method is not supported as spans cannot be boxed. To compare two spans, use operator ==. - /// - /// - /// In all cases. - /// -#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member - [Obsolete("Equals() on ReadOnlyTensorSpan will always throw an exception. Use the equality operator instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) => - throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); - - /// - /// This method is not supported as spans cannot be boxed. - /// - /// - /// In all cases. - /// - [Obsolete("GetHashCode() on ReadOnlyTensorSpan will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => - throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); -#pragma warning restore CS0809 - - /// - /// Returns a 0-length read-only span whose base is the null pointer. - /// - public static ReadOnlyTensorSpan Empty => default; - - /// - /// Casts a read-only span of to a read-only span of . - /// - /// The element type of the source read-only span, which must be derived from . - /// The source read-only span. No copy is made. - /// A read-only span with elements cast to the new type. - /// This method uses a covariant cast, producing a read-only span that shares the same memory as the source. The relationships expressed in the type constraints ensure that the cast is a safe operation. - public static ReadOnlyTensorSpan CastUp(ReadOnlyTensorSpan items) where TDerived : class?, T - { - return new ReadOnlyTensorSpan(ref Unsafe.As(ref items._reference), items._shape.Lengths, items._shape.Strides, items._shape._memoryLength); - } - - /// Gets an enumerator for this span. - public Enumerator GetEnumerator() => new Enumerator(this); - - /// Enumerates the elements of a . - public ref struct Enumerator - { - /// The span being enumerated. - private readonly ReadOnlyTensorSpan _span; - /// The current index that the enumerator is on. - private Span _curIndexes; - /// The total item count. - private nint _items; - - /// Initializes the enumerator. - /// The span to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(ReadOnlyTensorSpan span) - { - _span = span; - _items = -1; - _curIndexes = new nint[_span.Rank]; - _curIndexes[_span.Rank - 1] = -1; - } - - /// Advances the enumerator to the next element of the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - TensorSpanHelpers.AdjustIndexes(_span.Rank - 1, 1, _curIndexes, _span.Lengths); - - if (_items < _span.FlattenedLength) - _items++; - - return _items < _span.FlattenedLength; - } - - /// Gets the element at the current position of the enumerator. - public ref readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _span[_curIndexes]; - } - } - - /// - /// Returns a reference to the 0th element of the ReadOnlyTensorSpan. If the ReadOnlyTensorSpan is empty, returns null reference. - /// - /// - /// This method can be used for pinning and is required to support the use of span within a fixed statement. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref readonly T GetPinnableReference() - { - // Ensure that the native code has just one forward branch that is predicted-not-taken. - ref T ret = ref Unsafe.NullRef(); - if (_shape.FlattenedLength != 0) ret = ref _reference; - return ref ret; - } - - /// - /// Copies the contents of this read-only span into destination span. If the source - /// and destinations overlap, this method behaves as if the original values are in - /// a temporary location before the destination is overwritten. - /// - /// The span to copy items into. - /// - /// The destination ReadOnlyTensorSpan is shorter than the source ReadOnlyTensorSpan. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(scoped TensorSpan destination) - { - // Using "if (!TryCopyTo(...))" results in two branches: one for the length - // check, and one for the result of TryCopyTo. Since these checks are equivalent, - // we can optimize by performing the check once ourselves then calling Memmove directly. - if (TensorHelpers.IsBroadcastableTo(Lengths, destination.Lengths)) - { - scoped Span curIndexes; - nint[]? curIndexesArray; - - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(destination.Rank); - curIndexes = curIndexesArray.AsSpan(0, destination.Rank); - - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[destination.Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - nint[] tempLengths = Tensor.GetSmallestBroadcastableLengths(Lengths, destination.Lengths); - - TensorSpan destinationSlice = destination.Slice(tempLengths); - ReadOnlyTensorSpan srcSlice = Tensor.LazyBroadcast(this, tempLengths); - nint copyLength = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Lengths[^1] : 1; - int indexToAdjust = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Rank - 2 : srcSlice.Rank - 1; - - while (copiedValues < destination.FlattenedLength) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destinationSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, destinationSlice.Strides, destinationSlice.Lengths)), ref Unsafe.Add(ref srcSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, srcSlice.Strides, srcSlice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes(indexToAdjust, 1, curIndexes, tempLengths); - copiedValues += copyLength; - } - Debug.Assert(copiedValues == destination.FlattenedLength, "Didn't copy the right amount to the array."); - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - else - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - } - - /// - /// Copies the contents of this read-only span into destination span. - /// - /// if the copy succeeded; if the destination span is shorter than the source span and no data is written to the destination. - /// The span to copy items into. - /// - /// If the source and destinations overlap, this method behaves as if - /// the original values are in a temporary location before the destination is overwritten. - /// - public bool TryCopyTo(scoped TensorSpan destination) - { - bool retVal = false; - - if (TensorHelpers.IsBroadcastableTo(Lengths, destination.Lengths)) - { - scoped Span curIndexes; - nint[]? curIndexesArray; - - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(destination.Rank); - curIndexes = curIndexesArray.AsSpan(0, destination.Rank); - - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[destination.Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - nint[] tempLengths = Tensor.GetSmallestBroadcastableLengths(Lengths, destination.Lengths); - - TensorSpan destinationSlice = destination.Slice(tempLengths); - ReadOnlyTensorSpan srcSlice = Tensor.LazyBroadcast(this, tempLengths); - nint copyLength = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Lengths[^1] : 1; - int indexToAdjust = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Rank - 2 : srcSlice.Rank - 1; - - while (copiedValues < destination.FlattenedLength) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destinationSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, destinationSlice.Strides, destinationSlice.Lengths)), ref Unsafe.Add(ref srcSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, srcSlice.Strides, srcSlice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes(indexToAdjust, 1, curIndexes, tempLengths); - copiedValues += copyLength; - } - Debug.Assert(copiedValues == destination.FlattenedLength, "Didn't copy the right amount to the array."); - retVal = true; - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - - return retVal; - } - - /// - /// Defines an implicit conversion of an array to a . - /// - public static implicit operator ReadOnlyTensorSpan(T[]? array) => new ReadOnlyTensorSpan(array); - - /// - /// Returns a with the name of the type and the number of elements. - /// - public override string ToString() => $"System.Numerics.Tensors.ReadOnlyTensorSpan<{typeof(T).Name}>[{_shape.FlattenedLength}]"; - - /// - /// Returns a reference to specified element of the TensorSpan. - /// - /// The indexes for the slice. - /// - /// Any index is less than 0 or greater than or equal to FlattenedLength. - /// - public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan indexes) - { - NRange[] ranges = new NRange[indexes.Length]; - for (int i = 0; i < indexes.Length; i++) - { - ranges[i] = new NRange(checked((int)indexes[i].GetOffset(Lengths[i])), Lengths[i]); - } - return Slice(ranges); - } - - /// - /// Takes in the lengths of the dimensions and slices according to them. - /// - /// The dimension lengths. - /// A based on the provided . - internal ReadOnlyTensorSpan Slice(scoped ReadOnlySpan lengths) - { - NRange[] ranges = new NRange[lengths.Length]; - for (int i = 0; i < lengths.Length; i++) - { - ranges[i] = new NRange(0, lengths[i]); - } - return Slice(ranges); - } - - /// - /// Forms a slice out of the given span. - /// - /// The ranges for the slice. - /// A based on the provided . - - public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) - { - if (ranges.Length != Lengths.Length) - throw new ArgumentOutOfRangeException(nameof(ranges), "Number of dimensions to slice does not equal the number of dimensions in the span"); - - ReadOnlyTensorSpan toReturn; - scoped Span lengths; - scoped Span offsets; - nint[]? lengthsArray; - nint[]? offsetsArray; - if (Rank > TensorShape.MaxInlineRank) - { - lengthsArray = ArrayPool.Shared.Rent(Rank); - lengths = lengthsArray.AsSpan(0, Rank); - - offsetsArray = ArrayPool.Shared.Rent(Rank); - offsets = offsetsArray.AsSpan(0, Rank); - } - else - { - lengths = stackalloc nint[Rank]; - offsets = stackalloc nint[Rank]; - - lengthsArray = null; - offsetsArray = null; - } - lengths.Clear(); - offsets.Clear(); - - for (int i = 0; i < ranges.Length; i++) - { - (offsets[i], lengths[i]) = ranges[i].GetOffsetAndLength(Lengths[i]); - } - - // FlattenedLength is computed everytime so using a local to cache the value. - nint flattenedLength = FlattenedLength; - nint index = 0; - - if (flattenedLength != 0) - { - for (int i = 0; i < offsets.Length; i++) - { - index += Strides[i] * (offsets[i]); - } - } - - if ((index >= _shape._memoryLength || index < 0) && flattenedLength != 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - toReturn = new ReadOnlyTensorSpan(ref Unsafe.Add(ref _reference, index), lengths, _shape.Strides, _shape._memoryLength - index); - - if (offsetsArray != null) - ArrayPool.Shared.Return(offsetsArray); - if (lengthsArray != null) - ArrayPool.Shared.Return(lengthsArray); - - return toReturn; - } - - /// - /// Flattens the contents of this span into the provided . - /// - /// The span to copy items into. - public bool TryFlattenTo(scoped Span destination) - { - bool retVal = false; - if (destination.Length < _shape.FlattenedLength) - { - scoped Span curIndexes; - nint[]? curIndexesArray; - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(Rank); - curIndexes = curIndexesArray.AsSpan(0, Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - while (copiedValues < _shape.FlattenedLength) - { - TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - copiedValues += Lengths[Rank - 1]; - } - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - retVal = true; - } - return retVal; - } - - /// - /// Flattens the contents of this span into the provided . - /// - /// The span to copy items into. - public void FlattenTo(scoped Span destination) - { - if (destination.Length < _shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - if (_shape.FlattenedLength == 0) - return; - - scoped Span curIndexes; - nint[]? curIndexesArray; - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(Rank); - curIndexes = curIndexesArray.AsSpan(0, Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - while (copiedValues < _shape.FlattenedLength) - { - if (Strides[Rank - 1] == 0) - { - destination.Slice(checked((int)copiedValues), (int)Lengths[Rank - 1]).Fill(Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths))); - } - else - { - TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - } - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - copiedValues += Lengths[Rank - 1]; - } - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs new file mode 100644 index 00000000000000..33247ce9f8a155 --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -0,0 +1,413 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static System.Numerics.Tensors.TensorOperation; + +#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member + +namespace System.Numerics.Tensors +{ + /// + /// Represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type-safe and memory-safe. + /// + [DebuggerTypeProxy(typeof(TensorSpanDebugView<>))] + [DebuggerDisplay("{ToString(),raw}")] + [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public readonly ref struct ReadOnlyTensorSpan + { + /// + public static ReadOnlyTensorSpan Empty => default; + + internal readonly TensorShape _shape; + internal readonly ref T _reference; + + /// Creates a new tensor over the entirety of the target array. + /// The target array. + /// + /// Returns default when is null. + /// The created tensor span has a single dimension that is the same length as . + /// + public ReadOnlyTensorSpan(T[]? array) + { + _shape = TensorShape.Create(array); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); + } + + /// Creates a new tensor over the portion of the target array beginning at the specified start index and using the specified lengths and strides. + /// The target array. + /// The index at which to begin the tensor. + /// The lengths of the dimensions. If an empty span is provided, the created tensor will have a single dimension that is the same length as . + /// The strides of each dimension. If an empty span is provided, then strides will be automatically calculated from . + /// Returns default when is null. + /// + /// Thrown when one of the following conditions is met: + /// * is null and or is not empty + /// * is not in range of + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// * is not empty and has a length different from + /// * is not empty and contains an element that is negative + /// * is not empty and contains an element that is zero in a non leading position + /// + public ReadOnlyTensorSpan(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(array, start, lengths, strides); + _reference = ref (array is not null) + ? ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (uint)start) + : ref Unsafe.NullRef(); + } + + /// Creates a new tensor span over the entirety of the target span. + /// The target span. + /// The created tensor span has a single dimension that is the same length as . + public ReadOnlyTensorSpan(ReadOnlySpan span) + { + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length); + _reference = ref reference; + } + + /// Creates a new tensor span over the target span using the specified lengths and strides. + /// The target span. + /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . + /// The strides of each dimension. If an empty span is provided, then strides will be automatically calculated from . + /// + /// Thrown when one of the following conditions is met: + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// * is not empty and has a length different from + /// * is not empty and contains an element that is negative + /// * is not empty and contains an element that is zero in a non leading position + /// + public ReadOnlyTensorSpan(ReadOnlySpan span, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length, lengths, strides); + _reference = ref reference; + } + + /// Creates a new tensor span over the entirety of the target array. + /// The target array. + /// + /// Returns default when is null. + /// The created tensor span has a single dimension that is the same length as . + /// + public ReadOnlyTensorSpan(Array? array) + { + _shape = TensorShape.Create(array); + _reference = ref (array is not null) + ? ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)) + : ref Unsafe.NullRef(); + } + + /// Creates a new tensor span over the portion of the target array beginning at the specified start index and using the specified lengths and strides. + /// The target array. + /// The index at which to begin the tensor span. + /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . + /// The strides of each dimension. If an empty span is provided, then strides will be automatically calculated from . + /// + /// Returns default when is null. + /// + /// + /// + /// Thrown when one of the following conditions is met: + /// * is null and or is not empty + /// * is not in range of + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// * is not empty and has a length different from + /// * is not empty and contains an element that is negative + /// * is not empty and contains an element that is zero in a non leading position + /// + public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(array, start, lengths, strides, out nint linearOffset); + _reference = ref (array is not null) + ? ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), linearOffset) + : ref Unsafe.NullRef(); + } + + /// Creates a new tensor span over the target unmanaged buffer. + /// The pointer to the start of the target unmanaged buffer. + /// The number of elements the target unmanaged buffer contains. + /// Returns default when is null. + /// is null and is not zero + + [CLSCompliant(false)] + public unsafe ReadOnlyTensorSpan(T* data, nint dataLength) + { + _shape = TensorShape.Create(data, dataLength); + _reference = ref Unsafe.AsRef(data); + } + + /// Creates a new tensor span over the target unmanaged buffer using the specified lengths and strides. + /// The pointer to the start of the target unmanaged buffer. + /// The number of elements the target unmanaged buffer contains. + /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . + /// The strides of each dimension. If an empty span is provided, then strides will be automatically calculated from . + /// Returns default when is null. + /// + /// Thrown when one of the following conditions is met: + /// * is null and is not zero + /// * is null and or is not empty + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than + /// * is not empty and has a length different from + /// * is not empty and contains an element that is negative + /// * is not empty and contains an element that is zero in a non leading position + /// + [CLSCompliant(false)] + public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(data, dataLength, lengths, strides); + _reference = ref Unsafe.AsRef(data); + } + + // Constructor for internal use only. It is not safe to expose publicly. + internal ReadOnlyTensorSpan(ref T data, nint dataLength) + { + _shape = TensorShape.Create(ref data, dataLength); + _reference = ref data; + } + + internal ReadOnlyTensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(ref data, dataLength, lengths, strides); + _reference = ref data; + } + + internal ReadOnlyTensorSpan(ref T reference, scoped in TensorShape shape) + { + _reference = ref reference; + _shape = shape; + } + + /// + public ref readonly T this[params scoped ReadOnlySpan indexes] + { + get => ref Unsafe.Add(ref _reference, _shape.GetLinearOffset(indexes)); + } + + /// + public ref readonly T this[params scoped ReadOnlySpan indexes] + { + get => ref Unsafe.Add(ref _reference, _shape.GetLinearOffset(indexes)); + } + + /// + public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] + { + get => Slice(ranges); + } + + /// + public nint FlattenedLength => _shape.FlattenedLength; + + /// + public bool IsEmpty => _shape.IsEmpty; + + /// + [UnscopedRef] + public ReadOnlySpan Lengths => _shape.Lengths; + + /// + public int Rank => Lengths.Length; + + /// + [UnscopedRef] + public ReadOnlySpan Strides => _shape.Strides; + + /// Returns a value that indicates whether two tensor spans are equal. + /// The first tensor span to compare. + /// The second tensor span to compare. + /// true if the two tensor span are equal; otherwise, false. + /// Two tensor span are equal if they have the same length and the corresponding elements of and point to the same memory. Note that the test for equality does not attempt to determine whether the contents are equal. + public static bool operator ==(in ReadOnlyTensorSpan left, in ReadOnlyTensorSpan right) + => Unsafe.AreSame(ref left._reference, ref right._reference) + && left._shape == right._shape; + + /// Returns a value that indicates whether two tensor spans are not equal. + /// The first tensor span to compare. + /// The second tensor span to compare. + /// true if the two tensor span are not equal; otherwise, false. + /// Two tensor span are not equal if they have the different lengths or if the corresponding elements of and do not point to the same memory. Note that the test for equality does not attempt to determine whether the contents are not equal. + public static bool operator !=(in ReadOnlyTensorSpan left, in ReadOnlyTensorSpan right) => !(left == right); + + /// Defines an implicit conversion of an array to a readonly tensor span. + /// The array to convert to a readonly tensor span. + /// The readonly tensor span that corresponds to . + public static implicit operator ReadOnlyTensorSpan(T[]? array) => new ReadOnlyTensorSpan(array); + + /// Casts a tensor span of to a tensor span of . + /// The element type of the source tensor span, which must be derived from . + /// The source tensor span. No copy is made. + /// A tensor span with elements cast to the new type. + /// This method uses a covariant cast, producing a tensor span that shares the same memory as the source. The relationships expressed in the type constraints ensure that the cast is a safe operation. + public static ReadOnlyTensorSpan CastUp(in ReadOnlyTensorSpan items) + where TDerived : class?, T + { + return new ReadOnlyTensorSpan( + ref Unsafe.As(ref items._reference), + items._shape + ); + } + + /// + public void CopyTo(scoped in TensorSpan destination) + { + if (!TryCopyTo(destination)) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// Calls to this method are not supported. + /// Not supported. + /// Calls to this method are not supported. + /// Calls to this method are not supported. + /// This method is not supported as tensor spans cannot be boxed. To compare two tensor spans, use operator ==. + [Obsolete("Equals() on ReadOnlyTensorSpan will always throw an exception. Use the equality operator instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + + /// + public void FlattenTo(scoped Span destination) + { + if (!TryFlattenTo(destination)) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// Gets an enumerator for the readonly tensor span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Calls to this method are not supported. + /// Calls to this method are not supported. + /// Calls to this method are not supported. + /// This method is not supported as tensor spans cannot be boxed. + [Obsolete("GetHashCode() on ReadOnlyTensorSpan will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ref readonly T GetPinnableReference() + { + // Ensure that the native code has just one forward branch that is predicted-not-taken. + ref T ret = ref Unsafe.NullRef(); + if (_shape.FlattenedLength != 0) ret = ref _reference; + return ref ret; + } + + /// + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan start) + { + TensorShape shape = _shape.Slice(start, out nint linearOffset); + return new ReadOnlyTensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); + } + + /// + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan startIndex) + { + TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + return new ReadOnlyTensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); + } + + /// + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan range) + { + TensorShape shape = _shape.Slice(range, out nint linearOffset); + return new ReadOnlyTensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); + } + + /// Returns the string representation of the tensor span. + /// The string representation of the tensor span. + public override string ToString() => $"System.Numerics.Tensors.ReadOnlyTensorSpan<{typeof(T).Name}>[{_shape}]"; + + /// + public bool TryCopyTo(scoped in TensorSpan destination) + { + if (TensorShape.AreCompatible(_shape, destination._shape)) + { + TensorOperation.Invoke, T, T>(this, destination); + return true; + } + return false; + } + + /// + public bool TryFlattenTo(scoped Span destination) + { + if (_shape.FlattenedLength <= destination.Length) + { + TensorOperation.Invoke, T, T>(this, destination); + return true; + } + return false; + } + + /// Enumerates the elements of a tensor span. + public ref struct Enumerator + { + private readonly ReadOnlyTensorSpan _span; + private nint[] _indexes; + private nint _linearOffset; + private nint _itemsEnumerated; + + internal Enumerator(ReadOnlyTensorSpan span) + { + _span = span; + _indexes = new nint[span.Rank]; + + _linearOffset = 0; + _itemsEnumerated = -1; + } + + /// Gets the element at the current position of the enumerator. + public readonly ref readonly T Current => ref Unsafe.Add(ref _span._reference, _linearOffset); + + /// Advances the enumerator to the next element of the tensor span. + public bool MoveNext() + { + if (_itemsEnumerated == _span._shape.FlattenedLength) + { + return false; + } + + _linearOffset = _span._shape.AdjustToNextIndex(_linearOffset, _indexes); + + _itemsEnumerated++; + return true; + } + + /// Sets the enumerator to its initial position, which is before the first element in the tensor span. + public void Reset() + { + Array.Clear(_indexes); + _linearOffset = 0; + _itemsEnumerated = -1; + } + } + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs deleted file mode 100644 index 29b48f4a613e03..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; -using Microsoft.VisualBasic; - -#pragma warning disable CS8601 // Possible null reference assignment. -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - -namespace System.Numerics.Tensors -{ - /// - /// Provides methods for creating tensors. - /// - public static partial class Tensor - { - /// - /// Creates a and initializes it with the default value of T. If is true, the memory will be pinned. - /// - /// A indicating the lengths of each dimension. - /// A whether the underlying data should be pinned or not. - public static Tensor Create(scoped ReadOnlySpan lengths, bool pinned = false) - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = pinned ? GC.AllocateArray((int)linearLength, pinned) : (new T[linearLength]); - return Create(values, lengths, [], pinned); - } - - /// - /// Creates a and initializes it with the default value of T. If is true, the memory will be pinned. - /// - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A whether the underlying data should be pinned or not. - public static Tensor Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = pinned ? GC.AllocateArray((int)linearLength, pinned) : (new T[linearLength]); - return Create(values, lengths, strides, pinned); - } - - /// - /// Creates a from the provided . If the product of the - /// does not equal the length of the array, an exception will be thrown. - /// - /// An array of the backing memory. - /// A indicating the lengths of each dimension. - /// A indicating whether the were pinned or not. - /// - public static Tensor Create(T[] values, scoped ReadOnlySpan lengths, bool pinned = false) - => Create(values, lengths, [], pinned); - - /// - /// Creates a from the provided . If the product of the - /// does not equal the length of the array, an exception will be thrown. - /// - /// An array of the backing memory. - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A indicating whether the were pinned or not. - /// - public static Tensor Create(T[] values, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) - { - return new Tensor(values, lengths, strides, memoryOffset: 0, pinned); - } - - /// - /// Creates a and initializes it with the data from . - /// - /// A with the data to use for the initialization. - /// A indicating the lengths of each dimension. - /// A indicating whether the were pinned or not. - - public static Tensor Create(IEnumerable values, scoped ReadOnlySpan lengths, bool pinned = false) - { - T[] data = values.ToArray(); - return new Tensor(data, lengths.IsEmpty ? [data.Length] : lengths, memoryOffset: 0, pinned); - } - - /// - /// Creates a and initializes it with the data from . - /// - /// A with the data to use for the initialization. - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A indicating whether the were pinned or not. - public static Tensor Create(IEnumerable values, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) - { - T[] data = values.ToArray(); - return new Tensor(data, lengths.IsEmpty ? [data.Length] : lengths, strides, memoryOffset: 0, pinned); - } - - #region Normal - /// - /// Creates a and initializes it with random data in a gaussian normal distribution. - /// - /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillGaussianNormalDistribution(params scoped ReadOnlySpan lengths) - where T : IFloatingPoint - { - return CreateAndFillGaussianNormalDistribution(Random.Shared, lengths); - } - - /// - /// Creates a and initializes it with random data in a gaussian normal distribution. - /// - /// - /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillGaussianNormalDistribution(Random random, params scoped ReadOnlySpan lengths) - where T : IFloatingPoint - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = new T[linearLength]; - GaussianDistribution(values, linearLength, random); - return new Tensor(values, lengths, memoryOffset: 0, isPinned: false); - } - - private static void GaussianDistribution(in Span values, nint linearLength, Random random) - where T : IFloatingPoint - { - for (int i = 0; i < linearLength; i++) - { - double u1 = 1.0 - random.NextDouble(); - double u2 = 1.0 - random.NextDouble(); - values[i] = T.CreateChecked(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); - } - } - #endregion - - - /// - /// Creates a and initializes it with random data uniformly distributed. - /// - /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillUniformDistribution(params scoped ReadOnlySpan lengths) - where T : IFloatingPoint - { - return CreateAndFillUniformDistribution(Random.Shared, lengths); - } - - /// - /// Creates a and initializes it with random data uniformly distributed. - /// - /// - /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillUniformDistribution(Random random, params scoped ReadOnlySpan lengths) - where T : IFloatingPoint - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = new T[linearLength]; - for (int i = 0; i < values.Length; i++) - values[i] = T.CreateChecked(random.NextDouble()); - - return new Tensor(values, lengths, memoryOffset: 0, isPinned: false); - } - - /// - /// Creates a and does not initialize it. If is true, the memory will be pinned. - /// - /// A indicating the lengths of each dimension. - /// A whether the underlying data should be pinned or not. - public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned = false) - => CreateUninitialized(lengths, [], pinned); - - /// - /// Creates a and does not initialize it. If is true, the memory will be pinned. - /// - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A whether the underlying data should be pinned or not. - public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) - { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = GC.AllocateUninitializedArray((int)linearLength, pinned); - return new Tensor(values, lengths, strides, memoryOffset: 0, pinned); - } - - /// - /// Fills the given with random data in a Gaussian normal distribution. - /// can optionally be provided for seeding. - /// - /// The element type. - /// The destination where the data will be stored. - /// to provide random seeding. Defaults to if not provided. - /// - public static ref readonly TensorSpan FillGaussianNormalDistribution(in TensorSpan destination, Random? random = null) where T : IFloatingPoint - { - Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - - GaussianDistribution(span, destination._shape._memoryLength, random ?? Random.Shared); - - return ref destination; - } - - /// - /// Fills the given with random data in a uniform distribution. - /// can optionally be provided for seeding. - /// - /// The element type. - /// The destination where the data will be stored. - /// to provide random seeding. Defaults to if not provided. - /// - public static ref readonly TensorSpan FillUniformDistribution(in TensorSpan destination, Random? random = null) where T : IFloatingPoint - { - Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - random ??= Random.Shared; - for (int i = 0; i < span.Length; i++) - span[i] = T.CreateChecked(random.NextDouble()); - - return ref destination; - } - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 4878350449c319..a40430301cd24d 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -1,751 +1,7267 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Buffers; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Reflection.Metadata.Ecma335; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using static System.Runtime.InteropServices.JavaScript.JSType; - -#pragma warning disable CS8601 // Possible null reference assignment. -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. namespace System.Numerics.Tensors { - /// - /// Represents a tensor. - /// + /// Provides methods for tensor operations. [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - public sealed class Tensor : ITensor, ITensor, T> + public static partial class Tensor { - /// A byref or a native ptr. - internal readonly T[] _values; - /// The number of elements this Tensor contains. - internal readonly nint _flattenedLength; - /// The lengths of each dimension. - internal readonly nint[] _lengths; - /// The strides representing the memory offsets for each dimension. - internal readonly nint[] _strides; - /// If the backing memory is permanently pinned (so not just using a fixed statement). - internal readonly bool _isPinned; - /// The offset of the first element in the backing memory. - internal readonly int _memoryOffset; + #region AsTensorSpan + /// + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array) => new ReadOnlyTensorSpan(array); + + /// + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new ReadOnlyTensorSpan(array, start, lengths, strides); + + /// + public static TensorSpan AsTensorSpan(this T[]? array) => new TensorSpan(array); + + /// + public static TensorSpan AsTensorSpan(this T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new TensorSpan(array, start, lengths, strides); + #endregion + #region Broadcast /// - /// Creates a new empty Tensor. + /// Broadcast the data from to the smallest broadcastable shape compatible with . Creates a new and allocates new memory. /// - internal Tensor() + /// Input . + /// Other to make shapes broadcastable. + public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, scoped in ReadOnlyTensorSpan lengthsSource) { - _flattenedLength = 0; - _values = []; - _lengths = []; - _strides = []; - _memoryOffset = 0; + return Broadcast(source, lengthsSource.Lengths); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Tensor(T[]? values, ReadOnlySpan lengths, int memoryOffset, bool isPinned = false) : this(values, lengths, Array.Empty(), memoryOffset, isPinned) { } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Tensor(T[]? values, ReadOnlySpan lengths, ReadOnlySpan strides, int memoryOffset, bool isPinned = false) + /// + /// Broadcast the data from to the new shape . Creates a new and allocates new memory. + /// If the shape of the is not compatible with the new shape, an exception is thrown. + /// + /// Input . + /// of the desired new shape. + /// Thrown when the shapes are not broadcast compatible. + public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, scoped ReadOnlySpan lengths) { - if (values == null) - { - if (_flattenedLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - _flattenedLength = 0; - _values = []; - _lengths = []; - _strides = []; - _memoryOffset = memoryOffset; - return; // returns default - } - - _lengths = lengths.IsEmpty ? [values.Length] : lengths.ToArray(); - _memoryOffset = memoryOffset; - - if (_memoryOffset < 0 || (_memoryOffset >= values.Length && values.Length != 0 )) - ThrowHelper.ThrowIndexOutOfRangeException(); - - _flattenedLength = TensorSpanHelpers.CalculateTotalLength(_lengths); - _strides = strides.IsEmpty ? TensorSpanHelpers.CalculateStrides(_lengths, _flattenedLength) : strides.ToArray(); - TensorSpanHelpers.ValidateStrides(_strides, _lengths); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(_strides, _lengths); - - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)maxElements >= (ulong)(uint)(values.Length - memoryOffset) && values.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } - else - { - if (((uint)maxElements >= (uint)(values.Length - memoryOffset)) && values.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, lengths); - _values = values; - _isPinned = isPinned; + ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); + Tensor output = Tensor.CreateUninitialized(intermediate.Lengths); + intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref output._values[0], (int)output.FlattenedLength)); + return output; } + #endregion + #region BroadcastTo /// - /// Creates a and initializes it with the default value of T. If is true, the memory will be pinned. + /// Broadcast the data from to . /// - /// A indicating the lengths of each dimension. - /// A whether the underlying data should be pinned or not. - static Tensor ITensor, T>.Create(scoped ReadOnlySpan lengths, bool pinned) + /// Input . + /// + public static void BroadcastTo(this Tensor source, in TensorSpan destination) { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = pinned ? GC.AllocateArray((int)linearLength, pinned) : (new T[linearLength]); - return new Tensor(values, lengths.ToArray(), memoryOffset: 0, pinned); + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); + if (!destination.Lengths.SequenceEqual(newSize)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + + ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); + intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); } /// - /// Creates a and initializes it with the default value of T. If is true, the memory will be pinned. + /// Broadcast the data from to . /// - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A whether the underlying data should be pinned or not. - static Tensor ITensor, T>.Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) + /// Input . + /// Other to make shapes broadcastable. + public static void BroadcastTo(in this TensorSpan source, in TensorSpan destination) { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = pinned ? GC.AllocateArray((int)linearLength, pinned) : (new T[linearLength]); - return new Tensor(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned); + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); + if (!destination.Lengths.SequenceEqual(newSize)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + + ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); + intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); } /// - /// Creates a and does not initialize it. If is true, the memory will be pinned. + /// Broadcast the data from to . /// - /// A indicating the lengths of each dimension. - /// A whether the underlying data should be pinned or not. - static Tensor ITensor, T>.CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned) + /// Input . + /// + public static void BroadcastTo(in this ReadOnlyTensorSpan source, in TensorSpan destination) { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = GC.AllocateUninitializedArray((int)linearLength, pinned); - return new Tensor(values, lengths.ToArray(), memoryOffset: 0, pinned); + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); + if (!destination.Lengths.SequenceEqual(newSize)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + + ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); + intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); } + // Lazy/non-copy broadcasting, internal only for now. /// - /// Creates a and does not initialize it. If is true, the memory will be pinned. + /// Broadcast the data from to the new shape . Creates a new + /// but no memory is allocated. It manipulates the strides to achieve this affect. + /// If the shape of the is not compatible with the new shape, an exception is thrown. /// - /// A indicating the lengths of each dimension. - /// A indicating the strides of each dimension. - /// A whether the underlying data should be pinned or not. - static Tensor ITensor, T>.CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) + /// Input . + /// of the desired new shape. + /// Thrown when the shapes are not broadcast compatible. + internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlySpan lengths) { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = GC.AllocateUninitializedArray((int)linearLength, pinned); - return new Tensor(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned); - } + if (input.Lengths.SequenceEqual(lengths)) + return new TensorSpan(ref input._reference, input._shape.LinearLength, lengths, input.Strides); - // ITensor - /// - /// The Empty Tensor. - /// - public static Tensor Empty { get; } = new(); + if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - /// - /// Gets a value indicating whether this is empty. - /// - /// if this tensor is empty; otherwise, . - public bool IsEmpty => _lengths.Length == 0; + nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); - /// - /// Gets a value indicating whether the backing memory of the is pinned."/> - /// - /// if the backing memory is pinned; otherwise, . - public bool IsPinned => _isPinned; + if (newSize == input.FlattenedLength) + return Reshape(input, lengths); - /// - /// Gets a value indicating the rank, or number of dimensions, of this . - /// - /// with the number of dimensions. - public int Rank => _lengths.Length; + nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, lengths.Length); + nint[] strides = new nint[lengths.Length]; - /// - /// The number of items in the . - /// - /// with the number of items. - public nint FlattenedLength => _flattenedLength; + nint stride = 1; - /// - /// Gets the length of each dimension in this . - /// - /// with the lengths of each dimension. - public ReadOnlySpan Lengths => _lengths; + for (int i = strides.Length - 1; i >= 0; i--) + { + if ((intermediateShape[i] == 1 && lengths[i] != 1) || (intermediateShape[i] == 1 && lengths[i] == 1)) + strides[i] = 0; + else + { + strides[i] = stride; + stride *= intermediateShape[i]; + } + } - /// - /// Gets the length of each dimension in this . - /// - /// with the lengths of each dimension. - ReadOnlySpan IReadOnlyTensor.Lengths => _lengths; + TensorSpan output = new TensorSpan(ref input._reference, input._shape.LinearLength, lengths, strides); + return output; + } + // Lazy/non-copy broadcasting, internal only for now. /// - /// Gets the strides of each dimension in this . + /// Broadcast the data from to the new shape . Creates a new + /// but no memory is allocated. It manipulates the strides to achieve this affect. + /// If the shape of the is not compatible with the new shape, an exception is thrown. /// - /// with the strides of each dimension. - public ReadOnlySpan Strides => _strides; + /// Input . + /// of the desired new shape. + /// Thrown when the shapes are not broadcast compatible. + internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan input, ReadOnlySpan shape) + { + if (input.Lengths.SequenceEqual(shape)) + return new TensorSpan(ref input._reference, input._shape.LinearLength, shape, input.Strides); - /// - /// Gets the strides of each dimension in this . - /// - /// with the strides of each dimension. - ReadOnlySpan IReadOnlyTensor.Strides => _strides; + if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - bool ITensor.IsReadOnly => false; + nint newSize = TensorSpanHelpers.CalculateFlattenedLength(shape); - object IReadOnlyTensor.this[params scoped ReadOnlySpan indexes] => this[indexes]!; + if (newSize == input.FlattenedLength) + return Reshape(input, shape); - object IReadOnlyTensor.this[params scoped ReadOnlySpan indexes] => this[indexes]!; + nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, shape.Length); + nint[] strides = new nint[shape.Length]; - object ITensor.this[params scoped ReadOnlySpan indexes] { get => this[indexes]!; set => this[indexes] = (T)value; } - object ITensor.this[params scoped ReadOnlySpan indexes] { get => this[indexes]!; set => this[indexes] = (T)value; } + nint stride = 1; - /// - /// Returns a reference to specified element of the Tensor. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - public ref T this[params scoped ReadOnlySpan indexes] => ref AsTensorSpan()[indexes]; + for (int i = strides.Length - 1; i >= 0; i--) + { + if ((intermediateShape[i] == 1 && shape[i] != 1) || (intermediateShape[i] == 1 && shape[i] == 1)) + strides[i] = 0; + else + { + strides[i] = stride; + stride *= intermediateShape[i]; + } + } - /// - /// Returns a reference to specified element of the Tensor. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - public ref T this[params scoped ReadOnlySpan indexes] => ref AsTensorSpan()[indexes]; + TensorSpan output = new TensorSpan(ref input._reference, input._shape.LinearLength, shape, strides); + return output; + } + + // Lazy/non-copy broadcasting, internal only for now. /// - /// Returns a slice of the Tensor. + /// Broadcast the data from to the new shape . Creates a new + /// but no memory is allocated. It manipulates the strides to achieve this affect. + /// If the shape of the is not compatible with the new shape, an exception is thrown. /// - /// - /// - /// - /// Thrown when any index is less than 0 or any index is greater than or equal to FlattenedLength - /// - public Tensor this[params ReadOnlySpan ranges] + /// Input . + /// of the desired new shape. + /// Thrown when the shapes are not broadcast compatible. + internal static Tensor LazyBroadcast(Tensor input, ReadOnlySpan lengths) { - get - { - if (ranges.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); + if (input.Lengths.SequenceEqual(lengths)) + return new Tensor(input._values, lengths, input._start, isPinned: false); - return Slice(ranges); - } - set + if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + + nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); + + if (newSize == input.FlattenedLength) + return Reshape(input, lengths); + + nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, lengths.Length); + nint[] strides = new nint[lengths.Length]; + + nint stride = 1; + + for (int i = strides.Length - 1; i >= 0; i--) { - TensorSpan srcSpan; - if (ranges == ReadOnlySpan.Empty) + if ((intermediateShape[i] == 1 && lengths[i] != 1) || (intermediateShape[i] == 1 && lengths[i] == 1)) + strides[i] = 0; + else { - if (!Lengths.SequenceEqual(value.Lengths)) - ThrowHelper.ThrowArgument_SetSliceNoRange(nameof(value)); - srcSpan = AsTensorSpan().Slice(Lengths); + strides[i] = stride; + stride *= intermediateShape[i]; } - else - srcSpan = AsTensorSpan().Slice(ranges); + } - if (!srcSpan.Lengths.SequenceEqual(value.Lengths)) - ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(value)); + Tensor output = new Tensor(input._values, input._start, lengths, strides); - value.AsTensorSpan().CopyTo(srcSpan); - } + return output; } + #endregion + #region Concatenate /// - /// Returns the specified element of the Tensor. + /// Join a sequence of tensors along an existing axis. /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - T ITensor, T>.this[params ReadOnlySpan indexes] + /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). + public static Tensor Concatenate(params scoped ReadOnlySpan> tensors) { - get - { - return this[indexes]; - } - set - { - this[indexes] = value; - } + return ConcatenateOnDimension(0, tensors); } /// - /// Returns the specified element of the Tensor. + /// Join a sequence of tensors along an existing axis. /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - T ITensor, T>.this[params ReadOnlySpan indexes] + /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). + /// The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0. + public static Tensor ConcatenateOnDimension(int dimension, params scoped ReadOnlySpan> tensors) { - get + if (tensors.Length < 2) + ThrowHelper.ThrowArgument_ConcatenateTooFewTensors(); + + if (dimension < -1 || dimension > tensors[0].Rank) + ThrowHelper.ThrowArgument_InvalidAxis(); + + // Calculate total space needed. + nint totalLength = 0; + for (int i = 0; i < tensors.Length; i++) + totalLength += TensorSpanHelpers.CalculateFlattenedLength(tensors[i].Lengths); + + nint sumOfAxis = 0; + // If axis != -1, make sure all dimensions except the one to concatenate on match. + if (dimension != -1) + { + sumOfAxis = tensors[0].Lengths[dimension]; + for (int i = 1; i < tensors.Length; i++) + { + if (tensors[0].Rank != tensors[i].Rank) + ThrowHelper.ThrowArgument_InvalidConcatenateShape(); + for (int j = 0; j < tensors[0].Rank; j++) + { + if (j != dimension) + { + if (tensors[0].Lengths[j] != tensors[i].Lengths[j]) + ThrowHelper.ThrowArgument_InvalidConcatenateShape(); + } + } + sumOfAxis += tensors[i].Lengths[dimension]; + } + } + + Tensor tensor; + if (dimension == -1) { - return this[indexes]; + tensor = Tensor.Create([totalLength]); } - set + else { - this[indexes] = value; + nint[] lengths = new nint[tensors[0].Rank]; + tensors[0].Lengths.CopyTo(lengths); + lengths[dimension] = sumOfAxis; + tensor = Tensor.Create(lengths); } - } - - /// - /// Returns the specified element of the ReadOnlyTensor. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - T IReadOnlyTensor, T>.this[params ReadOnlySpan indexes] => AsReadOnlyTensorSpan()[indexes]; - /// - /// Returns the specified element of the ReadOnlyTensor. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to FlattenedLength - /// - T IReadOnlyTensor, T>.this[params ReadOnlySpan indexes] => AsReadOnlyTensorSpan()[indexes]; + ConcatenateOnDimension(dimension, tensors, tensor); + return tensor; + } /// - /// Returns a slice of the ReadOnlyTensor. + /// Join a sequence of tensors along an existing axis. /// - /// - /// - /// - /// Thrown when any index is less than 0 or any index is greater than or equal to FlattenedLength - /// - Tensor IReadOnlyTensor, T>.this[params ReadOnlySpan ranges] + /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). + /// + public static ref readonly TensorSpan Concatenate(scoped ReadOnlySpan> tensors, in TensorSpan destination) { - get - { - if (ranges.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return Slice(ranges); - } + return ref ConcatenateOnDimension(0, tensors, destination); } - // REVIEW: WE WILL WANT THIS CHANGED FROM A BOOL TO SOME FILTER EXPRESSION. /// - /// + /// Join a sequence of tensors along an existing axis. /// - /// - /// - /// - public Tensor this[Tensor filter] - { - get - { - if (filter.Lengths.Length != Lengths.Length) - throw new ArgumentOutOfRangeException(nameof(filter), "Number of dimensions does not equal the number of dimensions in the span"); + /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). + /// The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0. + /// - for (int i = 0; i < filter.Lengths.Length; i++) - { - if (filter.Lengths[i] != Lengths[i]) - ThrowHelper.ThrowArgument_FilterTensorMustEqualTensorLength(); - } + public static ref readonly TensorSpan ConcatenateOnDimension(int dimension, scoped ReadOnlySpan> tensors, in TensorSpan destination) + { + if (tensors.Length < 2) + ThrowHelper.ThrowArgument_ConcatenateTooFewTensors(); - Span srcSpan = _values; - Span filterSpan = filter._values; + if (dimension < -1 || dimension > tensors[0].Rank) + ThrowHelper.ThrowArgument_InvalidAxis(); - nint linearLength = TensorHelpers.CountTrueElements(filter); + // Calculate total space needed. + nint totalLength = 0; + for (int i = 0; i < tensors.Length; i++) + totalLength += TensorSpanHelpers.CalculateFlattenedLength(tensors[i].Lengths); - T[] values = _isPinned ? GC.AllocateArray((int)linearLength, _isPinned) : (new T[linearLength]); - int index = 0; - for (int i = 0; i < filterSpan.Length; i++) + nint sumOfAxis = 0; + // If axis != -1, make sure all dimensions except the one to concatenate on match. + if (dimension != -1) + { + sumOfAxis = tensors[0].Lengths[dimension]; + for (int i = 1; i < tensors.Length; i++) { - if (filterSpan[i]) + if (tensors[0].Rank != tensors[i].Rank) + ThrowHelper.ThrowArgument_InvalidConcatenateShape(); + for (int j = 0; j < tensors[0].Rank; j++) { - values[i] = srcSpan[index++]; + if (j != dimension) + { + if (tensors[0].Lengths[j] != tensors[i].Lengths[j]) + ThrowHelper.ThrowArgument_InvalidConcatenateShape(); + } } + sumOfAxis += tensors[i].Lengths[dimension]; } - return new Tensor(values, [linearLength], _memoryOffset, _isPinned); - } - } - - /// - /// Defines an implicit conversion of an array to a . - /// - public static implicit operator Tensor(T[] array) => new Tensor(array, [array.Length], memoryOffset: 0); + // Make sure the destination tensor has the correct shape. + nint[] lengths = new nint[tensors[0].Rank]; + tensors[0].Lengths.CopyTo(lengths); + lengths[dimension] = sumOfAxis; - /// - /// Defines an implicit conversion of a to a . - /// - public static implicit operator TensorSpan(Tensor value) => value.AsTensorSpan(); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + } + Span dstSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)totalLength); + nint valuesCopied = 0; - /// - /// Defines an implicit conversion of a to a . - /// - public static implicit operator ReadOnlyTensorSpan(Tensor value) => value.AsReadOnlyTensorSpan(); + scoped Span curIndex; + nint[]? curIndexArray; - /// - /// Converts this to a pointing to the same backing memory."/> - /// - /// - public TensorSpan AsTensorSpan() => new TensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _memoryOffset), _lengths, _strides, _values.Length - _memoryOffset); + if (tensors[0].Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(tensors[0].Rank); + curIndex = curIndexArray.AsSpan(0, tensors[0].Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[tensors[0].Rank]; + } + curIndex.Clear(); - /// - /// Converts this to a pointing to the same backing memory based on the provided ranges."/> - /// - /// The ranges you want in the . - /// based on the provided ranges. - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + nint srcIndex; + nint copyLength; - /// - /// Converts this to a pointing to the same backing memory based on the provided start locations."/> - /// - /// The start location you want in the . - /// based on the provided ranges. - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + while (valuesCopied < totalLength) + { + for (int i = 0; i < tensors.Length; i++) + { + srcIndex = TensorSpanHelpers.ComputeLinearIndex(curIndex, tensors[i].Strides, tensors[i].Lengths); + copyLength = CalculateCopyLength(tensors[i].Lengths, dimension); + Span srcSpan = MemoryMarshal.CreateSpan(ref tensors[i]._values[srcIndex], (int)copyLength); + TensorSpanHelpers.Memmove(dstSpan, srcSpan, copyLength, valuesCopied); + valuesCopied += copyLength; + } + TensorSpanHelpers.AdjustIndexes(dimension - 1, 1, curIndex, tensors[0].Lengths); + } - /// - /// Converts this to a pointing to the same backing memory based on the provided start indexes."/> - /// - /// The ranges you want in the . - /// based on the provided ranges. - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex) => AsTensorSpan().Slice(startIndex); + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); - /// - /// Converts this to a pointing to the same backing memory."/> - /// - /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _memoryOffset), _lengths, _strides, _flattenedLength); + return ref destination; + } - /// - /// Converts this to a pointing to the same backing memory based on the provided ranges."/> - /// - /// The ranges you want in the - /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + private static nint CalculateCopyLength(ReadOnlySpan lengths, int startingAxis) + { + // When starting axis is -1 we want all the data at once same as if starting axis is 0 + if (startingAxis == -1) + startingAxis = 0; + nint length = 1; + for (int i = startingAxis; i < lengths.Length; i++) + { + length *= lengths[i]; + } + return length; + } + #endregion - /// - /// Converts this to a pointing to the same backing memory based on the provided start locations."/> - /// - /// The start locations you want in the - /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + #region Create + /// + /// A new tensor with the specified lengths. + public static Tensor Create(scoped ReadOnlySpan lengths, bool pinned = false) + => new Tensor(lengths, strides: [], pinned); - /// - /// Converts this to a pointing to the same backing memory based on the provided start indexes."/> - /// - /// The start indexes you want in the - /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex) => AsTensorSpan().Slice(startIndex); + /// + /// A new tensor with the specified and . + public static Tensor Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) + => new Tensor(lengths, strides, pinned); - /// - /// Returns a reference to the 0th element of the Tensor. If the Tensor is empty, returns null reference. - /// It can be used for pinning and is required to support the use of Tensor within a fixed statement. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref T GetPinnableReference() => ref AsTensorSpan().GetPinnableReference(); + /// + /// A new tensor that uses as its backing buffer. + public static Tensor Create(T[] array) + => new Tensor(array); - /// - /// Returns a reference to the 0th element of the ReadOnlyTensor. If the ReadOnlyTensor is empty, returns null reference. - /// It can be used for pinning and is required to support the use of ReadOnlyTensor within a fixed statement. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - ref readonly T IReadOnlyTensor, T>.GetPinnableReference() => ref AsReadOnlyTensorSpan().GetPinnableReference(); + /// + /// A new tensor that uses as its backing buffer and with the specified and . + public static Tensor Create(T[] array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new Tensor(array, start, lengths, strides); - /// - /// Forms a slice out of the given tensor - /// - /// The ranges for the slice - /// without copying the provided ranges. - public Tensor Slice(params ReadOnlySpan start) + /// Creates a tensor that contains elements copied from the specified enumerable. + /// The enumerable whose elements are copied to the new tensor. + /// true to pin the underlying buffer. The default is false. + /// A new tensor that contains elements copied from . + public static Tensor Create(IEnumerable enumerable, bool pinned = false) { - if (start.Length != Lengths.Length) - throw new ArgumentOutOfRangeException(nameof(start), "Number of dimensions to slice does not equal the number of dimensions in the span"); - - scoped Span lengths; - scoped Span offsets; - nint[]? lengthsArray; - nint[]? offsetsArray; - if (Rank > TensorShape.MaxInlineRank) + if (pinned) { - lengthsArray = ArrayPool.Shared.Rent(Rank); - lengths = lengthsArray.AsSpan(0, Rank); + T[] array = enumerable.ToArray(); + + Tensor tensor = CreateUninitialized([array.Length], pinned); + array.CopyTo(tensor._values); - offsetsArray = ArrayPool.Shared.Rent(Rank); - offsets = offsetsArray.AsSpan(0, Rank); + return tensor; } else { - lengths = stackalloc nint[Rank]; - offsets = stackalloc nint[Rank]; - - lengthsArray = null; - offsetsArray = null; + T[] array = enumerable.ToArray(); + return Create(array); } - lengths.Clear(); - offsets.Clear(); + } - for (int i = 0; i < start.Length; i++) + /// Creates a tensor that contains elements copied from the specified enumerable. + /// The enumerable whose elements are copied to the new tensor. + /// The lengths of each dimension. + /// The strides of each dimension. + /// true to pin the underlying buffer. The default is false. + /// A new tensor that contains elements copied from . + public static Tensor Create(IEnumerable enumerable, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) + { + if (pinned) { - (offsets[i], lengths[i]) = start[i].GetOffsetAndLength(Lengths[i]); - } + T[] array = enumerable.ToArray(); - // When we have an empty Tensor and someone wants to slice all of it, we should return an empty Tensor. - // FlattenedLength is computed everytime so using a local to cache the value. - nint flattenedLength = FlattenedLength; - int memoryOffset = 0; + Tensor tensor = CreateUninitialized(lengths, strides, pinned); + array.CopyTo(tensor._values); - if (flattenedLength != 0) + return tensor; + } + else { - for (int i = 0; i < offsets.Length; i++) - { - memoryOffset += (int)(Strides[i] * offsets[i]); - } + T[] array = enumerable.ToArray(); + return Create(array, start: 0, lengths, strides); } - - if ((memoryOffset >= _values.Length || memoryOffset < 0) && flattenedLength != 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - Tensor toReturn = new Tensor(_values, lengths, Strides, memoryOffset, _isPinned); - - if (offsetsArray != null) - ArrayPool.Shared.Return(offsetsArray); - if (lengthsArray != null) - ArrayPool.Shared.Return(lengthsArray); - - return toReturn; } /// - /// Forms a slice out of the given tensor + /// Creates a and initializes it with random data in a gaussian normal distribution. /// - /// The start indexes for the slice - /// without copying the provided ranges. - public Tensor Slice(params ReadOnlySpan start) + /// A indicating the lengths of each dimension. + public static Tensor CreateAndFillGaussianNormalDistribution(params scoped ReadOnlySpan lengths) + where T : IFloatingPoint { - NRange[] ranges = new NRange[start.Length]; - for (int i = 0; i < start.Length; i++) - { - ranges[i] = new NRange(start[i], new NIndex(0, fromEnd: true)); - } - return Slice(ranges); + return CreateAndFillGaussianNormalDistribution(Random.Shared, lengths); } /// - /// Forms a slice out of the given tensor + /// Creates a and initializes it with random data in a gaussian normal distribution. /// - /// The start indexes for the slice - /// without copying the provided ranges. - public Tensor Slice(params ReadOnlySpan startIndex) + /// + /// A indicating the lengths of each dimension. + public static Tensor CreateAndFillGaussianNormalDistribution(Random random, params scoped ReadOnlySpan lengths) + where T : IFloatingPoint { - NRange[] ranges = new NRange[startIndex.Length]; - for (int i = 0; i < startIndex.Length; i++) - { - ranges[i] = new NRange(startIndex[i], new NIndex(0, fromEnd: true)); - } - return Slice(ranges); + Tensor tensor = CreateUninitialized(lengths); + FillGaussianNormalDistribution(tensor, random); + return tensor; } - /// - /// Clears the contents of this tensor. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Clear() => AsTensorSpan().Clear(); /// - /// Copies the contents of this tensor into destination span. If the source - /// and destinations overlap, this method behaves as if the original values in - /// a temporary location before the destination is overwritten. + /// Creates a and initializes it with random data uniformly distributed. /// - /// The span to copy items into. - /// - /// Thrown when the destination TensorSpan is shorter than the source Tensor. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(scoped TensorSpan destination) => AsTensorSpan().CopyTo(destination); + /// A indicating the lengths of each dimension. + public static Tensor CreateAndFillUniformDistribution(params scoped ReadOnlySpan lengths) + where T : IFloatingPoint + { + return CreateAndFillUniformDistribution(Random.Shared, lengths); + } /// - /// Fills the contents of this span with the given value. + /// Creates a and initializes it with random data uniformly distributed. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Fill(T value) => AsTensorSpan().Fill(value); + /// + /// A indicating the lengths of each dimension. + public static Tensor CreateAndFillUniformDistribution(Random random, params scoped ReadOnlySpan lengths) + where T : IFloatingPoint + { + Tensor tensor = CreateUninitialized(lengths); + FillUniformDistribution(tensor, random); + return tensor; + } + + /// + public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned = false) + { + TensorShape shape = TensorShape.Create(lengths, strides: []); + T[] array = GC.AllocateUninitializedArray(checked((int)(shape.LinearLength)), pinned); + return new Tensor(array, start: 0, in shape, pinned); + } + + /// + public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) + { + TensorShape shape = TensorShape.Create(lengths, strides); + T[] values = GC.AllocateUninitializedArray(checked((int)(shape.LinearLength)), pinned); + return new Tensor(values, start: 0, in shape, pinned); + } + #endregion + #region Fill /// - /// Fills the contents of this span with the given value. + /// Fills the given with random data in a Gaussian normal distribution. + /// can optionally be provided for seeding. /// - public void Fill(object value) => Fill(value is T t ? t : throw new ArgumentException($"Cannot convert {value} to {typeof(T)}")); + /// The element type. + /// The destination where the data will be stored. + /// to provide random seeding. Defaults to if not provided. + /// + public static ref readonly TensorSpan FillGaussianNormalDistribution(in TensorSpan destination, Random? random = null) where T : IFloatingPoint + { + Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + random ??= Random.Shared; + + for (int i = 0; i < span.Length; i++) + { + double u1 = 1.0 - random.NextDouble(); + double u2 = 1.0 - random.NextDouble(); + span[i] = T.CreateChecked(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); + } + + return ref destination; + } /// - /// Copies the contents of this tensor into destination span. If the source - /// and destinations overlap, this method behaves as if the original values in - /// a temporary location before the destination is overwritten. + /// Fills the given with random data in a uniform distribution. + /// can optionally be provided for seeding. /// - /// The span to copy items into. - /// If the destination span is shorter than the source tensor, this method - /// return false and no data is written to the destination. - public bool TryCopyTo(scoped TensorSpan destination) => AsTensorSpan().TryCopyTo(destination); + /// The element type. + /// The destination where the data will be stored. + /// to provide random seeding. Defaults to if not provided. + /// + public static ref readonly TensorSpan FillUniformDistribution(in TensorSpan destination, Random? random = null) where T : IFloatingPoint + { + Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + random ??= Random.Shared; + for (int i = 0; i < span.Length; i++) + span[i] = T.CreateChecked(random.NextDouble()); + + return ref destination; + } + #endregion + #region Equals /// - /// Flattens the contents of this Tensor into the provided . + /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size + /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> /// - /// The span to copy items into. - public void FlattenTo(scoped Span destination) => AsTensorSpan().FlattenTo(destination); + /// First to compare. + /// Second to compare. + /// A where the value is true if the elements are equal and false if they are not. + public static Tensor Equals(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IEqualityOperators + { + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; + } /// - /// Flattens the contents of this Tensor into the provided . + /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size + /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> /// - /// The span to copy items into. - public bool TryFlattenTo(scoped Span destination) => AsTensorSpan().TryFlattenTo(destination); + /// First to compare. + /// Second to compare. + /// + /// A where the value is true if the elements are equal and false if they are not. + public static ref readonly TensorSpan Equals(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IEqualityOperators + { + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return ref destination; + } - // IEnumerable /// - /// Gets an for the . + /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size + /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> /// - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// First to compare. + /// Second value to compare. + /// A where the value is true if the elements are equal and false if they are not. + public static Tensor Equals(in ReadOnlyTensorSpan x, T y) + where T : IEqualityOperators + { + Tensor destination = CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; + } - // IEnumerable /// - /// Gets an for the . + /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size + /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> /// - /// - public IEnumerator GetEnumerator() => new Enumerator(this); + /// First to compare. + /// Second value to compare. + /// + /// A where the value is true if the elements are equal and false if they are not. + public static ref readonly TensorSpan Equals(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IEqualityOperators + { + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return ref destination; + } + #endregion + #region EqualsAll /// - /// Gets an for the ."/> + /// Compares the elements of two to see if all elements of are equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are eqaul to . /// - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private struct Enumerator : IEnumerator + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are equal to . + public static bool EqualsAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IEqualityOperators { - /// The span being enumerated. - private readonly Tensor _tensor; - /// - /// - /// - private nint[] _curIndices; - /// The total item count. - private nint _items; - /// Initialize the enumerator. - /// The tensor to enumerate. - internal Enumerator(Tensor tensor) - { - _tensor = tensor; - _items = -1; - _curIndices = new nint[_tensor.Rank]; - _curIndices[_tensor.Rank - 1] = -1; - } + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - /// Advances the enumerator to the next element of the span. - public bool MoveNext() - { - TensorSpanHelpers.AdjustIndexes(_tensor.Rank - 1, 1, ref _curIndices, _tensor.Lengths); + scoped Span curIndex; + nint[]? curIndexArray; - _items++; - return _items < _tensor.FlattenedLength; + if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); } - - /// - /// Resets the enumerator to the beginning of the span. - /// - public void Reset() + else { - Array.Clear(_curIndices); - _curIndices[_tensor.Rank - 1] = -1; + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Rank]; } + curIndex.Clear(); - /// - /// - /// - public void Dispose() + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) { - + if (broadcastedLeft[curIndex] != broadcastedRight[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); } - /// - /// Current T value of the - /// - T IEnumerator.Current => _tensor[_curIndices]; + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); - /// - /// Current of the - /// - object? IEnumerator.Current => _tensor[_curIndices]; + return true; } - // REVIEW: PENDING API REVIEW TO DETERMINE IMPLEMENTATION /// - /// Gets the hash code for the . + /// Compares the elements of two to see if all elements of are equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are eqaul to . /// - /// The hash code of the tensor. - /// In all cases. - public override int GetHashCode() + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are equal to . + public static bool EqualsAll(in ReadOnlyTensorSpan x, T y) + where T : IEqualityOperators { - throw new NotImplementedException(); + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] != y) + return false; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; } + #endregion + #region EqualsAny /// - /// Get a string representation of the tensor. + /// Compares the elements of two to see if any elements of are equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are equal to . /// - private void ToMetadataString(StringBuilder sb) + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are equal to . + public static bool EqualsAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IEqualityOperators { - sb.Append('['); + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); - for (int i = 0; i < Rank; i++) + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) { - sb.Append(Lengths[i]); - if (i + 1 < Rank) - sb.Append('x'); + if (broadcastedLeft[curIndex] == broadcastedRight[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); } - sb.Append($"], type = {typeof(T)}, isPinned = {IsPinned}"); + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; } /// - /// Creates a representation of the ."/> + /// Compares the elements of two to see if any elements of are equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are equal to . /// - /// Maximum Length of each dimension - /// A representation of the - public string ToString(params ReadOnlySpan maximumLengths) + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are equal to . + public static bool EqualsAny(in ReadOnlyTensorSpan x, T y) + where T : IEqualityOperators { - if (maximumLengths.IsEmpty) + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) { - maximumLengths = Rank <= TensorShape.MaxInlineRank ? stackalloc nint[Rank] : new nint[Rank]; + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); - var sb = new StringBuilder(); - ToMetadataString(sb); - sb.AppendLine("{"); - ((ReadOnlyTensorSpan)AsTensorSpan()).ToString(sb, maximumLengths); - sb.AppendLine("}"); - return sb.ToString(); + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] == y) + return true; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; } + #endregion + #region FilteredUpdate /// - /// Pins and gets a to the backing memory. + /// Updates the tensor with the where the is true. /// - /// A which has pinned the backing memory. - public MemoryHandle GetPinnedHandle() + /// Input . + /// Input filter where if the index is true then it will update the . + /// Value to update in the . + public static ref readonly TensorSpan FilteredUpdate(in this TensorSpan tensor, scoped in ReadOnlyTensorSpan filter, T value) { - GCHandle handle = GCHandle.Alloc(_values, GCHandleType.Pinned); - unsafe + if (filter.Lengths.Length != tensor.Lengths.Length) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(filter)); + + Span srcSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); + Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape.LinearLength); + + for (int i = 0; i < filterSpan.Length; i++) { - return new MemoryHandle(Unsafe.AsPointer(ref GetPinnableReference()), handle); + if (filterSpan[i]) + { + srcSpan[i] = value; + } } + + return ref tensor; + } + + /// + /// Updates the tensor with the where the is true. + /// If dimensions are not the same an exception is thrown. + /// + /// Input . + /// Input filter where if the index is true then it will update the . + /// Values to update in the . + public static ref readonly TensorSpan FilteredUpdate(in this TensorSpan tensor, scoped in ReadOnlyTensorSpan filter, scoped in ReadOnlyTensorSpan values) + { + if (filter.Lengths.Length != tensor.Lengths.Length) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(filter)); + if (values.Rank != 1) + ThrowHelper.ThrowArgument_1DTensorRequired(nameof(values)); + + Span dstSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); + Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape.LinearLength); + Span valuesSpan = MemoryMarshal.CreateSpan(ref values._reference, (int)values._shape.LinearLength); + + int index = 0; + for (int i = 0; i < filterSpan.Length; i++) + { + if (filterSpan[i]) + { + dstSpan[i] = valuesSpan[index++]; + } + } + + return ref tensor; + } + #endregion + + #region GreaterThan + /// + /// Compares the elements of two to see which elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// A where the value is true if the elements in are greater than and + /// false if they are not. + public static Tensor GreaterThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + result = Tensor.Create(x.Lengths, false); + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + result = Tensor.Create(newSize, false); + } + + GreaterThan(x, y, result); + return result; + } + + /// + /// Compares the elements of two to see which elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// + /// A where the value is true if the elements in are greater than and + /// false if they are not. + public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + scoped ReadOnlyTensorSpan left; + scoped ReadOnlyTensorSpan right; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = x; + right = y; + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = LazyBroadcast(x, newSize); + right = LazyBroadcast(y, newSize); + } + + scoped Span curIndex; + nint[]? curIndexArray; + + if (right.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(right.Rank); + curIndex = curIndexArray.AsSpan(0, right.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[right.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < left.FlattenedLength; i++) + { + destination[curIndex] = left[curIndex] > right[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, right.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are greater than . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static Tensor GreaterThan(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(x.Lengths, false); + GreaterThan(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are greater than . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + destination[curIndex] = x[curIndex] > y; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares to see which elements are greater than . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static Tensor GreaterThan(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(y.Lengths, false); + GreaterThan(x, y, result); + return result; + } + + /// + /// Compares to see which elements are greater than . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static ref readonly TensorSpan GreaterThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + destination[curIndex] = x > y[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + #endregion + + #region GreaterThanOrEqual + /// + /// Compares the elements of two to see which elements of are greater than or equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// A where the value is true if the elements in are greater than and + /// false if they are not. + public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + result = Tensor.Create(x.Lengths, false); + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + result = Tensor.Create(newSize, false); + } + + GreaterThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares the elements of two to see which elements of are greater than or equal to . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// + /// A where the value is true if the elements in are greater than and + /// false if they are not. + public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + scoped ReadOnlyTensorSpan left; + scoped ReadOnlyTensorSpan right; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = x; + right = y; + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = LazyBroadcast(x, newSize); + right = LazyBroadcast(y, newSize); + } + + scoped Span curIndex; + nint[]? curIndexArray; + + if (right.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(right.Rank); + curIndex = curIndexArray.AsSpan(0, right.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[right.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < left.FlattenedLength; i++) + { + destination[curIndex] = left[curIndex] >= right[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, right.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are greater than or equal to . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(x.Lengths, false); + GreaterThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are greater than or equal to . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + destination[curIndex] = x[curIndex] >= y; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares to see which elements are greater than or equal to . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static Tensor GreaterThanOrEqual(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(y.Lengths, false); + GreaterThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares to see which elements are greater than or equal to . + /// It returns a where the value is true if the elements in are greater than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are greater than + /// and false if they are not. + public static ref readonly TensorSpan GreaterThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + destination[curIndex] = x >= y[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + #endregion + + #region GreaterThanAny + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] > y) + return true; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x > y[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + #endregion + + #region GreaterThanOrEqualAny + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] >= y) + return true; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanOrEqualAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x >= y[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + #endregion + + #region GreaterThanAll + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] <= y) + return false; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x <= y[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + #endregion + + #region GreaterThanOrEqualAll + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] < y) + return false; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x < y[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + #endregion + + #region LessThan + /// + /// Compares the elements of two to see which elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static Tensor LessThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + result = Tensor.Create(x.Lengths, false); + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + result = Tensor.Create(newSize, false); + } + + LessThan(x, y, result); + return result; + } + + /// + /// Compares the elements of two to see which elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + scoped ReadOnlyTensorSpan left; + scoped ReadOnlyTensorSpan right; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = x; + right = y; + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = LazyBroadcast(x, newSize); + right = LazyBroadcast(y, newSize); + } + + scoped Span curIndex; + nint[]? curIndexArray; + + if (right.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(right.Rank); + curIndex = curIndexArray.AsSpan(0, right.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[right.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < left.FlattenedLength; i++) + { + destination[curIndex] = left[curIndex] < right[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, right.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThan(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(x.Lengths, false); + LessThan(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + destination[curIndex] = x[curIndex] < y; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThan(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(y.Lengths, false); + LessThan(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + destination[curIndex] = x < y[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + #endregion + + #region LessThanOrEqual + /// + /// Compares the elements of two to see which elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + result = Tensor.Create(x.Lengths, false); + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + result = Tensor.Create(newSize, false); + } + + LessThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares the elements of two to see which elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// First to compare. + /// Second to compare. + /// + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + scoped ReadOnlyTensorSpan left; + scoped ReadOnlyTensorSpan right; + if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = x; + right = y; + } + else + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + left = LazyBroadcast(x, newSize); + right = LazyBroadcast(y, newSize); + } + + scoped Span curIndex; + nint[]? curIndexArray; + + if (right.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(right.Rank); + curIndex = curIndexArray.AsSpan(0, right.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[right.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < left.FlattenedLength; i++) + { + destination[curIndex] = left[curIndex] <= right[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, right.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(x.Lengths, false); + LessThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + destination[curIndex] = x[curIndex] <= y; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThanOrEqual(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + Tensor result = Tensor.Create(y.Lengths, false); + LessThanOrEqual(x, y, result); + return result; + } + + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators + { + if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + destination[curIndex] = x <= y[curIndex]; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return ref destination; + } + #endregion + + #region LessThanAny + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] < y) + return true; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x < y[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + #endregion + + #region LessThanOrEqualAny + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] <= y) + return true; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i <= y.FlattenedLength; i++) + { + if (x <= y[curIndex]) + return true; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return false; + } + #endregion + + #region LessThanAll + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First to compare. + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] >= y) + return false; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x >= y[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + #endregion + + #region LessThanOrEqualAll + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); + ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); + ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); + + scoped Span curIndex; + nint[]? curIndexArray; + + if (broadcastedRight.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; + } + curIndex.Clear(); + + for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) + { + if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First to compare. + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (x.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(x.Rank); + curIndex = curIndexArray.AsSpan(0, x.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[x.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < x.FlattenedLength; i++) + { + if (x[curIndex] > y) + return false; + TensorShape.AdjustToNextIndex(curIndex, x.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanOrEqualAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + scoped Span curIndex; + nint[]? curIndexArray; + + if (y.Rank > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(y.Rank); + curIndex = curIndexArray.AsSpan(0, y.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[y.Rank]; + } + curIndex.Clear(); + + for (int i = 0; i < y.FlattenedLength; i++) + { + if (x > y[curIndex]) + return false; + TensorShape.AdjustToNextIndex(curIndex, y.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + + return true; + } + #endregion + + #region Permute + + /// + /// Swaps the dimensions of the tensor according to the parameter. + /// If is a 1D tensor, it will return . Otherwise it creates a new + /// with the new axis ordering by allocating new memory. + /// + /// Input + /// with the new axis ordering. + public static Tensor PermuteDimensions(this Tensor tensor, params ReadOnlySpan dimensions) + { + if (tensor.Rank == 1) + { + return tensor; + } + else + { + T[] values = tensor.IsPinned ? GC.AllocateArray((int)tensor._flattenedLength) : (new T[tensor._flattenedLength]); + nint[] lengths = new nint[tensor.Rank]; + Tensor outTensor; + TensorSpan ospan; + TensorSpan ispan; + ReadOnlySpan permutation; + + if (dimensions.IsEmpty) + { + int[] tempPermutation = new int[tensor.Rank]; + for (int i = 0; i < tensor.Rank; i++) + { + lengths[i] = tensor._lengths[tensor.Rank - 1 - i]; + tempPermutation[i] = tensor.Rank - 1 - i; + } + + permutation = tempPermutation; + } + else + { + if (dimensions.Length != tensor.Lengths.Length) + ThrowHelper.ThrowArgument_PermuteAxisOrder(); + for (int i = 0; i < lengths.Length; i++) + lengths[i] = tensor.Lengths[dimensions[i]]; + permutation = dimensions.ToArray(); + } + outTensor = new Tensor(values, tensor._start, lengths, strides: [], tensor._isPinned); + + ospan = outTensor.AsTensorSpan(); + ispan = tensor.AsTensorSpan(); + + scoped Span indexes; + nint[]? indicesArray; + scoped Span permutedIndices; + nint[]? permutedIndicesArray; + if (outTensor.Rank > 6) + { + indicesArray = ArrayPool.Shared.Rent(outTensor.Rank); + indexes = indicesArray.AsSpan(0, outTensor.Rank); + indexes.Clear(); + + permutedIndicesArray = ArrayPool.Shared.Rent(outTensor.Rank); + permutedIndices = permutedIndicesArray.AsSpan(0, outTensor.Rank); + permutedIndices.Clear(); + } + else + { + indicesArray = null; + indexes = stackalloc nint[outTensor.Rank]; + permutedIndicesArray = null; + permutedIndices = stackalloc nint[outTensor.Rank]; + } + + for (int i = 0; i < tensor._flattenedLength; i++) + { + TensorHelpers.PermuteIndices(indexes, permutedIndices, permutation); + ospan[permutedIndices] = ispan[indexes]; + TensorShape.AdjustToNextIndex(indexes, tensor.Lengths); + } + + if (indicesArray != null && permutedIndicesArray != null) + { + ArrayPool.Shared.Return(indicesArray); + ArrayPool.Shared.Return(permutedIndicesArray); + } + + return outTensor; + } + } + #endregion + + #region Reshape + /// + /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. + /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, + /// an exception is thrown. + /// + /// you want to reshape. + /// with the new dimensions. + public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan lengths) + { + if (tensor.Lengths.SequenceEqual(lengths)) + return tensor; + + if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) + { + ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); + } + + nint[] arrLengths = lengths.ToArray(); + // Calculate wildcard info. + if (lengths.Contains(-1)) + { + if (lengths.Count(-1) > 1) + ThrowHelper.ThrowArgument_OnlyOneWildcard(); + nint tempTotal = tensor.FlattenedLength; + for (int i = 0; i < lengths.Length; i++) + { + if (lengths[i] != -1) + { + tempTotal /= lengths[i]; + } + } + arrLengths[lengths.IndexOf(-1)] = tempTotal; + + } + + nint tempLinear = TensorSpanHelpers.CalculateFlattenedLength(arrLengths); + if (tempLinear != tensor.FlattenedLength) + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + + nint[] strides; + + // If we contain a 0 stride we can only add dimensions of length 1. + if (tensor.Strides.Contains(0)) + { + List origStrides = new List(tensor.Strides.ToArray()); + int lengthOffset = 0; + for (int i = 0; i < arrLengths.Length; i++) + { + if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + lengthOffset++; + else if (arrLengths[i] == 1) + { + if (lengthOffset == tensor.Rank) + origStrides.Add(tensor.Strides[lengthOffset - 1]); + else + origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); + } + else + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + } + strides = origStrides.ToArray(); + } + else + strides = TensorSpanHelpers.CalculateStrides(arrLengths); + + return new Tensor(tensor._values, arrLengths, strides, tensor._start); + } + + /// + /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. + /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, + /// an exception is thrown. + /// + /// you want to reshape. + /// with the new dimensions. + public static TensorSpan Reshape(in this TensorSpan tensor, params scoped ReadOnlySpan lengths) + { + if (tensor.Lengths.SequenceEqual(lengths)) + return tensor; + + if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) + { + ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); + } + + nint[] arrLengths = lengths.ToArray(); + // Calculate wildcard info. + if (lengths.Contains(-1)) + { + if (lengths.Count(-1) > 1) + ThrowHelper.ThrowArgument_OnlyOneWildcard(); + nint tempTotal = tensor.FlattenedLength; + for (int i = 0; i < lengths.Length; i++) + { + if (lengths[i] != -1) + { + tempTotal /= lengths[i]; + } + } + arrLengths[lengths.IndexOf(-1)] = tempTotal; + + } + + nint tempLinear = TensorSpanHelpers.CalculateFlattenedLength(arrLengths); + if (tempLinear != tensor.FlattenedLength) + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + + nint[] strides; + + // If we contain a 0 stride we can only add dimensions of length 1. + if (tensor.Strides.Contains(0)) + { + List origStrides = new List(tensor.Strides.ToArray()); + int lengthOffset = 0; + for (int i = 0; i < arrLengths.Length; i++) + { + if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + { + lengthOffset++; + } + else if (arrLengths[i] == 1) + { + if (lengthOffset == tensor.Rank) + origStrides.Add(tensor.Strides[lengthOffset - 1]); + else + origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); + } + else + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + } + strides = origStrides.ToArray(); + } + else + strides = TensorSpanHelpers.CalculateStrides(arrLengths); + + TensorSpan output = new TensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape.LinearLength); + return output; + } + + /// + /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. + /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, + /// an exception is thrown. + /// + /// you want to reshape. + /// with the new dimensions. + public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan tensor, params scoped ReadOnlySpan lengths) + { + if (tensor.Lengths.SequenceEqual(lengths)) + return tensor; + + if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) + { + ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); + } + + nint[] arrLengths = lengths.ToArray(); + // Calculate wildcard info. + if (lengths.Contains(-1)) + { + if (lengths.Count(-1) > 1) + ThrowHelper.ThrowArgument_OnlyOneWildcard(); + nint tempTotal = tensor.FlattenedLength; + for (int i = 0; i < lengths.Length; i++) + { + if (lengths[i] != -1) + { + tempTotal /= lengths[i]; + } + } + arrLengths[lengths.IndexOf(-1)] = tempTotal; + + } + + nint tempLinear = TensorSpanHelpers.CalculateFlattenedLength(arrLengths); + if (tempLinear != tensor.FlattenedLength) + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + + nint[] strides; + + // If we contain a 0 stride we can only add dimensions of length 1. + if (tensor.Strides.Contains(0)) + { + List origStrides = new List(tensor.Strides.ToArray()); + int lengthOffset = 0; + for (int i = 0; i < arrLengths.Length; i++) + { + if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + lengthOffset++; + else if (arrLengths[i] == 1) + { + if (lengthOffset == tensor.Rank) + origStrides.Add(tensor.Strides[lengthOffset - 1]); + else + origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); + } + else + ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); + } + strides = origStrides.ToArray(); + } + else + strides = TensorSpanHelpers.CalculateStrides(arrLengths); + + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape.LinearLength); + return output; + } + #endregion + + #region Resize + /// + /// Creates a new , allocates new memory, and copies the data from . If the final shape is smaller all data after + /// that point is ignored. + /// + /// Input . + /// of the desired new shape. + public static Tensor Resize(Tensor tensor, ReadOnlySpan lengths) + { + nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); + T[] values = tensor.IsPinned ? GC.AllocateArray((int)newSize) : (new T[newSize]); + Tensor output = new Tensor(values, lengths, tensor._start, isPinned: false); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor.AsTensorSpan()._reference, (int)tensor._values.Length); + Span ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength); + if (newSize > tensor._values.Length) + TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); + else + TensorSpanHelpers.Memmove(ospan, span, newSize); + + return output; + } + + /// + /// Copies the data from . If the final shape is smaller all data after that point is ignored. + /// If the final shape is bigger it is filled with 0s. + /// + /// Input . + /// Destination with the desired new shape. + public static void ResizeTo(scoped in Tensor tensor, in TensorSpan destination) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._values[0], tensor._values.Length); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + if (destination._shape.LinearLength > tensor._values.Length) + TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); + else + TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + } + + /// + /// Copies the data from . If the final shape is smaller all data after that point is ignored. + /// If the final shape is bigger it is filled with 0s. + /// + /// Input . + /// Destination with the desired new shape. + public static void ResizeTo(scoped in TensorSpan tensor, in TensorSpan destination) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + if (destination._shape.LinearLength > tensor._shape.LinearLength) + TensorSpanHelpers.Memmove(ospan, span, tensor._shape.LinearLength); + else + TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + } + + /// + /// Copies the data from . If the final shape is smaller all data after that point is ignored. + /// If the final shape is bigger it is filled with 0s. + /// + /// Input . + /// Destination with the desired new shape. + public static void ResizeTo(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + if (destination._shape.LinearLength > tensor._shape.LinearLength) + TensorSpanHelpers.Memmove(ospan, span, tensor._shape.LinearLength); + else + TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + } + #endregion + + #region Reverse + /// + /// Reverse the order of elements in the . The shape of the tensor is preserved, but the elements are reordered. + /// + /// Input . + public static Tensor Reverse(in ReadOnlyTensorSpan tensor) + { + Tensor output = Tensor.Create(tensor.Lengths); + ReverseDimension(tensor, output, -1); + + return output; + } + + /// + /// Reverse the order of elements in the along the given dimension. The shape of the tensor is preserved, but the elements are reordered. + /// defaults to -1 when not provided, which reverses the entire tensor. + /// + /// Input . + /// dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor. + public static Tensor ReverseDimension(in ReadOnlyTensorSpan tensor, int dimension) + { + Tensor output = Tensor.Create(tensor.Lengths); + ReverseDimension(tensor, output, dimension); + + return output; + } + + /// + /// Reverse the order of elements in the . The shape of the tensor is preserved, but the elements are reordered. + /// + /// Input . + /// + public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination) + { + return ref ReverseDimension(tensor, destination, -1); + } + + /// + /// Reverse the order of elements in the along the given axis. The shape of the tensor is preserved, but the elements are reordered. + /// defaults to -1 when not provided, which reverses the entire span. + /// + /// Input . + /// + /// dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor. + public static ref readonly TensorSpan ReverseDimension(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination, int dimension) + { + if (dimension == -1) + { + nint index = tensor._shape.LinearLength - 1; + Span inputSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); + Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + for (int i = 0; i <= tensor._shape.LinearLength / 2; i++) + { + outputSpan[i] = inputSpan[(int)index]; + outputSpan[(int)index--] = inputSpan[i]; + } + } + else + { + nint copyLength = 1; + for (nint i = dimension; i < tensor.Lengths.Length; i++) + { + copyLength *= tensor.Lengths[(int)i]; + } + copyLength /= tensor.Lengths[(int)dimension]; + + scoped Span oIndices; + nint[]? oIndicesArray; + scoped Span iIndices; + nint[]? iIndicesArray; + if (tensor.Rank > 6) + { + oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); + oIndices = oIndicesArray.AsSpan(0, tensor.Rank); + oIndices.Clear(); + + iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); + iIndices = iIndicesArray.AsSpan(0, tensor.Rank); + iIndices.Clear(); + } + else + { + oIndicesArray = null; + oIndices = stackalloc nint[tensor.Rank]; + iIndicesArray = null; + iIndices = stackalloc nint[tensor.Rank]; + } + + iIndices[(int)dimension] = tensor.Lengths[(int)dimension] - 1; + nint copiedValues = 0; + ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); + + while (copiedValues < tensor.FlattenedLength) + { + TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destination._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, tensor.Strides, tensor.Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); + TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, tensor.Lengths); + TensorSpanHelpers.AdjustIndexesDown((int)dimension, 1, iIndices, tensor.Lengths); + copiedValues += copyLength; + } + + if (oIndicesArray != null && iIndicesArray != null) + { + ArrayPool.Shared.Return(oIndicesArray); + ArrayPool.Shared.Return(iIndicesArray); + } + } + + return ref destination; + } + #endregion + + #region SequenceEqual + /// + /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). + /// + public static bool SequenceEqual(this scoped in TensorSpan tensor, scoped in ReadOnlyTensorSpan other) + where T : IEquatable? + { + return tensor.FlattenedLength == other.FlattenedLength + && tensor._shape.LinearLength == other._shape.LinearLength + && tensor.Lengths.SequenceEqual(other.Lengths) + && MemoryMarshal.CreateReadOnlySpan(in tensor.GetPinnableReference(), (int)tensor._shape.LinearLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other._shape.LinearLength)); + } + + /// + /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). + /// + public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan tensor, scoped in ReadOnlyTensorSpan other) + where T : IEquatable? + { + return tensor.FlattenedLength == other.FlattenedLength + && tensor._shape.LinearLength == other._shape.LinearLength + && tensor.Lengths.SequenceEqual(other.Lengths) + && MemoryMarshal.CreateReadOnlySpan(in tensor.GetPinnableReference(), (int)tensor._shape.LinearLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other._shape.LinearLength)); + } + #endregion + + #region SetSlice + /// + /// Sets a slice of the given with the provided for the given + /// + /// Input . + /// The values you want to set in the . + /// The ranges you want to set. + public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan values, params ReadOnlySpan ranges) + { + SetSlice((TensorSpan)tensor, values, ranges); + + return tensor; + } + + /// + /// Sets a slice of the given with the provided for the given + /// + /// Input . + /// The values you want to set in the . + /// The ranges you want to set. + public static ref readonly TensorSpan SetSlice(this in TensorSpan tensor, scoped in ReadOnlyTensorSpan values, params scoped ReadOnlySpan ranges) + { + TensorSpan srcSpan; + if (ranges == ReadOnlySpan.Empty) + { + if (!TensorHelpers.IsBroadcastableTo(values.Lengths, tensor.Lengths)) + ThrowHelper.ThrowArgument_SetSliceNoRange(nameof(values)); + srcSpan = tensor; + } + else + srcSpan = tensor.Slice(ranges); + + if (!TensorHelpers.IsContiguousAndDense(srcSpan)) + ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); + + if (!TensorHelpers.IsBroadcastableTo(values.Lengths, srcSpan.Lengths)) + ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); + + values.CopyTo(srcSpan); + + return ref tensor; + } + #endregion + + #region Split + /// + /// Split a into along the given . If the tensor cannot be split + /// evenly on the given an exception is thrown. + /// + /// Input . + /// How many times to split the + /// The axis to split on. + public static Tensor[] Split(scoped in ReadOnlyTensorSpan tensor, int splitCount, nint dimension) + { + if (tensor.Lengths[(int)dimension] % splitCount != 0) + ThrowHelper.ThrowArgument_SplitNotSplitEvenly(); + + Tensor[] outputs = new Tensor[splitCount]; + + nint totalToCopy = tensor.FlattenedLength / splitCount; + nint copyLength = 1; + for (nint i = dimension; i < tensor.Lengths.Length; i++) + { + copyLength *= tensor.Lengths[(int)i]; + } + copyLength /= splitCount; + nint[] newShape = tensor.Lengths.ToArray(); + newShape[(int)dimension] = newShape[(int)dimension] / splitCount; + + scoped Span oIndices; + nint[]? oIndicesArray; + scoped Span iIndices; + nint[]? iIndicesArray; + if (tensor.Rank > 6) + { + oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); + oIndices = oIndicesArray.AsSpan(0, tensor.Rank); + oIndices.Clear(); + + iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); + iIndices = iIndicesArray.AsSpan(0, tensor.Rank); + iIndices.Clear(); + } + else + { + oIndicesArray = null; + oIndices = stackalloc nint[tensor.Rank]; + iIndicesArray = null; + iIndices = stackalloc nint[tensor.Rank]; + } + + for (int i = 0; i < outputs.Length; i++) + { + T[] values = new T[(int)totalToCopy]; + outputs[i] = new Tensor(values, newShape, memoryOffset: 0); + oIndices.Clear(); + iIndices.Clear(); + + iIndices[(int)dimension] = i; + ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); + TensorSpan oslice = outputs[i].AsTensorSpan().Slice(outputs[i]._lengths); + + nint copiedValues = 0; + while (copiedValues < totalToCopy) + { + TensorSpanHelpers.Memmove(ref Unsafe.Add(ref oslice._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, outputs[0].Strides, outputs[0].Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); + TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, outputs[i]._lengths); + TensorSpanHelpers.AdjustIndexes((int)dimension - 1, 1, iIndices, tensor.Lengths); + copiedValues += copyLength; + } + } + + if (oIndicesArray != null && iIndicesArray != null) + { + ArrayPool.Shared.Return(oIndicesArray); + ArrayPool.Shared.Return(iIndicesArray); + } + + return outputs; + } + #endregion + + #region Squeeze + /// + /// Removes all dimensions of length one from the . + /// + /// The to remove all dimensions of length 1. + public static Tensor Squeeze(this Tensor tensor) + { + return SqueezeDimension(tensor, -1); + } + + /// + /// Removes axis of length one from the for the given . + /// If the dimension is not of length one it will throw an exception. + /// + /// The to remove dimension of length 1. + /// The dimension to remove. + public static Tensor SqueezeDimension(this Tensor tensor, int dimension) + { + if (dimension >= tensor.Rank) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + + nint[] lengths; + nint[] strides; + + List tempLengths = new List(); + if (dimension == -1) + { + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (tensor.Lengths[i] != 1) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + else + { + if (tensor.Lengths[dimension] != 1) + { + ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); + } + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (i != dimension) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + + return new Tensor(tensor._values, lengths, strides, tensor._start); + } + + /// + /// Removes all dimensions of length one from the . + /// + /// The to remove all dimensions of length 1. + public static TensorSpan Squeeze(in this TensorSpan tensor) + { + return SqueezeDimension(tensor, -1); + } + + /// + /// Removes axis of length one from the for the given . + /// If the dimension is not of length one it will throw an exception. + /// + /// The to remove dimension of length 1. + /// The dimension to remove. + public static TensorSpan SqueezeDimension(in this TensorSpan tensor, int dimension) + { + if (dimension >= tensor.Rank) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + + nint[] lengths; + nint[] strides; + + List tempLengths = new List(); + if (dimension == -1) + { + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (tensor.Lengths[i] != 1) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + else + { + if (tensor.Lengths[dimension] != 1) + { + ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); + } + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (i != dimension) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + + return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + } + + /// + /// Removes all dimensions of length one from the . + /// + /// The to remove all dimensions of length 1. + public static ReadOnlyTensorSpan Squeeze(in this ReadOnlyTensorSpan tensor) + { + return SqueezeDimension(tensor, -1); + } + + /// + /// Removes axis of length one from the for the given . + /// If the dimension is not of length one it will throw an exception. + /// + /// The to remove dimension of length 1. + /// The dimension to remove. + public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSpan tensor, int dimension) + { + if (dimension >= tensor.Rank) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + + nint[] lengths; + nint[] strides; + + List tempLengths = new List(); + if (dimension == -1) + { + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (tensor.Lengths[i] != 1) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + else + { + if (tensor.Lengths[dimension] != 1) + { + ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); + } + for (int i = 0; i < tensor.Lengths.Length; i++) + { + if (i != dimension) + { + tempLengths.Add(tensor.Lengths[i]); + } + } + lengths = tempLengths.ToArray(); + strides = TensorSpanHelpers.CalculateStrides(lengths); + } + + return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + } + #endregion + + #region Stack + /// + /// Join multiple along a new dimension that is added at position 0. All tensors must have the same shape. + /// + /// Input . + public static Tensor Stack(params ReadOnlySpan> tensors) + { + return StackAlongDimension(0, tensors); + } + + /// + /// Join multiple along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have the same shape. + /// + /// Input . + /// Index of where the new dimension will be. + public static Tensor StackAlongDimension(int dimension, params ReadOnlySpan> tensors) + { + if (tensors.Length < 2) + ThrowHelper.ThrowArgument_StackTooFewTensors(); + + for (int i = 1; i < tensors.Length; i++) + { + if (!TensorShape.AreLengthsTheSame(tensors[0], tensors[i])) + ThrowHelper.ThrowArgument_StackShapesNotSame(); + } + + if (dimension < 0) + dimension = tensors[0].Rank - dimension; + + Tensor[] outputs = new Tensor[tensors.Length]; + for (int i = 0; i < tensors.Length; i++) + { + outputs[i] = Tensor.Unsqueeze(tensors[i], dimension); + } + return Tensor.ConcatenateOnDimension(dimension, outputs); + } + + /// + /// Join multiple along a new dimension that is added at position 0. All tensors must have the same shape. + /// + /// Input . + /// + public static ref readonly TensorSpan Stack(scoped in ReadOnlySpan> tensors, in TensorSpan destination) + { + return ref StackAlongDimension(tensors, destination, 0); + } + + /// + /// Join multiple along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have the same shape. + /// + /// Input . + /// + /// Index of where the new dimension will be. + public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlySpan> tensors, in TensorSpan destination, int dimension) + { + if (tensors.Length < 2) + ThrowHelper.ThrowArgument_StackTooFewTensors(); + + for (int i = 1; i < tensors.Length; i++) + { + if (!TensorShape.AreLengthsTheSame(tensors[0], tensors[i])) + ThrowHelper.ThrowArgument_StackShapesNotSame(); + } + + if (dimension < 0) + dimension = tensors[0].Rank - dimension; + + Tensor[] outputs = new Tensor[tensors.Length]; + for (int i = 0; i < tensors.Length; i++) + { + outputs[i] = Tensor.Unsqueeze(tensors[i], dimension); + } + return ref Tensor.ConcatenateOnDimension(dimension, outputs, destination); + } + #endregion + + #region ToString + /// + /// Creates a representation of the ."/> + /// + /// The you want to represent as a string. + /// Maximum Length of each dimension + /// A representation of the + public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) => + ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + + /// + /// Creates a representation of the ."/> + /// + /// + /// The you want to represent as a string. + /// Maximum Length of each dimension + public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) + { + StringBuilder sb = new(); + ToString(in tensor, sb, maximumLengths); + return sb.ToString(); + } + + internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBuilder sb, params ReadOnlySpan maximumLengths) + { + if (maximumLengths.Length != tensor.Rank) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + + scoped Span curIndexes; + nint[]? curIndexesArray; + if (tensor.Rank > 6) + { + curIndexesArray = ArrayPool.Shared.Rent(tensor.Rank); + curIndexes = curIndexesArray.AsSpan(0, tensor.Rank); + curIndexes.Clear(); + } + else + { + curIndexesArray = null; + curIndexes = stackalloc nint[tensor.Rank]; + } + + nint copiedValues = 0; + + T[] values = new T[tensor.Lengths[tensor.Rank - 1]]; + while (copiedValues < tensor.FlattenedLength) + { + var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, tensor.Strides, tensor.Lengths)), [tensor.Lengths[tensor.Rank - 1]], [1], tensor.Lengths[tensor.Rank - 1]); + sb.Append('{'); + sp.FlattenTo(values); + sb.Append(string.Join(",", values)); + sb.AppendLine("}"); + + TensorSpanHelpers.AdjustIndexes(tensor.Rank - 2, 1, curIndexes, tensor.Lengths); + copiedValues += tensor.Lengths[tensor.Rank - 1]; + } + + if (curIndexesArray != null) + ArrayPool.Shared.Return(curIndexesArray); + } + + /// + /// Creates a representation of the ."/> + /// + /// The you want to represent as a string. + /// Maximum Length of each dimension + /// A representation of the + public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + + #endregion + + #region Transpose + /// + /// Swaps the last two dimensions of the tensor. + /// + /// Input . + public static Tensor Transpose(Tensor tensor) + { + if (tensor.Lengths.Length < 2) + ThrowHelper.ThrowArgument_TransposeTooFewDimensions(); + + Span dimension = tensor.Rank <= TensorShape.MaxInlineRank ? stackalloc int[tensor.Rank] : new int[tensor.Rank]; + TensorSpanHelpers.FillRange(dimension); + + int temp = dimension[tensor.Rank - 1]; + dimension[tensor.Rank - 1] = dimension[tensor.Rank - 2]; + dimension[tensor.Rank - 2] = temp; + + return PermuteDimensions(tensor, dimension); + } + #endregion + + #region TryBroadcastTo + /// + /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in + /// If the shapes are not compatible, false is returned. + /// + /// Input . + /// Destination . + public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan destination) + { + return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + } + + /// + /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in + /// If the shapes are not compatible, false is returned. + /// + /// Input . + /// Destination . + public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan destination) + { + return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + } + + /// + /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in + /// If the shapes are not compatible, false is returned. + /// + /// Input . + /// Destination . + public static bool TryBroadcastTo(in this ReadOnlyTensorSpan tensor, in TensorSpan destination) + { + if (!TensorHelpers.IsBroadcastableTo(tensor.Lengths, destination.Lengths)) + return false; + + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(tensor.Lengths, destination.Lengths); + if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + return false; + + LazyBroadcast(tensor, newSize).CopyTo(destination); + return true; + } + #endregion + + #region Unsqueeze + /// + /// Insert a new dimension of length 1 that will appear at the dimension position. + /// + /// The to add a dimension of length 1. + /// The index of the dimension to add. + public static Tensor Unsqueeze(this Tensor tensor, int dimension) + { + if (dimension > tensor.Lengths.Length) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + if (dimension < 0) + dimension = tensor.Rank - dimension; + + Span lengths = tensor._lengths.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor._lengths.Length + 1] : + new nint[tensor._lengths.Length + 1]; + tensor._lengths.AsSpan(0, dimension).CopyTo(lengths); + tensor._lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1)); + lengths[dimension] = 1; + + Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Strides.Length + 1] : + new nint[tensor.Strides.Length + 1]; + if (dimension == tensor.Rank) + { + tensor.Strides.CopyTo(strides); + strides[dimension] = tensor.Strides[dimension - 1]; + } + else + { + tensor.Strides.Slice(0, dimension).CopyTo(strides); + tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); + strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + } + + return new Tensor(tensor._values, lengths, strides, tensor._start); + } + + /// + /// Insert a new dimension of length 1 that will appear at the dimension position. + /// + /// The to add a dimension of length 1. + /// The index of the dimension to add. + public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimension) + { + if (dimension > tensor.Lengths.Length) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + if (dimension < 0) + dimension = tensor.Rank - dimension; + + Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Lengths.Length + 1] : + new nint[tensor.Lengths.Length + 1]; + tensor.Lengths.Slice(0, dimension).CopyTo(lengths); + tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); + lengths[dimension] = 1; + + Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Strides.Length + 1] : + new nint[tensor.Strides.Length + 1]; + if (dimension == tensor.Rank) + { + tensor.Strides.CopyTo(strides); + strides[dimension] = tensor.Strides[dimension - 1]; + } + else + { + tensor.Strides.Slice(0, dimension).CopyTo(strides); + tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); + strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + } + + return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + } + + /// + /// Insert a new dimension of length 1 that will appear at the dimension position. + /// + /// The to add a dimension of length 1. + /// The index of the dimension to add. + public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan tensor, int dimension) + { + if (dimension > tensor.Lengths.Length) + ThrowHelper.ThrowArgument_AxisLargerThanRank(); + if (dimension < 0) + dimension = tensor.Rank - dimension; + + Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Lengths.Length + 1] : + new nint[tensor.Lengths.Length + 1]; + tensor.Lengths.Slice(0, dimension).CopyTo(lengths); + tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); + lengths[dimension] = 1; + + Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Strides.Length + 1] : + new nint[tensor.Strides.Length + 1]; + if (dimension == tensor.Rank) + { + tensor.Strides.CopyTo(strides); + strides[dimension] = tensor.Strides[dimension - 1]; + } + else + { + tensor.Strides.Slice(0, dimension).CopyTo(strides); + tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); + strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + } + + return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + } + #endregion + + #region TensorPrimitives + #region Abs + /// + /// Takes the absolute value of each element of the and returns a new with the result. + /// + /// The to take the abs of. + public static Tensor Abs(in ReadOnlyTensorSpan x) + where T : INumberBase + { + Tensor output = Tensor.Create(x.Lengths); + Abs(x, output); + return output; + } + + /// + /// Takes the absolute value of each element of the and returns a new with the result. + /// + /// The to take the abs of. + /// The destination. + public static ref readonly TensorSpan Abs(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : INumberBase + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Abs); + } + #endregion + + #region Acos + /// + /// Takes the inverse cosine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor Acos(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Acos(x, output); + return output; + } + + /// + /// Takes the inverse cosine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan Acos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acos); + } + #endregion + + #region Acosh + /// + /// Takes the inverse hyperbolic cosine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor Acosh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Acosh(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic cosine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan Acosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acosh); + } + #endregion + + #region AcosPi + /// + /// Takes the inverse hyperbolic cosine divided by pi of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor AcosPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + AcosPi(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic cosine divided by pi of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan AcosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AcosPi); + } + #endregion + + #region Add + /// + /// Adds each element of to each element of and returns a new with the result. + /// + /// The of values to add. + /// The second of values to add. + public static Tensor Add(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IAdditionOperators, IAdditiveIdentity + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Add(x, y, output); + return output; + } + + /// + /// Adds to each element of and returns a new with the result. + /// + /// The of values to add. + /// The to add to each element of . + public static Tensor Add(in ReadOnlyTensorSpan x, T y) + where T : IAdditionOperators, IAdditiveIdentity + { + Tensor output = Tensor.Create(x.Lengths); + Add(x, y, output); + return output; + } + + /// + /// Adds each element of to each element of and returns a new with the result. + /// + /// The of values to add. + /// The second of values to add. + /// + public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IAdditionOperators, IAdditiveIdentity + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Add); + } + + /// + /// Adds to each element of and returns a new with the result. + /// + /// The of values to add. + /// The to add to each element of . + /// + public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IAdditionOperators, IAdditiveIdentity + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Add); + } + #endregion + + #region Asin + /// + /// Takes the inverse sin of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor Asin(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Asin(x, output); + return output; + } + + /// + /// Takes the inverse sin of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan Asin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asin); + } + #endregion + + #region Asinh + /// + /// Takes the inverse hyperbolic sine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor Asinh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Asinh(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic sine of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan Asinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asinh); + } + #endregion + + #region AsinPi + /// + /// Takes the inverse hyperbolic sine divided by pi of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor AsinPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + AsinPi(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic sine divided by pi of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan AsinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AsinPi); + } + #endregion + + #region Atan + /// + /// Takes the arc tangent of each element of the and returns a new with the result. + /// + /// The input + public static Tensor Atan(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Atan(x, output); + return output; + } + + /// + /// Takes the arc tangent of each element of the and returns a new with the result. + /// + /// The input + /// + public static ref readonly TensorSpan Atan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atan); + } + #endregion + + #region Atan2 + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Atan2(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); + } + + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2(in ReadOnlyTensorSpan x, T y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(x.Lengths); + + Atan2(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2); + } + + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2(T x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(y.Lengths); + + Atan2(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); + } + #endregion + + #region Atan2Pi + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Atan2Pi(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); + } + + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, T y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(x.Lengths); + + Atan2Pi(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); + } + + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor Atan2Pi(T x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(y.Lengths); + + Atan2Pi(x, y, output); + return output; + } + + /// + /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Atan2Pi(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); + + } + #endregion + + #region Atanh + /// + /// Takes the inverse hyperbolic tangent of each element of the and returns a new with the result. + /// + /// The input . + public static Tensor Atanh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Atanh(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic tangent of each element of the and returns a new with the result. + /// + /// The input . + /// + public static ref readonly TensorSpan Atanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atanh); + } + #endregion + + #region AtanPi + /// + /// Takes the inverse hyperbolic tangent divided by pi of each element of the and returns a new with the result. + /// + /// The input. + public static Tensor AtanPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + AtanPi(x, output); + return output; + } + + /// + /// Takes the inverse hyperbolic tangent divided by pi of each element of the and returns a new with the result. + /// + /// The input. + /// + public static ref readonly TensorSpan AtanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AtanPi); + } + #endregion + + #region Average + /// + /// Returns the average of the elements in the tensor. + /// + /// The to take the mean of. + /// representing the mean. + public static T Average(scoped in ReadOnlyTensorSpan x) + where T : INumberBase + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Average); + } + #endregion + + #region BitwiseAnd + /// + /// Computes the element-wise bitwise and of the two input and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IBitwiseOperators + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + BitwiseAnd(x, y, output); + return output; + } + + /// + /// Computes the element-wise bitwise and of the two input and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); + } + + /// + /// Computes the element-wise bitwise and of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, T y) + where T : IBitwiseOperators + { + Tensor output = Tensor.Create(x.Lengths); + + BitwiseAnd(x, y, output); + return output; + } + + /// + /// Computes the element-wise bitwise and of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + /// + public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); + } + #endregion + + #region BitwiseOr + /// + /// Computes the element-wise bitwise of of the two input and returns a new with the result. + /// + /// The left . + /// The right . + public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IBitwiseOperators + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + BitwiseOr(x, y, output); + return output; + } + + /// + /// Computes the element-wise bitwise of of the two input and returns a new with the result. + /// + /// The left . + /// The right . + /// + public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); + } + + /// + /// Computes the element-wise bitwise or of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, T y) + where T : IBitwiseOperators + { + Tensor output = Tensor.Create(x.Lengths); + + BitwiseOr(x, y, output); + return output; + } + + /// + /// Computes the element-wise bitwise or of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + /// + public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); + } + #endregion + + #region CubeRoot + /// + /// Computes the element-wise cube root of the input and returns a new with the result. + /// + /// The left . + public static Tensor Cbrt(in ReadOnlyTensorSpan x) + where T : IRootFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Cbrt(x, output); + return output; + } + + /// + /// Computes the element-wise cube root of the input and returns a new with the result. + /// + /// The left . + /// + public static ref readonly TensorSpan Cbrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IRootFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cbrt); + } + #endregion + + #region Ceiling + /// + /// Computes the element-wise ceiling of the input and returns a new with the result. + /// + /// The left . + public static Tensor Ceiling(in ReadOnlyTensorSpan x) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Ceiling(x, output); + return output; + } + + /// + /// Computes the element-wise ceiling of the input and returns a new with the result. + /// + /// The left . + /// + public static ref readonly TensorSpan Ceiling(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPoint + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Ceiling); + } + #endregion + + #region ConvertChecked + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + public static Tensor ConvertChecked(in ReadOnlyTensorSpan source) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + Tensor output = Tensor.Create(source.Lengths); + + ConvertChecked(source, output); + return output; + } + + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + /// + public static ref readonly TensorSpan ConvertChecked(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertChecked); + } + #endregion + + #region ConvertSaturating + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + public static Tensor ConvertSaturating(in ReadOnlyTensorSpan source) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + Tensor output = Tensor.Create(source.Lengths); + + ConvertSaturating(source, output); + return output; + } + + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + /// + public static ref readonly TensorSpan ConvertSaturating(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertSaturating); + } + #endregion + + #region ConvertTruncating + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + public static Tensor ConvertTruncating(in ReadOnlyTensorSpan source) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + Tensor output = Tensor.Create(source.Lengths); + + ConvertTruncating(source, output); + return output; + } + + /// + /// Copies to a new converting each + /// value to a value. + /// + /// The input . + /// + public static ref readonly TensorSpan ConvertTruncating(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertTruncating); + } + #endregion + + #region CopySign + /// + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new tensor with the result. + /// + /// Input . + /// The number with the associated sign. + public static Tensor CopySign(in ReadOnlyTensorSpan x, T sign) + where T : INumber + { + Tensor output = Create(x.Lengths); + + CopySign(x, sign, output); + return output; + } + + /// + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new with the result. + /// + /// Input . + /// The with the associated signs. + public static Tensor CopySign(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan sign) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(sign.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, sign.Lengths)); + } + + CopySign(x, sign, output); + return output; + } + + /// + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new tensor with the result. + /// + /// Input . + /// The number with the associated sign. + /// + public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, T sign, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, sign, destination, TensorPrimitives.CopySign); + } + + /// + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new with the result. + /// + /// Input . + /// The with the associated signs. + /// + public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan sign, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, sign, destination, TensorPrimitives.CopySign); + } + #endregion + + #region Cos + /// + /// Takes the cosine of each element of the and returns a new with the result. + /// + /// The to take the cosine of. + public static Tensor Cos(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Cos(x, output); + return output; + } + + /// + /// Takes the cosine of each element of the and returns a new with the result. + /// + /// The to take the cosine of. + /// + public static ref readonly TensorSpan Cos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cos); + } + #endregion + + #region Cosh + /// + /// Takes the hyperbolic cosine of each element of the and returns a new with the result. + /// + /// The to take the cosine of. + public static Tensor Cosh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Cosh(x, output); + return output; + } + + /// + /// Takes the hyperbolic cosine of each element of the and returns a new with the result. + /// + /// The to take the cosine of. + /// + public static ref readonly TensorSpan Cosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cosh); + } + #endregion + + #region CosineSimilarity + /// + /// Compute cosine similarity between and . + /// + /// The first + /// The second + public static Tensor CosineSimilarity(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IRootFunctions + { + if (x.Rank != 2) + ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); + + if (y.Rank != 2) + ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); + + if (x.Lengths[1] != y.Lengths[1]) + ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); + + nint dim1 = x.Lengths[0]; + nint dim2 = y.Lengths[0]; + + T[] values = new T[dim1 * dim2]; + + Tensor output = Tensor.Create(values, [dim1, dim2]); + + CosineSimilarity(x, y, output); + + return output; + } + + /// + /// Compute cosine similarity between and . + /// + /// The first + /// The second + /// + public static ref readonly TensorSpan CosineSimilarity(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IRootFunctions + { + if (x.Rank != 2) + ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); + + if (y.Rank != 2) + ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); + + if (x.Lengths[1] != y.Lengths[1]) + ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); + + nint dim1 = x.Lengths[0]; + nint dim2 = y.Lengths[0]; + + if (destination.Lengths[0] != dim1 || destination.Lengths[1] != dim2) + ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); + + Span values = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + + scoped Span leftIndexes = stackalloc nint[2]; + scoped Span rightIndexes = stackalloc nint[2]; + + int outputOffset = 0; + + ReadOnlySpan lspan; + ReadOnlySpan rspan; + int rowLength = (int)x.Lengths[1]; + for (int i = 0; i < dim1; i++) + { + for (int j = 0; j < dim2; j++) + { + lspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref x._reference, TensorSpanHelpers.ComputeLinearIndex(leftIndexes, x.Strides, x.Lengths)), (int)rowLength); + rspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref y._reference, TensorSpanHelpers.ComputeLinearIndex(rightIndexes, y.Strides, y.Lengths)), (int)rowLength); + values[outputOffset++] = TensorPrimitives.CosineSimilarity(lspan, rspan); + rightIndexes[0]++; + } + rightIndexes[0] = 0; + leftIndexes[0]++; + } + + return ref destination; + + } + #endregion + + #region CosPi + /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new with the results. + /// The input + /// + /// + /// This method effectively computes .CosPi([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static Tensor CosPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + CosPi(x, output); + return output; + } + + /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new with the results. + /// The input + /// + /// + /// + /// This method effectively computes .CosPi([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static ref readonly TensorSpan CosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.CosPi); + } + #endregion + + #region DegreesToRadians + /// + /// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results. + /// + /// The input . + public static Tensor DegreesToRadians(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + DegreesToRadians(x, output); + return output; + } + + /// + /// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results. + /// + /// The input . + /// + public static ref readonly TensorSpan DegreesToRadians(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.DegreesToRadians); + } + #endregion + + #region Distance + /// + /// Computes the distance between two points, specified as non-empty, equal-length tensors of numbers, in Euclidean space. + /// + /// The input . + /// The input . + public static T Distance(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y) + where T : IRootFunctions + { + return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Distance); + } + #endregion + + #region Divide + /// + /// Divides each element of by and returns a new with the result. + /// + /// Input . + /// The divisor + public static Tensor Divide(in ReadOnlyTensorSpan x, T y) + where T : IDivisionOperators + { + Tensor output = Create(x.Lengths); + Divide(x, y, output); + return output; + } + + /// + /// Divides by each element of and returns a new with the result."/> + /// + /// The value to be divided. + /// The divisor. + public static Tensor Divide(T x, in ReadOnlyTensorSpan y) + where T : IDivisionOperators + { + Tensor output = Tensor.Create(y.Lengths); + Divide(x, y, output); + return output; + } + + /// + /// Divides each element of by its corresponding element in and returns + /// a new with the result. + /// + /// The to be divided. + /// The divisor. + public static Tensor Divide(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IDivisionOperators + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Divide(x, y, output); + return output; + } + + /// + /// Divides each element of by and returns a new with the result. + /// + /// Input . + /// The divisor + /// + public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IDivisionOperators + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Divide); + } + + /// + /// Divides by each element of and returns a new with the result."/> + /// + /// The value to be divided. + /// The divisor. + /// + public static ref readonly TensorSpan Divide(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IDivisionOperators + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); + } + + /// + /// Divides each element of by its corresponding element in and returns + /// a new with the result. + /// + /// The to be divided. + /// The divisor. + /// + public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IDivisionOperators + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); + } + #endregion + + #region Dot + /// + /// Computes the dot product of two tensors containing numbers. + /// + /// The input . + /// The input . + public static T Dot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IAdditionOperators, IAdditiveIdentity, IMultiplicativeIdentity, IMultiplyOperators + { + return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Dot); + } + #endregion + + #region Exp + /// + /// Computes the element-wise result of raising e to the single-precision floating-point number powers in the specified tensor. + /// + /// The input . + public static Tensor Exp(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Exp(x, output); + return output; + } + + /// + /// Computes the element-wise result of raising e to the single-precision floating-point number powers in the specified tensor. + /// + /// The input . + /// + public static ref readonly TensorSpan Exp(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp); + } + #endregion + + #region Exp10 + /// + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor. + /// + /// The input . + public static Tensor Exp10(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Exp10(x, output); + return output; + } + + /// + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor. + /// + /// The input . + /// + public static ref readonly TensorSpan Exp10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10); + } + #endregion + + #region Exp10M1 + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. + /// The input . + public static Tensor Exp10M1(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Exp10M1(x, output); + return output; + } + + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. + /// The input . + /// + public static ref readonly TensorSpan Exp10M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10M1); + } + #endregion + + #region Exp2 + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. + /// The input . + public static Tensor Exp2(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Exp2(x, output); + return output; + } + + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan Exp2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2); + } + #endregion + + #region Exp2M1 + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. + /// The input . + public static Tensor Exp2M1(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Exp2M1(x, output); + return output; + } + + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. + /// The input . + /// + public static ref readonly TensorSpan Exp2M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2M1); + } + #endregion + + #region ExpM1 + /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. + /// The input . + public static Tensor ExpM1(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + ExpM1(x, output); + return output; + } + + /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. + /// The input . + /// + public static ref readonly TensorSpan ExpM1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ExpM1); + } + #endregion + + #region Floor + /// Computes the element-wise floor of numbers in the specified tensor. + /// The input . + public static Tensor Floor(in ReadOnlyTensorSpan x) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Floor(x, output); + return output; + } + + /// Computes the element-wise floor of numbers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan Floor(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPoint + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Floor); + } + #endregion + + #region Hypotenuse + /// + /// Computes the element-wise hypotenuse given values from two tensors representing the lengths of the shorter sides in a right-angled triangle. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// + /// Left . + /// Right . + public static Tensor Hypot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IRootFunctions + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Hypot(x, y, output); + return output; + } + + /// + /// Computes the element-wise hypotenuse given values from two tensors representing the lengths of the shorter sides in a right-angled triangle. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// + /// Left . + /// Right . + /// + public static ref readonly TensorSpan Hypot(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IRootFunctions + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Hypot); + } + #endregion + + #region Ieee754Remainder + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// Left . + /// Right . + public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Ieee754Remainder(x, y, output); + return output; + } + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// Left . + /// Right . + /// + public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + } + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The left . + /// The right . + public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, T y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(x.Lengths); + + Ieee754Remainder(x, y, output); + return output; + } + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + } + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The left . + /// The right . + public static Tensor Ieee754Remainder(T x, in ReadOnlyTensorSpan y) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(y.Lengths); + + Ieee754Remainder(x, y, output); + return output; + } + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Ieee754Remainder(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + } + #endregion + + #region ILogB + /// Computes the element-wise integer logarithm of numbers in the specified tensor. + /// The input . + public static Tensor ILogB(in ReadOnlyTensorSpan x) + where T : IFloatingPointIeee754 + { + Tensor output = Tensor.Create(x.Lengths, x.Strides); + ILogB(x, output); + return output; + } + + /// Computes the element-wise integer logarithm of numbers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan ILogB(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPointIeee754 + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ILogB); + } + #endregion + + #region IndexOfMax + /// Searches for the index of the largest number in the specified tensor. + /// The input . + public static int IndexOfMax(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + return TensorPrimitives.IndexOfMax(span); + } + #endregion + + #region IndexOfMaxMagnitude + /// Searches for the index of the number with the largest magnitude in the specified tensor. + /// The input . + public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + return TensorPrimitives.IndexOfMaxMagnitude(span); + } + #endregion + + #region IndexOfMin + /// Searches for the index of the smallest number in the specified tensor. + /// The input . + public static int IndexOfMin(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + return TensorPrimitives.IndexOfMin(span); + } + #endregion + + #region IndexOfMinMagnitude + /// + /// Searches for the index of the number with the smallest magnitude in the specified tensor. + /// + /// The input . + public static int IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + return TensorPrimitives.IndexOfMinMagnitude(span); + } + #endregion + + #region LeadingZeroCount + /// + /// Computes the element-wise leading zero count of numbers in the specified tensor. + /// + /// The input . + public static Tensor LeadingZeroCount(in ReadOnlyTensorSpan x) + where T : IBinaryInteger + { + Tensor output = Tensor.Create(x.Lengths); + LeadingZeroCount(x, output); + return output; + } + + /// + /// Computes the element-wise leading zero count of numbers in the specified tensor. + /// + /// The input . + /// + public static ref readonly TensorSpan LeadingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IBinaryInteger + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LeadingZeroCount); + } + #endregion + + #region Log + /// + /// Takes the natural logarithm of each element of the and returns a new with the result. + /// + /// The to take the natural logarithm of. + public static Tensor Log(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Log(x, output); + return output; + } + + /// + /// Takes the natural logarithm of each element of the and returns a new with the result. + /// + /// The to take the natural logarithm of. + /// + public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log); + } + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor + /// The second tensor + public static Tensor Log(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : ILogarithmicFunctions + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Log(x, y, output); + return output; + } + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor + /// The second tensor + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Log); + } + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor + /// The second tensor + public static Tensor Log(in ReadOnlyTensorSpan x, T y) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + + Log(x, y, output); + return output; + } + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor + /// The second tensor + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Log); + } + #endregion + + #region Log10 + /// + /// Takes the base 10 logarithm of each element of the and returns a new with the result. + /// + /// The to take the base 10 logarithm of. + public static Tensor Log10(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Log10(x, output); + return output; + } + + /// + /// Takes the base 10 logarithm of each element of the and returns a new with the result. + /// + /// The to take the base 10 logarithm of. + /// + public static ref readonly TensorSpan Log10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10); + } + #endregion + + #region Log10P1 + /// + /// Takes the base 10 logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the base 10 logarithm of. + public static Tensor Log10P1(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Log10P1(x, output); + return output; + } + + /// + /// Takes the base 10 logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the base 10 logarithm of. + /// + public static ref readonly TensorSpan Log10P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10P1); + } + #endregion + + #region Log2 + /// + /// Takes the base 2 logarithm of each element of the and returns a new with the result. + /// + /// The to take the base 2 logarithm of. + public static Tensor Log2(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Log2(x, output); + return output; + } + + /// + /// Takes the base 2 logarithm of each element of the and returns a new with the result. + /// + /// The to take the base 2 logarithm of. + /// + public static ref readonly TensorSpan Log2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2); + } + #endregion + + #region Log2P1 + /// + /// Takes the base 2 logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the base 2 logarithm of. + public static Tensor Log2P1(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Log2P1(x, output); + return output; + } + + /// + /// Takes the base 2 logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the base 2 logarithm of. + /// + public static ref readonly TensorSpan Log2P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2P1); + } + #endregion + + #region LogP1 + /// + /// Takes the natural logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the natural logarithm of. + public static Tensor LogP1(in ReadOnlyTensorSpan x) + where T : ILogarithmicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + LogP1(x, output); + return output; + } + + /// + /// Takes the natural logarithm plus 1 of each element of the and returns a new with the result. + /// + /// The to take the natural logarithm of. + /// + public static ref readonly TensorSpan LogP1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ILogarithmicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LogP1); + } + #endregion + + #region Max + /// Searches for the largest number in the specified tensor. + /// The input .. + public static T Max(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Max); + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor Max(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Max(x, y, output); + return output; + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Max); + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor Max(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + Max(x, y, output); + return output; + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Max); + } + #endregion + + #region MaxMagnitude + /// Searches for the number with the largest magnitude in the specified tensor. + /// The input .. + public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitude); + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MaxMagnitude(x, y, output); + return output; + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MaxMagnitude(x, y, output); + return output; + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); + } + #endregion + + #region MaxMagnitudeNumber + /// Searches for the number with the largest magnitude in the specified tensor. + /// The input .. + public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) + where T : INumberBase + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitudeNumber); + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MaxMagnitudeNumber(x, y, output); + return output; + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MaxMagnitudeNumber(x, y, output); + return output; + } + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); + } + #endregion + + #region MaxNumber + /// Searches for the largest number in the specified tensor. + /// The input .. + public static T MaxNumber(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxNumber); + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MaxNumber(x, y, output); + return output; + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MaxNumber(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MaxNumber(x, y, output); + return output; + } + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); + } + #endregion + + #region Min + /// Searches for the smallest number in the specified tensor. + /// The input . + public static T Min(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Min); + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor Min(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Min(x, y, output); + return output; + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Min); + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor Min(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + Min(x, y, output); + return output; + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Min); + } + #endregion + + #region MinMagnitude + /// Searches for the number with the smallest magnitude in the specified tensor. + /// The input . + public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitude); + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MinMagnitude(x, y, output); + return output; + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MinMagnitude(x, y, output); + return output; + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); + } + #endregion + + #region MinMagnitudeNumber + /// Searches for the number with the smallest magnitude in the specified tensor. + /// The input .. + public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) + where T : INumberBase + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitudeNumber); + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MinMagnitudeNumber(x, y, output); + return output; + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MinMagnitudeNumber(x, y, output); + return output; + } + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); + } + #endregion + + #region MinNumber + /// Searches for the smallest number in the specified tensor. + /// The input .. + public static T MinNumber(scoped in ReadOnlyTensorSpan x) + where T : INumber + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinNumber); + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : INumber + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + MinNumber(x, y, output); + return output; + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinNumber); + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + public static Tensor MinNumber(in ReadOnlyTensorSpan x, T y) + where T : INumber + { + Tensor output = Tensor.Create(x.Lengths); + MinNumber(x, y, output); + return output; + } + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : INumber + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinNumber); + } + #endregion + + #region Multiply + /// + /// Multiplies each element of with and returns a new with the result. + /// + /// Input + /// value to multiply by. + public static Tensor Multiply(in ReadOnlyTensorSpan x, T y) + where T : IMultiplyOperators, IMultiplicativeIdentity + { + Tensor output = Tensor.Create(x.Lengths); + Multiply((ReadOnlyTensorSpan)x, y, output); + return output; + } + + /// + /// Multiplies each element of with and returns a new with the result. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// + /// Left for multiplication. + /// Right for multiplication. + public static Tensor Multiply(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IMultiplyOperators, IMultiplicativeIdentity + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Multiply((ReadOnlyTensorSpan)x, y, output); + return output; + } + + /// + /// Multiplies each element of with and returns a new with the result. + /// + /// Input + /// value to multiply by. + /// + public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IMultiplyOperators, IMultiplicativeIdentity + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.Multiply(span, y, ospan); + return ref destination; + } + + /// + /// Multiplies each element of with and returns a new with the result. + /// If the shapes are not the same they are broadcast to the smallest compatible shape. + /// + /// Left for multiplication. + /// Right for multiplication. + /// + public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IMultiplyOperators, IMultiplicativeIdentity + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Multiply); + } + #endregion + + #region Negate + /// Computes the element-wise negation of each number in the specified tensor. + /// The + public static Tensor Negate(in ReadOnlyTensorSpan x) + where T : IUnaryNegationOperators + { + Tensor output = Tensor.Create(x.Lengths); + Negate(x, output); + return output; + } + + /// Computes the element-wise negation of each number in the specified tensor. + /// The + /// + public static ref readonly TensorSpan Negate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IUnaryNegationOperators + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Negate); + } + #endregion + + #region Norm + /// + /// Takes the norm of the and returns the result. + /// + /// The to take the norm of. + public static T Norm(scoped in ReadOnlyTensorSpan x) + where T : IRootFunctions + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Norm); + } + #endregion + + #region OnesComplement + /// Computes the element-wise one's complement of numbers in the specified tensor. + /// The + public static Tensor OnesComplement(in ReadOnlyTensorSpan x) + where T : IBitwiseOperators + { + Tensor output = Tensor.Create(x.Lengths); + OnesComplement(x, output); + return output; + } + + /// Computes the element-wise one's complement of numbers in the specified tensor. + /// The + /// + public static ref readonly TensorSpan OnesComplement(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.OnesComplement); + } + #endregion + + #region PopCount + /// Computes the element-wise population count of numbers in the specified tensor. + /// The + public static Tensor PopCount(in ReadOnlyTensorSpan x) + where T : IBinaryInteger + { + Tensor output = Tensor.Create(x.Lengths); + PopCount(x, output); + return output; + } + + /// Computes the element-wise population count of numbers in the specified tensor. + /// The + /// + public static ref readonly TensorSpan PopCount(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IBinaryInteger + { + return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.PopCount); + } + #endregion + + #region Pow + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + public static Tensor Pow(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IPowerFunctions + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Pow(x, y, output); + return output; + } + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + /// + public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IPowerFunctions + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); + } + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + public static Tensor Pow(in ReadOnlyTensorSpan x, T y) + where T : IPowerFunctions + { + Tensor output = Tensor.Create(x.Lengths); + + Pow(x, y, output); + return output; + } + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + /// + public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IPowerFunctions + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Pow); + } + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + public static Tensor Pow(T x, in ReadOnlyTensorSpan y) + where T : IPowerFunctions + { + Tensor output = Tensor.Create(y.Lengths); + + Pow(x, y, output); + return output; + } + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The input . + /// The second input + /// + public static ref readonly TensorSpan Pow(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IPowerFunctions + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); + } + #endregion + + #region Product + /// Computes the product of all elements in the specified non-empty tensor of numbers. + /// The input . + public static T Product(scoped in ReadOnlyTensorSpan x) + where T : IMultiplicativeIdentity, IMultiplyOperators + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Product); + } + #endregion + + #region RadiansToDegrees + /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. + /// The input . + public static Tensor RadiansToDegrees(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + RadiansToDegrees(x, output); + return output; + } + + /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. + /// The input . + /// + public static ref readonly TensorSpan RadiansToDegrees(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.RadiansToDegrees); + } + #endregion + + #region Reciprocal + /// Computes the element-wise reciprocal of numbers in the specified tensor. + /// The input . + public static Tensor Reciprocal(in ReadOnlyTensorSpan x) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Reciprocal(x, output); + return output; + } + + /// Computes the element-wise reciprocal of numbers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan Reciprocal(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPoint + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Reciprocal); + } + #endregion + + #region RootN + /// Computes the element-wise n-th root of the values in the specified tensor. + /// The tensor, represented as a span. + /// The degree of the root to be computed, represented as a scalar. + public static Tensor RootN(in ReadOnlyTensorSpan x, int n) + where T : IRootFunctions + { + Tensor output = Tensor.Create(x.Lengths); + RootN(x, n, output); + return output; + } + + /// Computes the element-wise n-th root of the values in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The degree of the root to be computed, represented as a scalar. + public static ref readonly TensorSpan RootN(scoped in ReadOnlyTensorSpan x, int n, in TensorSpan destination) + where T : IRootFunctions + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.RootN(span, n, ospan); + return ref destination; + } + #endregion + + #region RotateLeft + /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. + /// The tensor + /// The number of bits to rotate, represented as a scalar. + /// Destination is too short. + public static Tensor RotateLeft(in ReadOnlyTensorSpan x, int rotateAmount) + where T : IBinaryInteger + { + Tensor output = Tensor.Create(x.Lengths); + RotateLeft(x, rotateAmount, output); + return output; + } + + /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. + /// The tensor + /// The number of bits to rotate, represented as a scalar. + /// + /// Destination is too short. + public static ref readonly TensorSpan RotateLeft(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) + where T : IBinaryInteger + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.RotateLeft(span, rotateAmount, ospan); + return ref destination; + } + #endregion + + #region RotateRight + /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. + /// The tensor + /// The number of bits to rotate, represented as a scalar. + /// Destination is too short. + public static Tensor RotateRight(in ReadOnlyTensorSpan x, int rotateAmount) + where T : IBinaryInteger + { + Tensor output = Tensor.Create(x.Lengths); + RotateRight(x, rotateAmount, output); + return output; + } + + /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. + /// The tensor + /// The number of bits to rotate, represented as a scalar. + /// + /// Destination is too short. + public static ref readonly TensorSpan RotateRight(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) + where T : IBinaryInteger + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.RotateRight(span, rotateAmount, ospan); + return ref destination; + } + #endregion + + #region Round + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + public static Tensor Round(in ReadOnlyTensorSpan x) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Round(x, output); + return output; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPoint + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Round); + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + /// + public static Tensor Round(in ReadOnlyTensorSpan x, int digits, MidpointRounding mode) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Round(x, digits, mode, output); + return output; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + /// + /// + public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, MidpointRounding mode, in TensorSpan destination) + where T : IFloatingPoint + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.Round(span, digits, mode, ospan); + return ref destination; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + public static Tensor Round(in ReadOnlyTensorSpan x, int digits) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Round(x, digits, output); + return output; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + /// + public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, in TensorSpan destination) + where T : IFloatingPoint + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.Round(span, digits, ospan); + return ref destination; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + public static Tensor Round(in ReadOnlyTensorSpan x, MidpointRounding mode) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Round(x, mode, output); + return output; + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The input . + /// + /// + public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, MidpointRounding mode, in TensorSpan destination) + where T : IFloatingPoint + { + if (destination._shape.LinearLength < x._shape.LinearLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + TensorPrimitives.Round(span, mode, ospan); + return ref destination; + } + #endregion + + #region Sigmoid + /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. + /// The input . + public static Tensor Sigmoid(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Sigmoid(x, output); + return output; + } + + /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. + /// The input . + /// + public static ref readonly TensorSpan Sigmoid(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sigmoid); + } + #endregion + + #region Sin + /// + /// Takes the sin of each element of the and returns a new with the result. + /// + /// The to take the sin of. + public static Tensor Sin(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Sin(x, output); + return output; + } + + /// + /// Takes the sin of each element of the and returns a new with the result. + /// + /// The to take the sin of. + /// + public static ref readonly TensorSpan Sin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sin); + } + #endregion + + #region Sinh + /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. + /// The to take the sin of. + public static Tensor Sinh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Sinh(x, output); + return output; + } + + /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. + /// The to take the sin of. + /// + public static ref readonly TensorSpan Sinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sinh); + } + #endregion + + #region SinPi + /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. + /// The to take the sin of. + public static Tensor SinPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + SinPi(x, output); + return output; + } + + /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. + /// The to take the sin of. + /// + public static ref readonly TensorSpan SinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SinPi); + } + #endregion + + #region SoftMax + /// Computes the softmax function over the specified non-empty tensor of numbers. + /// The to take the sin of. + public static Tensor SoftMax(in ReadOnlyTensorSpan x) + where T : IExponentialFunctions + { + Tensor output = Tensor.Create(x.Lengths); + SoftMax(x, output); + return output; + } + + /// Computes the softmax function over the specified non-empty tensor of numbers. + /// The to take the sin of. + /// + public static ref readonly TensorSpan SoftMax(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IExponentialFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SoftMax); } + #endregion + + #region Sqrt + /// + /// Takes the square root of each element of the and returns a new with the result. + /// + /// The to take the square root of. + public static Tensor Sqrt(in ReadOnlyTensorSpan x) + where T : IRootFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Sqrt(x, output); + return output; + } + + /// + /// Takes the square root of each element of the and returns a new with the result. + /// + /// The to take the square root of. + /// + public static ref readonly TensorSpan Sqrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IRootFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sqrt); + } + #endregion + + #region StdDev + /// + /// Returns the standard deviation of the elements in the tensor. + /// + /// The to take the standard deviation of. + /// representing the standard deviation. + public static T StdDev(in ReadOnlyTensorSpan x) + where T : IRootFunctions + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.StdDev); + } + #endregion + + #region Subtract + /// + /// Subtracts from each element of and returns a new with the result. + /// + /// The . + /// The to subtract. + public static Tensor Subtract(in ReadOnlyTensorSpan x, T y) + where T : ISubtractionOperators + { + Tensor output = Create(x.Lengths); + Subtract(x, y, output); + return output; + } + + /// + /// Subtracts each element of from and returns a new with the result. + /// + /// The to be subtracted from. + /// The of values to subtract. + public static Tensor Subtract(T x, in ReadOnlyTensorSpan y) + where T : ISubtractionOperators + { + Tensor output = Create(y.Lengths); + Subtract(x, y, output); + return output; + } + + /// + /// Subtracts each element of from and returns a new with the result. + /// + /// The with values to be subtracted from. + /// The with values to subtract. + public static Tensor Subtract(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : ISubtractionOperators + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Subtract(x, y, output); + return output; + } + + /// + /// Subtracts from each element of and returns a new with the result. + /// + /// The with values to be subtracted from. + /// The value to subtract. + /// + public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : ISubtractionOperators + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Subtract); + } + + /// + /// Subtracts each element of from and returns a new with the result. + /// + /// The value to be subtracted from. + /// The values to subtract. + /// + public static ref readonly TensorSpan Subtract(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : ISubtractionOperators + { + return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); + } + + /// + /// Subtracts each element of from and returns a new with the result. + /// + /// The of values to be subtracted from. + /// The of values to subtract. + /// + public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : ISubtractionOperators + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); + } + #endregion + + #region Sum + /// + /// Sums the elements of the specified tensor. + /// + /// Tensor to sum + /// + public static T Sum(scoped in ReadOnlyTensorSpan x) + where T : IAdditionOperators, IAdditiveIdentity + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Sum); + } + #endregion + + #region SumOfSquares + /// + /// Sums the squared elements of the specified tensor. + /// + /// Tensor to sum squares of + /// + internal static T SumOfSquares(scoped in ReadOnlyTensorSpan x) + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.SumOfSquares); + } + #endregion + + #region Tan + /// Computes the element-wise tangent of the value in the specified tensor. + /// The to take the sin of. + public static Tensor Tan(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Tan(x, output); + return output; + } + + /// Computes the element-wise tangent of the value in the specified tensor. + /// The to take the sin of. + /// + public static ref readonly TensorSpan Tan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tan); + } + #endregion + + #region Tanh + /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. + /// The to take the sin of. + public static Tensor Tanh(in ReadOnlyTensorSpan x) + where T : IHyperbolicFunctions + { + Tensor output = Tensor.Create(x.Lengths); + Tanh(x, output); + return output; + } + + /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. + /// The to take the sin of. + /// + public static ref readonly TensorSpan Tanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IHyperbolicFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tanh); + } + #endregion + + #region TanPi + /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. + /// The to take the sin of. + public static Tensor TanPi(in ReadOnlyTensorSpan x) + where T : ITrigonometricFunctions + { + Tensor output = Tensor.Create(x.Lengths); + TanPi(x, output); + return output; + } + + /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. + /// The to take the sin of. + /// + public static ref readonly TensorSpan TanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : ITrigonometricFunctions + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TanPi); + } + #endregion + + #region TrailingZeroCount + /// Computes the element-wise trailing zero count of numbers in the specified tensor. + /// The input . + public static Tensor TrailingZeroCount(in ReadOnlyTensorSpan x) + where T : IBinaryInteger + { + Tensor output = Tensor.Create(x.Lengths); + TrailingZeroCount(x, output); + return output; + } + + /// Computes the element-wise trailing zero count of numbers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan TrailingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IBinaryInteger + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TrailingZeroCount); + } + #endregion + + #region Truncate + /// Computes the element-wise truncation of numbers in the specified tensor. + /// The input . + public static Tensor Truncate(in ReadOnlyTensorSpan x) + where T : IFloatingPoint + { + Tensor output = Tensor.Create(x.Lengths); + Truncate(x, output); + return output; + } + + /// Computes the element-wise truncation of numbers in the specified tensor. + /// The input . + /// + public static ref readonly TensorSpan Truncate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) + where T : IFloatingPoint + { + return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Truncate); + } + #endregion + + #region Xor + /// Computes the element-wise XOR of numbers in the specified tensors. + /// The left . + /// The right . + public static Tensor Xor(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IBitwiseOperators + { + Tensor output; + if (x.Lengths.SequenceEqual(y.Lengths)) + { + output = Tensor.Create(x.Lengths); + } + else + { + output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); + } + + Xor(x, y, output); + return output; + } + + /// Computes the element-wise XOR of numbers in the specified tensors. + /// The left . + /// The right . + /// + public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Xor); + } + + /// + /// Computes the element-wise Xor of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + public static Tensor Xor(in ReadOnlyTensorSpan x, T y) + where T : IBitwiseOperators + { + Tensor output = Tensor.Create(x.Lengths); + Xor(x, y, output); + return output; + } + + /// + /// Computes the element-wise Xor of the two input and returns a new with the result. + /// + /// The left . + /// The second value. + /// + public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + where T : IBitwiseOperators + { + return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Xor); + } + #endregion + + /// + /// Gets the smallest broadcastable lengths for two shapes. + /// + /// The first shape to broadcast. + /// The second shape to broadcast. + /// The smallest lengths these shapes can be broadcast to. + /// The lengths of and are not broadcast compatible. + public static nint[] GetSmallestBroadcastableLengths(ReadOnlySpan shape1, ReadOnlySpan shape2) + { + if (!TensorHelpers.IsBroadcastableTo(shape1, shape2)) + ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + + nint[] intermediateShape = TensorHelpers.GetIntermediateShape(shape1, shape2.Length); + for (int i = 1; i <= shape1.Length; i++) + { + intermediateShape[^i] = Math.Max(intermediateShape[^i], shape1[^i]); + } + for (int i = 1; i <= shape2.Length; i++) + { + intermediateShape[^i] = Math.Max(intermediateShape[^i], shape2[^i]); + } + + return intermediateShape; + } + + #region TensorPrimitivesHelpers + private delegate void PerformCalculationSpanInSpanOut(ReadOnlySpan input, Span output); + + private delegate void PerformCalculationSpanInTInSpanOut(ReadOnlySpan input, T value, Span output); + + private delegate void PerformCalculationTInSpanInSpanOut(T value, ReadOnlySpan input, Span output); + + private delegate void PerformCalculationTwoSpanInSpanOut(ReadOnlySpan input, ReadOnlySpan inputTwo, Span output); + + private delegate T PerformCalculationTwoSpanInTOut(ReadOnlySpan input, ReadOnlySpan inputTwo); + + private delegate T PerformCalculationSpanInTOut(ReadOnlySpan input); + + private static T TensorPrimitivesHelperSpanInTOut(scoped in ReadOnlyTensorSpan input, PerformCalculationSpanInTOut performCalculation) + { + if (TensorHelpers.IsContiguousAndDense(input)) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); + return performCalculation(span); + } + // Flattening needs to happen + else + { + // TODO: Can optimize this to not need to realize the broadcasts + // That will need to be done on a per method basis. + nint flattenedLength = input.FlattenedLength; + T[] flattened = new T[flattenedLength]; + input.FlattenTo(flattened); + return performCalculation(flattened); + } + } + + private static T TensorPrimitivesHelperTwoSpanInTOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, PerformCalculationTwoSpanInTOut performCalculation) + { + // If sizes are the same. + if (TensorHelpers.IsContiguousAndDense(left) && TensorHelpers.IsContiguousAndDense(right) && TensorShape.AreLengthsTheSame(left, right)) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left._shape.FlattenedLength); + ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right._shape.FlattenedLength); + return performCalculation(span, rspan); + } + // Broadcasting needs to happen. + else + { + // Have a couple different possible cases here. + // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) + // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) + // Because we are returning a single T though we need to actual realize the broadcasts at this point to perform the calculations. + + // TODO: Can optimize this to not need to realize the broadcasts + // That will need to be done on a per method basis. + nint[] newLengths = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); + nint newLength = TensorSpanHelpers.CalculateFlattenedLength(newLengths); + TensorSpan broadcastedLeft = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); + TensorSpan broadcastedRight = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); + BroadcastTo(left, broadcastedLeft); + BroadcastTo(right, broadcastedRight); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref broadcastedLeft._reference, (int)broadcastedLeft.FlattenedLength); + ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref broadcastedRight._reference, (int)broadcastedRight.FlattenedLength); + return performCalculation(span, rspan); + } + } + + private static ref readonly TensorSpan TensorPrimitivesHelperSpanInSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationSpanInSpanOut performCalculation) + { + // Make sure destination has enough memory + if (destination._shape.LinearLength < input._shape.FlattenedLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + // Make sure destination shape works with input shape + TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); + + Span destinationSpan; + ReadOnlySpan inputSpan; + + // Memory is contiguous for both input and destination + if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) + { + inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); + destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape.FlattenedLength); + performCalculation(inputSpan, destinationSpan); + } + else + { + scoped Span curIndex; + nint[]? curIndexArray; + if (input.Lengths.Length > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, input.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[input.Lengths.Length]; + } + curIndex.Clear(); + + int copiedValues = 0; + nint rowLength = input.Lengths[^1]; + + while (copiedValues < slicedDestination.FlattenedLength) + { + inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); + destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); + performCalculation(inputSpan, destinationSpan); + copiedValues += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + } + + return ref destination; + } + + private static ref readonly TensorSpan TensorPrimitivesHelperSpanInTInSpanOut(scoped in ReadOnlyTensorSpan input, T value, in TensorSpan destination, PerformCalculationSpanInTInSpanOut performCalculation) + { + if (destination._shape.LinearLength < input._shape.FlattenedLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + // Make sure destination shape works with input shape + TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); + + ReadOnlySpan inputSpan; + Span destinationSpan; + + if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) + { + inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); + destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + performCalculation(inputSpan, value, destinationSpan); + } + else + { + scoped Span curIndex; + nint[]? curIndexArray; + if (input.Lengths.Length > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, input.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[input.Lengths.Length]; + } + curIndex.Clear(); + + int copiedValues = 0; + nint rowLength = input.Lengths[^1]; + + while (copiedValues < slicedDestination.FlattenedLength) + { + inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); + destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); + performCalculation(inputSpan, value, destinationSpan); + copiedValues += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + } + + return ref destination; + } + + private static ref readonly TensorSpan TensorPrimitivesHelperTInSpanInSpanOut(T value, scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationTInSpanInSpanOut performCalculation) + { + if (destination._shape.LinearLength < input._shape.FlattenedLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + // Make sure destination shape works with input shape + TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); + + ReadOnlySpan inputSpan; + Span destinationSpan; + + if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) + { + inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.LinearLength); + destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); + performCalculation(value, inputSpan, destinationSpan); + } + else + { + scoped Span curIndex; + nint[]? curIndexArray; + if (input.Lengths.Length > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); + curIndex = curIndexArray.AsSpan(0, input.Rank); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[input.Lengths.Length]; + } + curIndex.Clear(); + + int copiedValues = 0; + nint rowLength = input.Lengths[^1]; + + while (copiedValues < slicedDestination.FlattenedLength) + { + inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); + destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); + performCalculation(value, inputSpan, destinationSpan); + copiedValues += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + } + + return ref destination; + } + + private static ref readonly TensorSpan TensorPrimitivesHelperTwoSpanInSpanOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, in TensorSpan destination, PerformCalculationTwoSpanInSpanOut performCalculation) + { + nint[] newSize = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); + + TensorSpan slicedDestination = destination.Slice(newSize); + + // If sizes are the same and memory is contiguous for all tensors + if (TensorShape.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right) && TensorHelpers.IsContiguousAndDense(left) + && TensorHelpers.IsContiguousAndDense(right) && TensorHelpers.IsContiguousAndDense(slicedDestination)) + { + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, left._shape.LinearLength <= left.FlattenedLength ? (int)left._shape.LinearLength : (int)left.FlattenedLength); + ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, right._shape.LinearLength <= right.FlattenedLength ? (int)right._shape.LinearLength : (int)right.FlattenedLength); + Span ospan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape.LinearLength); + performCalculation(span, rspan, ospan); + return ref destination; + } + // Broadcasting needs to happen. + else + { + // Have a couple different possible cases here. + // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) + // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) + + ReadOnlyTensorSpan broadcastedLeft = Tensor.LazyBroadcast(left, newSize); + ReadOnlyTensorSpan broadcastedRight = Tensor.LazyBroadcast(right, newSize); + + nint rowLength = newSize[^1]; + Span ospan; + ReadOnlySpan ispan; + Span buffer = new T[rowLength]; + + scoped Span curIndex; + nint[]? curIndexArray; + if (newSize.Length > TensorShape.MaxInlineRank) + { + curIndexArray = ArrayPool.Shared.Rent(newSize.Length); + curIndex = curIndexArray.AsSpan(0, newSize.Length); + } + else + { + curIndexArray = null; + curIndex = stackalloc nint[newSize.Length]; + } + curIndex.Clear(); + + int outputOffset = 0; + // neither row contiguous + if (broadcastedLeft.Strides[^1] == 0 && broadcastedRight.Strides[^1] == 0) + { + Span buffer2 = new T[rowLength]; + + while (outputOffset < slicedDestination.FlattenedLength) + { + ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); + buffer.Fill(broadcastedLeft[curIndex]); + buffer2.Fill(broadcastedRight[curIndex]); + performCalculation(buffer, buffer2, ospan); + outputOffset += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); + } + } + // tensor not row contiguous + else if (broadcastedLeft.Strides[^1] == 0) + { + while (outputOffset < slicedDestination.FlattenedLength) + { + ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); + buffer.Fill(broadcastedLeft[curIndex]); + ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); + performCalculation(buffer, ispan, ospan); + outputOffset += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); + } + } + // right not row contiguous + else if (broadcastedRight.Strides[^1] == 0) + { + while (outputOffset < slicedDestination.FlattenedLength) + { + ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); + buffer.Fill(broadcastedRight[curIndex]); + ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); + performCalculation(ispan, buffer, ospan); + outputOffset += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); + } + } + // both row contiguous + else + { + Span rspan; + while (outputOffset < slicedDestination.FlattenedLength) + { + ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); + ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); + rspan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); + performCalculation(ispan, rspan, ospan); + outputOffset += (int)rowLength; + TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); + } + } + + if (curIndexArray != null) + ArrayPool.Shared.Return(curIndexArray); + } + return ref destination; + } + + #endregion + + #endregion } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs deleted file mode 100644 index 6b9dbec121b207..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs +++ /dev/null @@ -1,7183 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Buffers; -using static System.Runtime.InteropServices.JavaScript.JSType; -using System.Security.Cryptography; -using System.Runtime.Serialization; -using System.Diagnostics.CodeAnalysis; - -#pragma warning disable CS8601 // Possible null reference assignment. -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - -namespace System.Numerics.Tensors -{ - /// - /// Provides methods for tensor operations. - /// - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - public static partial class Tensor - { - #region AsReadOnlySpan - /// - /// Extension method to more easily create a TensorSpan from an array. - /// - /// The type of the elements in the array - /// The with the data - /// The shape for the - /// - public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, params scoped ReadOnlySpan lengths) => new(array, 0, lengths, default); - #endregion - - #region AsTensorSpan - /// - /// Extension method to more easily create a TensorSpan from an array. - /// - /// The type of the elements in the array - /// The with the data - /// The shape for the - /// - public static TensorSpan AsTensorSpan(this T[]? array, params scoped ReadOnlySpan lengths) => new(array, 0, lengths, default); - #endregion - - #region Broadcast - /// - /// Broadcast the data from to the smallest broadcastable shape compatible with . Creates a new and allocates new memory. - /// - /// Input . - /// Other to make shapes broadcastable. - public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, scoped in ReadOnlyTensorSpan lengthsSource) - { - return Broadcast(source, lengthsSource.Lengths); - } - - /// - /// Broadcast the data from to the new shape . Creates a new and allocates new memory. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, scoped ReadOnlySpan lengths) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, lengths); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - Tensor output = Tensor.CreateUninitialized(intermediate.Lengths); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref output._values[0], (int)output.FlattenedLength)); - return output; - } - #endregion - - #region BroadcastTo - /// - /// Broadcast the data from to . - /// - /// Input . - /// - public static void BroadcastTo(this Tensor source, in TensorSpan destination) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); - } - - /// - /// Broadcast the data from to . - /// - /// Input . - /// Other to make shapes broadcastable. - public static void BroadcastTo(in this TensorSpan source, in TensorSpan destination) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); - } - - /// - /// Broadcast the data from to . - /// - /// Input . - /// - public static void BroadcastTo(in this ReadOnlyTensorSpan source, in TensorSpan destination) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlySpan shape) - { - if (input.Lengths.SequenceEqual(shape)) - return new TensorSpan(ref input._reference, shape, input.Strides, input._shape._memoryLength); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - nint newSize = TensorSpanHelpers.CalculateTotalLength(shape); - - if (newSize == input.FlattenedLength) - return Reshape(input, shape); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, shape.Length); - nint[] strides = new nint[shape.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && shape[i] != 1) || (intermediateShape[i] == 1 && shape[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._shape._memoryLength); - - return output; - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan input, ReadOnlySpan shape) - { - if (input.Lengths.SequenceEqual(shape)) - return new TensorSpan(ref input._reference, shape, input.Strides, input._shape._memoryLength); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - nint newSize = TensorSpanHelpers.CalculateTotalLength(shape); - - if (newSize == input.FlattenedLength) - return Reshape(input, shape); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, shape.Length); - nint[] strides = new nint[shape.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && shape[i] != 1) || (intermediateShape[i] == 1 && shape[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._shape._memoryLength); - - return output; - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static Tensor LazyBroadcast(Tensor input, ReadOnlySpan lengths) - { - if (input.Lengths.SequenceEqual(lengths)) - return new Tensor(input._values, lengths, input._memoryOffset, isPinned: false); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - nint newSize = TensorSpanHelpers.CalculateTotalLength(lengths); - - if (newSize == input.FlattenedLength) - return Reshape(input, lengths); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, lengths.Length); - nint[] strides = new nint[lengths.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && lengths[i] != 1) || (intermediateShape[i] == 1 && lengths[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - Tensor output = new Tensor(input._values, lengths, strides, input._memoryOffset); - - return output; - } - #endregion - - #region Concatenate - /// - /// Join a sequence of tensors along an existing axis. - /// - /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). - public static Tensor Concatenate(params scoped ReadOnlySpan> tensors) - { - return ConcatenateOnDimension(0, tensors); - } - - /// - /// Join a sequence of tensors along an existing axis. - /// - /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). - /// The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0. - public static Tensor ConcatenateOnDimension(int dimension, params scoped ReadOnlySpan> tensors) - { - if (tensors.Length < 2) - ThrowHelper.ThrowArgument_ConcatenateTooFewTensors(); - - if (dimension < -1 || dimension > tensors[0].Rank) - ThrowHelper.ThrowArgument_InvalidAxis(); - - // Calculate total space needed. - nint totalLength = 0; - for (int i = 0; i < tensors.Length; i++) - totalLength += TensorSpanHelpers.CalculateTotalLength(tensors[i].Lengths); - - nint sumOfAxis = 0; - // If axis != -1, make sure all dimensions except the one to concatenate on match. - if (dimension != -1) - { - sumOfAxis = tensors[0].Lengths[dimension]; - for (int i = 1; i < tensors.Length; i++) - { - if (tensors[0].Rank != tensors[i].Rank) - ThrowHelper.ThrowArgument_InvalidConcatenateShape(); - for (int j = 0; j < tensors[0].Rank; j++) - { - if (j != dimension) - { - if (tensors[0].Lengths[j] != tensors[i].Lengths[j]) - ThrowHelper.ThrowArgument_InvalidConcatenateShape(); - } - } - sumOfAxis += tensors[i].Lengths[dimension]; - } - } - - Tensor tensor; - if (dimension == -1) - { - tensor = Tensor.Create([totalLength]); - } - else - { - nint[] lengths = new nint[tensors[0].Rank]; - tensors[0].Lengths.CopyTo(lengths); - lengths[dimension] = sumOfAxis; - tensor = Tensor.Create(lengths); - } - - ConcatenateOnDimension(dimension, tensors, tensor); - return tensor; - } - - /// - /// Join a sequence of tensors along an existing axis. - /// - /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). - /// - public static ref readonly TensorSpan Concatenate(scoped ReadOnlySpan> tensors, in TensorSpan destination) - { - return ref ConcatenateOnDimension(0, tensors, destination); - } - - /// - /// Join a sequence of tensors along an existing axis. - /// - /// The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default). - /// The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0. - /// - - public static ref readonly TensorSpan ConcatenateOnDimension(int dimension, scoped ReadOnlySpan> tensors, in TensorSpan destination) - { - if (tensors.Length < 2) - ThrowHelper.ThrowArgument_ConcatenateTooFewTensors(); - - if (dimension < -1 || dimension > tensors[0].Rank) - ThrowHelper.ThrowArgument_InvalidAxis(); - - // Calculate total space needed. - nint totalLength = 0; - for (int i = 0; i < tensors.Length; i++) - totalLength += TensorSpanHelpers.CalculateTotalLength(tensors[i].Lengths); - - nint sumOfAxis = 0; - // If axis != -1, make sure all dimensions except the one to concatenate on match. - if (dimension != -1) - { - sumOfAxis = tensors[0].Lengths[dimension]; - for (int i = 1; i < tensors.Length; i++) - { - if (tensors[0].Rank != tensors[i].Rank) - ThrowHelper.ThrowArgument_InvalidConcatenateShape(); - for (int j = 0; j < tensors[0].Rank; j++) - { - if (j != dimension) - { - if (tensors[0].Lengths[j] != tensors[i].Lengths[j]) - ThrowHelper.ThrowArgument_InvalidConcatenateShape(); - } - } - sumOfAxis += tensors[i].Lengths[dimension]; - } - - // Make sure the destination tensor has the correct shape. - nint[] lengths = new nint[tensors[0].Rank]; - tensors[0].Lengths.CopyTo(lengths); - lengths[dimension] = sumOfAxis; - - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - } - Span dstSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)totalLength); - nint valuesCopied = 0; - - scoped Span curIndex; - nint[]? curIndexArray; - - if (tensors[0].Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(tensors[0].Rank); - curIndex = curIndexArray.AsSpan(0, tensors[0].Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[tensors[0].Rank]; - } - curIndex.Clear(); - - nint srcIndex; - nint copyLength; - - while (valuesCopied < totalLength) - { - for (int i = 0; i < tensors.Length; i++) - { - srcIndex = TensorSpanHelpers.ComputeLinearIndex(curIndex, tensors[i].Strides, tensors[i].Lengths); - copyLength = CalculateCopyLength(tensors[i].Lengths, dimension); - Span srcSpan = MemoryMarshal.CreateSpan(ref tensors[i]._values[srcIndex], (int)copyLength); - TensorSpanHelpers.Memmove(dstSpan, srcSpan, copyLength, valuesCopied); - valuesCopied += copyLength; - } - TensorSpanHelpers.AdjustIndexes(dimension - 1, 1, curIndex, tensors[0].Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - private static nint CalculateCopyLength(ReadOnlySpan lengths, int startingAxis) - { - // When starting axis is -1 we want all the data at once same as if starting axis is 0 - if (startingAxis == -1) - startingAxis = 0; - nint length = 1; - for (int i = startingAxis; i < lengths.Length; i++) - { - length *= lengths[i]; - } - return length; - } - #endregion - - #region Equals - /// - /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size - /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements are equal and false if they are not. - public static Tensor Equals(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IEqualityOperators - { - Tensor result; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - Equals(x, y, result); - return result; - } - - /// - /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size - /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements are equal and false if they are not. - public static ref readonly TensorSpan Equals(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IEqualityOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] == right[curIndex]; - TensorSpanHelpers.AdjustIndexes(right.Rank - 1, 1, curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size - /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> - /// - /// First to compare. - /// Second value to compare. - /// A where the value is true if the elements are equal and false if they are not. - public static Tensor Equals(in ReadOnlyTensorSpan x, T y) - where T : IEqualityOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - Equals(x, y, result); - return result; - } - - /// - /// Compares the elements of two for equality. If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size - /// before they are compared. It returns a where the value is true if the elements are equal and false if they are not."/> - /// - /// First to compare. - /// Second value to compare. - /// - /// A where the value is true if the elements are equal and false if they are not. - public static ref readonly TensorSpan Equals(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IEqualityOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] == y; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region EqualsAll - /// - /// Compares the elements of two to see if all elements of are equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are eqaul to . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are equal to . - public static bool EqualsAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IEqualityOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] != broadcastedRight[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are eqaul to . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are equal to . - public static bool EqualsAll(in ReadOnlyTensorSpan x, T y) - where T : IEqualityOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] != y) - return false; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - #endregion - - #region EqualsAny - /// - /// Compares the elements of two to see if any elements of are equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are equal to . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are equal to . - public static bool EqualsAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IEqualityOperators - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] == broadcastedRight[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are equal to . - /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are equal to . - public static bool EqualsAny(in ReadOnlyTensorSpan x, T y) - where T : IEqualityOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] == y) - return true; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region FilteredUpdate - /// - /// Updates the tensor with the where the is true. - /// - /// Input . - /// Input filter where if the index is true then it will update the . - /// Value to update in the . - public static ref readonly TensorSpan FilteredUpdate(in this TensorSpan tensor, scoped in ReadOnlyTensorSpan filter, T value) - { - if (filter.Lengths.Length != tensor.Lengths.Length) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(filter)); - - Span srcSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape._memoryLength); - - for (int i = 0; i < filterSpan.Length; i++) - { - if (filterSpan[i]) - { - srcSpan[i] = value; - } - } - - return ref tensor; - } - - /// - /// Updates the tensor with the where the is true. - /// If dimensions are not the same an exception is thrown. - /// - /// Input . - /// Input filter where if the index is true then it will update the . - /// Values to update in the . - public static ref readonly TensorSpan FilteredUpdate(in this TensorSpan tensor, scoped in ReadOnlyTensorSpan filter, scoped in ReadOnlyTensorSpan values) - { - if (filter.Lengths.Length != tensor.Lengths.Length) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(filter)); - if (values.Rank != 1) - ThrowHelper.ThrowArgument_1DTensorRequired(nameof(values)); - - Span dstSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape._memoryLength); - Span valuesSpan = MemoryMarshal.CreateSpan(ref values._reference, (int)values._shape._memoryLength); - - int index = 0; - for (int i = 0; i < filterSpan.Length; i++) - { - if (filterSpan[i]) - { - dstSpan[i] = valuesSpan[index++]; - } - } - - return ref tensor; - } - #endregion - - #region GreaterThan - /// - /// Compares the elements of two to see which elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements in are greater than and - /// false if they are not. - public static Tensor GreaterThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - GreaterThan(x, y, result); - return result; - } - - /// - /// Compares the elements of two to see which elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements in are greater than and - /// false if they are not. - public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] > right[curIndex]; - TensorSpanHelpers.AdjustIndexes(right.Rank - 1, 1, curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are greater than . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static Tensor GreaterThan(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - GreaterThan(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are greater than . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] > y; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares to see which elements are greater than . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static Tensor GreaterThan(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - GreaterThan(x, y, result); - return result; - } - - /// - /// Compares to see which elements are greater than . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static ref readonly TensorSpan GreaterThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x > y[curIndex]; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region GreaterThanOrEqual - /// - /// Compares the elements of two to see which elements of are greater than or equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements in are greater than and - /// false if they are not. - public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - GreaterThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of two to see which elements of are greater than or equal to . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements in are greater than and - /// false if they are not. - public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] >= right[curIndex]; - TensorSpanHelpers.AdjustIndexes(right.Rank - 1, 1, curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are greater than or equal to . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - GreaterThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are greater than or equal to . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] >= y; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares to see which elements are greater than or equal to . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static Tensor GreaterThanOrEqual(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - GreaterThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares to see which elements are greater than or equal to . - /// It returns a where the value is true if the elements in are greater than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are greater than - /// and false if they are not. - public static ref readonly TensorSpan GreaterThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x >= y[curIndex]; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region GreaterThanAny - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] > y) - return true; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x > y[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region GreaterThanOrEqualAny - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] >= y) - return true; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . - /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanOrEqualAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x >= y[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region GreaterThanAll - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] <= y) - return false; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x <= y[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - #endregion - - #region GreaterThanOrEqualAll - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] < y) - return false; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x < y[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - #endregion - - #region LessThan - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static Tensor LessThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - LessThan(x, y, result); - return result; - } - - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] < right[curIndex]; - TensorSpanHelpers.AdjustIndexes(right.Rank - 1, 1, curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThan(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - LessThan(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] < y; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThan(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - LessThan(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x < y[curIndex]; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region LessThanOrEqual - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorHelpers.AreLengthsTheSame(x, y)) - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] <= right[curIndex]; - TensorSpanHelpers.AdjustIndexes(right.Rank - 1, 1, curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] <= y; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThanOrEqual(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x <= y[curIndex]; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region LessThanAny - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] < y) - return true; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x < y[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region LessThanOrEqualAny - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] <= y) - return true; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i <= y.FlattenedLength; i++) - { - if (x <= y[curIndex]) - return true; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region LessThanAll - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] >= y) - return false; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x >= y[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - #endregion - - #region LessThanOrEqualAll - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(broadcastedRight.Rank - 1, 1, curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] > y) - return false; - TensorSpanHelpers.AdjustIndexes(x.Rank - 1, 1, curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - - /// - /// Compares the elements of two to see if all elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x > y[curIndex]) - return false; - TensorSpanHelpers.AdjustIndexes(y.Rank - 1, 1, curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } - #endregion - - #region Permute - - /// - /// Swaps the dimensions of the tensor according to the parameter. - /// If is a 1D tensor, it will return . Otherwise it creates a new - /// with the new axis ordering by allocating new memory. - /// - /// Input - /// with the new axis ordering. - public static Tensor PermuteDimensions(this Tensor tensor, params ReadOnlySpan dimensions) - { - if (tensor.Rank == 1) - { - return tensor; - } - else - { - T[] values = tensor.IsPinned ? GC.AllocateArray((int)tensor._flattenedLength) : (new T[tensor._flattenedLength]); - nint[] lengths = new nint[tensor.Rank]; - Tensor outTensor; - TensorSpan ospan; - TensorSpan ispan; - ReadOnlySpan permutation; - - if (dimensions.IsEmpty) - { - int[] tempPermutation = new int[tensor.Rank]; - for (int i = 0; i < tensor.Rank; i++) - { - lengths[i] = tensor._lengths[tensor.Rank - 1 - i]; - tempPermutation[i] = tensor.Rank - 1 - i; - } - - permutation = tempPermutation; - } - else - { - if (dimensions.Length != tensor.Lengths.Length) - ThrowHelper.ThrowArgument_PermuteAxisOrder(); - for (int i = 0; i < lengths.Length; i++) - lengths[i] = tensor.Lengths[dimensions[i]]; - permutation = dimensions.ToArray(); - } - outTensor = new Tensor(values, lengths, Array.Empty(), tensor._memoryOffset, tensor._isPinned); - - ospan = outTensor.AsTensorSpan(); - ispan = tensor.AsTensorSpan(); - - scoped Span indexes; - nint[]? indicesArray; - scoped Span permutedIndices; - nint[]? permutedIndicesArray; - if (outTensor.Rank > 6) - { - indicesArray = ArrayPool.Shared.Rent(outTensor.Rank); - indexes = indicesArray.AsSpan(0, outTensor.Rank); - indexes.Clear(); - - permutedIndicesArray = ArrayPool.Shared.Rent(outTensor.Rank); - permutedIndices = permutedIndicesArray.AsSpan(0, outTensor.Rank); - permutedIndices.Clear(); - } - else - { - indicesArray = null; - indexes = stackalloc nint[outTensor.Rank]; - permutedIndicesArray = null; - permutedIndices = stackalloc nint[outTensor.Rank]; - } - - for (int i = 0; i < tensor._flattenedLength; i++) - { - TensorHelpers.PermuteIndices(indexes, permutedIndices, permutation); - ospan[permutedIndices] = ispan[indexes]; - TensorSpanHelpers.AdjustIndexes(outTensor.Rank - 1, 1, indexes, tensor._lengths); - } - - if (indicesArray != null && permutedIndicesArray != null) - { - ArrayPool.Shared.Return(indicesArray); - ArrayPool.Shared.Return(permutedIndicesArray); - } - - return outTensor; - } - } - #endregion - - #region Reshape - /// - /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. - /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, - /// an exception is thrown. - /// - /// you want to reshape. - /// with the new dimensions. - public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan lengths) - { - if (tensor.Lengths.SequenceEqual(lengths)) - return tensor; - - if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) - { - ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); - } - - nint[] arrLengths = lengths.ToArray(); - // Calculate wildcard info. - if (lengths.Contains(-1)) - { - if (lengths.Count(-1) > 1) - ThrowHelper.ThrowArgument_OnlyOneWildcard(); - nint tempTotal = tensor._flattenedLength; - for (int i = 0; i < lengths.Length; i++) - { - if (lengths[i] != -1) - { - tempTotal /= lengths[i]; - } - } - arrLengths[lengths.IndexOf(-1)] = tempTotal; - - } - - nint tempLinear = TensorSpanHelpers.CalculateTotalLength(arrLengths); - if (tempLinear != tensor.FlattenedLength) - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - - nint[] strides; - - // If we contain a 0 stride we can only add dimensions of length 1. - if (tensor.Strides.Contains(0)) - { - List origStrides = new List(tensor.Strides.ToArray()); - int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) - { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) - lengthOffset++; - else if (arrLengths[i] == 1) - { - if (lengthOffset == tensor.Rank) - origStrides.Add(tensor.Strides[lengthOffset - 1]); - else - origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); - } - else - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - } - strides = origStrides.ToArray(); - } - else - strides = TensorSpanHelpers.CalculateStrides(arrLengths); - - return new Tensor(tensor._values, arrLengths, strides, tensor._memoryOffset); - } - - /// - /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. - /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, - /// an exception is thrown. - /// - /// you want to reshape. - /// with the new dimensions. - public static TensorSpan Reshape(in this TensorSpan tensor, params scoped ReadOnlySpan lengths) - { - if (tensor.Lengths.SequenceEqual(lengths)) - return tensor; - - if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) - { - ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); - } - - nint[] arrLengths = lengths.ToArray(); - // Calculate wildcard info. - if (lengths.Contains(-1)) - { - if (lengths.Count(-1) > 1) - ThrowHelper.ThrowArgument_OnlyOneWildcard(); - nint tempTotal = tensor.FlattenedLength; - for (int i = 0; i < lengths.Length; i++) - { - if (lengths[i] != -1) - { - tempTotal /= lengths[i]; - } - } - arrLengths[lengths.IndexOf(-1)] = tempTotal; - - } - - nint tempLinear = TensorSpanHelpers.CalculateTotalLength(arrLengths); - if (tempLinear != tensor.FlattenedLength) - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - - nint[] strides; - - // If we contain a 0 stride we can only add dimensions of length 1. - if (tensor.Strides.Contains(0)) - { - List origStrides = new List(tensor.Strides.ToArray()); - int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) - { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) - { - lengthOffset++; - } - else if (arrLengths[i] == 1) - { - if (lengthOffset == tensor.Rank) - origStrides.Add(tensor.Strides[lengthOffset - 1]); - else - origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); - } - else - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - } - strides = origStrides.ToArray(); - } - else - strides = TensorSpanHelpers.CalculateStrides(arrLengths); - - TensorSpan output = new TensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape._memoryLength); - return output; - } - - /// - /// Reshapes the tensor to the specified . If one of the lengths is -1, it will be calculated automatically. - /// Does not change the length of the underlying memory nor does it allocate new memory. If the new shape is not compatible with the old shape, - /// an exception is thrown. - /// - /// you want to reshape. - /// with the new dimensions. - public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan tensor, params scoped ReadOnlySpan lengths) - { - if (tensor.Lengths.SequenceEqual(lengths)) - return tensor; - - if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) - { - ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); - } - - nint[] arrLengths = lengths.ToArray(); - // Calculate wildcard info. - if (lengths.Contains(-1)) - { - if (lengths.Count(-1) > 1) - ThrowHelper.ThrowArgument_OnlyOneWildcard(); - nint tempTotal = tensor.FlattenedLength; - for (int i = 0; i < lengths.Length; i++) - { - if (lengths[i] != -1) - { - tempTotal /= lengths[i]; - } - } - arrLengths[lengths.IndexOf(-1)] = tempTotal; - - } - - nint tempLinear = TensorSpanHelpers.CalculateTotalLength(arrLengths); - if (tempLinear != tensor.FlattenedLength) - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - - nint[] strides; - - // If we contain a 0 stride we can only add dimensions of length 1. - if (tensor.Strides.Contains(0)) - { - List origStrides = new List(tensor.Strides.ToArray()); - int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) - { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) - lengthOffset++; - else if (arrLengths[i] == 1) - { - if (lengthOffset == tensor.Rank) - origStrides.Add(tensor.Strides[lengthOffset - 1]); - else - origStrides.Insert(i, tensor.Strides[i] * tensor.Lengths[i]); - } - else - ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); - } - strides = origStrides.ToArray(); - } - else - strides = TensorSpanHelpers.CalculateStrides(arrLengths); - - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape._memoryLength); - return output; - } - #endregion - - #region Resize - /// - /// Creates a new , allocates new memory, and copies the data from . If the final shape is smaller all data after - /// that point is ignored. - /// - /// Input . - /// of the desired new shape. - public static Tensor Resize(Tensor tensor, ReadOnlySpan lengths) - { - nint newSize = TensorSpanHelpers.CalculateTotalLength(lengths); - T[] values = tensor.IsPinned ? GC.AllocateArray((int)newSize) : (new T[newSize]); - Tensor output = new Tensor(values, lengths, tensor._memoryOffset, isPinned: false); - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor.AsTensorSpan()._reference, (int)tensor._values.Length); - Span ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength); - if (newSize > tensor._values.Length) - TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); - else - TensorSpanHelpers.Memmove(ospan, span, newSize); - - return output; - } - - /// - /// Copies the data from . If the final shape is smaller all data after that point is ignored. - /// If the final shape is bigger it is filled with 0s. - /// - /// Input . - /// Destination with the desired new shape. - public static void ResizeTo(scoped in Tensor tensor, in TensorSpan destination) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._values[0], tensor._values.Length); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - if (destination._shape._memoryLength > tensor._values.Length) - TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); - else - TensorSpanHelpers.Memmove(ospan, span, destination._shape._memoryLength); - } - - /// - /// Copies the data from . If the final shape is smaller all data after that point is ignored. - /// If the final shape is bigger it is filled with 0s. - /// - /// Input . - /// Destination with the desired new shape. - public static void ResizeTo(scoped in TensorSpan tensor, in TensorSpan destination) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - if (destination._shape._memoryLength > tensor._shape._memoryLength) - TensorSpanHelpers.Memmove(ospan, span, tensor._shape._memoryLength); - else - TensorSpanHelpers.Memmove(ospan, span, destination._shape._memoryLength); - } - - /// - /// Copies the data from . If the final shape is smaller all data after that point is ignored. - /// If the final shape is bigger it is filled with 0s. - /// - /// Input . - /// Destination with the desired new shape. - public static void ResizeTo(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - if (destination._shape._memoryLength > tensor._shape._memoryLength) - TensorSpanHelpers.Memmove(ospan, span, tensor._shape._memoryLength); - else - TensorSpanHelpers.Memmove(ospan, span, destination._shape._memoryLength); - } - #endregion - - #region Reverse - /// - /// Reverse the order of elements in the . The shape of the tensor is preserved, but the elements are reordered. - /// - /// Input . - public static Tensor Reverse(in ReadOnlyTensorSpan tensor) - { - Tensor output = Tensor.Create(tensor.Lengths); - ReverseDimension(tensor, output, -1); - - return output; - } - - /// - /// Reverse the order of elements in the along the given dimension. The shape of the tensor is preserved, but the elements are reordered. - /// defaults to -1 when not provided, which reverses the entire tensor. - /// - /// Input . - /// dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor. - public static Tensor ReverseDimension(in ReadOnlyTensorSpan tensor, int dimension) - { - Tensor output = Tensor.Create(tensor.Lengths); - ReverseDimension(tensor, output, dimension); - - return output; - } - - /// - /// Reverse the order of elements in the . The shape of the tensor is preserved, but the elements are reordered. - /// - /// Input . - /// - public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination) - { - return ref ReverseDimension(tensor, destination, -1); - } - - /// - /// Reverse the order of elements in the along the given axis. The shape of the tensor is preserved, but the elements are reordered. - /// defaults to -1 when not provided, which reverses the entire span. - /// - /// Input . - /// - /// dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor. - public static ref readonly TensorSpan ReverseDimension(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination, int dimension) - { - if (dimension == -1) - { - nint index = tensor._shape._memoryLength - 1; - Span inputSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); - Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - for (int i = 0; i <= tensor._shape._memoryLength / 2; i++) - { - outputSpan[i] = inputSpan[(int)index]; - outputSpan[(int)index--] = inputSpan[i]; - } - } - else - { - nint copyLength = 1; - for (nint i = dimension; i < tensor.Lengths.Length; i++) - { - copyLength *= tensor.Lengths[(int)i]; - } - copyLength /= tensor.Lengths[(int)dimension]; - - scoped Span oIndices; - nint[]? oIndicesArray; - scoped Span iIndices; - nint[]? iIndicesArray; - if (tensor.Rank > 6) - { - oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - oIndices = oIndicesArray.AsSpan(0, tensor.Rank); - oIndices.Clear(); - - iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - iIndices = iIndicesArray.AsSpan(0, tensor.Rank); - iIndices.Clear(); - } - else - { - oIndicesArray = null; - oIndices = stackalloc nint[tensor.Rank]; - iIndicesArray = null; - iIndices = stackalloc nint[tensor.Rank]; - } - - iIndices[(int)dimension] = tensor.Lengths[(int)dimension] - 1; - nint copiedValues = 0; - ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); - - while (copiedValues < tensor.FlattenedLength) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destination._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, tensor.Strides, tensor.Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, tensor.Lengths); - TensorSpanHelpers.AdjustIndexesDown((int)dimension, 1, iIndices, tensor.Lengths); - copiedValues += copyLength; - } - - if (oIndicesArray != null && iIndicesArray != null) - { - ArrayPool.Shared.Return(oIndicesArray); - ArrayPool.Shared.Return(iIndicesArray); - } - } - - return ref destination; - } - #endregion - - #region SequenceEqual - /// - /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). - /// - public static bool SequenceEqual(this scoped in TensorSpan tensor, scoped in ReadOnlyTensorSpan other) - where T : IEquatable? - { - return tensor.FlattenedLength == other.FlattenedLength - && tensor._shape._memoryLength == other._shape._memoryLength - && tensor.Lengths.SequenceEqual(other.Lengths) - && MemoryMarshal.CreateReadOnlySpan(in tensor.GetPinnableReference(), (int)tensor._shape._memoryLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other._shape._memoryLength)); - } - - /// - /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). - /// - public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan tensor, scoped in ReadOnlyTensorSpan other) - where T : IEquatable? - { - return tensor.FlattenedLength == other.FlattenedLength - && tensor._shape._memoryLength == other._shape._memoryLength - && tensor.Lengths.SequenceEqual(other.Lengths) - && MemoryMarshal.CreateReadOnlySpan(in tensor.GetPinnableReference(), (int)tensor._shape._memoryLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other._shape._memoryLength)); - } - #endregion - - #region SetSlice - /// - /// Sets a slice of the given with the provided for the given - /// - /// Input . - /// The values you want to set in the . - /// The ranges you want to set. - public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan values, params ReadOnlySpan ranges) - { - SetSlice((TensorSpan)tensor, values, ranges); - - return tensor; - } - - /// - /// Sets a slice of the given with the provided for the given - /// - /// Input . - /// The values you want to set in the . - /// The ranges you want to set. - public static ref readonly TensorSpan SetSlice(this in TensorSpan tensor, scoped in ReadOnlyTensorSpan values, params scoped ReadOnlySpan ranges) - { - TensorSpan srcSpan; - if (ranges == ReadOnlySpan.Empty) - { - if (!TensorHelpers.IsBroadcastableTo(values.Lengths, tensor.Lengths)) - ThrowHelper.ThrowArgument_SetSliceNoRange(nameof(values)); - srcSpan = tensor; - } - else - srcSpan = tensor.Slice(ranges); - - if (!TensorHelpers.IsContiguousAndDense(srcSpan)) - ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); - - if (!TensorHelpers.IsBroadcastableTo(values.Lengths, srcSpan.Lengths)) - ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); - - values.CopyTo(srcSpan); - - return ref tensor; - } - #endregion - - #region Split - /// - /// Split a into along the given . If the tensor cannot be split - /// evenly on the given an exception is thrown. - /// - /// Input . - /// How many times to split the - /// The axis to split on. - public static Tensor[] Split(scoped in ReadOnlyTensorSpan tensor, int splitCount, nint dimension) - { - if (tensor.Lengths[(int)dimension] % splitCount != 0) - ThrowHelper.ThrowArgument_SplitNotSplitEvenly(); - - Tensor[] outputs = new Tensor[splitCount]; - - nint totalToCopy = tensor.FlattenedLength / splitCount; - nint copyLength = 1; - for (nint i = dimension; i < tensor.Lengths.Length; i++) - { - copyLength *= tensor.Lengths[(int)i]; - } - copyLength /= splitCount; - nint[] newShape = tensor.Lengths.ToArray(); - newShape[(int)dimension] = newShape[(int)dimension] / splitCount; - - scoped Span oIndices; - nint[]? oIndicesArray; - scoped Span iIndices; - nint[]? iIndicesArray; - if (tensor.Rank > 6) - { - oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - oIndices = oIndicesArray.AsSpan(0, tensor.Rank); - oIndices.Clear(); - - iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - iIndices = iIndicesArray.AsSpan(0, tensor.Rank); - iIndices.Clear(); - } - else - { - oIndicesArray = null; - oIndices = stackalloc nint[tensor.Rank]; - iIndicesArray = null; - iIndices = stackalloc nint[tensor.Rank]; - } - - for (int i = 0; i < outputs.Length; i++) - { - T[] values = new T[(int)totalToCopy]; - outputs[i] = new Tensor(values, newShape, memoryOffset: 0); - oIndices.Clear(); - iIndices.Clear(); - - iIndices[(int)dimension] = i; - ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); - TensorSpan oslice = outputs[i].AsTensorSpan().Slice(outputs[i]._lengths); - - nint copiedValues = 0; - while (copiedValues < totalToCopy) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref oslice._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, outputs[0].Strides, outputs[0].Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, outputs[i]._lengths); - TensorSpanHelpers.AdjustIndexes((int)dimension - 1, 1, iIndices, tensor.Lengths); - copiedValues += copyLength; - } - } - - if (oIndicesArray != null && iIndicesArray != null) - { - ArrayPool.Shared.Return(oIndicesArray); - ArrayPool.Shared.Return(iIndicesArray); - } - - return outputs; - } - #endregion - - #region Squeeze - /// - /// Removes all dimensions of length one from the . - /// - /// The to remove all dimensions of length 1. - public static Tensor Squeeze(this Tensor tensor) - { - return SqueezeDimension(tensor, -1); - } - - /// - /// Removes axis of length one from the for the given . - /// If the dimension is not of length one it will throw an exception. - /// - /// The to remove dimension of length 1. - /// The dimension to remove. - public static Tensor SqueezeDimension(this Tensor tensor, int dimension) - { - if (dimension >= tensor.Rank) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - - nint[] lengths; - nint[] strides; - - List tempLengths = new List(); - if (dimension == -1) - { - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (tensor.Lengths[i] != 1) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - else - { - if (tensor.Lengths[dimension] != 1) - { - ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); - } - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (i != dimension) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - - return new Tensor(tensor._values, lengths, strides, tensor._memoryOffset); - } - - /// - /// Removes all dimensions of length one from the . - /// - /// The to remove all dimensions of length 1. - public static TensorSpan Squeeze(in this TensorSpan tensor) - { - return SqueezeDimension(tensor, -1); - } - - /// - /// Removes axis of length one from the for the given . - /// If the dimension is not of length one it will throw an exception. - /// - /// The to remove dimension of length 1. - /// The dimension to remove. - public static TensorSpan SqueezeDimension(in this TensorSpan tensor, int dimension) - { - if (dimension >= tensor.Rank) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - - nint[] lengths; - nint[] strides; - - List tempLengths = new List(); - if (dimension == -1) - { - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (tensor.Lengths[i] != 1) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - else - { - if (tensor.Lengths[dimension] != 1) - { - ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); - } - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (i != dimension) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - - return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape._memoryLength); - } - - /// - /// Removes all dimensions of length one from the . - /// - /// The to remove all dimensions of length 1. - public static ReadOnlyTensorSpan Squeeze(in this ReadOnlyTensorSpan tensor) - { - return SqueezeDimension(tensor, -1); - } - - /// - /// Removes axis of length one from the for the given . - /// If the dimension is not of length one it will throw an exception. - /// - /// The to remove dimension of length 1. - /// The dimension to remove. - public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSpan tensor, int dimension) - { - if (dimension >= tensor.Rank) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - - nint[] lengths; - nint[] strides; - - List tempLengths = new List(); - if (dimension == -1) - { - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (tensor.Lengths[i] != 1) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - else - { - if (tensor.Lengths[dimension] != 1) - { - ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); - } - for (int i = 0; i < tensor.Lengths.Length; i++) - { - if (i != dimension) - { - tempLengths.Add(tensor.Lengths[i]); - } - } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); - } - - return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape._memoryLength); - } - #endregion - - #region Stack - /// - /// Join multiple along a new dimension that is added at position 0. All tensors must have the same shape. - /// - /// Input . - public static Tensor Stack(params ReadOnlySpan> tensors) - { - return StackAlongDimension(0, tensors); - } - - /// - /// Join multiple along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have the same shape. - /// - /// Input . - /// Index of where the new dimension will be. - public static Tensor StackAlongDimension(int dimension, params ReadOnlySpan> tensors) - { - if (tensors.Length < 2) - ThrowHelper.ThrowArgument_StackTooFewTensors(); - - for (int i = 1; i < tensors.Length; i++) - { - if (!TensorHelpers.AreLengthsTheSame(tensors[0], tensors[i])) - ThrowHelper.ThrowArgument_StackShapesNotSame(); - } - - if (dimension < 0) - dimension = tensors[0].Rank - dimension; - - Tensor[] outputs = new Tensor[tensors.Length]; - for (int i = 0; i < tensors.Length; i++) - { - outputs[i] = Tensor.Unsqueeze(tensors[i], dimension); - } - return Tensor.ConcatenateOnDimension(dimension, outputs); - } - - /// - /// Join multiple along a new dimension that is added at position 0. All tensors must have the same shape. - /// - /// Input . - /// - public static ref readonly TensorSpan Stack(scoped in ReadOnlySpan> tensors, in TensorSpan destination) - { - return ref StackAlongDimension(tensors, destination, 0); - } - - /// - /// Join multiple along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have the same shape. - /// - /// Input . - /// - /// Index of where the new dimension will be. - public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlySpan> tensors, in TensorSpan destination, int dimension) - { - if (tensors.Length < 2) - ThrowHelper.ThrowArgument_StackTooFewTensors(); - - for (int i = 1; i < tensors.Length; i++) - { - if (!TensorHelpers.AreLengthsTheSame(tensors[0], tensors[i])) - ThrowHelper.ThrowArgument_StackShapesNotSame(); - } - - if (dimension < 0) - dimension = tensors[0].Rank - dimension; - - Tensor[] outputs = new Tensor[tensors.Length]; - for (int i = 0; i < tensors.Length; i++) - { - outputs[i] = Tensor.Unsqueeze(tensors[i], dimension); - } - return ref Tensor.ConcatenateOnDimension(dimension, outputs, destination); - } - #endregion - - #region ToString - /// - /// Creates a representation of the ."/> - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - /// A representation of the - public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) => - ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); - - /// - /// Creates a representation of the ."/> - /// - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) - { - StringBuilder sb = new(); - ToString(in tensor, sb, maximumLengths); - return sb.ToString(); - } - - internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBuilder sb, params ReadOnlySpan maximumLengths) - { - if (maximumLengths.Length != tensor.Rank) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); - - scoped Span curIndexes; - nint[]? curIndexesArray; - if (tensor.Rank > 6) - { - curIndexesArray = ArrayPool.Shared.Rent(tensor.Rank); - curIndexes = curIndexesArray.AsSpan(0, tensor.Rank); - curIndexes.Clear(); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[tensor.Rank]; - } - - nint copiedValues = 0; - - T[] values = new T[tensor.Lengths[tensor.Rank - 1]]; - while (copiedValues < tensor.FlattenedLength) - { - var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, tensor.Strides, tensor.Lengths)), [tensor.Lengths[tensor.Rank - 1]], [1], tensor.Lengths[tensor.Rank - 1]); - sb.Append('{'); - sp.FlattenTo(values); - sb.Append(string.Join(",", values)); - sb.AppendLine("}"); - - TensorSpanHelpers.AdjustIndexes(tensor.Rank - 2, 1, curIndexes, tensor.Lengths); - copiedValues += tensor.Lengths[tensor.Rank - 1]; - } - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - - /// - /// Creates a representation of the ."/> - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - /// A representation of the - public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); - - #endregion - - #region Transpose - /// - /// Swaps the last two dimensions of the tensor. - /// - /// Input . - public static Tensor Transpose(Tensor tensor) - { - if (tensor.Lengths.Length < 2) - ThrowHelper.ThrowArgument_TransposeTooFewDimensions(); - - Span dimension = tensor.Rank <= TensorShape.MaxInlineRank ? stackalloc int[tensor.Rank] : new int[tensor.Rank]; - TensorSpanHelpers.FillRange(dimension); - - int temp = dimension[tensor.Rank - 1]; - dimension[tensor.Rank - 1] = dimension[tensor.Rank - 2]; - dimension[tensor.Rank - 2] = temp; - - return PermuteDimensions(tensor, dimension); - } - #endregion - - #region TryBroadcastTo - /// - /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in - /// If the shapes are not compatible, false is returned. - /// - /// Input . - /// Destination . - public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan destination) - { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); - } - - /// - /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in - /// If the shapes are not compatible, false is returned. - /// - /// Input . - /// Destination . - public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan destination) - { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); - } - - /// - /// Broadcast the data from to the smallest broadcastable shape compatible with and stores it in - /// If the shapes are not compatible, false is returned. - /// - /// Input . - /// Destination . - public static bool TryBroadcastTo(in this ReadOnlyTensorSpan tensor, in TensorSpan destination) - { - if (!TensorHelpers.IsBroadcastableTo(tensor.Lengths, destination.Lengths)) - return false; - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(tensor.Lengths, destination.Lengths); - if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, newSize)) - return false; - - LazyBroadcast(tensor, newSize).CopyTo(destination); - return true; - } - #endregion - - #region Unsqueeze - /// - /// Insert a new dimension of length 1 that will appear at the dimension position. - /// - /// The to add a dimension of length 1. - /// The index of the dimension to add. - public static Tensor Unsqueeze(this Tensor tensor, int dimension) - { - if (dimension > tensor.Lengths.Length) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - if (dimension < 0) - dimension = tensor.Rank - dimension; - - Span lengths = tensor._lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor._lengths.Length + 1] : - new nint[tensor._lengths.Length + 1]; - tensor._lengths.AsSpan(0, dimension).CopyTo(lengths); - tensor._lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; - - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; - if (dimension == tensor.Rank) - { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; - } - else - { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; - } - - return new Tensor(tensor._values, lengths, strides, tensor._memoryOffset); - } - - /// - /// Insert a new dimension of length 1 that will appear at the dimension position. - /// - /// The to add a dimension of length 1. - /// The index of the dimension to add. - public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimension) - { - if (dimension > tensor.Lengths.Length) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - if (dimension < 0) - dimension = tensor.Rank - dimension; - - Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Lengths.Length + 1] : - new nint[tensor.Lengths.Length + 1]; - tensor.Lengths.Slice(0, dimension).CopyTo(lengths); - tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; - - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; - if (dimension == tensor.Rank) - { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; - } - else - { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; - } - - return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape._memoryLength); - } - - /// - /// Insert a new dimension of length 1 that will appear at the dimension position. - /// - /// The to add a dimension of length 1. - /// The index of the dimension to add. - public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan tensor, int dimension) - { - if (dimension > tensor.Lengths.Length) - ThrowHelper.ThrowArgument_AxisLargerThanRank(); - if (dimension < 0) - dimension = tensor.Rank - dimension; - - Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Lengths.Length + 1] : - new nint[tensor.Lengths.Length + 1]; - tensor.Lengths.Slice(0, dimension).CopyTo(lengths); - tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; - - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; - if (dimension == tensor.Rank) - { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; - } - else - { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; - } - - return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape._memoryLength); - } - #endregion - - #region TensorPrimitives - #region Abs - /// - /// Takes the absolute value of each element of the and returns a new with the result. - /// - /// The to take the abs of. - public static Tensor Abs(in ReadOnlyTensorSpan x) - where T : INumberBase - { - Tensor output = Tensor.Create(x.Lengths); - Abs(x, output); - return output; - } - - /// - /// Takes the absolute value of each element of the and returns a new with the result. - /// - /// The to take the abs of. - /// The destination. - public static ref readonly TensorSpan Abs(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : INumberBase - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Abs); - } - #endregion - - #region Acos - /// - /// Takes the inverse cosine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor Acos(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Acos(x, output); - return output; - } - - /// - /// Takes the inverse cosine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan Acos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acos); - } - #endregion - - #region Acosh - /// - /// Takes the inverse hyperbolic cosine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor Acosh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Acosh(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic cosine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan Acosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acosh); - } - #endregion - - #region AcosPi - /// - /// Takes the inverse hyperbolic cosine divided by pi of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor AcosPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - AcosPi(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic cosine divided by pi of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan AcosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AcosPi); - } - #endregion - - #region Add - /// - /// Adds each element of to each element of and returns a new with the result. - /// - /// The of values to add. - /// The second of values to add. - public static Tensor Add(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IAdditionOperators, IAdditiveIdentity - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Add(x, y, output); - return output; - } - - /// - /// Adds to each element of and returns a new with the result. - /// - /// The of values to add. - /// The to add to each element of . - public static Tensor Add(in ReadOnlyTensorSpan x, T y) - where T : IAdditionOperators, IAdditiveIdentity - { - Tensor output = Tensor.Create(x.Lengths); - Add(x, y, output); - return output; - } - - /// - /// Adds each element of to each element of and returns a new with the result. - /// - /// The of values to add. - /// The second of values to add. - /// - public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IAdditionOperators, IAdditiveIdentity - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Add); - } - - /// - /// Adds to each element of and returns a new with the result. - /// - /// The of values to add. - /// The to add to each element of . - /// - public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IAdditionOperators, IAdditiveIdentity - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Add); - } - #endregion - - #region Asin - /// - /// Takes the inverse sin of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor Asin(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Asin(x, output); - return output; - } - - /// - /// Takes the inverse sin of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan Asin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asin); - } - #endregion - - #region Asinh - /// - /// Takes the inverse hyperbolic sine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor Asinh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Asinh(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic sine of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan Asinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asinh); - } - #endregion - - #region AsinPi - /// - /// Takes the inverse hyperbolic sine divided by pi of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor AsinPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - AsinPi(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic sine divided by pi of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan AsinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AsinPi); - } - #endregion - - #region Atan - /// - /// Takes the arc tangent of each element of the and returns a new with the result. - /// - /// The input - public static Tensor Atan(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Atan(x, output); - return output; - } - - /// - /// Takes the arc tangent of each element of the and returns a new with the result. - /// - /// The input - /// - public static ref readonly TensorSpan Atan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atan); - } - #endregion - - #region Atan2 - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Atan2(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); - } - - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2(in ReadOnlyTensorSpan x, T y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(x.Lengths); - - Atan2(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2); - } - - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2(T x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(y.Lengths); - - Atan2(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); - } - #endregion - - #region Atan2Pi - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Atan2Pi(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); - } - - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, T y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(x.Lengths); - - Atan2Pi(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); - } - - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor Atan2Pi(T x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(y.Lengths); - - Atan2Pi(x, y, output); - return output; - } - - /// - /// Takes the arc tangent of the two input , divides each element by pi, and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Atan2Pi(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); - - } - #endregion - - #region Atanh - /// - /// Takes the inverse hyperbolic tangent of each element of the and returns a new with the result. - /// - /// The input . - public static Tensor Atanh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Atanh(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic tangent of each element of the and returns a new with the result. - /// - /// The input . - /// - public static ref readonly TensorSpan Atanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atanh); - } - #endregion - - #region AtanPi - /// - /// Takes the inverse hyperbolic tangent divided by pi of each element of the and returns a new with the result. - /// - /// The input. - public static Tensor AtanPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - AtanPi(x, output); - return output; - } - - /// - /// Takes the inverse hyperbolic tangent divided by pi of each element of the and returns a new with the result. - /// - /// The input. - /// - public static ref readonly TensorSpan AtanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AtanPi); - } - #endregion - - #region Average - /// - /// Returns the average of the elements in the tensor. - /// - /// The to take the mean of. - /// representing the mean. - public static T Average(scoped in ReadOnlyTensorSpan x) - where T : INumberBase - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Average); - } - #endregion - - #region BitwiseAnd - /// - /// Computes the element-wise bitwise and of the two input and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IBitwiseOperators - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - BitwiseAnd(x, y, output); - return output; - } - - /// - /// Computes the element-wise bitwise and of the two input and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); - } - - /// - /// Computes the element-wise bitwise and of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, T y) - where T : IBitwiseOperators - { - Tensor output = Tensor.Create(x.Lengths); - - BitwiseAnd(x, y, output); - return output; - } - - /// - /// Computes the element-wise bitwise and of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - /// - public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); - } - #endregion - - #region BitwiseOr - /// - /// Computes the element-wise bitwise of of the two input and returns a new with the result. - /// - /// The left . - /// The right . - public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IBitwiseOperators - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - BitwiseOr(x, y, output); - return output; - } - - /// - /// Computes the element-wise bitwise of of the two input and returns a new with the result. - /// - /// The left . - /// The right . - /// - public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); - } - - /// - /// Computes the element-wise bitwise or of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, T y) - where T : IBitwiseOperators - { - Tensor output = Tensor.Create(x.Lengths); - - BitwiseOr(x, y, output); - return output; - } - - /// - /// Computes the element-wise bitwise or of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - /// - public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); - } - #endregion - - #region CubeRoot - /// - /// Computes the element-wise cube root of the input and returns a new with the result. - /// - /// The left . - public static Tensor Cbrt(in ReadOnlyTensorSpan x) - where T : IRootFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Cbrt(x, output); - return output; - } - - /// - /// Computes the element-wise cube root of the input and returns a new with the result. - /// - /// The left . - /// - public static ref readonly TensorSpan Cbrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IRootFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cbrt); - } - #endregion - - #region Ceiling - /// - /// Computes the element-wise ceiling of the input and returns a new with the result. - /// - /// The left . - public static Tensor Ceiling(in ReadOnlyTensorSpan x) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Ceiling(x, output); - return output; - } - - /// - /// Computes the element-wise ceiling of the input and returns a new with the result. - /// - /// The left . - /// - public static ref readonly TensorSpan Ceiling(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPoint - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Ceiling); - } - #endregion - - #region ConvertChecked - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - public static Tensor ConvertChecked(in ReadOnlyTensorSpan source) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - Tensor output = Tensor.Create(source.Lengths); - - ConvertChecked(source, output); - return output; - } - - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - /// - public static ref readonly TensorSpan ConvertChecked(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertChecked); - } - #endregion - - #region ConvertSaturating - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - public static Tensor ConvertSaturating(in ReadOnlyTensorSpan source) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - Tensor output = Tensor.Create(source.Lengths); - - ConvertSaturating(source, output); - return output; - } - - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - /// - public static ref readonly TensorSpan ConvertSaturating(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertSaturating); - } - #endregion - - #region ConvertTruncating - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - public static Tensor ConvertTruncating(in ReadOnlyTensorSpan source) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - Tensor output = Tensor.Create(source.Lengths); - - ConvertTruncating(source, output); - return output; - } - - /// - /// Copies to a new converting each - /// value to a value. - /// - /// The input . - /// - public static ref readonly TensorSpan ConvertTruncating(scoped in ReadOnlyTensorSpan source, in TensorSpan destination) - where TFrom : IEquatable, IEqualityOperators, INumberBase - where TTo : INumberBase - { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertTruncating); - } - #endregion - - #region CopySign - /// - /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new tensor with the result. - /// - /// Input . - /// The number with the associated sign. - public static Tensor CopySign(in ReadOnlyTensorSpan x, T sign) - where T : INumber - { - Tensor output = Create(x.Lengths); - - CopySign(x, sign, output); - return output; - } - - /// - /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new with the result. - /// - /// Input . - /// The with the associated signs. - public static Tensor CopySign(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan sign) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(sign.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, sign.Lengths)); - } - - CopySign(x, sign, output); - return output; - } - - /// - /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new tensor with the result. - /// - /// Input . - /// The number with the associated sign. - /// - public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, T sign, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, sign, destination, TensorPrimitives.CopySign); - } - - /// - /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new with the result. - /// - /// Input . - /// The with the associated signs. - /// - public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan sign, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, sign, destination, TensorPrimitives.CopySign); - } - #endregion - - #region Cos - /// - /// Takes the cosine of each element of the and returns a new with the result. - /// - /// The to take the cosine of. - public static Tensor Cos(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Cos(x, output); - return output; - } - - /// - /// Takes the cosine of each element of the and returns a new with the result. - /// - /// The to take the cosine of. - /// - public static ref readonly TensorSpan Cos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cos); - } - #endregion - - #region Cosh - /// - /// Takes the hyperbolic cosine of each element of the and returns a new with the result. - /// - /// The to take the cosine of. - public static Tensor Cosh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Cosh(x, output); - return output; - } - - /// - /// Takes the hyperbolic cosine of each element of the and returns a new with the result. - /// - /// The to take the cosine of. - /// - public static ref readonly TensorSpan Cosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cosh); - } - #endregion - - #region CosineSimilarity - /// - /// Compute cosine similarity between and . - /// - /// The first - /// The second - public static Tensor CosineSimilarity(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IRootFunctions - { - if (x.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); - - if (y.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); - - if (x.Lengths[1] != y.Lengths[1]) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - nint dim1 = x.Lengths[0]; - nint dim2 = y.Lengths[0]; - - T[] values = new T[dim1 * dim2]; - - Tensor output = Tensor.Create(values, [dim1, dim2]); - - CosineSimilarity(x, y, output); - - return output; - } - - /// - /// Compute cosine similarity between and . - /// - /// The first - /// The second - /// - public static ref readonly TensorSpan CosineSimilarity(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IRootFunctions - { - if (x.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); - - if (y.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); - - if (x.Lengths[1] != y.Lengths[1]) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - nint dim1 = x.Lengths[0]; - nint dim2 = y.Lengths[0]; - - if (destination.Lengths[0] != dim1 || destination.Lengths[1] != dim2) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - Span values = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - - scoped Span leftIndexes = stackalloc nint[2]; - scoped Span rightIndexes = stackalloc nint[2]; - - int outputOffset = 0; - - ReadOnlySpan lspan; - ReadOnlySpan rspan; - int rowLength = (int)x.Lengths[1]; - for (int i = 0; i < dim1; i++) - { - for (int j = 0; j < dim2; j++) - { - lspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref x._reference, TensorSpanHelpers.ComputeLinearIndex(leftIndexes, x.Strides, x.Lengths)), (int)rowLength); - rspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref y._reference, TensorSpanHelpers.ComputeLinearIndex(rightIndexes, y.Strides, y.Lengths)), (int)rowLength); - values[outputOffset++] = TensorPrimitives.CosineSimilarity(lspan, rspan); - rightIndexes[0]++; - } - rightIndexes[0] = 0; - leftIndexes[0]++; - } - - return ref destination; - - } - #endregion - - #region CosPi - /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new with the results. - /// The input - /// - /// - /// This method effectively computes .CosPi([i]). - /// - /// - /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. - /// - /// - public static Tensor CosPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - CosPi(x, output); - return output; - } - - /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new with the results. - /// The input - /// - /// - /// - /// This method effectively computes .CosPi([i]). - /// - /// - /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. - /// - /// - public static ref readonly TensorSpan CosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.CosPi); - } - #endregion - - #region DegreesToRadians - /// - /// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results. - /// - /// The input . - public static Tensor DegreesToRadians(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - DegreesToRadians(x, output); - return output; - } - - /// - /// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results. - /// - /// The input . - /// - public static ref readonly TensorSpan DegreesToRadians(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.DegreesToRadians); - } - #endregion - - #region Distance - /// - /// Computes the distance between two points, specified as non-empty, equal-length tensors of numbers, in Euclidean space. - /// - /// The input . - /// The input . - public static T Distance(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y) - where T : IRootFunctions - { - return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Distance); - } - #endregion - - #region Divide - /// - /// Divides each element of by and returns a new with the result. - /// - /// Input . - /// The divisor - public static Tensor Divide(in ReadOnlyTensorSpan x, T y) - where T : IDivisionOperators - { - Tensor output = Create(x.Lengths); - Divide(x, y, output); - return output; - } - - /// - /// Divides by each element of and returns a new with the result."/> - /// - /// The value to be divided. - /// The divisor. - public static Tensor Divide(T x, in ReadOnlyTensorSpan y) - where T : IDivisionOperators - { - Tensor output = Tensor.Create(y.Lengths); - Divide(x, y, output); - return output; - } - - /// - /// Divides each element of by its corresponding element in and returns - /// a new with the result. - /// - /// The to be divided. - /// The divisor. - public static Tensor Divide(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IDivisionOperators - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Divide(x, y, output); - return output; - } - - /// - /// Divides each element of by and returns a new with the result. - /// - /// Input . - /// The divisor - /// - public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IDivisionOperators - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Divide); - } - - /// - /// Divides by each element of and returns a new with the result."/> - /// - /// The value to be divided. - /// The divisor. - /// - public static ref readonly TensorSpan Divide(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IDivisionOperators - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); - } - - /// - /// Divides each element of by its corresponding element in and returns - /// a new with the result. - /// - /// The to be divided. - /// The divisor. - /// - public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IDivisionOperators - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); - } - #endregion - - #region Dot - /// - /// Computes the dot product of two tensors containing numbers. - /// - /// The input . - /// The input . - public static T Dot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IAdditionOperators, IAdditiveIdentity, IMultiplicativeIdentity, IMultiplyOperators - { - return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Dot); - } - #endregion - - #region Exp - /// - /// Computes the element-wise result of raising e to the single-precision floating-point number powers in the specified tensor. - /// - /// The input . - public static Tensor Exp(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Exp(x, output); - return output; - } - - /// - /// Computes the element-wise result of raising e to the single-precision floating-point number powers in the specified tensor. - /// - /// The input . - /// - public static ref readonly TensorSpan Exp(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp); - } - #endregion - - #region Exp10 - /// - /// Computes the element-wise result of raising 10 to the number powers in the specified tensor. - /// - /// The input . - public static Tensor Exp10(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Exp10(x, output); - return output; - } - - /// - /// Computes the element-wise result of raising 10 to the number powers in the specified tensor. - /// - /// The input . - /// - public static ref readonly TensorSpan Exp10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10); - } - #endregion - - #region Exp10M1 - /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. - /// The input . - public static Tensor Exp10M1(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Exp10M1(x, output); - return output; - } - - /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. - /// The input . - /// - public static ref readonly TensorSpan Exp10M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10M1); - } - #endregion - - #region Exp2 - /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. - /// The input . - public static Tensor Exp2(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Exp2(x, output); - return output; - } - - /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan Exp2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2); - } - #endregion - - #region Exp2M1 - /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. - /// The input . - public static Tensor Exp2M1(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Exp2M1(x, output); - return output; - } - - /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. - /// The input . - /// - public static ref readonly TensorSpan Exp2M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2M1); - } - #endregion - - #region ExpM1 - /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. - /// The input . - public static Tensor ExpM1(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - ExpM1(x, output); - return output; - } - - /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. - /// The input . - /// - public static ref readonly TensorSpan ExpM1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ExpM1); - } - #endregion - - #region Floor - /// Computes the element-wise floor of numbers in the specified tensor. - /// The input . - public static Tensor Floor(in ReadOnlyTensorSpan x) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Floor(x, output); - return output; - } - - /// Computes the element-wise floor of numbers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan Floor(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPoint - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Floor); - } - #endregion - - #region Hypotenuse - /// - /// Computes the element-wise hypotenuse given values from two tensors representing the lengths of the shorter sides in a right-angled triangle. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// - /// Left . - /// Right . - public static Tensor Hypot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IRootFunctions - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Hypot(x, y, output); - return output; - } - - /// - /// Computes the element-wise hypotenuse given values from two tensors representing the lengths of the shorter sides in a right-angled triangle. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// - /// Left . - /// Right . - /// - public static ref readonly TensorSpan Hypot(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IRootFunctions - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Hypot); - } - #endregion - - #region Ieee754Remainder - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// Left . - /// Right . - public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Ieee754Remainder(x, y, output); - return output; - } - - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// Left . - /// Right . - /// - public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); - } - - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// The left . - /// The right . - public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, T y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(x.Lengths); - - Ieee754Remainder(x, y, output); - return output; - } - - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); - } - - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// The left . - /// The right . - public static Tensor Ieee754Remainder(T x, in ReadOnlyTensorSpan y) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(y.Lengths); - - Ieee754Remainder(x, y, output); - return output; - } - - /// Computes the element-wise remainder of the numbers in the specified tensors. - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Ieee754Remainder(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); - } - #endregion - - #region ILogB - /// Computes the element-wise integer logarithm of numbers in the specified tensor. - /// The input . - public static Tensor ILogB(in ReadOnlyTensorSpan x) - where T : IFloatingPointIeee754 - { - Tensor output = Tensor.Create(x.Lengths, x.Strides); - ILogB(x, output); - return output; - } - - /// Computes the element-wise integer logarithm of numbers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan ILogB(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPointIeee754 - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ILogB); - } - #endregion - - #region IndexOfMax - /// Searches for the index of the largest number in the specified tensor. - /// The input . - public static int IndexOfMax(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - return TensorPrimitives.IndexOfMax(span); - } - #endregion - - #region IndexOfMaxMagnitude - /// Searches for the index of the number with the largest magnitude in the specified tensor. - /// The input . - public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - return TensorPrimitives.IndexOfMaxMagnitude(span); - } - #endregion - - #region IndexOfMin - /// Searches for the index of the smallest number in the specified tensor. - /// The input . - public static int IndexOfMin(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - return TensorPrimitives.IndexOfMin(span); - } - #endregion - - #region IndexOfMinMagnitude - /// - /// Searches for the index of the number with the smallest magnitude in the specified tensor. - /// - /// The input . - public static int IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - return TensorPrimitives.IndexOfMinMagnitude(span); - } - #endregion - - #region LeadingZeroCount - /// - /// Computes the element-wise leading zero count of numbers in the specified tensor. - /// - /// The input . - public static Tensor LeadingZeroCount(in ReadOnlyTensorSpan x) - where T : IBinaryInteger - { - Tensor output = Tensor.Create(x.Lengths); - LeadingZeroCount(x, output); - return output; - } - - /// - /// Computes the element-wise leading zero count of numbers in the specified tensor. - /// - /// The input . - /// - public static ref readonly TensorSpan LeadingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IBinaryInteger - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LeadingZeroCount); - } - #endregion - - #region Log - /// - /// Takes the natural logarithm of each element of the and returns a new with the result. - /// - /// The to take the natural logarithm of. - public static Tensor Log(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Log(x, output); - return output; - } - - /// - /// Takes the natural logarithm of each element of the and returns a new with the result. - /// - /// The to take the natural logarithm of. - /// - public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log); - } - - /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. - /// The first tensor - /// The second tensor - public static Tensor Log(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : ILogarithmicFunctions - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Log(x, y, output); - return output; - } - - /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. - /// The first tensor - /// The second tensor - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Log); - } - - /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. - /// The first tensor - /// The second tensor - public static Tensor Log(in ReadOnlyTensorSpan x, T y) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - - Log(x, y, output); - return output; - } - - /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. - /// The first tensor - /// The second tensor - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Log); - } - #endregion - - #region Log10 - /// - /// Takes the base 10 logarithm of each element of the and returns a new with the result. - /// - /// The to take the base 10 logarithm of. - public static Tensor Log10(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Log10(x, output); - return output; - } - - /// - /// Takes the base 10 logarithm of each element of the and returns a new with the result. - /// - /// The to take the base 10 logarithm of. - /// - public static ref readonly TensorSpan Log10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10); - } - #endregion - - #region Log10P1 - /// - /// Takes the base 10 logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the base 10 logarithm of. - public static Tensor Log10P1(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Log10P1(x, output); - return output; - } - - /// - /// Takes the base 10 logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the base 10 logarithm of. - /// - public static ref readonly TensorSpan Log10P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10P1); - } - #endregion - - #region Log2 - /// - /// Takes the base 2 logarithm of each element of the and returns a new with the result. - /// - /// The to take the base 2 logarithm of. - public static Tensor Log2(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Log2(x, output); - return output; - } - - /// - /// Takes the base 2 logarithm of each element of the and returns a new with the result. - /// - /// The to take the base 2 logarithm of. - /// - public static ref readonly TensorSpan Log2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2); - } - #endregion - - #region Log2P1 - /// - /// Takes the base 2 logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the base 2 logarithm of. - public static Tensor Log2P1(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Log2P1(x, output); - return output; - } - - /// - /// Takes the base 2 logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the base 2 logarithm of. - /// - public static ref readonly TensorSpan Log2P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2P1); - } - #endregion - - #region LogP1 - /// - /// Takes the natural logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the natural logarithm of. - public static Tensor LogP1(in ReadOnlyTensorSpan x) - where T : ILogarithmicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - LogP1(x, output); - return output; - } - - /// - /// Takes the natural logarithm plus 1 of each element of the and returns a new with the result. - /// - /// The to take the natural logarithm of. - /// - public static ref readonly TensorSpan LogP1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ILogarithmicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LogP1); - } - #endregion - - #region Max - /// Searches for the largest number in the specified tensor. - /// The input .. - public static T Max(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Max); - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor Max(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Max(x, y, output); - return output; - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Max); - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor Max(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - Max(x, y, output); - return output; - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Max); - } - #endregion - - #region MaxMagnitude - /// Searches for the number with the largest magnitude in the specified tensor. - /// The input .. - public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitude); - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxMagnitude(x, y, output); - return output; - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MaxMagnitude(x, y, output); - return output; - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); - } - #endregion - - #region MaxMagnitudeNumber - /// Searches for the number with the largest magnitude in the specified tensor. - /// The input .. - public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) - where T : INumberBase - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitudeNumber); - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxMagnitudeNumber(x, y, output); - return output; - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MaxMagnitudeNumber(x, y, output); - return output; - } - - /// Computes the element-wise number with the largest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); - } - #endregion - - #region MaxNumber - /// Searches for the largest number in the specified tensor. - /// The input .. - public static T MaxNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxNumber); - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxNumber(x, y, output); - return output; - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MaxNumber(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MaxNumber(x, y, output); - return output; - } - - /// Computes the element-wise maximum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); - } - #endregion - - #region Min - /// Searches for the smallest number in the specified tensor. - /// The input . - public static T Min(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Min); - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor Min(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Min(x, y, output); - return output; - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Min); - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor Min(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - Min(x, y, output); - return output; - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Min); - } - #endregion - - #region MinMagnitude - /// Searches for the number with the smallest magnitude in the specified tensor. - /// The input . - public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitude); - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinMagnitude(x, y, output); - return output; - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MinMagnitude(x, y, output); - return output; - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); - } - #endregion - - #region MinMagnitudeNumber - /// Searches for the number with the smallest magnitude in the specified tensor. - /// The input .. - public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) - where T : INumberBase - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitudeNumber); - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinMagnitudeNumber(x, y, output); - return output; - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MinMagnitudeNumber(x, y, output); - return output; - } - - /// Computes the element-wise number with the smallest magnitude in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); - } - #endregion - - #region MinNumber - /// Searches for the smallest number in the specified tensor. - /// The input .. - public static T MinNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinNumber); - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinNumber(x, y, output); - return output; - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinNumber); - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - public static Tensor MinNumber(in ReadOnlyTensorSpan x, T y) - where T : INumber - { - Tensor output = Tensor.Create(x.Lengths); - MinNumber(x, y, output); - return output; - } - - /// Computes the element-wise minimum of the numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The destination tensor, represented as a span. - public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : INumber - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinNumber); - } - #endregion - - #region Multiply - /// - /// Multiplies each element of with and returns a new with the result. - /// - /// Input - /// value to multiply by. - public static Tensor Multiply(in ReadOnlyTensorSpan x, T y) - where T : IMultiplyOperators, IMultiplicativeIdentity - { - Tensor output = Tensor.Create(x.Lengths); - Multiply((ReadOnlyTensorSpan)x, y, output); - return output; - } - - /// - /// Multiplies each element of with and returns a new with the result. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// - /// Left for multiplication. - /// Right for multiplication. - public static Tensor Multiply(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IMultiplyOperators, IMultiplicativeIdentity - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Multiply((ReadOnlyTensorSpan)x, y, output); - return output; - } - - /// - /// Multiplies each element of with and returns a new with the result. - /// - /// Input - /// value to multiply by. - /// - public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IMultiplyOperators, IMultiplicativeIdentity - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.Multiply(span, y, ospan); - return ref destination; - } - - /// - /// Multiplies each element of with and returns a new with the result. - /// If the shapes are not the same they are broadcast to the smallest compatible shape. - /// - /// Left for multiplication. - /// Right for multiplication. - /// - public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IMultiplyOperators, IMultiplicativeIdentity - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Multiply); - } - #endregion - - #region Negate - /// Computes the element-wise negation of each number in the specified tensor. - /// The - public static Tensor Negate(in ReadOnlyTensorSpan x) - where T : IUnaryNegationOperators - { - Tensor output = Tensor.Create(x.Lengths); - Negate(x, output); - return output; - } - - /// Computes the element-wise negation of each number in the specified tensor. - /// The - /// - public static ref readonly TensorSpan Negate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IUnaryNegationOperators - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Negate); - } - #endregion - - #region Norm - /// - /// Takes the norm of the and returns the result. - /// - /// The to take the norm of. - public static T Norm(scoped in ReadOnlyTensorSpan x) - where T : IRootFunctions - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Norm); - } - #endregion - - #region OnesComplement - /// Computes the element-wise one's complement of numbers in the specified tensor. - /// The - public static Tensor OnesComplement(in ReadOnlyTensorSpan x) - where T : IBitwiseOperators - { - Tensor output = Tensor.Create(x.Lengths); - OnesComplement(x, output); - return output; - } - - /// Computes the element-wise one's complement of numbers in the specified tensor. - /// The - /// - public static ref readonly TensorSpan OnesComplement(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.OnesComplement); - } - #endregion - - #region PopCount - /// Computes the element-wise population count of numbers in the specified tensor. - /// The - public static Tensor PopCount(in ReadOnlyTensorSpan x) - where T : IBinaryInteger - { - Tensor output = Tensor.Create(x.Lengths); - PopCount(x, output); - return output; - } - - /// Computes the element-wise population count of numbers in the specified tensor. - /// The - /// - public static ref readonly TensorSpan PopCount(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IBinaryInteger - { - return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.PopCount); - } - #endregion - - #region Pow - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - public static Tensor Pow(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IPowerFunctions - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Pow(x, y, output); - return output; - } - - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - /// - public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IPowerFunctions - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); - } - - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - public static Tensor Pow(in ReadOnlyTensorSpan x, T y) - where T : IPowerFunctions - { - Tensor output = Tensor.Create(x.Lengths); - - Pow(x, y, output); - return output; - } - - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - /// - public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IPowerFunctions - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Pow); - } - - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - public static Tensor Pow(T x, in ReadOnlyTensorSpan y) - where T : IPowerFunctions - { - Tensor output = Tensor.Create(y.Lengths); - - Pow(x, y, output); - return output; - } - - /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. - /// The input . - /// The second input - /// - public static ref readonly TensorSpan Pow(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IPowerFunctions - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); - } - #endregion - - #region Product - /// Computes the product of all elements in the specified non-empty tensor of numbers. - /// The input . - public static T Product(scoped in ReadOnlyTensorSpan x) - where T : IMultiplicativeIdentity, IMultiplyOperators - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Product); - } - #endregion - - #region RadiansToDegrees - /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. - /// The input . - public static Tensor RadiansToDegrees(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - RadiansToDegrees(x, output); - return output; - } - - /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. - /// The input . - /// - public static ref readonly TensorSpan RadiansToDegrees(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.RadiansToDegrees); - } - #endregion - - #region Reciprocal - /// Computes the element-wise reciprocal of numbers in the specified tensor. - /// The input . - public static Tensor Reciprocal(in ReadOnlyTensorSpan x) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Reciprocal(x, output); - return output; - } - - /// Computes the element-wise reciprocal of numbers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan Reciprocal(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPoint - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Reciprocal); - } - #endregion - - #region RootN - /// Computes the element-wise n-th root of the values in the specified tensor. - /// The tensor, represented as a span. - /// The degree of the root to be computed, represented as a scalar. - public static Tensor RootN(in ReadOnlyTensorSpan x, int n) - where T : IRootFunctions - { - Tensor output = Tensor.Create(x.Lengths); - RootN(x, n, output); - return output; - } - - /// Computes the element-wise n-th root of the values in the specified tensor. - /// The tensor, represented as a span. - /// The destination tensor, represented as a span. - /// The degree of the root to be computed, represented as a scalar. - public static ref readonly TensorSpan RootN(scoped in ReadOnlyTensorSpan x, int n, in TensorSpan destination) - where T : IRootFunctions - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.RootN(span, n, ospan); - return ref destination; - } - #endregion - - #region RotateLeft - /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. - /// The tensor - /// The number of bits to rotate, represented as a scalar. - /// Destination is too short. - public static Tensor RotateLeft(in ReadOnlyTensorSpan x, int rotateAmount) - where T : IBinaryInteger - { - Tensor output = Tensor.Create(x.Lengths); - RotateLeft(x, rotateAmount, output); - return output; - } - - /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. - /// The tensor - /// The number of bits to rotate, represented as a scalar. - /// - /// Destination is too short. - public static ref readonly TensorSpan RotateLeft(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) - where T : IBinaryInteger - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.RotateLeft(span, rotateAmount, ospan); - return ref destination; - } - #endregion - - #region RotateRight - /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. - /// The tensor - /// The number of bits to rotate, represented as a scalar. - /// Destination is too short. - public static Tensor RotateRight(in ReadOnlyTensorSpan x, int rotateAmount) - where T : IBinaryInteger - { - Tensor output = Tensor.Create(x.Lengths); - RotateRight(x, rotateAmount, output); - return output; - } - - /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. - /// The tensor - /// The number of bits to rotate, represented as a scalar. - /// - /// Destination is too short. - public static ref readonly TensorSpan RotateRight(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) - where T : IBinaryInteger - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.RotateRight(span, rotateAmount, ospan); - return ref destination; - } - #endregion - - #region Round - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - public static Tensor Round(in ReadOnlyTensorSpan x) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Round(x, output); - return output; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPoint - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Round); - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - /// - public static Tensor Round(in ReadOnlyTensorSpan x, int digits, MidpointRounding mode) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Round(x, digits, mode, output); - return output; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - /// - /// - public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, MidpointRounding mode, in TensorSpan destination) - where T : IFloatingPoint - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.Round(span, digits, mode, ospan); - return ref destination; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - public static Tensor Round(in ReadOnlyTensorSpan x, int digits) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Round(x, digits, output); - return output; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - /// - public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, in TensorSpan destination) - where T : IFloatingPoint - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.Round(span, digits, ospan); - return ref destination; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - public static Tensor Round(in ReadOnlyTensorSpan x, MidpointRounding mode) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Round(x, mode, output); - return output; - } - - /// Computes the element-wise rounding of the numbers in the specified tensor - /// The input . - /// - /// - public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, MidpointRounding mode, in TensorSpan destination) - where T : IFloatingPoint - { - if (destination._shape._memoryLength < x._shape._memoryLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - TensorPrimitives.Round(span, mode, ospan); - return ref destination; - } - #endregion - - #region Sigmoid - /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. - /// The input . - public static Tensor Sigmoid(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Sigmoid(x, output); - return output; - } - - /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. - /// The input . - /// - public static ref readonly TensorSpan Sigmoid(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sigmoid); - } - #endregion - - #region Sin - /// - /// Takes the sin of each element of the and returns a new with the result. - /// - /// The to take the sin of. - public static Tensor Sin(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Sin(x, output); - return output; - } - - /// - /// Takes the sin of each element of the and returns a new with the result. - /// - /// The to take the sin of. - /// - public static ref readonly TensorSpan Sin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sin); - } - #endregion - - #region Sinh - /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. - /// The to take the sin of. - public static Tensor Sinh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Sinh(x, output); - return output; - } - - /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. - /// The to take the sin of. - /// - public static ref readonly TensorSpan Sinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sinh); - } - #endregion - - #region SinPi - /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. - /// The to take the sin of. - public static Tensor SinPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - SinPi(x, output); - return output; - } - - /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. - /// The to take the sin of. - /// - public static ref readonly TensorSpan SinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SinPi); - } - #endregion - - #region SoftMax - /// Computes the softmax function over the specified non-empty tensor of numbers. - /// The to take the sin of. - public static Tensor SoftMax(in ReadOnlyTensorSpan x) - where T : IExponentialFunctions - { - Tensor output = Tensor.Create(x.Lengths); - SoftMax(x, output); - return output; - } - - /// Computes the softmax function over the specified non-empty tensor of numbers. - /// The to take the sin of. - /// - public static ref readonly TensorSpan SoftMax(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IExponentialFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SoftMax); - } - #endregion - - #region Sqrt - /// - /// Takes the square root of each element of the and returns a new with the result. - /// - /// The to take the square root of. - public static Tensor Sqrt(in ReadOnlyTensorSpan x) - where T : IRootFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Sqrt(x, output); - return output; - } - - /// - /// Takes the square root of each element of the and returns a new with the result. - /// - /// The to take the square root of. - /// - public static ref readonly TensorSpan Sqrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IRootFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sqrt); - } - #endregion - - #region StdDev - /// - /// Returns the standard deviation of the elements in the tensor. - /// - /// The to take the standard deviation of. - /// representing the standard deviation. - public static T StdDev(in ReadOnlyTensorSpan x) - where T : IRootFunctions - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.StdDev); - } - #endregion - - #region Subtract - /// - /// Subtracts from each element of and returns a new with the result. - /// - /// The . - /// The to subtract. - public static Tensor Subtract(in ReadOnlyTensorSpan x, T y) - where T : ISubtractionOperators - { - Tensor output = Create(x.Lengths); - Subtract(x, y, output); - return output; - } - - /// - /// Subtracts each element of from and returns a new with the result. - /// - /// The to be subtracted from. - /// The of values to subtract. - public static Tensor Subtract(T x, in ReadOnlyTensorSpan y) - where T : ISubtractionOperators - { - Tensor output = Create(y.Lengths); - Subtract(x, y, output); - return output; - } - - /// - /// Subtracts each element of from and returns a new with the result. - /// - /// The with values to be subtracted from. - /// The with values to subtract. - public static Tensor Subtract(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : ISubtractionOperators - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Subtract(x, y, output); - return output; - } - - /// - /// Subtracts from each element of and returns a new with the result. - /// - /// The with values to be subtracted from. - /// The value to subtract. - /// - public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : ISubtractionOperators - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Subtract); - } - - /// - /// Subtracts each element of from and returns a new with the result. - /// - /// The value to be subtracted from. - /// The values to subtract. - /// - public static ref readonly TensorSpan Subtract(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : ISubtractionOperators - { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); - } - - /// - /// Subtracts each element of from and returns a new with the result. - /// - /// The of values to be subtracted from. - /// The of values to subtract. - /// - public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : ISubtractionOperators - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); - } - #endregion - - #region Sum - /// - /// Sums the elements of the specified tensor. - /// - /// Tensor to sum - /// - public static T Sum(scoped in ReadOnlyTensorSpan x) - where T : IAdditionOperators, IAdditiveIdentity - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Sum); - } - #endregion - - #region SumOfSquares - /// - /// Sums the squared elements of the specified tensor. - /// - /// Tensor to sum squares of - /// - internal static T SumOfSquares(scoped in ReadOnlyTensorSpan x) - where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators - { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.SumOfSquares); - } - #endregion - - #region Tan - /// Computes the element-wise tangent of the value in the specified tensor. - /// The to take the sin of. - public static Tensor Tan(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Tan(x, output); - return output; - } - - /// Computes the element-wise tangent of the value in the specified tensor. - /// The to take the sin of. - /// - public static ref readonly TensorSpan Tan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tan); - } - #endregion - - #region Tanh - /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. - /// The to take the sin of. - public static Tensor Tanh(in ReadOnlyTensorSpan x) - where T : IHyperbolicFunctions - { - Tensor output = Tensor.Create(x.Lengths); - Tanh(x, output); - return output; - } - - /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. - /// The to take the sin of. - /// - public static ref readonly TensorSpan Tanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IHyperbolicFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tanh); - } - #endregion - - #region TanPi - /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. - /// The to take the sin of. - public static Tensor TanPi(in ReadOnlyTensorSpan x) - where T : ITrigonometricFunctions - { - Tensor output = Tensor.Create(x.Lengths); - TanPi(x, output); - return output; - } - - /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. - /// The to take the sin of. - /// - public static ref readonly TensorSpan TanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : ITrigonometricFunctions - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TanPi); - } - #endregion - - #region TrailingZeroCount - /// Computes the element-wise trailing zero count of numbers in the specified tensor. - /// The input . - public static Tensor TrailingZeroCount(in ReadOnlyTensorSpan x) - where T : IBinaryInteger - { - Tensor output = Tensor.Create(x.Lengths); - TrailingZeroCount(x, output); - return output; - } - - /// Computes the element-wise trailing zero count of numbers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan TrailingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IBinaryInteger - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TrailingZeroCount); - } - #endregion - - #region Truncate - /// Computes the element-wise truncation of numbers in the specified tensor. - /// The input . - public static Tensor Truncate(in ReadOnlyTensorSpan x) - where T : IFloatingPoint - { - Tensor output = Tensor.Create(x.Lengths); - Truncate(x, output); - return output; - } - - /// Computes the element-wise truncation of numbers in the specified tensor. - /// The input . - /// - public static ref readonly TensorSpan Truncate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) - where T : IFloatingPoint - { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Truncate); - } - #endregion - - #region Xor - /// Computes the element-wise XOR of numbers in the specified tensors. - /// The left . - /// The right . - public static Tensor Xor(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IBitwiseOperators - { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Xor(x, y, output); - return output; - } - - /// Computes the element-wise XOR of numbers in the specified tensors. - /// The left . - /// The right . - /// - public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Xor); - } - - /// - /// Computes the element-wise Xor of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - public static Tensor Xor(in ReadOnlyTensorSpan x, T y) - where T : IBitwiseOperators - { - Tensor output = Tensor.Create(x.Lengths); - Xor(x, y, output); - return output; - } - - /// - /// Computes the element-wise Xor of the two input and returns a new with the result. - /// - /// The left . - /// The second value. - /// - public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IBitwiseOperators - { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Xor); - } - #endregion - - /// - /// Gets the smallest broadcastable lengths for two shapes. - /// - /// The first shape to broadcast. - /// The second shape to broadcast. - /// The smallest lengths these shapes can be broadcast to. - /// The lengths of and are not broadcast compatible. - public static nint[] GetSmallestBroadcastableLengths(ReadOnlySpan shape1, ReadOnlySpan shape2) - { - if (!TensorHelpers.IsBroadcastableTo(shape1, shape2)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(shape1, shape2.Length); - for (int i = 1; i <= shape1.Length; i++) - { - intermediateShape[^i] = Math.Max(intermediateShape[^i], shape1[^i]); - } - for (int i = 1; i <= shape2.Length; i++) - { - intermediateShape[^i] = Math.Max(intermediateShape[^i], shape2[^i]); - } - - return intermediateShape; - } - - #region TensorPrimitivesHelpers - private delegate void PerformCalculationSpanInSpanOut(ReadOnlySpan input, Span output); - - private delegate void PerformCalculationSpanInTInSpanOut(ReadOnlySpan input, T value, Span output); - - private delegate void PerformCalculationTInSpanInSpanOut(T value, ReadOnlySpan input, Span output); - - private delegate void PerformCalculationTwoSpanInSpanOut(ReadOnlySpan input, ReadOnlySpan inputTwo, Span output); - - private delegate T PerformCalculationTwoSpanInTOut(ReadOnlySpan input, ReadOnlySpan inputTwo); - - private delegate T PerformCalculationSpanInTOut(ReadOnlySpan input); - - private static T TensorPrimitivesHelperSpanInTOut(scoped in ReadOnlyTensorSpan input, PerformCalculationSpanInTOut performCalculation) - { - if (TensorHelpers.IsContiguousAndDense(input)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); - return performCalculation(span); - } - // Flattening needs to happen - else - { - // TODO: Can optimize this to not need to realize the broadcasts - // That will need to be done on a per method basis. - nint flattenedLength = input.FlattenedLength; - T[] flattened = new T[flattenedLength]; - input.FlattenTo(flattened); - return performCalculation(flattened); - } - } - - private static T TensorPrimitivesHelperTwoSpanInTOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, PerformCalculationTwoSpanInTOut performCalculation) - { - // If sizes are the same. - if (TensorHelpers.IsContiguousAndDense(left) && TensorHelpers.IsContiguousAndDense(right) && TensorHelpers.AreLengthsTheSame(left, right)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left._shape.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right._shape.FlattenedLength); - return performCalculation(span, rspan); - } - // Broadcasting needs to happen. - else - { - // Have a couple different possible cases here. - // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) - // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) - // Because we are returning a single T though we need to actual realize the broadcasts at this point to perform the calculations. - - // TODO: Can optimize this to not need to realize the broadcasts - // That will need to be done on a per method basis. - nint[] newLengths = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); - nint newLength = TensorSpanHelpers.CalculateTotalLength(newLengths); - TensorSpan broadcastedLeft = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); - TensorSpan broadcastedRight = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); - BroadcastTo(left, broadcastedLeft); - BroadcastTo(right, broadcastedRight); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref broadcastedLeft._reference, (int)broadcastedLeft.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref broadcastedRight._reference, (int)broadcastedRight.FlattenedLength); - return performCalculation(span, rspan); - } - } - - private static ref readonly TensorSpan TensorPrimitivesHelperSpanInSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationSpanInSpanOut performCalculation) - { - // Make sure destination has enough memory - if (destination._shape._memoryLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - Span destinationSpan; - ReadOnlySpan inputSpan; - - // Memory is contiguous for both input and destination - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape.FlattenedLength); - performCalculation(inputSpan, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(inputSpan, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperSpanInTInSpanOut(scoped in ReadOnlyTensorSpan input, T value, in TensorSpan destination, PerformCalculationSpanInTInSpanOut performCalculation) - { - if (destination._shape._memoryLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - ReadOnlySpan inputSpan; - Span destinationSpan; - - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); - performCalculation(inputSpan, value, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(inputSpan, value, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperTInSpanInSpanOut(T value, scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationTInSpanInSpanOut performCalculation) - { - if (destination._shape._memoryLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - ReadOnlySpan inputSpan; - Span destinationSpan; - - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); - destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - performCalculation(value, inputSpan, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(value, inputSpan, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperTwoSpanInSpanOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, in TensorSpan destination, PerformCalculationTwoSpanInSpanOut performCalculation) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); - - TensorSpan slicedDestination = destination.Slice(newSize); - - // If sizes are the same and memory is contiguous for all tensors - if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right) && TensorHelpers.IsContiguousAndDense(left) - && TensorHelpers.IsContiguousAndDense(right) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, left._shape._memoryLength <= left.FlattenedLength ? (int)left._shape._memoryLength : (int)left.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, right._shape._memoryLength <= right.FlattenedLength ? (int)right._shape._memoryLength : (int)right.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape._memoryLength); - performCalculation(span, rspan, ospan); - return ref destination; - } - // Broadcasting needs to happen. - else - { - // Have a couple different possible cases here. - // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) - // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) - - ReadOnlyTensorSpan broadcastedLeft = Tensor.LazyBroadcast(left, newSize); - ReadOnlyTensorSpan broadcastedRight = Tensor.LazyBroadcast(right, newSize); - - nint rowLength = newSize[^1]; - Span ospan; - ReadOnlySpan ispan; - Span buffer = new T[rowLength]; - - scoped Span curIndex; - nint[]? curIndexArray; - if (newSize.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(newSize.Length); - curIndex = curIndexArray.AsSpan(0, newSize.Length); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[newSize.Length]; - } - curIndex.Clear(); - - int outputOffset = 0; - // neither row contiguous - if (broadcastedLeft.Strides[^1] == 0 && broadcastedRight.Strides[^1] == 0) - { - Span buffer2 = new T[rowLength]; - - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedLeft[curIndex]); - buffer2.Fill(broadcastedRight[curIndex]); - performCalculation(buffer, buffer2, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // tensor not row contiguous - else if (broadcastedLeft.Strides[^1] == 0) - { - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedLeft[curIndex]); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); - performCalculation(buffer, ispan, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // right not row contiguous - else if (broadcastedRight.Strides[^1] == 0) - { - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedRight[curIndex]); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); - performCalculation(ispan, buffer, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // both row contiguous - else - { - Span rspan; - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); - rspan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); - performCalculation(ispan, rspan, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - return ref destination; - } - - #endregion - - #endregion - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs deleted file mode 100644 index c368c65fd1c6d5..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace System.Numerics.Tensors -{ - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - internal static class TensorHelpers - { - /// - /// Counts the number of true elements in a boolean filter tensor so we know how much space we will need. - /// - /// - /// How many boolean values are true. - public static nint CountTrueElements(scoped in ReadOnlyTensorSpan filter) - { - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)filter._shape._memoryLength); - nint count = 0; - for (int i = 0; i < filterSpan.Length; i++) - { - if (filterSpan[i]) - count++; - } - - return count; - } - - internal static bool IsBroadcastableTo(Tensor tensor1, Tensor tensor2) - where T : IEquatable, IEqualityOperators => IsBroadcastableTo(tensor1.Lengths, tensor2.Lengths); - - internal static bool IsBroadcastableTo(ReadOnlySpan lengths1, ReadOnlySpan lengths2) - { - int lengths1Index = lengths1.Length - 1; - int lengths2Index = lengths2.Length - 1; - - bool areCompatible = true; - - nint s1; - nint s2; - - if (lengths1.Length == 0 || lengths2.Length == 0) - return false; - - while (lengths1Index >= 0 || lengths2Index >= 0) - { - // if a dimension is missing in one of the shapes, it is considered to be 1 - if (lengths1Index < 0) - s1 = 1; - else - s1 = lengths1[lengths1Index--]; - - if (lengths2Index < 0) - s2 = 1; - else - s2 = lengths2[lengths2Index--]; - - if (s1 == s2 || (s1 == 1 && s2 > 1) || (s2 == 1 && s1 > 1)) { } - else - { - areCompatible = false; - break; - } - } - - return areCompatible; - } - - internal static nint[] GetIntermediateShape(ReadOnlySpan shape1, int shape2Length) - { - int shape1Index = shape1.Length - 1; - int newShapeIndex = Math.Max(shape1.Length, shape2Length) - 1; - nint[] newShape = new nint[Math.Max(shape1.Length, shape2Length)]; - - while (newShapeIndex >= 0) - { - // if a dimension is missing in one of the shapes, it is considered to be 1 - if (shape1Index < 0) - newShape[newShapeIndex--] = 1; - else - newShape[newShapeIndex--] = shape1[shape1Index--]; - } - - return newShape; - } - - internal static bool IsUnderlyingStorageSameSize(scoped in ReadOnlyTensorSpan tensor1, scoped in ReadOnlyTensorSpan tensor2) - => tensor1._shape._memoryLength == tensor2._shape._memoryLength; - - internal static bool IsUnderlyingStorageSameSize(Tensor tensor1, Tensor tensor2) - => tensor1._values.Length == tensor2._values.Length; - - internal static bool AreLengthsTheSame(scoped in ReadOnlyTensorSpan tensor1, scoped in ReadOnlyTensorSpan tensor2) - => tensor1.Lengths.SequenceEqual(tensor2.Lengths); - - internal static bool AreLengthsTheSame(ReadOnlySpan lengths1, ReadOnlySpan lengths2) - => lengths1.SequenceEqual(lengths2); - - internal static bool IsContiguousAndDense(scoped in ReadOnlyTensorSpan tensor) - { - // Right most dimension must be 1 for a dense tensor. - if (tensor._shape.Strides[^1] != 1) - return false; - - // For other dimensions, the stride must be equal to the product of the dimensions to the right. - for (int i = tensor._shape._rank - 2; i >= 0; i--) - { - if (tensor._shape.Strides[i] != TensorPrimitives.Product(tensor.Lengths.Slice(i + 1, tensor.Lengths.Length - i - 1))) - return false; - } - return true; - } - - internal static void PermuteIndices(Span indices, Span permutedIndices, ReadOnlySpan permutation) - { - for (int i = 0; i < indices.Length; i++) - { - permutedIndices[i] = indices[permutation[i]]; - } - } - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs new file mode 100644 index 00000000000000..50d3a6793d81d8 --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace System.Numerics.Tensors +{ + internal static class TensorOperation + { + public static void Invoke(in TensorSpan x) + where TOperation : TensorOperation.IOperation + { + scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + + for (nint i = 0; i < x.FlattenedLength; i++) + { + linearOffset = x._shape.AdjustToNextIndex(linearOffset, indexes); + TOperation.Invoke( + ref Unsafe.Add(ref x._reference, linearOffset) + ); + } + + rentedBuffer.Dispose(); + } + + public static void Invoke(in TensorSpan destination, TArg scalar) + where TOperation : TensorOperation.IUnaryOperation_Scalar + { + scoped Span indexes = RentedBuffer.Create(destination.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + linearOffset = destination._shape.AdjustToNextIndex(linearOffset, indexes); + TOperation.Invoke( + ref Unsafe.Add(ref destination._reference, linearOffset), + scalar + ); + } + + rentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, Span destination) + where TOperation : TensorOperation.IUnaryOperation_Span + { + scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + + for (int i = 0; i < destination.Length; i++) + { + linearOffset = x._shape.AdjustToNextIndex(linearOffset, indexes); + TOperation.Invoke( + in Unsafe.Add(ref x._reference, linearOffset), + ref destination[i] + ); + } + + rentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) + where TOperation : TensorOperation.IUnaryOperation_Span + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + yLinearOffset = y._shape.AdjustToNextIndex(yLinearOffset, yIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + in Unsafe.Add(ref y._reference, yLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + yRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, TArg y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + y, + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in TensorSpan destination) + { + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) + { + + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, out Tensor destination) + { + + } + + public readonly struct Clear + : IOperation + { + public static void Invoke(ref T destination) + { + destination = default!; + } + + public static void Invoke(Span destination) + { + destination.Clear(); + } + } + + public readonly struct CopyTo + : IUnaryOperation_Span + { + public static void Invoke(ref readonly T source, ref T destination) + { + destination = source; + } + + public static void Invoke(ReadOnlySpan source, Span destination) + { + source.CopyTo(destination); + } + } + + public readonly struct Equals + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IEqualityOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left == right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left == right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] == right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] == right[i]); + } + } + } + + public readonly struct Fill + : IUnaryOperation_Scalar + { + public static void Invoke(ref T destination, T value) + { + destination = value; + } + + public static void Invoke(Span destination, T value) + { + destination.Fill(value); + } + } + + public interface IBinaryOperation_Tensor_Scalar + { + static abstract void Invoke(ref readonly T x, T y, ref TResult destination); + static abstract void Invoke(ReadOnlySpan x, T y, Span destination); + } + + public interface IBinaryOperation_Tensor_Tensor + { + static abstract void Invoke(ref readonly T x, ref readonly T y, ref TResult destination); + static abstract void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination); + } + + public interface IOperation + { + static abstract void Invoke(ref T destination); + static abstract void Invoke(Span destination); + } + + public interface IUnaryOperation_Scalar + { + static abstract void Invoke(ref TResult destination, T x); + static abstract void Invoke(Span destination, T x); + } + + public interface IUnaryOperation_Span + { + static abstract void Invoke(ref readonly T x, ref TResult destination); + static abstract void Invoke(ReadOnlySpan x, Span destination); + } + + private ref struct RentedBuffer : IDisposable + { + private nint[]? _array; + private TensorShape.InlineBuffer _inline; + + public static Span Create(int rank, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) + { + linearOffset = 0; + + if (rank > TensorShape.MaxInlineRank) + { + rentedBuffer._array = ArrayPool.Shared.Rent(rank); + Unsafe.SkipInit(out rentedBuffer._inline); + + rentedBuffer._array[rank - 1] = -1; + return rentedBuffer._array.AsSpan(0, rank); + } + else + { + rentedBuffer._array = null; + rentedBuffer._inline = default; + + rentedBuffer._inline[rank - 1] = -1; + return ((Span)rentedBuffer._inline)[..rank]; + } + } + + public void Dispose() + { + if (_array is not null) + { + ArrayPool.Shared.Return(_array); + _array = null; + } + } + } + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 5aa155efff6a46..bcf1bf0add6f75 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -8,59 +9,911 @@ namespace System.Numerics.Tensors { + // TensorShape tracks the core information required to safely interact with memory + // for tensors and tensor spans alike. + // + // We avoid allocating for small ranks up to MaxInlineRank in size and allocate a + // single buffer for larger ranks. This buffer will always be precisely `InlineBufferCount * rank` + // in size where the first rank elements are the length, the next rank elements are + // the strides, and then the next rank elements are the rank order. + // + // We cache both a flattened length and a linear length to avoid recalculating these + // key properties. The flattened length is the total number of elements represented + // by the tensor, however due to implicit broadcasting this may be greater than the + // amount of memory that actually backs the tensor. While the linear length is the + // backing storage size that is present. This gives us the following invariants: + // * linearLength <= flattenedLength + // * (flattenedLength % linearLength) == 0 + // + // These invariants allow us to safely and efficiently index into the backing storage + // as we know that memory functionally wraps around after linearLength elements. It + // also means that we only support broadcasting to greater dimensions and thus lengths + // strides and the backing memory form a strict relationship that is validated by the + // public constructors. [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - internal readonly struct TensorShape + internal readonly struct TensorShape : IEquatable { - // Used to determine when we need to allocate a metadata array - public const int MaxInlineArraySize = 5; + // The layout of the fields here is very particular and is intentionally designed to + // be compact and cache-friendly. The current size on a 64-bit system is 108+4 bytes + // and this fits within 2 cache lines, where 64 bytes is the typical cache line size. + // + // The TensorSpan and ReadOnlyTensorSpan types then track a byref field which takes + // an additional 8 bytes. This leaves 8 bytes still available for use for other scenarios + // if required. - // Used to determine when we can stack alloc for indexing vs when we need to allocate - public const int MaxInlineRank = 8; + internal const int MaxInlineRank = 4; + private const int InlineBufferCount = 3; - internal readonly nint[]? _metadata; // 8 bytes + private readonly nint[]? _metadata; // 8 bytes - internal readonly nint _memoryLength; // 8 bytes - internal readonly int _rank; // 4 bytes + private readonly nint _flattenedLength; // 8 bytes + private readonly nint _linearLength; // 8 bytes - private readonly NintBuffer _lengths; - private readonly NintBuffer _strides; + private readonly InlineBuffer _inlineLengths; // 4x8 bytes (32) + private readonly InlineBuffer _inlineStrides; // 4x8 bytes (32) + private readonly InlineBuffer _inlineLinearRankOrder; // 4x4 bytes (16) - internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan strides) + private readonly int _rank; // 4 bytes + + private TensorShape(nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) { - _memoryLength = memoryLength; - _rank = lengths.Length; - if (lengths.Length > MaxInlineArraySize) + int rank = lengths.Length; + + if (rank == 0) + { + lengths = [linearLength]; + rank = 1; + } + + scoped Span destinationLengths; + scoped Span destinationStrides; + scoped Span destinationLinearRankOrder; + + if (rank > MaxInlineRank) + { + nint[] metadata = new nint[rank * InlineBufferCount]; + + destinationLengths = metadata.AsSpan(rank * 0, rank); + destinationStrides = metadata.AsSpan(rank * 1, rank); + destinationLinearRankOrder = MemoryMarshal.CreateSpan( + ref Unsafe.As(ref metadata[rank * 2]), + rank + ); + + _metadata = metadata; + } + else { - _metadata = new nint[lengths.Length + strides.Length]; - lengths.CopyTo(MemoryMarshal.CreateSpan(ref _metadata[0], lengths.Length)); - strides.CopyTo(MemoryMarshal.CreateSpan(ref _metadata[lengths.Length], strides.Length)); + destinationLengths = ((Span)_inlineLengths)[..rank]; + destinationStrides = ((Span)_inlineStrides)[..rank]; + destinationLinearRankOrder = ((Span)_inlineLinearRankOrder)[..rank]; + } + + if (linearRankOrder.Length == 0) + { + // The linearRankOrder is expected to be in "row-major" order, that is otherwise + // known as "big-endian" order where the dimensions that are farthest apart + // (i.e. have the greatest impact to computing a linear index) appear first. + // + // So, when no rank order is specified by the user we simply populate this + // as 0 to rank-1. In the case strides is also not specified, this will be + // correct "as is"; otherwise, we will sort this based on which stride is + // largest prior to validating the user provided strides. + + for (int i = 0; i < destinationLinearRankOrder.Length; i++) + { + destinationLinearRankOrder[i] = i; + } + } + else if (linearRankOrder.Length != rank) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); } else { - lengths.CopyTo(_lengths); - strides.CopyTo(_strides); + // If a rank order was specified, then we need to ensure that it is valid, + // which should mean that when sorting we have values from 0 to rank-1. + // + // While this does copy the rank order twice, the typical rank is expected + // to be small and so the cost should be minimal. + + linearRankOrder.CopyTo(destinationLinearRankOrder); + destinationLinearRankOrder.Sort(); + + for (int i = 0; i < linearRankOrder.Length; i++) + { + if (destinationLinearRankOrder[i] != i) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + } + + linearRankOrder.CopyTo(destinationLinearRankOrder); + } + + nint flattenedLength = 1; + + if (strides.Length == 0) + { + // When no strides is specified, we need to computing them based on the given + // rank order. We use destinationLinearRankOrder here to ensure that we have a + // correct order even if no rank order was specified by the user. + // + // To do this, we simply iterate the rank order from least to most significant + // so that the strides match the expected order, being the product of all previous + // dimension lengths. + + for (int i = 0; i < destinationLinearRankOrder.Length; i++) + { + int rankIndex = destinationLinearRankOrder.Length - (i + 1); + int linearRankIndex = destinationLinearRankOrder[rankIndex]; + nint length = lengths[linearRankIndex]; + + if (length <= 0) + { + ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); + } + + flattenedLength = checked(flattenedLength * length); + + destinationLengths[linearRankIndex] = length; + destinationStrides[linearRankIndex] = flattenedLength; + } + } + else if (strides.Length != lengths.Length) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); } + else + { + // If a strides was specified, then we need to ensure it is valid as well, + // which should mean that when sorted by rank order (most to least significant) + // each stride should be greater than or equal to the previous stride multiplied + // by the dimension length. + // + // The reason it is "or equal" and not simply "greater than" is because a dimension + // can be length 1 and thus the stride is the same as the previous rank. + // + // Additionally, when sorted we allow for the first n (most significant) strides to be + // specified as 0 in which case we automatically compute that to be the same as stride + // n+1. This makes it convenient to support implicit broadcasting where higher dimensions + // aren't actually stored in memory. + + int i = 0; + + while (i < destinationLinearRankOrder.Length) + { + int rankIndex = destinationLinearRankOrder.Length - (i + 1); + int linearRankIndex = destinationLinearRankOrder[rankIndex]; + nint length = lengths[linearRankIndex]; + + if (length <= 0) + { + ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); + } + + nint stride = strides[linearRankIndex]; + + if (stride < 0) + { + ThrowHelper.ThrowArgument_StrideIsNegative(); + } + + if (stride == 0) + { + // We end up handling i twice due to the break here + // but this shouldn't be significant and makes it + // easier to ensure that the flattened length is correct. + break; + } + + flattenedLength = checked(flattenedLength * length); + + destinationLengths[linearRankIndex] = length; + destinationStrides[linearRankIndex] = flattenedLength; + + i++; + } + + if (linearLength == -1) + { + linearLength = flattenedLength; + } + else + { + ArgumentOutOfRangeException.ThrowIfNotEqual(linearLength, flattenedLength); + } + + while (i < destinationLinearRankOrder.Length) + { + int rankIndex = destinationLinearRankOrder.Length - (i + 1); + int linearRankIndex = destinationLinearRankOrder[rankIndex]; + nint length = lengths[linearRankIndex]; + + if (length <= 0) + { + ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); + } + + nint stride = strides[linearRankIndex]; + ArgumentOutOfRangeException.ThrowIfNotEqual(stride, 0); + + flattenedLength = checked(flattenedLength * length); + + destinationLengths[linearRankIndex] = length; + destinationStrides[linearRankIndex] = 0; + + i++; + } + } + + Debug.Assert((flattenedLength % linearLength) == 0); + + _flattenedLength = flattenedLength; + _linearLength = linearLength; + + _rank = rank; } - [InlineArray(MaxInlineArraySize)] // 5x8 bytes (40) - private struct NintBuffer + private TensorShape(nint flattenedLength, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder, int rank) + { + Debug.Assert((flattenedLength % linearLength) == 0); + + Debug.Assert(lengths.Length == rank); + Debug.Assert(strides.Length == rank); + Debug.Assert(linearRankOrder.Length == rank); + + scoped Span destinationLengths; + scoped Span destinationStrides; + scoped Span destinationLinearRankOrder; + + if (rank > MaxInlineRank) + { + nint[] metadata = new nint[rank * InlineBufferCount]; + + destinationLengths = metadata.AsSpan(rank * 0, rank); + destinationStrides = metadata.AsSpan(rank * 1, rank); + destinationLinearRankOrder = MemoryMarshal.CreateSpan( + ref Unsafe.As(ref metadata[rank * 2]), + rank + ); + + _metadata = metadata; + } + else + { + destinationLengths = ((Span)_inlineLengths)[..rank]; + destinationStrides = ((Span)_inlineStrides)[..rank]; + destinationLinearRankOrder = ((Span)_inlineLinearRankOrder)[..rank]; + } + + _flattenedLength = flattenedLength; + _linearLength = linearLength; + + lengths.CopyTo(destinationLengths); + strides.CopyTo(destinationStrides); + linearRankOrder.CopyTo(destinationLinearRankOrder); + + _rank = rank; + } + + public nint FlattenedLength => _flattenedLength; + + public bool IsEmpty => _flattenedLength == 0; + + public nint LinearLength => _linearLength; + + [UnscopedRef] + public ReadOnlySpan Lengths { - public nint e0; + get + { + if (_metadata is not nint[] metadata) + { + return ((ReadOnlySpan)_inlineLengths)[.._rank]; + } + else + { + int rank = metadata.Length / InlineBufferCount; + return metadata.AsSpan(rank * 0, rank); + } + } } [UnscopedRef] - public ReadOnlySpan Lengths => (_metadata is null) - ? ((ReadOnlySpan)_lengths).Slice(0, _rank) - : MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetArrayDataReference(_metadata), _rank); + public ReadOnlySpan LinearRankOrder + { + get + { + if (_metadata is not nint[] metadata) + { + return ((ReadOnlySpan)_inlineLinearRankOrder)[.._rank]; + } + else + { + int rank = metadata.Length / InlineBufferCount; + return MemoryMarshal.CreateSpan( + ref Unsafe.As(ref metadata[rank * 2]), + rank + ); + } + } + } + + public int Rank => _rank; [UnscopedRef] - public ReadOnlySpan Strides => (_metadata is null) - ? ((ReadOnlySpan)_strides).Slice(0, _rank) - : MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetArrayDataReference(_metadata), _rank * 2).Slice(_rank); + public ReadOnlySpan Strides + { + get + { + if (_metadata is not nint[] metadata) + { + return ((ReadOnlySpan)_inlineStrides)[.._rank]; + } + else + { + int rank = metadata.Length / InlineBufferCount; + return metadata.AsSpan(rank * 1, rank); + } + } + } + + public static bool operator ==(in TensorShape left, in TensorShape right) + { + int rank = left.Rank; + + if (rank != right.Rank) + { + return false; + } + + if (left.FlattenedLength != right.FlattenedLength) + { + return false; + } + + if (left.LinearLength != right.LinearLength) + { + return false; + } + + ReadOnlySpan leftLengths = left.Lengths; + ReadOnlySpan leftLinearRankOrder = left.LinearRankOrder; + ReadOnlySpan leftStrides = left.Strides; + + ReadOnlySpan rightLengths = right.Lengths; + ReadOnlySpan rightLinearRankOrder = right.LinearRankOrder; + ReadOnlySpan rightStrides = right.Strides; + + for (int i = 0; i < rank; i++) + { + // We need to compare lengths and strides based on the linearRankOrder + // to ensure that two tensors representing the same memory, but where + // the shapes are logically, but not physically, transposed are considered + // equal. + + int leftRankIndex = leftLinearRankOrder[i]; + int rightRankIndex = rightLinearRankOrder[i]; + + if (leftLengths[leftRankIndex] != rightLengths[rightRankIndex]) + { + return false; + } + + if (leftStrides[leftRankIndex] != rightStrides[rightRankIndex]) + { + return false; + } + } + + return true; + } + + public static bool operator !=(in TensorShape left, in TensorShape right) => !(left == right); + + public nint AdjustToNextIndex(nint linearOffset, Span indexes) + { + Debug.Assert(indexes.Length == Rank); + + ReadOnlySpan lengths = Lengths; + ReadOnlySpan strides = Strides; + + for (int i = 0; i < indexes.Length; i++) + { + int rankIndex = indexes.Length - (i + 1); + + nint length = lengths[rankIndex]; + nint stride = strides[rankIndex]; + + nint index = ++indexes[rankIndex]; + linearOffset += stride; + + if (index < length) + { + break; + } + + indexes[rankIndex] = 0; + linearOffset -= (stride * length); + } + + return linearOffset; + } + + public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2) + { + scoped ReadOnlySpan lengths1 = shape1.Lengths; + scoped ReadOnlySpan lengths2 = shape2.Lengths; + + int rankDelta = shape1.Rank - shape2.Rank; + + if (rankDelta != 0) + { + if (rankDelta < 0) + { + lengths1 = shape2.Lengths; + lengths2 = shape1.Lengths; + + rankDelta = -rankDelta; + Debug.Assert(rankDelta > 0); + } + + if (lengths1[..rankDelta].ContainsAnyExcept(1)) + { + return false; + } + lengths1 = lengths1[rankDelta..]; + } + + return lengths1.SequenceEqual(lengths2); + } + + public static bool AreLengthsTheSame(in TensorShape shape1, in TensorShape shape2) + { + return AreLengthsTheSame(shape1.Lengths, shape2.Lengths); + } + + public static bool AreLengthsTheSame(ReadOnlySpan lengths1, ReadOnlySpan lengths2) + { + return lengths1.SequenceEqual(lengths2); + } + + public static TensorShape Create(Array? array) + { + if (array is not null) + { + nint linearLength = (nint)array.LongLength; + + if (linearLength != 0) + { + int rank = array.Rank; + + nint[]? lengthsArray = null; + InlineBuffer lengthsBuffer; + scoped Span lengths; + + if (rank > MaxInlineRank) + { + lengthsArray = ArrayPool.Shared.Rent(rank); + lengths = lengthsArray.AsSpan(0, rank); + } + else + { + Unsafe.SkipInit(out lengthsBuffer); + lengths = ((Span)lengthsBuffer)[..rank]; + } + + for (int i = 0; i < rank; i++) + { + lengths[i] = array.GetLength(i); + } + + TensorShape result = new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths, + strides: [], + linearRankOrder: [], + rank + ); + + if (lengthsArray is not null) + { + ArrayPool.Shared.Return(lengthsArray); + } + return result; + } + } + return default; + } + + public static TensorShape Create(Array? array, scoped ReadOnlySpan start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, out nint linearOffset) + { + nint computedOffset = 0; + + nint[]? intermediateLengthsArray = null; + InlineBuffer intermediateLengthsBuffer; + scoped Span intermediateLengths = default; + + if (array is not null) + { + int rank = array.Rank; + + if ((start.Length != 0) && (start.Length != rank)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + nint linearLength = (nint)array.LongLength; + + if (linearLength != 0) + { + if (lengths.Length == 0) + { + // When no lengths are specified we need to retrieve them from the array + // since that has the expected shape. We don't need to validate the strides + // however as that will be done by the TensorShape constructor. + + if (rank > MaxInlineRank) + { + intermediateLengthsArray = ArrayPool.Shared.Rent(rank); + intermediateLengths = intermediateLengthsArray.AsSpan(0, rank); + } + else + { + Unsafe.SkipInit(out intermediateLengthsBuffer); + intermediateLengths = ((Span)intermediateLengthsBuffer)[..rank]; + } + + for (int i = 0; i < rank; i++) + { + intermediateLengths[i] = array.GetLength(i); + } + + lengths = intermediateLengths; + } + + if (start.Length != 0) + { + // In the case a starting index is specified, we need to compute the linear + // index that is the actual starting position. Additionally, if no lengths + // were specified we want to adjust the lengths computed from the array to + // ensure they remain correct. However, we don't validate or adjust the lengths + // if they were user specified as we expect them to already be correct. This + // is because we allow users to do a "reshape" as part of construction and so + // the lengths and strides can mismatch what the underlying multidimensional + // array may have itself. + + nint stride = 1; + + for (int i = 0; i < start.Length; i++) + { + int index = start.Length - (i + 1); + int offset = start[index]; + int length = array.GetLength(index); + + if ((offset < 0) || (offset > length)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + computedOffset += (offset * stride); + stride *= length; + + if (intermediateLengths.Length != 0) + { + intermediateLengths[index] -= offset; + } + } + } + + if ((computedOffset < 0) || (computedOffset > linearLength)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + TensorShape result = new TensorShape(linearLength - computedOffset, lengths, strides, linearRankOrder: []); + + if (intermediateLengthsArray is not null) + { + ArrayPool.Shared.Return(intermediateLengthsArray); + } + linearOffset = computedOffset; + return result; + } + } + else if (start.Length != 0) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + if ((lengths.Length != 0) || (strides.Length != 0)) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + linearOffset = computedOffset; + return default; + } + + public static TensorShape Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new TensorShape(linearLength: -1, lengths, strides, linearRankOrder: []); + + public static TensorShape Create(T[]? array) + { + if (array is not null) + { + int linearLength = array.Length; + + if (linearLength != 0) + { + return new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths: [linearLength], + strides: [1], + linearRankOrder: [0], + rank: 1 + ); + } + } + return default; + } + + public static TensorShape Create(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + if (array is not null) + { + int linearLength = array.Length; + + if ((start < 0) || (start > linearLength)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + linearLength -= start; + + if (linearLength != 0) + { + return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); + } + } + else if (start != 0) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + if ((lengths.Length != 0) || (strides.Length != 0)) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + return default; + } + + public static TensorShape Create(ref T reference, nint linearLength) + { + if (!Unsafe.IsNullRef(ref reference)) + { + if (linearLength != 0) + { + return new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths: [linearLength], + strides: [1], + linearRankOrder: [0], + rank: 1 + ); + } + } + else if (linearLength != 0) + { + ThrowHelper.ThrowArgument_LengthIsNonZeroForNullReference(); + } + return default; + } + + public static TensorShape Create(ref T reference, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + if (!Unsafe.IsNullRef(ref reference)) + { + if (linearLength != 0) + { + return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); + } + } + else if (linearLength != 0) + { + ThrowHelper.ThrowArgument_LengthIsNonZeroForNullReference(); + } + + if ((lengths.Length != 0) || (strides.Length != 0)) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + return default; + } + + public static unsafe TensorShape Create(T* address, nint linearLength) + => Create(ref Unsafe.AsRef(address), linearLength); + + public static unsafe TensorShape Create(T* address, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => Create(ref Unsafe.AsRef(address), linearLength, lengths, strides); + + public override bool Equals([NotNullWhen(true)] object? obj) + => (obj is TensorShape other) && (this == other); + + public bool Equals(TensorShape other) => (this == other); + + public override int GetHashCode() => base.GetHashCode(); - public nint FlattenedLength => TensorSpanHelpers.CalculateTotalLength(Lengths); + public nint GetLinearOffset(ReadOnlySpan state) + where TGetOffsetAndLength : IGetOffsetAndLength + { + ReadOnlySpan lengths = Lengths; + ReadOnlySpan strides = Strides; + + if ((state.Length != lengths.Length) || + (state.Length != strides.Length)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + nint linearOffset = 0; + + for (int i = 0; i < state.Length; i++) + { + nint length = lengths[i]; + nint stride = strides[i]; + + nint offset = TGetOffsetAndLength.GetOffset(state, i, length); + linearOffset += (offset * stride); + } + + return linearOffset; + } + + public TensorShape Slice(ReadOnlySpan state, out nint linearOffset) + where TGetOffsetAndLength : IGetOffsetAndLength + { + int rank = Rank; + + nint[]? intermediateLengthsArray = null; + InlineBuffer intermediateLengthsBuffer; + scoped Span intermediateLengths; + + if (rank > MaxInlineRank) + { + intermediateLengthsArray = ArrayPool.Shared.Rent(rank); + intermediateLengths = intermediateLengthsArray.AsSpan(0, rank); + } + else + { + Unsafe.SkipInit(out intermediateLengthsBuffer); + intermediateLengths = ((Span)intermediateLengthsBuffer)[..rank]; + } + + ReadOnlySpan previousLengths = Lengths; + ReadOnlySpan strides = Strides; + ReadOnlySpan linearRankOrder = LinearRankOrder; + + if ((state.Length != previousLengths.Length) || + (state.Length != linearRankOrder.Length) || + (state.Length != strides.Length)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + // The previous strides and rank order persist in the new shape with + // only the lengths having changed based on the new starting index. + // + // Accordingly, we can also simplify some of the checks as we can + // assume that the previousShape is already valid and the new shape + // will strictly be the same size or smaller. + + nint flattenedLength = 0; + nint linearLength = 0; + nint computedOffset = 0; + + for (int i = 0; i < state.Length; i++) + { + int rankIndex = state.Length - (i + 1); + int linearRankIndex = linearRankOrder[rankIndex]; + + nint previousLength = previousLengths[linearRankIndex]; + nint stride = strides[linearRankIndex]; + + (nint offset, nint length) = TGetOffsetAndLength.GetOffsetAndLength(state, linearRankIndex, previousLength); + flattenedLength *= length; + + intermediateLengths[linearRankIndex] = length; + computedOffset += (offset * stride); + } + + TensorShape result = new TensorShape( + flattenedLength, + linearLength, + intermediateLengths, + strides, + linearRankOrder, + rank + ); + + if (intermediateLengthsArray is not null) + { + ArrayPool.Shared.Return(intermediateLengthsArray); + } - public bool IsEmpty => FlattenedLength == 0; + linearOffset = computedOffset; + return result; + } + + public interface IGetOffsetAndLength + { + static abstract nint GetOffset(ReadOnlySpan state, int rankIndex, nint previousLength); + static abstract (nint Offset, nint Length) GetOffsetAndLength(ReadOnlySpan state, int rankIndex, nint previousLength); + } + + [InlineArray(MaxInlineRank)] + public struct InlineBuffer + { + public T e0; + } + + public readonly struct GetOffsetAndLengthForNInt : IGetOffsetAndLength + { + public static nint GetOffset(ReadOnlySpan indexes, int rankIndex, nint previousLength) + { + nint offset = indexes[rankIndex]; + + if ((offset < 0) || (offset >= previousLength)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + return offset; + } + + public static (nint Offset, nint Length) GetOffsetAndLength(ReadOnlySpan indexes, int rankIndex, nint previousLength) + { + nint offset = GetOffset(indexes, rankIndex, previousLength); + return (offset, previousLength - offset); + } + } + + public readonly struct GetOffsetAndLengthForNIndex : IGetOffsetAndLength + { + public static nint GetOffset(ReadOnlySpan indexes, int rankIndex, nint previousLength) + { + nint offset = indexes[rankIndex].GetOffset(previousLength); + + if ((offset < 0) || (offset >= previousLength)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + return offset; + } + + public static (nint Offset, nint Length) GetOffsetAndLength(ReadOnlySpan indexes, int rankIndex, nint previousLength) + { + nint offset = GetOffset(indexes, rankIndex, previousLength); + return (offset, previousLength - offset); + } + } + + public readonly struct GetOffsetAndLengthForNRange : IGetOffsetAndLength + { + public static nint GetOffset(ReadOnlySpan ranges, int rankIndex, nint previousLength) + { + return ranges[rankIndex].Start.GetOffset(previousLength); + } + + public static (nint Offset, nint Length) GetOffsetAndLength(ReadOnlySpan ranges, int rankIndex, nint previousLength) + { + return ranges[rankIndex].GetOffsetAndLength(previousLength); + } + } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index a71eaebac3822a..0175606188ecd3 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using static System.Numerics.Tensors.TensorOperation; -#pragma warning disable 0809 //warning CS0809: Obsolete member 'TensorSpan.Equals(object)' overrides non-obsolete member 'object.Equals(object)' +#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member namespace System.Numerics.Tensors { @@ -22,489 +22,217 @@ namespace System.Numerics.Tensors [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] public readonly ref struct TensorSpan { - /// A byref or a native ptr. - internal readonly ref T _reference; - internal readonly TensorShape _shape; + /// + public static TensorSpan Empty => default; + internal readonly TensorShape _shape; + internal readonly ref T _reference; - /// - /// Creates a new span over the entirety of the target array. - /// - /// The target array. - /// Returns default when is null. + /// /// is covariant and its type is not exactly T[]. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TensorSpan(T[]? array) : this(array, 0, [array?.Length ?? 0], []) + public TensorSpan(T[]? array) { - } + ThrowHelper.ThrowIfArrayTypeMismatch(array); - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the span. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The strides of each dimension. If default or span of length 0 is provided, then strides will be automatically calculated. - /// Returns default when is null. - /// is covariant and its type is not exactly T[]. - /// - /// The specified or end index is not in the range (<0 or >FlattenedLength). - /// - public TensorSpan(T[]? array, Index startIndex, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) - : this(array, startIndex.GetOffset(array?.Length ?? 0), lengths, strides) - { + _shape = TensorShape.Create(array); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); } - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the span. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The strides of each dimension. If default or span of length 0 is provided, then strides will be automatically calculated. - /// Returns default when is null. + /// /// is covariant and its type is not exactly T[]. - /// - /// The specified or end index is not in the range (<0 or >FlattenedLength). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public TensorSpan(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - if (lengths.IsEmpty && array != null) - lengths = [array.Length]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - if (array == null) - { - if (start != 0 || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - - if (!typeof(T).IsValueType && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); + ThrowHelper.ThrowIfArrayTypeMismatch(array); - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)maxElements >= (ulong)(uint)array.Length && array.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } - else - { - if (((uint)start > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - start)) && array.Length != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - } + _shape = TensorShape.Create(array, start, lengths, strides); + _reference = ref (array is not null) + ? ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (uint)start) + : ref Unsafe.NullRef(); + } - _shape = new TensorShape(array.Length - start, lengths, strides); - _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */); + /// + public TensorSpan(Span span) + { + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length); + _reference = ref reference; } - /// - /// Creates a new over the provided . The new will - /// have a rank of 1 and a length equal to the length of the provided . - /// - /// The target span. - public TensorSpan(Span span) : this(span, [span.Length], []) { } - - /// - /// Creates a new over the provided using the specified lengths and strides. - /// - /// The target span. - /// The lengths of each dimension. - /// The strides for each dimension. The strides will be automatically calculated if not provided. + /// public TensorSpan(Span span, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - if (lengths.IsEmpty) - lengths = [span.Length]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (span.IsEmpty ? maxElements != 0 : maxElements >= span.Length) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - - _shape = new TensorShape(span.Length, lengths, strides); - _reference = ref MemoryMarshal.GetReference(span); + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length, lengths, strides); + _reference = ref reference; } - /// - /// Creates a new over the provided . The new will - /// have a rank of 1 and a length equal to the length of the provided . - /// - /// The target array. - public TensorSpan(Array? array) : - this(array, - ReadOnlySpan.Empty, - array == null ? - [0] : - TensorSpanHelpers.FillLengths(array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], array), - []) + /// + /// is covariant and its type is not exactly T[]. + public TensorSpan(Array? array) { + ThrowHelper.ThrowIfArrayTypeMismatch(array); + + _shape = TensorShape.Create(array); + _reference = ref (array is not null) + ? ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)) + : ref Unsafe.NullRef(); } - /// - /// Creates a new over the provided using the specified start offsets, lengths, and strides. - /// - /// The target array. - /// The starting offset for each dimension. - /// The lengths of each dimension. - /// The strides for each dimension. The strides will be automatically calculated if not provided. + /// + /// is covariant and its type is not exactly T[]. public TensorSpan(Array? array, scoped ReadOnlySpan start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - if (lengths.IsEmpty && array != null) - { - lengths = TensorSpanHelpers.FillLengths( - array.Rank < TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], - array); - } - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - if (array == null) - { - if (!start.IsEmpty || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - if (array.GetType().GetElementType() != typeof(T)) - ThrowHelper.ThrowArrayTypeMismatchException(); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - - nint startOffset = TensorSpanHelpers.ComputeStartOffsetSystemArray(array, start); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)startOffset + (ulong)(uint)maxElements >= (ulong)(uint)array.Length && array.Length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - else - { - if (((uint)startOffset > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - startOffset)) && array.Length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } + ThrowHelper.ThrowIfArrayTypeMismatch(array); - _shape = new TensorShape(array.Length - startOffset, lengths, strides); - _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); + _shape = TensorShape.Create(array, start, lengths, strides, out nint linearOffset); + _reference = ref (array is not null) + ? ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), linearOffset) + : ref Unsafe.NullRef(); } - /// - /// Creates a new over the provided using the specified start offsets, lengths, and strides. - /// - /// The target array. - /// The starting offset for each dimension. - /// The lengths of each dimension. - /// The strides for each dimension. The strides will be automatically calculated if not provided. - public TensorSpan(Array? array, scoped ReadOnlySpan startIndex, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + /// + [CLSCompliant(false)] + public unsafe TensorSpan(T* data, nint dataLength) { - if (lengths.IsEmpty && array != null) - { - lengths = TensorSpanHelpers.FillLengths( - array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], - array); - } - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - if (array == null) - { - if (!startIndex.IsEmpty || linearLength != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - this = default; - return; // returns default - } - if (array.GetType().GetElementType() != typeof(T)) - ThrowHelper.ThrowArrayTypeMismatchException(); - - nint startOffset = TensorSpanHelpers.ComputeStartOffsetSystemArray(array, startIndex); - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (Environment.Is64BitProcess) - { - // See comment in Span.Slice for how this works. - if ((ulong)(uint)startOffset + (ulong)(uint)maxElements > (ulong)(uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - else - { - if ((uint)startOffset > (uint)array.Length || (uint)maxElements >= (uint)(array.Length - startOffset)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - } - - _shape = new TensorShape(array.Length, lengths, strides); - _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); + _shape = TensorShape.Create(data, dataLength); + _reference = ref Unsafe.AsRef(data); } - /// - /// Creates a new span over the target unmanaged buffer. - /// - /// An unmanaged data that points to memory. - /// The number of elements the unmanaged memory can hold. - /// - /// This constructor is quite dangerous, because the length is not checked. - /// But if this creation is correct, then all subsequent uses are correct. - /// - [CLSCompliant(false)] - public unsafe TensorSpan(T* data, nint dataLength) : this(data, dataLength, [dataLength], []) { } - - /// - /// Creates a new span over the target unmanaged buffer. - /// - /// An unmanaged data that points to memory. - /// The number of elements the unmanaged memory can hold. - /// The lengths of the dimensions. If default is provided, it's assumed to have one dimension with a length equal to the length of the data. - /// The lengths of the strides. If nothing is provided, it figures out the default stride configuration. - /// - /// is a reference type or contains pointers and hence cannot be stored in unmanaged memory. - /// - /// - /// The specified length is negative. - /// - /// - /// This constructor is quite dangerous, because the length is not checked. - /// But if this creation is correct, then all subsequent uses are correct. - /// + /// [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe TensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - if (dataLength < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); - - if (lengths.IsEmpty) - lengths = [dataLength]; - - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - - strides = strides.IsEmpty ? (ReadOnlySpan)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides; - TensorSpanHelpers.ValidateStrides(strides, lengths); - - nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); - if (maxElements >= dataLength && dataLength != 0) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); + _shape = TensorShape.Create(data, dataLength, lengths, strides); + _reference = ref Unsafe.AsRef(data); + } - _shape = new TensorShape(dataLength, lengths, strides); - _reference = ref *data; + internal TensorSpan(ref T data, nint dataLength) + { + _shape = TensorShape.Create(ref data, dataLength); + _reference = ref data; } - // Constructor for internal use only. It is not safe to expose publicly. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal TensorSpan(ref T reference, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, nint memoryLength) + internal TensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); + _shape = TensorShape.Create(ref data, dataLength, lengths, strides); + _reference = ref data; + } - _shape = new TensorShape(memoryLength, lengths, strides); + internal TensorSpan(ref T reference, scoped in TensorShape shape) + { _reference = ref reference; + _shape = shape; } - /// - /// Returns a reference to specified element of the TensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or greater than or equal to FlattenedLength. - /// + /// public ref T this[params scoped ReadOnlySpan indexes] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (indexes.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _shape._memoryLength || index < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return ref Unsafe.Add(ref _reference, index /* force zero-extension */); - } + get => ref Unsafe.Add(ref _reference, _shape.GetLinearOffset(indexes)); } - /// - /// Returns a reference to specified element of the TensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or greater than or equal to FlattenedLength. - /// + /// public ref T this[params scoped ReadOnlySpan indexes] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - - if (indexes.Length != Rank) - ThrowHelper.ThrowIndexOutOfRangeException(); - - nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _shape._memoryLength || index < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - return ref Unsafe.Add(ref _reference, index /* force zero-extension */); - } + get => ref Unsafe.Add(ref _reference, _shape.GetLinearOffset(indexes)); } - /// - /// Returns a slice of the TensorSpan. - /// - /// - /// - /// - /// Any index is less than 0 or greater than or equal to FlattenedLength. - /// + /// public TensorSpan this[params scoped ReadOnlySpan ranges] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return Slice(ranges); - } - set - { - value.CopyTo(this[ranges]); - } + get => Slice(ranges); + set => value.CopyTo(Slice(ranges)); } - /// - /// The number of items in the span. - /// + /// public nint FlattenedLength => _shape.FlattenedLength; - /// - /// Gets a value indicating whether this is empty. - /// - /// if this span is empty; otherwise, . + /// public bool IsEmpty => _shape.IsEmpty; - /// - /// Gets the length of each dimension in this . - /// + /// [UnscopedRef] public ReadOnlySpan Lengths => _shape.Lengths; - /// - /// Gets the rank, aka the number of dimensions, of this . - /// + /// public int Rank => Lengths.Length; - /// - /// Gets the strides of this - /// + /// [UnscopedRef] public ReadOnlySpan Strides => _shape.Strides; - /// - /// Compares two spans and returns false if left and right point at the same memory and have the same length. - /// This operator does *not* check to see if the *contents* are equal. - /// - public static bool operator !=(TensorSpan left, TensorSpan right) => !(left == right); - - /// - /// Compares two spans and returns true if left and right point at the same memory and have the same length. - /// This operator does *not* check to see if the *contents* are equal. - /// - public static bool operator ==(TensorSpan left, TensorSpan right) => - left._shape.FlattenedLength == right._shape.FlattenedLength && - left.Rank == right.Rank && - left._shape.Lengths.SequenceEqual(right._shape.Lengths) && - left._shape.Strides.SequenceEqual(right._shape.Strides) && - Unsafe.AreSame(ref left._reference, ref right._reference); - - /// - /// This method is not supported as spans cannot be boxed. To compare two spans, use operator ==. - /// - /// - /// In all cases. - /// - [Obsolete("Equals() on TensorSpan will always throw an exception. Use the equality operator instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) => - throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + /// + public static bool operator ==(in TensorSpan left, in TensorSpan right) + => Unsafe.AreSame(ref left._reference, ref right._reference) + && left._shape == right._shape; - /// - /// This method is not supported as spans cannot be boxed. - /// - /// - /// In all cases. - /// - [Obsolete("GetHashCode() on TensorSpan will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => - throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + /// + public static bool operator !=(in TensorSpan left, in TensorSpan right) => !(left == right); - /// - /// Returns an empty - /// - public static TensorSpan Empty => default; + /// Defines an implicit conversion of an array to a tensor span. + /// The array to convert to a tensor span. + /// The tensor span that corresponds to . + public static implicit operator TensorSpan(T[]? array) => new TensorSpan(array); - /// Gets an enumerator for this span. - public Enumerator GetEnumerator() => new Enumerator(this); + /// Defines an implicit conversion of a tensor to a readonly tensor span. + /// The tensor to convert to a readonly tensor span. + /// The tensor that corresponds to . + public static implicit operator ReadOnlyTensorSpan(scoped in TensorSpan tensor) => + new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape); - /// Enumerates the elements of a . - public ref struct Enumerator - { - /// The span being enumerated. - private readonly TensorSpan _span; - /// The current index that the enumerator is on. - private Span _curIndexes; - /// The total item count. - private nint _items; - - /// Initialize the enumerator. - /// The span to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(TensorSpan span) - { - _span = span; - _items = -1; - _curIndexes = new nint[_span.Rank]; + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan(ref _reference, in _shape); - _curIndexes[_span.Rank - 1] = -1; - } + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsReadOnlyTensorSpan().Slice(start); - /// Advances the enumerator to the next element of the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - TensorSpanHelpers.AdjustIndexes(_span.Rank - 1, 1, _curIndexes, _span.Lengths); + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex) => AsReadOnlyTensorSpan().Slice(startIndex); + + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range) => AsReadOnlyTensorSpan().Slice(range); - if (_items < _span.FlattenedLength) - _items++; + /// + public void Clear() => TensorOperation.Invoke, T>(this); - return _items < _span.FlattenedLength; + /// + public void CopyTo(scoped in TensorSpan destination) + { + if (!TryCopyTo(destination)) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); } + } - /// Gets the element at the current position of the enumerator. - public ref T Current + /// + [Obsolete("Equals() on TensorSpan will always throw an exception. Use the equality operator instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + + /// + public void Fill(T value) => TensorOperation.Invoke, T, T>(this, value); + + /// + public void FlattenTo(scoped Span destination) + { + if (!TryFlattenTo(destination)) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _span[_curIndexes]; + ThrowHelper.ThrowArgumentException_DestinationTooShort(); } } - /// - /// Returns a reference to the 0th element of the TensorSpan. If the TensorSpan is empty, returns null reference. - /// It can be used for pinning and is required to support the use of span within a fixed statement. - /// + /// Gets an enumerator for the tensor span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// + [Obsolete("GetHashCode() on TensorSpan will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + + /// [EditorBrowsable(EditorBrowsableState.Never)] public ref T GetPinnableReference() { @@ -514,320 +242,90 @@ public ref T GetPinnableReference() return ref ret; } - /// - /// Clears the contents of this span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() + /// + public TensorSpan Slice(params scoped ReadOnlySpan start) { - scoped Span curIndexes; - nint[]? curIndexesArray; - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(Rank); - curIndexes = curIndexesArray.AsSpan(0, Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[Rank]; - } - curIndexes.Clear(); - - nint clearedValues = 0; - while (clearedValues < _shape.FlattenedLength) - { - TensorSpanHelpers.Clear(ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), (nuint)Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - clearedValues += Lengths[Rank - 1]; - } - } - - /// - /// Fills the contents of this span with the given value. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Fill(T value) - { - MemoryMarshal.CreateSpan(ref _reference, (int)_shape._memoryLength).Fill(value); + TensorShape shape = _shape.Slice(start, out nint linearOffset); + return new TensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); } - /// - /// Copies the contents of this span into destination span. If the source - /// and destinations overlap, this method behaves as if the original values are in - /// a temporary location before the destination is overwritten. - /// - /// The span to copy items into. - /// - /// The destination TensorSpan is shorter than the source Span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(scoped TensorSpan destination) + /// + public TensorSpan Slice(params scoped ReadOnlySpan startIndex) { - // Using "if (!TryCopyTo(...))" results in two branches: one for the length - // check, and one for the result of TryCopyTo. Since these checks are equivalent, - // we can optimize by performing the check once ourselves then calling Memmove directly. - if (TensorHelpers.IsBroadcastableTo(Lengths, destination.Lengths)) - { - scoped Span curIndexes; - nint[]? curIndexesArray; - - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(destination.Rank); - curIndexes = curIndexesArray.AsSpan(0, destination.Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[destination.Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - nint[] tempLengths = Tensor.GetSmallestBroadcastableLengths(Lengths, destination.Lengths); - - TensorSpan destinationSlice = destination.Slice(tempLengths); - ReadOnlyTensorSpan srcSlice = Tensor.LazyBroadcast(this, tempLengths); - nint copyLength = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Lengths[^1] : 1; - int indexToAdjust = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Rank - 2 : srcSlice.Rank - 1; - - while (copiedValues < destination.FlattenedLength) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destinationSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, destinationSlice.Strides, destinationSlice.Lengths)), ref Unsafe.Add(ref srcSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, srcSlice.Strides, srcSlice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes(indexToAdjust, 1, curIndexes, tempLengths); - copiedValues += copyLength; - } - Debug.Assert(copiedValues == destination.FlattenedLength, "Didn't copy the right amount to the array."); - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - else - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } + TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + return new TensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); } - /// - /// Copies the contents of this span into destination span. If the source - /// and destinations overlap, this method behaves as if the original values are in - /// a temporary location before the destination is overwritten. - /// - /// The span to copy items into. - /// If the destination span is shorter than the source span, this method - /// return false and no data is written to the destination. - public bool TryCopyTo(scoped TensorSpan destination) + /// + public TensorSpan Slice(params scoped ReadOnlySpan range) { - bool retVal = false; - - if (TensorHelpers.IsBroadcastableTo(Lengths, destination.Lengths)) - { - scoped Span curIndexes; - nint[]? curIndexesArray; - - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(destination.Rank); - curIndexes = curIndexesArray.AsSpan(0, destination.Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[destination.Rank]; - } - curIndexes.Clear(); - - nint copiedValues = 0; - nint[] tempLengths = Tensor.GetSmallestBroadcastableLengths(Lengths, destination.Lengths); - - TensorSpan destinationSlice = destination.Slice(tempLengths); - ReadOnlyTensorSpan srcSlice = Tensor.LazyBroadcast(this, tempLengths); - nint copyLength = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Lengths[^1] : 1; - int indexToAdjust = srcSlice.Strides[^1] == 1 && TensorHelpers.IsContiguousAndDense(srcSlice) ? srcSlice.Rank - 2 : srcSlice.Rank - 1; - - while (copiedValues < destination.FlattenedLength) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destinationSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, destinationSlice.Strides, destinationSlice.Lengths)), ref Unsafe.Add(ref srcSlice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, srcSlice.Strides, srcSlice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes(indexToAdjust, 1, curIndexes, tempLengths); - copiedValues += copyLength; - } - Debug.Assert(copiedValues == destination.FlattenedLength, "Didn't copy the right amount to the array."); - retVal = true; - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); - } - return retVal; + TensorShape shape = _shape.Slice(range, out nint linearOffset); + return new TensorSpan( + ref Unsafe.Add(ref _reference, linearOffset), + shape + ); } - /// - /// Implicitly converts an array to a . - /// - public static implicit operator TensorSpan(T[]? array) => new TensorSpan(array); + /// + public override string ToString() => $"System.Numerics.Tensors.TensorSpan<{typeof(T).Name}>[{_shape}]"; - /// - /// Implicitly converts a to a - /// - public static implicit operator ReadOnlyTensorSpan(TensorSpan span) => - new ReadOnlyTensorSpan(ref span._reference, span._shape.Lengths, span._shape.Strides, span._shape._memoryLength); - - /// - /// For , returns a new instance of string that represents the characters pointed to by the span. - /// Otherwise, returns a with the name of the type and the number of elements. - /// - public override string ToString() => $"System.Numerics.Tensors.TensorSpan<{typeof(T).Name}>[{_shape.FlattenedLength}]"; - - /// - /// Returns a reference to specified element of the TensorSpan. - /// - /// The indexes for the slice. - /// - /// - /// Any index is less than 0 or greater than or equal to FlattenedLength. - /// - public TensorSpan Slice(params scoped ReadOnlySpan indexes) - { - NRange[] ranges = new NRange[indexes.Length]; - for (int i = 0; i < indexes.Length; i++) - { - ranges[i] = new NRange(checked((int)indexes[i].GetOffset(Lengths[i])), Lengths[i]); - } - return Slice(ranges); - } + /// + public bool TryCopyTo(scoped in TensorSpan destination) => AsReadOnlyTensorSpan().TryCopyTo(destination); - /// - /// Slices a span according to the provided lengths of the dimensions. - /// - /// The dimension lengths. - /// A based on the provided . - internal TensorSpan Slice(params scoped ReadOnlySpan lengths) - { - NRange[] ranges = new NRange[lengths.Length]; - for (int i = 0; i < lengths.Length; i++) - { - ranges[i] = new NRange(0, lengths[i]); - } - return Slice(ranges); - } + /// + public bool TryFlattenTo(scoped Span destination) => AsReadOnlyTensorSpan().TryFlattenTo(destination); - /// - /// Forms a slice out of the given span. - /// - /// The ranges for the slice. - /// A based on the provided . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TensorSpan Slice(params scoped ReadOnlySpan ranges) + /// Enumerates the elements of a tensor span. + public ref struct Enumerator { - if (ranges.Length != Lengths.Length) - ThrowHelper.ThrowIndexOutOfRangeException(); - - TensorSpan toReturn; - scoped Span lengths; - scoped Span offsets; - nint[]? lengthsArray; - nint[]? offsetsArray; - if (Rank > TensorShape.MaxInlineRank) - { - lengthsArray = ArrayPool.Shared.Rent(Rank); - lengths = lengthsArray.AsSpan(0, Rank); + private readonly TensorSpan _span; + private nint[] _indexes; + private nint _linearOffset; + private nint _itemsEnumerated; - offsetsArray = ArrayPool.Shared.Rent(Rank); - offsets = offsetsArray.AsSpan(0, Rank); - } - else + internal Enumerator(TensorSpan span) { - lengths = stackalloc nint[Rank]; - offsets = stackalloc nint[Rank]; + _span = span; + _indexes = new nint[span.Rank]; - lengthsArray = null; - offsetsArray = null; - } - lengths.Clear(); - offsets.Clear(); + _indexes[^1] = -1; - for (int i = 0; i < ranges.Length; i++) - { - (offsets[i], lengths[i]) = ranges[i].GetOffsetAndLength(Lengths[i]); + _linearOffset = 0; + _itemsEnumerated = -1; } - // When we have an empty Tensor and someone wants to slice all of it, we should return an empty Tensor. - // FlattenedLength is computed everytime so using a local to cache the value. - nint flattenedLength = FlattenedLength; - nint index = 0; + /// Gets the element at the current position of the enumerator. + public readonly ref T Current => ref Unsafe.Add(ref _span._reference, _linearOffset); - if (flattenedLength != 0) + /// Advances the enumerator to the next element of the span. + public bool MoveNext() { - for (int i = 0; i < offsets.Length; i++) + if (_itemsEnumerated == _span._shape.FlattenedLength) { - index += Strides[i] * (offsets[i]); + return false; } - } - - if ((index >= _shape._memoryLength || index < 0) && flattenedLength != 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - - toReturn = new TensorSpan(ref Unsafe.Add(ref _reference, index), lengths, _shape.Strides, _shape._memoryLength - index); - if (offsetsArray != null) - ArrayPool.Shared.Return(offsetsArray); - if (lengthsArray != null) - ArrayPool.Shared.Return(lengthsArray); + _linearOffset = _span._shape.AdjustToNextIndex(_linearOffset, _indexes); - return toReturn; - } - - /// - /// Flattens the contents of this span into the provided . - /// - /// The span to copy items into. - public bool TryFlattenTo(scoped Span destination) - { - bool retVal = false; - if (destination.Length <= _shape.FlattenedLength) - { - FlattenTo(destination); - retVal = true; + _itemsEnumerated++; + return true; } - return retVal; - } - - /// - /// Flattens the contents of this span into the provided . - /// - /// The span to copy items into. - public void FlattenTo(scoped Span destination) - { - if (destination.Length < _shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - if (_shape.FlattenedLength == 0) - return; - - scoped Span curIndexes; - nint[]? curIndexesArray; - if (Rank > TensorShape.MaxInlineRank) - { - curIndexesArray = ArrayPool.Shared.Rent(Rank); - curIndexes = curIndexesArray.AsSpan(0, Rank); - } - else + /// Sets the enumerator to its initial position, which is before the first element in the tensor span. + public void Reset() { - curIndexesArray = null; - curIndexes = stackalloc nint[Rank]; - } - curIndexes.Clear(); + Array.Clear(_indexes); + _indexes[^1] = -1; - nint copiedValues = 0; - while (copiedValues < _shape.FlattenedLength) - { - TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - copiedValues += Lengths[Rank - 1]; + _linearOffset = 0; + _itemsEnumerated = -1; } - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.T.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.T.cs deleted file mode 100644 index f16e0c033d9a0c..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.T.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Numerics.Tensors -{ - internal static partial class TensorSpanHelpers // .T - { - public static unsafe void Memmove(Span destination, ReadOnlySpan source, nint length, nint dstOffset = 0) - { - source.Slice(0, checked((int)length)).CopyTo(destination.Slice(checked((int)dstOffset))); - } - - public static unsafe void Memmove(ref T[] destination, ref T source, nint length) - { - MemoryMarshal.CreateSpan(ref source, checked((int)length)).CopyTo(destination); - } - - public static unsafe void Memmove(ref T destination, ref T source, nint length) - { - MemoryMarshal.CreateSpan(ref source, checked((int)length)).CopyTo(MemoryMarshal.CreateSpan(ref destination, checked((int)length))); - } - - public static unsafe void Memmove(Span destination, ref T source, nint length) - { - MemoryMarshal.CreateSpan(ref source, checked((int)length)).CopyTo(destination); - } - - public static void Clear(ref T dest, nuint len) - { - while (len > 0) - { - nuint toClear = Math.Min(len, int.MaxValue); - MemoryMarshal.CreateSpan(ref dest, (int)toClear).Clear(); - dest = ref Unsafe.Add(ref dest, toClear); - len -= toClear; - } - } - - public static unsafe void Fill(ref T dest, nuint numElements, T value) - { - while (numElements > 0) - { - nuint toFill = Math.Min(numElements, int.MaxValue); - MemoryMarshal.CreateSpan(ref dest, (int)toFill).Fill(value); - dest = ref Unsafe.Add(ref dest, toFill); - numElements -= toFill; - } - } - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs deleted file mode 100644 index 0d5623eeed8303..00000000000000 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs +++ /dev/null @@ -1,306 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace System.Numerics.Tensors -{ - - [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] - internal static partial class TensorSpanHelpers - { - internal static bool AreShapesTheSame(ReadOnlyTensorSpan tensor1, ReadOnlyTensorSpan tensor2) - where T : IEquatable, IEqualityOperators => tensor1._shape.Lengths.SequenceEqual(tensor2._shape.Lengths); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint CalculateTotalLength(ReadOnlySpan lengths) - { - if (lengths.IsEmpty) - return 0; - nint totalLength = 1; - for (int i = 0; i < lengths.Length; i++) - { - if (lengths[i] < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - totalLength *= lengths[i]; - } - - if (totalLength < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return totalLength; - } - - /// - /// Gets the set of strides that can be used to calculate the offset of n-dimensions in a 1-dimensional layout - /// - /// - public static nint[] CalculateStrides(ReadOnlySpan lengths) - { - nint[] strides = new nint[lengths.Length]; - - if (lengths.Length == 1 && lengths[0] == 0 || lengths.Length == 0) - { - strides[0] = 0; - return strides; - } - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - strides[i] = stride; - stride *= lengths[i]; - } - - return strides; - } - - /// - /// Gets the set of strides that can be used to calculate the offset of n-dimensions in a 1-dimensional layout - /// - /// - public static nint[] CalculateStrides(ReadOnlySpan lengths, nint linearLength) - { - nint[] strides = new nint[lengths.Length]; - - if (linearLength == 0) - return strides; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - strides[i] = stride; - stride *= lengths[i]; - } - - return strides; - } - - /// - /// Calculates the 1-d index for n-d indexes in layout specified by strides. - /// - /// - /// - /// - /// - public static nint ComputeLinearIndex(ReadOnlySpan indexes, ReadOnlySpan strides, ReadOnlySpan lengths) - { - Debug.Assert(strides.Length == indexes.Length); - - nint index = 0; - for (int i = 0; i < indexes.Length; i++) - { - if (indexes[i] >= lengths[i] || indexes[i] < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - index += strides[i] * indexes[i]; - } - - return index; - } - - public static nint ComputeMaxLinearIndex(ReadOnlySpan strides, ReadOnlySpan lengths) - { - Debug.Assert(strides.Length == lengths.Length); - - nint index = 0; - for (int i = 0; i < lengths.Length; i++) - { - index += strides[i] * (lengths[i] - 1); - } - - return index; - } - - /// - /// Calculates the 1-d index for n-d indexes in layout specified by strides. - /// - /// - /// - /// - public static nint ComputeStartOffsetSystemArray(Array array, ReadOnlySpan indexes) - { - Debug.Assert(array.Rank == indexes.Length || indexes.Length == 0); - - if (indexes.Length == 0) - return 0; - - nint index = indexes[indexes.Length - 1]; - for (int i = indexes.Length - 2; i >= 0; i--) - { - if ((indexes[i] != 0 && indexes[i] >= array.GetLength(i)) || indexes[i] < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - index += array.GetLength(i) * indexes[i]; - } - - return index; - } - - /// - /// Calculates the 1-d index for n-d indexes in layout specified by strides. - /// - /// - /// - /// - public static nint ComputeStartOffsetSystemArray(Array array, ReadOnlySpan indexes) - { - Debug.Assert(array.Rank == indexes.Length || indexes.Length == 0); - - if (indexes.Length == 0) - return 0; - - nint index = indexes[indexes.Length - 1].GetOffset(array.GetLength(indexes.Length - 1)); - for (int i = indexes.Length - 2; i >= 0; i--) - { - nint offset = indexes[i].GetOffset(array.GetLength(i)); - if ((offset != 0 && offset >= array.GetLength(i)) || offset < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - index += array.GetLength(i) * offset; - } - - return index; - } - - /// - /// Calculates the 1-d index for n-d indexes in layout specified by strides. - /// - /// - /// - /// - /// - public static nint ComputeLinearIndex(ReadOnlySpan indexes, ReadOnlySpan strides, ReadOnlySpan lengths) - { - Debug.Assert(strides.Length == indexes.Length); - - nint index = 0; - for (int i = 0; i < indexes.Length; i++) - { - nint offset = indexes[i].GetOffset(lengths[i]); - if (offset >= lengths[i] || offset < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - index += strides[i] * offset; - } - - return index; - } - - public static void ValidateStrides(ReadOnlySpan strides, ReadOnlySpan lengths) - { - if (strides.Length != lengths.Length) - ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - - if (strides.Length == 0) - return; - - if (strides[lengths.Length - 1] < 0) - ThrowHelper.ThrowArgument_StrideLessThan0(); - - for (int i = lengths.Length - 1; i > 0; i--) - { - if (strides[i - 1] == 0) - continue; - else if (strides[i - 1] < 0) - ThrowHelper.ThrowArgument_StrideLessThan0(); - if (strides[i - 1] < strides[i] * lengths[i]) - ThrowHelper.ThrowArgument_StrideLessThan0(); - } - } - - /// - /// Takes the span holding the current index and increments it by the addend. If the length of the current spot is greater than the - /// length of that dimension then it rolls that over to the next dimension. - /// - /// The current index from the indexes we are on. - /// How much we are adding to the - /// The current indexes - /// The length of the TensorSpan we are iterating over. - public static void AdjustIndexes(int curIndex, nint addend, Span curIndexes, scoped ReadOnlySpan length) - { - if (addend <= 0 || curIndex < 0) - return; - curIndexes[curIndex] += addend; - - (nint Quotient, nint Remainder) result = Math.DivRem(curIndexes[curIndex], length[curIndex]); - - AdjustIndexes(curIndex - 1, result.Quotient, curIndexes, length); - curIndexes[curIndex] = result.Remainder; - } - - /// - /// Takes the span holding the current index and increments it by the addend. If the length of the current spot is greater than the - /// length of that dimension then it rolls that over to the next dimension. - /// - /// The current index from the indexes we are on. - /// How much we are adding to the - /// The current indexes - /// The length of the TensorSpan we are iterating over. - public static void AdjustIndexes(int curIndex, nint addend, ref nint[] curIndexes, ReadOnlySpan shape) - { - if (addend <= 0 || curIndex < 0) - return; - curIndexes[curIndex] += addend; - - (nint Quotient, nint Remainder) result = Math.DivRem(curIndexes[curIndex], shape[curIndex]); - - AdjustIndexes(curIndex - 1, result.Quotient, ref curIndexes, shape); - curIndexes[curIndex] = result.Remainder; - } - - /// - /// Takes the span holding the current index and decrements it by the addend. If the length of the current spot is greater than the - /// length of that dimension then it rolls that over to the next dimension. - /// - /// The current index from the indexes we are on. - /// How much we are subtracting from the - /// The current indexes - /// The length of the TensorSpan we are iterating over. - public static void AdjustIndexesDown(int curIndex, nint addend, Span curIndexes, ReadOnlySpan shape) - { - if (addend <= 0 || curIndex < 0) - return; - - curIndexes[curIndex] -= addend; - if (curIndexes[curIndex] < 0) - { - curIndexes[curIndex] = shape[curIndex] - 1; - AdjustIndexes(curIndex - 1, 1, curIndexes, shape); - } - } - - /// Fills with the corresponding lengths of the ranks in . - public static ReadOnlySpan FillLengths(Span lengths, Array array) - { - Debug.Assert(array is not null); - Debug.Assert(array.Rank == lengths.Length); - - for (int i = 0; i < lengths.Length; i++) - { - lengths[i] = array.GetLength(i); - } - - return lengths; - } - - /// Fills with values [i] = i. - public static void FillRange(Span span) - { - for (int i = 0; i < span.Length; i++) - { - span[i] = i; - } - } - - /// Fills with values [i] = i. - public static void FillRange(Span span) - { - for (int i = 0; i < span.Length; i++) - { - span[i] = i; - } - } - } -} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs new file mode 100644 index 00000000000000..9de83c2a471441 --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -0,0 +1,391 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Numerics.Tensors +{ + /// + /// Represents a tensor. + /// + [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public sealed class Tensor : ITensor, T> + { + /// Gets an empty tensor. + public static Tensor Empty { get; } = new(); + + internal readonly TensorShape _shape; + internal readonly T[] _values; + + internal readonly int _start; + internal readonly bool _isPinned; + + internal Tensor(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) + { + _shape = TensorShape.Create(lengths, strides); + _values = GC.AllocateArray(checked((int)(_shape.LinearLength)), pinned); + + _start = 0; + _isPinned = pinned; + } + + internal Tensor(T[]? array) + { + _shape = TensorShape.Create(array); + _values = (array is not null) ? array : []; + + _start = 0; + _isPinned = false; + } + + internal Tensor(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(array, start, lengths, strides); + _values = (array is not null) ? array : []; + + _start = start; + _isPinned = false; + } + + internal Tensor(T[] array, int start, in TensorShape shape, bool isPinned) + { + ThrowHelper.ThrowIfArrayTypeMismatch(array); + + _shape = shape; + _values = array; + + _start = start; + _isPinned = isPinned; + } + + private Tensor() + { + _shape = default; + _values = []; + + _start = 0; + _isPinned = false; + } + + /// + public ref T this[params scoped ReadOnlySpan indexes] + { + get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _shape.GetLinearOffset(indexes)); + } + + /// + public ref T this[params scoped ReadOnlySpan indexes] + { + get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _shape.GetLinearOffset(indexes)); + } + + /// + public Tensor this[params ReadOnlySpan ranges] + { + get => Slice(ranges); + set => value.CopyTo(Slice(ranges)); + } + + /// + public nint FlattenedLength => _shape.FlattenedLength; + + /// + public bool IsEmpty => _shape.IsEmpty; + + /// + public bool IsPinned => _isPinned; + + /// + public ReadOnlySpan Lengths => _shape.Lengths; + + /// + public int Rank => _shape.Rank; + + /// + public ReadOnlySpan Strides => _shape.Strides; + + /// Defines an implicit conversion of an array to a tensor. + /// The array to convert to a tensor. + /// The tensor span that corresponds to . + public static implicit operator Tensor(T[] array) => Tensor.Create(array); + + /// Defines an implicit conversion of a tensor to a tensor span. + /// The tensor to convert to a tensor span. + /// The tensor that corresponds to . + public static implicit operator TensorSpan(Tensor tensor) => tensor.AsTensorSpan(); + + /// + public static implicit operator ReadOnlyTensorSpan(Tensor tensor) => tensor.AsReadOnlyTensorSpan(); + + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), in _shape); + + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsReadOnlyTensorSpan().Slice(start); + + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex) => AsReadOnlyTensorSpan().Slice(startIndex); + + /// + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range) => AsReadOnlyTensorSpan().Slice(range); + + /// + public TensorSpan AsTensorSpan() => new TensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), in _shape); + + /// + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + + /// + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex) => AsTensorSpan().Slice(startIndex); + + /// + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan range) => AsTensorSpan().Slice(range); + + /// + public unsafe void Clear() => AsTensorSpan().Clear(); + + /// + public void CopyTo(scoped in TensorSpan destination) + { + if (!TryCopyTo(destination)) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// + public void Fill(T value) => AsTensorSpan().Fill(value); + + /// + public void FlattenTo(scoped Span destination) + { + if (!TryFlattenTo(destination)) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// Gets an enumerator for the readonly tensor. + public IEnumerator GetEnumerator() => new Enumerator(this); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ref T GetPinnableReference() + { + // Ensure that the native code has just one forward branch that is predicted-not-taken. + ref T ret = ref Unsafe.NullRef(); + if (_shape.FlattenedLength != 0) ret = ref MemoryMarshal.GetArrayDataReference(_values); + return ref ret; + } + + /// + public unsafe MemoryHandle GetPinnedHandle() + { + GCHandle handle = GCHandle.Alloc(_values, GCHandleType.Pinned); + return new MemoryHandle(Unsafe.AsPointer(ref GetPinnableReference()), handle); + } + + /// + public Tensor Slice(params ReadOnlySpan start) + { + TensorShape shape = _shape.Slice(start, out nint linearOffset); + + // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. + Debug.Assert((int)(linearOffset) == linearOffset); + + return new Tensor( + _values, + (int)(linearOffset), + in shape, + _isPinned + ); + } + + /// + public Tensor Slice(params ReadOnlySpan startIndex) + { + TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + + // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. + Debug.Assert((int)(linearOffset) == linearOffset); + + return new Tensor( + _values, + (int)(linearOffset), + in shape, + _isPinned + ); + } + + /// + public Tensor Slice(params ReadOnlySpan range) + { + TensorShape shape = _shape.Slice(range, out nint linearOffset); + + // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. + Debug.Assert((int)(linearOffset) == linearOffset); + + return new Tensor( + _values, + (int)(linearOffset), + in shape, + _isPinned + ); + } + + /// + public bool TryCopyTo(scoped in TensorSpan destination) => AsReadOnlyTensorSpan().TryCopyTo(destination); + + /// + public bool TryFlattenTo(scoped Span destination) => AsReadOnlyTensorSpan().TryFlattenTo(destination); + + /// + /// Creates a representation of the ."/> + /// + /// Maximum Length of each dimension + /// A representation of the + public string ToString(params ReadOnlySpan maximumLengths) + { + var sb = new StringBuilder($"System.Numerics.Tensors.Tensor<{typeof(T).Name}>[{_shape}]"); + + sb.AppendLine("{"); + ((ReadOnlyTensorSpan)AsTensorSpan()).ToString(sb, maximumLengths); + sb.AppendLine("}"); + + return sb.ToString(); + } + + // + // IEnumerable + // + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + // + // IEnumerable + // + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + // + // IReadOnlyTensor + // + + object? IReadOnlyTensor.this[params scoped ReadOnlySpan indexes] => this[indexes]; + + object? IReadOnlyTensor.this[params scoped ReadOnlySpan indexes] => this[indexes]; + + // + // IReadOnlyTensor + // + + ref readonly T IReadOnlyTensor, T>.this[params ReadOnlySpan indexes] => ref this[indexes]; + + ref readonly T IReadOnlyTensor, T>.this[params ReadOnlySpan indexes] => ref this[indexes]; + + [EditorBrowsable(EditorBrowsableState.Never)] + ref readonly T IReadOnlyTensor, T>.GetPinnableReference() => ref GetPinnableReference(); + + // + // ITensor + // + + bool ITensor.IsReadOnly => false; + + object? ITensor.this[params scoped ReadOnlySpan indexes] + { + get => this[indexes]; + + set + { + this[indexes] = (T)value!; + } + } + + object? ITensor.this[params scoped ReadOnlySpan indexes] + { + get => this[indexes]; + + set + { + this[indexes] = (T)value!; + } + } + + void ITensor.Fill(object value) => Fill(value is T t ? t : throw new ArgumentException($"Cannot convert {value} to {typeof(T)}")); + + // + // ITensor + // + + static Tensor ITensor, T>.Create(scoped ReadOnlySpan lengths, bool pinned) => Tensor.Create(lengths, pinned); + + static Tensor ITensor, T>.Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) => Tensor.Create(lengths, strides, pinned); + + static Tensor ITensor, T>.CreateUninitialized(scoped ReadOnlySpan lengths, bool pinned) => Tensor.Create(lengths, pinned); + + static Tensor ITensor, T>.CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) => Tensor.Create(lengths, strides, pinned); + + private struct Enumerator : IEnumerator + { + private readonly Tensor _tensor; + private nint[] _indexes; + private nint _linearOffset; + private nint _itemsEnumerated; + + internal Enumerator(Tensor tensor) + { + _tensor = tensor; + _indexes = new nint[tensor.Rank]; + + _linearOffset = tensor._start; + _itemsEnumerated = -1; + } + + /// + public readonly T Current => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_tensor._values), _linearOffset); + + /// + public bool MoveNext() + { + if (_itemsEnumerated == _tensor._shape.FlattenedLength) + { + return false; + } + + _linearOffset = _tensor._shape.AdjustToNextIndex(_linearOffset, _indexes); + + _itemsEnumerated++; + return true; + } + + /// + public void Reset() + { + Array.Clear(_indexes); + _linearOffset = _tensor._start; + _itemsEnumerated = -1; + } + + // + // IDisposable + // + + readonly void IDisposable.Dispose() { } + + // + // IEnumerator + // + + readonly object? IEnumerator.Current => Current; + } + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs index d2396172fb606e..acb746289635d8 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs @@ -28,6 +28,14 @@ public static void ThrowArgument_SpansMustBeNonEmpty() => public static void ThrowArgument_InputAndDestinationSpanMustNotOverlap() => throw new ArgumentException(SR.Argument_InputAndDestinationSpanMustNotOverlap, "destination"); + public static void ThrowIfArrayTypeMismatch(Array? array) + { + if ((array is not null) && !typeof(T).IsValueType && (array.GetType() != typeof(T[]))) + { + ThrowArrayTypeMismatchException(); + } + } + [DoesNotReturn] public static void ThrowArgument_DestinationSpansMustNotOverlap() => throw new ArgumentException(SR.Argument_DestinationSpansMustNotOverlap); @@ -68,6 +76,18 @@ public static void ThrowArgument_LengthsMustEqualArrayLength() throw new ArgumentOutOfRangeException(); } + [DoesNotReturn] + public static void ThrowArgument_LengthIsNegativeOrZero() + { + throw new ArgumentOutOfRangeException(); + } + + [DoesNotReturn] + public static void ThrowArgument_StartIndexOutOfBounds() + { + throw new ArgumentOutOfRangeException(); + } + [DoesNotReturn] public static void ThrowArgument_IndicesLengthMustEqualRank() => throw new ArgumentException(SR.ThrowArgument_IndicesLengthMustEqualRank); @@ -204,13 +224,19 @@ public static void ThrowArgument_InPlaceInvalidShape() } [DoesNotReturn] - public static void ThrowArgument_InvalidStridesAndLengths() + public static void ThrowArgument_InvalidTensorShape() { throw new ArgumentException(SR.ThrowArgument_InvalidStridesAndLengths); } [DoesNotReturn] - public static void ThrowArgument_StrideLessThan0() + public static void ThrowArgument_LengthIsNonZeroForNullReference() + { + throw new ArgumentOutOfRangeException(SR.ThrowArgument_LengthIsNonZeroForNullReference); + } + + [DoesNotReturn] + public static void ThrowArgument_StrideIsNegative() { throw new ArgumentOutOfRangeException(SR.ThrowArgument_StrideLessThan0); } From 4bc491e078a1d37bbd0829d0f5745de9e5f41b1e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 11 Apr 2025 10:50:47 -0700 Subject: [PATCH 02/30] Handle Equals, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, and the *All/*Any variants --- .../System.Numerics.Tensors.sln | 26 +- .../System/Numerics/Tensors/netcore/Tensor.cs | 1839 +++-------------- .../Tensors/netcore/TensorOperation.cs | 487 ++++- 3 files changed, 814 insertions(+), 1538 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln b/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln index cb5cf60398d544..2549fa59ae1328 100644 --- a/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln +++ b/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35906.104 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{9F20CEA1-2216-4432-BBBD-F01E05D17F23}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Numerics", "..\Microsoft.Bcl.Numerics\src\Microsoft.Bcl.Numerics.csproj", "{1578185F-C4FA-4866-936B-E62AAEDD03B7}" @@ -33,11 +37,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{7AC4B2C7-A55 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{841A2FA4-A95F-4612-A8B9-AD2EF769BC71}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{A21C99E7-E22B-470E-BF48-56B00AFE3D34}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{A21C99E7-E22B-470E-BF48-56B00AFE3D34}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{25B37C75-C737-4AE8-9260-74A79870C8B8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{25B37C75-C737-4AE8-9260-74A79870C8B8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{9482D7C5-F37C-40FC-B057-A16C1ED1C121}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9482D7C5-F37C-40FC-B057-A16C1ED1C121}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B}" EndProject @@ -105,23 +109,27 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {9F20CEA1-2216-4432-BBBD-F01E05D17F23} = {DE94CA7D-BB10-4865-85A6-6B694631247F} - {46AD9423-D8C3-44BB-A201-1CCCAB4C6DAF} = {DE94CA7D-BB10-4865-85A6-6B694631247F} - {4AF6A02D-82C8-4898-9EDF-01F107C25061} = {DE94CA7D-BB10-4865-85A6-6B694631247F} {1578185F-C4FA-4866-936B-E62AAEDD03B7} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} - {848DD000-3D22-4A25-A9D9-05AFF857A116} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} {21CB448A-3882-4337-B416-D1A3E0BCFFC5} = {7AC4B2C7-A55C-4C4F-9B02-77F5CBFFF4AB} + {848DD000-3D22-4A25-A9D9-05AFF857A116} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} + {46AD9423-D8C3-44BB-A201-1CCCAB4C6DAF} = {DE94CA7D-BB10-4865-85A6-6B694631247F} + {4AF6A02D-82C8-4898-9EDF-01F107C25061} = {DE94CA7D-BB10-4865-85A6-6B694631247F} {D9283CC0-07E1-417A-B73C-223F3EB7A277} = {841A2FA4-A95F-4612-A8B9-AD2EF769BC71} {DB954E01-898A-4FE2-A3AA-180D041AB08F} = {841A2FA4-A95F-4612-A8B9-AD2EF769BC71} {04FC0651-B9D0-448A-A28B-11B1D4A897F4} = {A21C99E7-E22B-470E-BF48-56B00AFE3D34} {683A7D28-CC55-4375-848D-E659075ECEE4} = {A21C99E7-E22B-470E-BF48-56B00AFE3D34} - {A21C99E7-E22B-470E-BF48-56B00AFE3D34} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} {1CBEAEA8-2CA1-4B07-9930-35A785205852} = {25B37C75-C737-4AE8-9260-74A79870C8B8} {BA7828B1-7953-47A0-AE5A-E22B501C4BD0} = {25B37C75-C737-4AE8-9260-74A79870C8B8} - {25B37C75-C737-4AE8-9260-74A79870C8B8} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} {57E57290-3A6A-43F8-8764-D4DC8151F89C} = {9482D7C5-F37C-40FC-B057-A16C1ED1C121} + {A21C99E7-E22B-470E-BF48-56B00AFE3D34} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} + {25B37C75-C737-4AE8-9260-74A79870C8B8} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} {9482D7C5-F37C-40FC-B057-A16C1ED1C121} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {10A5F2C3-5230-4916-9D4D-BBDB94851037} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{683a7d28-cc55-4375-848d-e659075ecee4}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{ba7828b1-7953-47a0-ae5a-e22b501c4bd0}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index a40430301cd24d..e44211afb4a4df 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -663,37 +663,8 @@ public static ref readonly TensorSpan Equals(scoped in ReadOnlyTensorSp public static bool EqualsAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IEqualityOperators { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] != broadcastedRight[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; + TensorOperation.ValidateCompatibility(x, y); + return TensorOperation.Invoke, T>(x, y); } /// @@ -705,35 +676,7 @@ public static bool EqualsAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpa /// Second to compare against. /// where the value is true if all elements in are equal to . public static bool EqualsAll(in ReadOnlyTensorSpan x, T y) - where T : IEqualityOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] != y) - return false; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } + where T : IEqualityOperators => TensorOperation.Invoke, T>(x, y); #endregion #region EqualsAny @@ -748,36 +691,12 @@ public static bool EqualsAll(in ReadOnlyTensorSpan x, T y) public static bool EqualsAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IEqualityOperators { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] == broadcastedRight[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + // The main loop early exits at the first false condition, so the TensorOperation + // checks x != y and returns false on first equal. This means we want to negate + // whatever the main loop returns as `true` means none are equal. - return false; + TensorOperation.ValidateCompatibility(x, y); + return !TensorOperation.Invoke, T>(x, y); } /// @@ -789,35 +708,7 @@ public static bool EqualsAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpa /// Value to compare against. /// where the value is true if any elements in are equal to . public static bool EqualsAny(in ReadOnlyTensorSpan x, T y) - where T : IEqualityOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] == y) - return true; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + where T : IEqualityOperators => !TensorOperation.Invoke, T>(x, y); #endregion #region FilteredUpdate @@ -891,19 +782,9 @@ public static ref readonly TensorSpan FilteredUpdate(in this TensorSpan public static Tensor GreaterThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - Tensor result; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - GreaterThan(x, y, result); - return result; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; } /// @@ -920,48 +801,8 @@ public static Tensor GreaterThan(in ReadOnlyTensorSpan x, in ReadOnl public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IComparisonOperators { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] > right[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); return ref destination; } @@ -977,9 +818,9 @@ public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTen public static Tensor GreaterThan(in ReadOnlyTensorSpan x, T y) where T : IComparisonOperators { - Tensor result = Tensor.Create(x.Lengths, false); - GreaterThan(x, y, result); - return result; + Tensor destination = Tensor.Create(x.Lengths, false); + GreaterThan(x, y, destination); + return destination; } /// @@ -995,33 +836,8 @@ public static Tensor GreaterThan(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IComparisonOperators { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] > y; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); return ref destination; } @@ -1035,12 +851,7 @@ public static ref readonly TensorSpan GreaterThan(scoped in ReadOnlyTen /// where the value is true if the elements in are greater than /// and false if they are not. public static Tensor GreaterThan(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - GreaterThan(x, y, result); - return result; - } + where T : IComparisonOperators => LessThan(y, x); /// /// Compares to see which elements are greater than . @@ -1053,37 +864,89 @@ public static Tensor GreaterThan(T x, in ReadOnlyTensorSpan y) /// where the value is true if the elements in are greater than /// and false if they are not. public static ref readonly TensorSpan GreaterThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators => ref LessThan(y, x, destination); + #endregion + + #region GreaterThanAll + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; + TensorOperation.ValidateCompatibility(x, y); + return TensorOperation.Invoke, T>(x, y); + } - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => TensorOperation.Invoke, T>(x, y); - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x > y[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } + /// + /// Compares the elements of two to see if all elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => LessThanAll(y, x); + #endregion - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + #region GreaterThanAny + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Second to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators + { + // The main loop early exits at the first false condition, so the TensorOperation + // checks !(x > y) and returns false on first equal. This means we want to negate + // whatever the main loop returns as `true` means none are equal. - return ref destination; + TensorOperation.ValidateCompatibility(x, y); + return !TensorOperation.Invoke, T>(x, y); } + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => !TensorOperation.Invoke, T>(x, y); + + /// + /// Compares the elements of two to see if any elements of are greater than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are greater than . + /// + /// First to compare. + /// Value to compare against. + /// where the value is true if any elements in are greater than . + public static bool GreaterThanAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => LessThanAny(y, x); #endregion #region GreaterThanOrEqual @@ -1100,19 +963,9 @@ public static ref readonly TensorSpan GreaterThan(T x, scoped in ReadOn public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - Tensor result; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - GreaterThanOrEqual(x, y, result); - return result; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; } /// @@ -1129,48 +982,8 @@ public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, in public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IComparisonOperators { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] >= right[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); return ref destination; } @@ -1186,9 +999,9 @@ public static ref readonly TensorSpan GreaterThanOrEqual(scoped in Read public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, T y) where T : IComparisonOperators { - Tensor result = Tensor.Create(x.Lengths, false); - GreaterThanOrEqual(x, y, result); - return result; + Tensor destination = Tensor.Create(x.Lengths, false); + GreaterThanOrEqual(x, y, destination); + return destination; } /// @@ -1204,35 +1017,10 @@ public static Tensor GreaterThanOrEqual(in ReadOnlyTensorSpan x, T y public static ref readonly TensorSpan GreaterThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IComparisonOperators { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] >= y; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return ref destination; + } /// /// Compares to see which elements are greater than or equal to . @@ -1244,12 +1032,7 @@ public static ref readonly TensorSpan GreaterThanOrEqual(scoped in Read /// where the value is true if the elements in are greater than /// and false if they are not. public static Tensor GreaterThanOrEqual(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - GreaterThanOrEqual(x, y, result); - return result; - } + where T : IComparisonOperators => LessThanOrEqual(y, x); /// /// Compares to see which elements are greater than or equal to . @@ -1262,160 +1045,46 @@ public static Tensor GreaterThanOrEqual(T x, in ReadOnlyTensorSpan y /// where the value is true if the elements in are greater than /// and false if they are not. public static ref readonly TensorSpan GreaterThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x >= y[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } + where T : IComparisonOperators => ref LessThanOrEqual(y, x, destination); #endregion - #region GreaterThanAny + #region GreaterThanOrEqualAll /// - /// Compares the elements of two to see if any elements of are greater than . + /// Compares the elements of two to see if all elements of are greater than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . + /// It returns a where the value is true if all elements in are greater than . /// /// First to compare. /// Second to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; + TensorOperation.ValidateCompatibility(x, y); + return TensorOperation.Invoke, T>(x, y); } /// - /// Compares the elements of two to see if any elements of are greater than . + /// Compares the elements of two to see if all elements of are greater than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . + /// It returns a where the value is true if all elements in are greater than . /// /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] > y) - return true; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => TensorOperation.Invoke, T>(x, y); /// - /// Compares the elements of two to see if any elements of are greater than . + /// Compares the elements of two to see if all elements of are greater than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are greater than . + /// It returns a where the value is true if all elements in are greater than . /// - /// First to compare. - /// Value to compare against. - /// where the value is true if any elements in are greater than . - public static bool GreaterThanAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x > y[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + /// First to compare. + /// Second to compare against. + /// where the value is true if all elements in are greater than . + public static bool GreaterThanOrEqualAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => LessThanOrEqualAll(y, x); #endregion #region GreaterThanOrEqualAny @@ -1430,36 +1099,12 @@ public static bool GreaterThanAny(T x, in ReadOnlyTensorSpan y) public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + // The main loop early exits at the first false condition, so the TensorOperation + // checks !(x >= y) and returns false on first equal. This means we want to negate + // whatever the main loop returns as `true` means none are equal. - return false; + TensorOperation.ValidateCompatibility(x, y); + return !TensorOperation.Invoke, T>(x, y); } /// @@ -1471,35 +1116,7 @@ public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadO /// Value to compare against. /// where the value is true if any elements in are greater than . public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] >= y) - return true; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + where T : IComparisonOperators => return !TensorOperation.Invoke, T>(x, y); /// /// Compares the elements of two to see if any elements of are greater than . @@ -1510,286 +1127,191 @@ public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, T y) /// Value to compare against. /// where the value is true if any elements in are greater than . public static bool GreaterThanOrEqualAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x >= y[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + where T : IComparisonOperators => LessThanOrEqualAny(y, x); #endregion - #region GreaterThanAll + #region LessThan /// - /// Compares the elements of two to see if all elements of are greater than . + /// Compares the elements of two to see which elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> /// /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + /// Second to compare. + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static Tensor LessThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; } /// - /// Compares the elements of two to see if all elements of are greater than . + /// Compares the elements of two to see which elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> /// /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(in ReadOnlyTensorSpan x, T y) + /// Second to compare. + /// + /// A where the value is true if the elements in are less than and + /// false if they are not. + public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IComparisonOperators { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] <= y) - return false; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return ref destination; + } - return true; + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThan(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators + { + Tensor destination = Tensor.Create(x.Lengths, false); + LessThan(x, y, destination); + return destination; } /// - /// Compares the elements of two to see if all elements of are greater than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> /// - /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanAll(T x, in ReadOnlyTensorSpan y) + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IComparisonOperators { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x <= y[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return ref destination; + } - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// where the value is true if the elements in are less than + /// and false if they are not. + public static Tensor LessThan(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThan(y, x); - return true; - } + /// + /// Compares the elements of a to see which elements are less than . + /// It returns a where the value is true if the elements in are less than + /// and false if they are not."/> + /// + /// to compare. + /// to compare against . + /// + /// where the value is true if the elements in are less than + /// and false if they are not. + public static ref readonly TensorSpan LessThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators => ref GreaterThan(y, x, destination); #endregion - #region GreaterThanOrEqualAll + #region LessThanAll /// - /// Compares the elements of two to see if all elements of are greater than . + /// Compares the elements of two to see if all elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// It returns a where the value is true if all elements in are less than . /// /// First to compare. /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + /// where the value is true if all elements in are less than . + public static bool LessThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedLeft.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Rank); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; + TensorOperation.ValidateCompatibility(x, y); + return TensorOperation.Invoke, T>(x, y); } /// - /// Compares the elements of two to see if all elements of are greater than . + /// Compares the elements of two to see if all elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// It returns a where the value is true if all elements in are less than . /// /// First to compare. - /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] < y) - return false; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => TensorOperation.Invoke, T>(x, y); - return true; - } + /// + /// Compares the elements of two to see if all elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if all elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if all elements in are less than . + public static bool LessThanAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThanAll(y, x); + #endregion + #region LessThanAny /// - /// Compares the elements of two to see if all elements of are greater than . + /// Compares the elements of two to see if any elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are greater than . + /// It returns a where the value is true if any elements in are less than . /// /// First to compare. /// Second to compare against. - /// where the value is true if all elements in are greater than . - public static bool GreaterThanOrEqualAll(T x, in ReadOnlyTensorSpan y) + /// where the value is true if any elements in are less than . + public static bool LessThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); + // The main loop early exits at the first false condition, so the TensorOperation + // checks !(x < y) and returns false on first equal. This means we want to negate + // whatever the main loop returns as `true` means none are equal. - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x < y[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } + TensorOperation.ValidateCompatibility(x, y); + return !TensorOperation.Invoke, T>(x, y); + } - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => !TensorOperation.Invoke, T>(x, y); - return true; - } + /// + /// Compares the elements of two to see if any elements of are less than . + /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. + /// It returns a where the value is true if any elements in are less than . + /// + /// First value to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThanAny(y, x); #endregion - #region LessThan + #region LessThanOrEqual /// /// Compares the elements of two to see which elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. @@ -1800,22 +1322,12 @@ public static bool GreaterThanOrEqualAll(T x, in ReadOnlyTensorSpan y) /// Second to compare. /// A where the value is true if the elements in are less than and /// false if they are not. - public static Tensor LessThan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - Tensor result; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - LessThan(x, y, result); - return result; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, bool>(x, y, destination); + return destination; } /// @@ -1829,51 +1341,11 @@ public static Tensor LessThan(in ReadOnlyTensorSpan x, in ReadOnlyTe /// /// A where the value is true if the elements in are less than and /// false if they are not. - public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IComparisonOperators { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] < right[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); return ref destination; } @@ -1886,12 +1358,12 @@ public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensor /// to compare against . /// where the value is true if the elements in are less than /// and false if they are not. - public static Tensor LessThan(in ReadOnlyTensorSpan x, T y) + public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, T y) where T : IComparisonOperators { - Tensor result = Tensor.Create(x.Lengths, false); - LessThan(x, y, result); - return result; + Tensor destination = Tensor.Create(x.Lengths, false); + LessThanOrEqual(x, y, destination); + return destination; } /// @@ -1904,36 +1376,11 @@ public static Tensor LessThan(in ReadOnlyTensorSpan x, T y) /// /// where the value is true if the elements in are less than /// and false if they are not. - public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) + public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IComparisonOperators { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] < y; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, bool>(x, y, destination); return ref destination; } @@ -1946,13 +1393,8 @@ public static ref readonly TensorSpan LessThan(scoped in ReadOnlyTensor /// to compare against . /// where the value is true if the elements in are less than /// and false if they are not. - public static Tensor LessThan(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - LessThan(x, y, result); - return result; - } + public static Tensor LessThanOrEqual(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThanOrEqual(y, x); /// /// Compares the elements of a to see which elements are less than . @@ -1964,498 +1406,11 @@ public static Tensor LessThan(T x, in ReadOnlyTensorSpan y) /// /// where the value is true if the elements in are less than /// and false if they are not. - public static ref readonly TensorSpan LessThan(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x < y[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region LessThanOrEqual - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - result = Tensor.Create(x.Lengths, false); - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - result = Tensor.Create(newSize, false); - } - - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of two to see which elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// First to compare. - /// Second to compare. - /// - /// A where the value is true if the elements in are less than and - /// false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - scoped ReadOnlyTensorSpan left; - scoped ReadOnlyTensorSpan right; - if (TensorShape.AreLengthsTheSame(x._shape, y._shape)) - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = x; - right = y; - } - else - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - left = LazyBroadcast(x, newSize); - right = LazyBroadcast(y, newSize); - } - - scoped Span curIndex; - nint[]? curIndexArray; - - if (right.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(right.Rank); - curIndex = curIndexArray.AsSpan(0, right.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[right.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < left.FlattenedLength; i++) - { - destination[curIndex] = left[curIndex] <= right[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, right.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThanOrEqual(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(x.Lengths, false); - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, x.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - destination[curIndex] = x[curIndex] <= y; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// where the value is true if the elements in are less than - /// and false if they are not. - public static Tensor LessThanOrEqual(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - Tensor result = Tensor.Create(y.Lengths, false); - LessThanOrEqual(x, y, result); - return result; - } - - /// - /// Compares the elements of a to see which elements are less than . - /// It returns a where the value is true if the elements in are less than - /// and false if they are not."/> - /// - /// to compare. - /// to compare against . - /// - /// where the value is true if the elements in are less than - /// and false if they are not. - public static ref readonly TensorSpan LessThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) - where T : IComparisonOperators - { - if (!TensorShape.AreLengthsTheSame(destination.Lengths, y.Lengths)) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - destination[curIndex] = x <= y[curIndex]; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return ref destination; - } - #endregion - - #region LessThanAny - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] < broadcastedRight[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] < y) - return true; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x < y[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - #endregion - - #region LessThanOrEqualAny - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] <= broadcastedRight[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] <= y) - return true; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } - - /// - /// Compares the elements of two to see if any elements of are less than . - /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if any elements in are less than . - /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if any elements in are less than . - public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i <= y.FlattenedLength; i++) - { - if (x <= y[curIndex]) - return true; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return false; - } + public static ref readonly TensorSpan LessThanOrEqual(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + where T : IComparisonOperators => ref GreaterThanOrEqual(y, x, destination); #endregion - #region LessThanAll + #region LessThanOrEqualAll /// /// Compares the elements of two to see if all elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. @@ -2464,39 +1419,11 @@ public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) /// First to compare. /// Second to compare against. /// where the value is true if all elements in are less than . - public static bool LessThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] >= broadcastedRight[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; + TensorOperation.ValidateCompatibility(x, y); + return TensorOperation.Invoke, T>(x, y); } /// @@ -2507,36 +1434,8 @@ public static bool LessThanAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorS /// First to compare. /// Second value to compare against. /// where the value is true if all elements in are less than . - public static bool LessThanAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] >= y) - return false; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } + public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => TensorOperation.Invoke, T>(x, y); /// /// Compares the elements of two to see if all elements of are less than . @@ -2546,159 +1445,51 @@ public static bool LessThanAll(in ReadOnlyTensorSpan x, T y) /// First value to compare. /// Second value to compare against. /// where the value is true if all elements in are less than . - public static bool LessThanAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x >= y[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } + public static bool LessThanOrEqualAll(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThanOrEqualAll(y, x); #endregion - #region LessThanOrEqualAll + #region LessThanOrEqualAny /// - /// Compares the elements of two to see if all elements of are less than . + /// Compares the elements of two to see if any elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . + /// It returns a where the value is true if any elements in are less than . /// /// First to compare. /// Second to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IComparisonOperators { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(x.Lengths, y.Lengths); - ReadOnlyTensorSpan broadcastedLeft = LazyBroadcast(x, newSize); - ReadOnlyTensorSpan broadcastedRight = LazyBroadcast(y, newSize); - - scoped Span curIndex; - nint[]? curIndexArray; - - if (broadcastedRight.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(broadcastedRight.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, broadcastedRight.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[broadcastedRight.Lengths.Length]; - } - curIndex.Clear(); - - for (int i = 0; i < broadcastedLeft.FlattenedLength; i++) - { - if (broadcastedLeft[curIndex] > broadcastedRight[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, broadcastedRight.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); + // The main loop early exits at the first false condition, so the TensorOperation + // checks !(x <= y) and returns false on first equal. This means we want to negate + // whatever the main loop returns as `true` means none are equal. - return true; + TensorOperation.ValidateCompatibility(x, y); + return !TensorOperation.Invoke, T>(x, y); } /// - /// Compares the elements of two to see if all elements of are less than . + /// Compares the elements of two to see if any elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . + /// It returns a where the value is true if any elements in are less than . /// /// First to compare. /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (x.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(x.Rank); - curIndex = curIndexArray.AsSpan(0, x.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[x.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < x.FlattenedLength; i++) - { - if (x[curIndex] > y) - return false; - TensorShape.AdjustToNextIndex(curIndex, x.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(in ReadOnlyTensorSpan x, T y) + where T : IComparisonOperators => !TensorOperation.Invoke, T>(x, y); /// - /// Compares the elements of two to see if all elements of are less than . + /// Compares the elements of two to see if any elements of are less than . /// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared. - /// It returns a where the value is true if all elements in are less than . + /// It returns a where the value is true if any elements in are less than . /// - /// First value to compare. - /// Second value to compare against. - /// where the value is true if all elements in are less than . - public static bool LessThanOrEqualAll(T x, in ReadOnlyTensorSpan y) - where T : IComparisonOperators - { - scoped Span curIndex; - nint[]? curIndexArray; - - if (y.Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(y.Rank); - curIndex = curIndexArray.AsSpan(0, y.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[y.Rank]; - } - curIndex.Clear(); - - for (int i = 0; i < y.FlattenedLength; i++) - { - if (x > y[curIndex]) - return false; - TensorShape.AdjustToNextIndex(curIndex, y.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - - return true; - } + /// First value to compare. + /// Second value to compare against. + /// where the value is true if any elements in are less than . + public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) + where T : IComparisonOperators => GreaterThanOrEqualAny(y, x); #endregion #region Permute diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 50d3a6793d81d8..13fa4736f16485 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -25,6 +26,65 @@ ref Unsafe.Add(ref x._reference, linearOffset) rentedBuffer.Dispose(); } + public static bool Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + { + bool result = false; + + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + + for (nint i = 0; i < x.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + yLinearOffset = y._shape.AdjustToNextIndex(yLinearOffset, yIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + in Unsafe.Add(ref y._reference, yLinearOffset), + ref result + ); + + if (!result) + { + break; + } + } + + xRentedBuffer.Dispose(); + yRentedBuffer.Dispose(); + + return result; + } + + public static bool Invoke(in ReadOnlyTensorSpan x, TArg y) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar + { + bool result = false; + + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + + for (nint i = 0; i < x.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + y, + ref result + ); + + if (!result) + { + return false; + } + } + + xRentedBuffer.Dispose(); + + return result; + } + public static void Invoke(in TensorSpan destination, TArg scalar) where TOperation : TensorOperation.IUnaryOperation_Scalar { @@ -43,7 +103,7 @@ ref Unsafe.Add(ref destination._reference, linearOffset), } public static void Invoke(in ReadOnlyTensorSpan x, Span destination) - where TOperation : TensorOperation.IUnaryOperation_Span + where TOperation : TensorOperation.IUnaryOperation_Tensor { scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); @@ -60,7 +120,7 @@ ref destination[i] } public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) - where TOperation : TensorOperation.IUnaryOperation_Span + where TOperation : TensorOperation.IUnaryOperation_Tensor { scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); @@ -106,7 +166,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) } public static void Invoke(in ReadOnlyTensorSpan x, TArg y, in TensorSpan destination) - where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); @@ -131,6 +191,10 @@ public static void ValidateCompatibility(in ReadOnlyTensorSpan(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + { + } + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) { @@ -156,7 +220,7 @@ public static void Invoke(Span destination) } public readonly struct CopyTo - : IUnaryOperation_Span + : IUnaryOperation_Tensor { public static void Invoke(ref readonly T source, ref T destination) { @@ -201,6 +265,63 @@ public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IEqualityOperators + { + // The main loop early exits at the first false condition, so we + // check x != y and returns false on first equal. The consumer will + // then negate whatever the main loop returns as `true` means none + // are equal. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left != right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left != right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = (left[i] != right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = (left[i] != right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + public readonly struct Fill : IUnaryOperation_Scalar { @@ -215,6 +336,362 @@ public static void Invoke(Span destination, T value) } } + public readonly struct GreaterThan + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left > right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left > right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] > right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] > right[i]); + } + } + } + + public readonly struct GreaterThanAny + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + // The main loop early exits at the first false condition, so we + // check !(x > y) and returns false on first not greater. The consumer will + // then negate whatever the main loop returns as `true` means none + // are greater. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = !(left > right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = !(left > right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] > right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] > right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + + public readonly struct GreaterThanOrEqual + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left >= right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left >= right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] >= right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] >= right[i]); + } + } + } + + public readonly struct GreaterThanOrEqualAny + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + // The main loop early exits at the first false condition, so we + // check !(x >= y) and returns false on first not greater or equal. + // The consumer will then negate whatever the main loop returns as + // `true` means none are greater or equal. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = !(left >= right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = !(left >= right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] >= right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] >= right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + + public readonly struct LessThan + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left < right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left < right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] < right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] < right[i]); + } + } + } + + public readonly struct LessThanAny + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + // The main loop early exits at the first false condition, so we + // check !(x < y) and returns false on first not lesser. The consumer will + // then negate whatever the main loop returns as `true` means none + // are lesser. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = !(left < right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = !(left < right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] < right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] < right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + + public readonly struct LessThanOrEqual + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left <= right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left <= right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] <= right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] <= right[i]); + } + } + } + + public readonly struct LessThanOrEqualAny + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IComparisonOperators + { + // The main loop early exits at the first false condition, so we + // check !(x <= y) and returns false on first not lesser or equal. + // The consumer will then negate whatever the main loop returns as + // `true` means none are lesser or equal. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = !(left <= right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = !(left <= right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] <= right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = !(left[i] <= right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + public interface IBinaryOperation_Tensor_Scalar { static abstract void Invoke(ref readonly T x, T y, ref TResult destination); @@ -239,7 +716,7 @@ public interface IUnaryOperation_Scalar static abstract void Invoke(Span destination, T x); } - public interface IUnaryOperation_Span + public interface IUnaryOperation_Tensor { static abstract void Invoke(ref readonly T x, ref TResult destination); static abstract void Invoke(ReadOnlySpan x, Span destination); From 47348fe55c6fa8e3f062a0b23934007ba112ecca Mon Sep 17 00:00:00 2001 From: Michael Sharp <51342856+michaelgsharp@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:55:55 -0600 Subject: [PATCH 03/30] Many implementations correctly swapped to new form. (#25) * Refactor Tensor to be more reusable and validate appropriate state * finishing tensor primitives work --------- Co-authored-by: Tanner Gooding --- .../ref/System.Numerics.Tensors.netcore.cs | 8 +- .../src/Resources/Strings.resx | 61 +- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 6 +- .../System/Numerics/Tensors/netcore/Tensor.cs | 1888 ++++++--------- .../Tensors/netcore/TensorOperation.cs | 2037 ++++++++++++++++- .../Numerics/Tensors/netcore/TensorShape.cs | 55 +- .../Numerics/Tensors/netcore/TensorSpan.cs | 4 +- .../Numerics/Tensors/netcore/Tensor_1.cs | 4 +- .../src/System/ThrowHelper.cs | 10 +- 9 files changed, 2736 insertions(+), 1337 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index 41a1c32987c353..f1130b7bbd1e2b 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -340,10 +340,10 @@ public static void BroadcastTo(this System.Numerics.Tensors.Tensor source, public static ref readonly System.Numerics.Tensors.TensorSpan Ieee754Remainder(T x, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IFloatingPointIeee754 { throw null; } public static System.Numerics.Tensors.Tensor ILogB(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IFloatingPointIeee754 { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan ILogB(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IFloatingPointIeee754 { throw null; } - public static int IndexOfMaxMagnitude(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } - public static int IndexOfMax(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } - public static int IndexOfMinMagnitude(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } - public static int IndexOfMin(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } + public static nint IndexOfMaxMagnitude(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } + public static nint IndexOfMax(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } + public static nint IndexOfMinMagnitude(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } + public static nint IndexOfMin(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.INumber { throw null; } public static System.Numerics.Tensors.Tensor LeadingZeroCount(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IBinaryInteger { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan LeadingZeroCount(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IBinaryInteger { throw null; } public static bool LessThanAll(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IComparisonOperators { throw null; } diff --git a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx index 272f6d9db8708c..208b9802175091 100644 --- a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx +++ b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -201,8 +201,8 @@ When no ranges are specified the values tensor must be equal in size as the input tensor. - - Lengths are not broadcast compatible. + + Lengths are not compatible with each other. The number of splits must perfectly divide the dimension. @@ -243,5 +243,4 @@ The provided length is non zero while the data reference is null. - - + \ No newline at end of file diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 33247ce9f8a155..4c7aa1f6e054ec 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -266,7 +266,7 @@ public void CopyTo(scoped in TensorSpan destination) { if (!TryCopyTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } @@ -285,7 +285,7 @@ public void FlattenTo(scoped Span destination) { if (!TryFlattenTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } @@ -348,7 +348,7 @@ ref Unsafe.Add(ref _reference, linearOffset), /// public bool TryCopyTo(scoped in TensorSpan destination) { - if (TensorShape.AreCompatible(_shape, destination._shape)) + if (TensorShape.AreCompatible(destination._shape, _shape, false)) { TensorOperation.Invoke, T, T>(this, destination); return true; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index e44211afb4a4df..ace77450c42f93 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -8,7 +8,11 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Serialization; using System.Text; +using System.Threading.Tasks; +using static System.Numerics.Tensors.TensorOperation; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace System.Numerics.Tensors { @@ -69,7 +73,7 @@ public static void BroadcastTo(this Tensor source, in TensorSpan destin { nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); @@ -84,7 +88,7 @@ public static void BroadcastTo(in this TensorSpan source, in TensorSpan { nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); @@ -99,7 +103,7 @@ public static void BroadcastTo(in this ReadOnlyTensorSpan source, in Tenso { nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); @@ -120,7 +124,7 @@ internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlyS return new TensorSpan(ref input._reference, input._shape.LinearLength, lengths, input.Strides); if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); @@ -163,7 +167,7 @@ internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan return new TensorSpan(ref input._reference, input._shape.LinearLength, shape, input.Strides); if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); nint newSize = TensorSpanHelpers.CalculateFlattenedLength(shape); @@ -206,7 +210,7 @@ internal static Tensor LazyBroadcast(Tensor input, ReadOnlySpan l return new Tensor(input._values, lengths, input._start, isPinned: false); if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); + ThrowHelper.ThrowArgument_LengthsNotCompatible(); nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); @@ -1116,7 +1120,7 @@ public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, in ReadO /// Value to compare against. /// where the value is true if any elements in are greater than . public static bool GreaterThanOrEqualAny(in ReadOnlyTensorSpan x, T y) - where T : IComparisonOperators => return !TensorOperation.Invoke, T>(x, y); + where T : IComparisonOperators => !TensorOperation.Invoke, T>(x, y); /// /// Compares the elements of two to see if any elements of are greater than . @@ -2623,9 +2627,9 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t public static Tensor Abs(in ReadOnlyTensorSpan x) where T : INumberBase { - Tensor output = Tensor.Create(x.Lengths); - Abs(x, output); - return output; + Tensor destination = CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2636,7 +2640,9 @@ public static Tensor Abs(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Abs(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : INumberBase { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Abs); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2648,9 +2654,9 @@ public static ref readonly TensorSpan Abs(scoped in ReadOnlyTensorSpan public static Tensor Acos(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Acos(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2661,7 +2667,9 @@ public static Tensor Acos(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Acos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acos); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2673,9 +2681,9 @@ public static ref readonly TensorSpan Acos(scoped in ReadOnlyTensorSpan public static Tensor Acosh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Acosh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2686,7 +2694,9 @@ public static Tensor Acosh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Acosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Acosh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2698,9 +2708,9 @@ public static ref readonly TensorSpan Acosh(scoped in ReadOnlyTensorSpan AcosPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - AcosPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2711,7 +2721,9 @@ public static Tensor AcosPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan AcosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AcosPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2724,18 +2736,9 @@ public static ref readonly TensorSpan AcosPi(scoped in ReadOnlyTensorSpan< public static Tensor Add(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IAdditionOperators, IAdditiveIdentity { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Add(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -2746,9 +2749,9 @@ public static Tensor Add(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan public static Tensor Add(in ReadOnlyTensorSpan x, T y) where T : IAdditionOperators, IAdditiveIdentity { - Tensor output = Tensor.Create(x.Lengths); - Add(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -2760,7 +2763,9 @@ public static Tensor Add(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IAdditionOperators, IAdditiveIdentity { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Add); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -2772,7 +2777,9 @@ public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IAdditionOperators, IAdditiveIdentity { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Add); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -2784,9 +2791,9 @@ public static ref readonly TensorSpan Add(scoped in ReadOnlyTensorSpan public static Tensor Asin(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Asin(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2797,7 +2804,9 @@ public static Tensor Asin(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Asin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asin); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2809,9 +2818,9 @@ public static ref readonly TensorSpan Asin(scoped in ReadOnlyTensorSpan public static Tensor Asinh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Asinh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2822,7 +2831,9 @@ public static Tensor Asinh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Asinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Asinh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2834,9 +2845,9 @@ public static ref readonly TensorSpan Asinh(scoped in ReadOnlyTensorSpan AsinPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - AsinPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2847,7 +2858,9 @@ public static Tensor AsinPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan AsinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AsinPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2859,9 +2872,9 @@ public static ref readonly TensorSpan AsinPi(scoped in ReadOnlyTensorSpan< public static Tensor Atan(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Atan(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -2872,7 +2885,9 @@ public static Tensor Atan(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Atan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -2885,18 +2900,9 @@ public static ref readonly TensorSpan Atan(scoped in ReadOnlyTensorSpan public static Tensor Atan2(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Atan2(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -2908,7 +2914,9 @@ public static Tensor Atan2(in ReadOnlyTensorSpan x, in ReadOnlyTensorSp public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -2919,10 +2927,9 @@ public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan Atan2(in ReadOnlyTensorSpan x, T y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(x.Lengths); - - Atan2(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -2934,7 +2941,9 @@ public static Tensor Atan2(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -2945,10 +2954,9 @@ public static ref readonly TensorSpan Atan2(scoped in ReadOnlyTensorSpan Atan2(T x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(y.Lengths); - - Atan2(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(y, x, destination); + return destination; } /// @@ -2960,7 +2968,9 @@ public static Tensor Atan2(T x, in ReadOnlyTensorSpan y) public static ref readonly TensorSpan Atan2(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(y, x, destination); + return ref destination; } #endregion @@ -2973,18 +2983,9 @@ public static ref readonly TensorSpan Atan2(T x, scoped in ReadOnlyTensorS public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Atan2Pi(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -2996,7 +2997,9 @@ public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, in ReadOnlyTensor public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3007,10 +3010,9 @@ public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, T y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(x.Lengths); - - Atan2Pi(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3022,7 +3024,9 @@ public static Tensor Atan2Pi(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3033,10 +3037,9 @@ public static ref readonly TensorSpan Atan2Pi(scoped in ReadOnlyTensorSpan public static Tensor Atan2Pi(T x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(y.Lengths); - - Atan2Pi(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(y, x, destination); + return destination; } /// @@ -3048,8 +3051,9 @@ public static Tensor Atan2Pi(T x, in ReadOnlyTensorSpan y) public static ref readonly TensorSpan Atan2Pi(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Atan2Pi); - + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(y, x, destination); + return ref destination; } #endregion @@ -3061,9 +3065,9 @@ public static ref readonly TensorSpan Atan2Pi(T x, scoped in ReadOnlyTenso public static Tensor Atanh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Atanh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3074,7 +3078,9 @@ public static Tensor Atanh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Atanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Atanh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3086,9 +3092,9 @@ public static ref readonly TensorSpan Atanh(scoped in ReadOnlyTensorSpan AtanPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - AtanPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3099,7 +3105,9 @@ public static Tensor AtanPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan AtanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.AtanPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3112,7 +3120,11 @@ public static ref readonly TensorSpan AtanPi(scoped in ReadOnlyTensorSpan< public static T Average(scoped in ReadOnlyTensorSpan x) where T : INumberBase { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Average); + // Get the flattenedLength first so we don't spend time computing if we'll fail due to overflow + + T flattenedLength = T.CreateChecked(x.FlattenedLength); + T sum = Sum(x); + return sum / flattenedLength; } #endregion @@ -3125,18 +3137,9 @@ public static T Average(scoped in ReadOnlyTensorSpan x) public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IBitwiseOperators { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - BitwiseAnd(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3148,7 +3151,9 @@ public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, in ReadOnlyTen public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3159,10 +3164,9 @@ public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorS public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, T y) where T : IBitwiseOperators { - Tensor output = Tensor.Create(x.Lengths); - - BitwiseAnd(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3174,7 +3178,9 @@ public static Tensor BitwiseAnd(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseAnd); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -3187,18 +3193,9 @@ public static ref readonly TensorSpan BitwiseAnd(scoped in ReadOnlyTensorS public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IBitwiseOperators { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - BitwiseOr(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3210,7 +3207,9 @@ public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3221,10 +3220,9 @@ public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSp public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, T y) where T : IBitwiseOperators { - Tensor output = Tensor.Create(x.Lengths); - - BitwiseOr(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3236,7 +3234,9 @@ public static Tensor BitwiseOr(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.BitwiseOr); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -3248,9 +3248,9 @@ public static ref readonly TensorSpan BitwiseOr(scoped in ReadOnlyTensorSp public static Tensor Cbrt(in ReadOnlyTensorSpan x) where T : IRootFunctions { - Tensor output = Tensor.Create(x.Lengths); - Cbrt(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3261,7 +3261,9 @@ public static Tensor Cbrt(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Cbrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IRootFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cbrt); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3273,9 +3275,9 @@ public static ref readonly TensorSpan Cbrt(scoped in ReadOnlyTensorSpan public static Tensor Ceiling(in ReadOnlyTensorSpan x) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Ceiling(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3286,7 +3288,9 @@ public static Tensor Ceiling(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Ceiling(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPoint { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Ceiling); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3300,10 +3304,9 @@ public static Tensor ConvertChecked(in ReadOnlyTensorSpan, IEqualityOperators, INumberBase where TTo : INumberBase { - Tensor output = Tensor.Create(source.Lengths); - - ConvertChecked(source, output); - return output; + Tensor destination = Tensor.CreateUninitialized(source.Lengths); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return destination; } /// @@ -3316,7 +3319,9 @@ public static ref readonly TensorSpan ConvertChecked(scoped in where TFrom : IEquatable, IEqualityOperators, INumberBase where TTo : INumberBase { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertChecked); + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return ref destination; } #endregion @@ -3330,10 +3335,9 @@ public static Tensor ConvertSaturating(in ReadOnlyTensorSpan, IEqualityOperators, INumberBase where TTo : INumberBase { - Tensor output = Tensor.Create(source.Lengths); - - ConvertSaturating(source, output); - return output; + Tensor destination = Tensor.CreateUninitialized(source.Lengths); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return destination; } /// @@ -3346,7 +3350,9 @@ public static ref readonly TensorSpan ConvertSaturating(scoped where TFrom : IEquatable, IEqualityOperators, INumberBase where TTo : INumberBase { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertSaturating); + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return ref destination; } #endregion @@ -3360,10 +3366,9 @@ public static Tensor ConvertTruncating(in ReadOnlyTensorSpan, IEqualityOperators, INumberBase where TTo : INumberBase { - Tensor output = Tensor.Create(source.Lengths); - - ConvertTruncating(source, output); - return output; + Tensor destination = Tensor.CreateUninitialized(source.Lengths); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return destination; } /// @@ -3376,7 +3381,9 @@ public static ref readonly TensorSpan ConvertTruncating(scoped where TFrom : IEquatable, IEqualityOperators, INumberBase where TTo : INumberBase { - return ref TensorPrimitivesHelperSpanInSpanOut(source, destination, TensorPrimitives.ConvertTruncating); + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, TFrom, TTo>(source, destination); + return ref destination; } #endregion @@ -3389,10 +3396,9 @@ public static ref readonly TensorSpan ConvertTruncating(scoped public static Tensor CopySign(in ReadOnlyTensorSpan x, T sign) where T : INumber { - Tensor output = Create(x.Lengths); - - CopySign(x, sign, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, sign, destination); + return destination; } /// @@ -3403,18 +3409,9 @@ public static Tensor CopySign(in ReadOnlyTensorSpan x, T sign) public static Tensor CopySign(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan sign) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(sign.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, sign.Lengths)); - } - - CopySign(x, sign, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, sign, destination); + return destination; } /// @@ -3426,7 +3423,9 @@ public static Tensor CopySign(in ReadOnlyTensorSpan x, in ReadOnlyTenso public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, T sign, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, sign, destination, TensorPrimitives.CopySign); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, sign, destination); + return ref destination; } /// @@ -3438,7 +3437,9 @@ public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpa public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan sign, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, sign, destination, TensorPrimitives.CopySign); + TensorOperation.ValidateCompatibility(x, sign, destination); + TensorOperation.Invoke, T, T>(x, sign, destination); + return ref destination; } #endregion @@ -3450,9 +3451,9 @@ public static ref readonly TensorSpan CopySign(scoped in ReadOnlyTensorSpa public static Tensor Cos(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Cos(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3463,7 +3464,9 @@ public static Tensor Cos(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Cos(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cos); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3475,9 +3478,9 @@ public static ref readonly TensorSpan Cos(scoped in ReadOnlyTensorSpan public static Tensor Cosh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Cosh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3488,7 +3491,9 @@ public static Tensor Cosh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Cosh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Cosh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3498,79 +3503,13 @@ public static ref readonly TensorSpan Cosh(scoped in ReadOnlyTensorSpan /// /// The first /// The second - public static Tensor CosineSimilarity(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : IRootFunctions - { - if (x.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); - - if (y.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); - - if (x.Lengths[1] != y.Lengths[1]) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - nint dim1 = x.Lengths[0]; - nint dim2 = y.Lengths[0]; - - T[] values = new T[dim1 * dim2]; - - Tensor output = Tensor.Create(values, [dim1, dim2]); - - CosineSimilarity(x, y, output); - - return output; - } - - /// - /// Compute cosine similarity between and . - /// - /// The first - /// The second - /// - public static ref readonly TensorSpan CosineSimilarity(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) + public static T CosineSimilarity(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IRootFunctions { - if (x.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(x)); - - if (y.Rank != 2) - ThrowHelper.ThrowArgument_2DTensorRequired(nameof(y)); - - if (x.Lengths[1] != y.Lengths[1]) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - nint dim1 = x.Lengths[0]; - nint dim2 = y.Lengths[0]; - - if (destination.Lengths[0] != dim1 || destination.Lengths[1] != dim2) - ThrowHelper.ThrowArgument_IncompatibleDimensions(x.Lengths[1], y.Lengths[1]); - - Span values = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - - scoped Span leftIndexes = stackalloc nint[2]; - scoped Span rightIndexes = stackalloc nint[2]; - - int outputOffset = 0; - - ReadOnlySpan lspan; - ReadOnlySpan rspan; - int rowLength = (int)x.Lengths[1]; - for (int i = 0; i < dim1; i++) - { - for (int j = 0; j < dim2; j++) - { - lspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref x._reference, TensorSpanHelpers.ComputeLinearIndex(leftIndexes, x.Strides, x.Lengths)), (int)rowLength); - rspan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref y._reference, TensorSpanHelpers.ComputeLinearIndex(rightIndexes, y.Strides, y.Lengths)), (int)rowLength); - values[outputOffset++] = TensorPrimitives.CosineSimilarity(lspan, rspan); - rightIndexes[0]++; - } - rightIndexes[0] = 0; - leftIndexes[0]++; - } - - return ref destination; - + TensorOperation.ValidateCompatibility(x, y); + ValueTuple result = (T.AdditiveIdentity, T.AdditiveIdentity, T.AdditiveIdentity); + TensorOperation.Invoke, T, ValueTuple>(x, y, ref result); + return result.Item1 / (T.Sqrt(result.Item2) * T.Sqrt(result.Item3)); } #endregion @@ -3592,9 +3531,9 @@ public static ref readonly TensorSpan CosineSimilarity(scoped in ReadOnlyT public static Tensor CosPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - CosPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new with the results. @@ -3615,7 +3554,9 @@ public static Tensor CosPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan CosPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.CosPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3627,9 +3568,9 @@ public static ref readonly TensorSpan CosPi(scoped in ReadOnlyTensorSpan DegreesToRadians(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - DegreesToRadians(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3640,7 +3581,9 @@ public static Tensor DegreesToRadians(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan DegreesToRadians(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.DegreesToRadians); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3653,7 +3596,10 @@ public static ref readonly TensorSpan DegreesToRadians(scoped in ReadOnlyT public static T Distance(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y) where T : IRootFunctions { - return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Distance); + TensorOperation.ValidateCompatibility(x, y); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, y, ref result); + return T.Sqrt(result); } #endregion @@ -3666,9 +3612,9 @@ public static T Distance(scoped in ReadOnlyTensorSpan x, scoped in ReadOnl public static Tensor Divide(in ReadOnlyTensorSpan x, T y) where T : IDivisionOperators { - Tensor output = Create(x.Lengths); - Divide(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3679,9 +3625,9 @@ public static Tensor Divide(in ReadOnlyTensorSpan x, T y) public static Tensor Divide(T x, in ReadOnlyTensorSpan y) where T : IDivisionOperators { - Tensor output = Tensor.Create(y.Lengths); - Divide(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3693,18 +3639,9 @@ public static Tensor Divide(T x, in ReadOnlyTensorSpan y) public static Tensor Divide(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IDivisionOperators { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Divide(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3716,7 +3653,9 @@ public static Tensor Divide(in ReadOnlyTensorSpan x, in ReadOnlyTensorS public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IDivisionOperators { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Divide); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3728,7 +3667,9 @@ public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan< public static ref readonly TensorSpan Divide(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IDivisionOperators { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -3741,7 +3682,9 @@ public static ref readonly TensorSpan Divide(T x, scoped in ReadOnlyTensor public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IDivisionOperators { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Divide); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -3754,7 +3697,10 @@ public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan< public static T Dot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IAdditionOperators, IAdditiveIdentity, IMultiplicativeIdentity, IMultiplyOperators { - return TensorPrimitivesHelperTwoSpanInTOut(x, y, TensorPrimitives.Dot); + TensorOperation.ValidateCompatibility(x, y); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, y, ref result); + return result; } #endregion @@ -3766,9 +3712,9 @@ public static T Dot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) public static Tensor Exp(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Exp(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3779,7 +3725,9 @@ public static Tensor Exp(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Exp(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3791,9 +3739,9 @@ public static ref readonly TensorSpan Exp(scoped in ReadOnlyTensorSpan public static Tensor Exp10(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Exp10(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -3804,7 +3752,9 @@ public static Tensor Exp10(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Exp10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3814,9 +3764,9 @@ public static ref readonly TensorSpan Exp10(scoped in ReadOnlyTensorSpan Exp10M1(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Exp10M1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. @@ -3825,7 +3775,9 @@ public static Tensor Exp10M1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Exp10M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp10M1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3835,9 +3787,9 @@ public static ref readonly TensorSpan Exp10M1(scoped in ReadOnlyTensorSpan public static Tensor Exp2(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Exp2(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. @@ -3846,7 +3798,9 @@ public static Tensor Exp2(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Exp2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3856,9 +3810,9 @@ public static ref readonly TensorSpan Exp2(scoped in ReadOnlyTensorSpan public static Tensor Exp2M1(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Exp2M1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. @@ -3867,7 +3821,9 @@ public static Tensor Exp2M1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Exp2M1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Exp2M1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3877,9 +3833,9 @@ public static ref readonly TensorSpan Exp2M1(scoped in ReadOnlyTensorSpan< public static Tensor ExpM1(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - ExpM1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. @@ -3888,7 +3844,9 @@ public static Tensor ExpM1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan ExpM1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ExpM1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3898,9 +3856,9 @@ public static ref readonly TensorSpan ExpM1(scoped in ReadOnlyTensorSpan Floor(in ReadOnlyTensorSpan x) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Floor(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise floor of numbers in the specified tensor. @@ -3909,7 +3867,9 @@ public static Tensor Floor(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Floor(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPoint { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Floor); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -3923,18 +3883,9 @@ public static ref readonly TensorSpan Floor(scoped in ReadOnlyTensorSpan Hypot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IRootFunctions { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Hypot(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -3947,7 +3898,9 @@ public static Tensor Hypot(in ReadOnlyTensorSpan x, in ReadOnlyTensorSp public static ref readonly TensorSpan Hypot(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IRootFunctions { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Hypot); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -3959,18 +3912,9 @@ public static ref readonly TensorSpan Hypot(scoped in ReadOnlyTensorSpan Ieee754Remainder(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Ieee754Remainder(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise remainder of the numbers in the specified tensors. @@ -3981,7 +3925,9 @@ public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, in ReadO public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise remainder of the numbers in the specified tensors. @@ -3990,10 +3936,9 @@ public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyT public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, T y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(x.Lengths); - - Ieee754Remainder(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise remainder of the numbers in the specified tensors. @@ -4003,7 +3948,9 @@ public static Tensor Ieee754Remainder(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise remainder of the numbers in the specified tensors. @@ -4012,10 +3959,9 @@ public static ref readonly TensorSpan Ieee754Remainder(scoped in ReadOnlyT public static Tensor Ieee754Remainder(T x, in ReadOnlyTensorSpan y) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(y.Lengths); - - Ieee754Remainder(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise remainder of the numbers in the specified tensors. @@ -4025,7 +3971,9 @@ public static Tensor Ieee754Remainder(T x, in ReadOnlyTensorSpan y) public static ref readonly TensorSpan Ieee754Remainder(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Ieee754Remainder); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4035,9 +3983,9 @@ public static ref readonly TensorSpan Ieee754Remainder(T x, scoped in Read public static Tensor ILogB(in ReadOnlyTensorSpan x) where T : IFloatingPointIeee754 { - Tensor output = Tensor.Create(x.Lengths, x.Strides); - ILogB(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, int>(x, destination); + return destination; } /// Computes the element-wise integer logarithm of numbers in the specified tensor. @@ -4046,25 +3994,28 @@ public static Tensor ILogB(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan ILogB(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPointIeee754 { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.ILogB); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, int>(x, destination); + return ref destination; } #endregion #region IndexOfMax /// Searches for the index of the largest number in the specified tensor. /// The input . - public static int IndexOfMax(scoped in ReadOnlyTensorSpan x) + public static nint IndexOfMax(scoped in ReadOnlyTensorSpan x) where T : INumber { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); return TensorPrimitives.IndexOfMax(span); } + #endregion #region IndexOfMaxMagnitude /// Searches for the index of the number with the largest magnitude in the specified tensor. /// The input . - public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan x) + public static nint IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); @@ -4075,7 +4026,7 @@ public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan x) #region IndexOfMin /// Searches for the index of the smallest number in the specified tensor. /// The input . - public static int IndexOfMin(scoped in ReadOnlyTensorSpan x) + public static nint IndexOfMin(scoped in ReadOnlyTensorSpan x) where T : INumber { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); @@ -4088,7 +4039,7 @@ public static int IndexOfMin(scoped in ReadOnlyTensorSpan x) /// Searches for the index of the number with the smallest magnitude in the specified tensor. /// /// The input . - public static int IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan x) + public static nint IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); @@ -4104,9 +4055,9 @@ public static int IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor LeadingZeroCount(in ReadOnlyTensorSpan x) where T : IBinaryInteger { - Tensor output = Tensor.Create(x.Lengths); - LeadingZeroCount(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4117,7 +4068,9 @@ public static Tensor LeadingZeroCount(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan LeadingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IBinaryInteger { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LeadingZeroCount); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4129,9 +4082,9 @@ public static ref readonly TensorSpan LeadingZeroCount(scoped in ReadOnlyT public static Tensor Log(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Log(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4142,7 +4095,9 @@ public static Tensor Log(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. @@ -4151,18 +4106,9 @@ public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan public static Tensor Log(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : ILogarithmicFunctions { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Log(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. @@ -4172,7 +4118,9 @@ public static Tensor Log(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Log); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. @@ -4181,10 +4129,9 @@ public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan public static Tensor Log(in ReadOnlyTensorSpan x, T y) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - - Log(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. @@ -4194,7 +4141,9 @@ public static Tensor Log(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Log); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4206,9 +4155,9 @@ public static ref readonly TensorSpan Log(scoped in ReadOnlyTensorSpan public static Tensor Log10(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Log10(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4219,7 +4168,9 @@ public static Tensor Log10(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Log10(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4231,9 +4182,9 @@ public static ref readonly TensorSpan Log10(scoped in ReadOnlyTensorSpan Log10P1(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Log10P1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4244,7 +4195,9 @@ public static Tensor Log10P1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Log10P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log10P1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4256,9 +4209,9 @@ public static ref readonly TensorSpan Log10P1(scoped in ReadOnlyTensorSpan public static Tensor Log2(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Log2(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4269,7 +4222,9 @@ public static Tensor Log2(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Log2(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4281,9 +4236,9 @@ public static ref readonly TensorSpan Log2(scoped in ReadOnlyTensorSpan public static Tensor Log2P1(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Log2P1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4294,7 +4249,9 @@ public static Tensor Log2P1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Log2P1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Log2P1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4306,9 +4263,9 @@ public static ref readonly TensorSpan Log2P1(scoped in ReadOnlyTensorSpan< public static Tensor LogP1(in ReadOnlyTensorSpan x) where T : ILogarithmicFunctions { - Tensor output = Tensor.Create(x.Lengths); - LogP1(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -4319,7 +4276,9 @@ public static Tensor LogP1(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan LogP1(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ILogarithmicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.LogP1); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4327,28 +4286,21 @@ public static ref readonly TensorSpan LogP1(scoped in ReadOnlyTensorSpanSearches for the largest number in the specified tensor. /// The input .. public static T Max(scoped in ReadOnlyTensorSpan x) - where T : INumber + where T : INumber, IMinMaxValue { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Max); + T result = T.MinValue; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } /// Computes the element-wise maximum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor Max(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber + where T : INumber, IMinMaxValue { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Max(x, y, output); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor output); + TensorOperation.Invoke, T, T>(x, y, output); return output; } @@ -4359,7 +4311,9 @@ public static Tensor Max(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Max); + TensorOperation.ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4368,9 +4322,9 @@ public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan public static Tensor Max(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - Max(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4380,7 +4334,9 @@ public static Tensor Max(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Max); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4390,7 +4346,9 @@ public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitude); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4399,18 +4357,9 @@ public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxMagnitude(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4420,7 +4369,9 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4429,9 +4380,9 @@ public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTenso public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MaxMagnitude(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4441,7 +4392,9 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitude); + ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4451,7 +4404,9 @@ public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTenso public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) where T : INumberBase { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxMagnitudeNumber); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4460,18 +4415,9 @@ public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxMagnitudeNumber(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4481,7 +4427,9 @@ public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4490,9 +4438,9 @@ public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnl public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MaxMagnitudeNumber(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise number with the largest magnitude in the specified tensors. @@ -4502,7 +4450,9 @@ public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxMagnitudeNumber); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4510,9 +4460,11 @@ public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnl /// Searches for the largest number in the specified tensor. /// The input .. public static T MaxNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber + where T : INumber, IMinMaxValue { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MaxNumber); + T result = T.MinValue; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4521,18 +4473,9 @@ public static T MaxNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MaxNumber(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4542,7 +4485,9 @@ public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4551,9 +4496,9 @@ public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSp public static Tensor MaxNumber(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MaxNumber(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise maximum of the numbers in the specified tensors. @@ -4563,251 +4508,241 @@ public static Tensor MaxNumber(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MaxNumber); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion #region Min - /// Searches for the smallest number in the specified tensor. - /// The input . + /// Searches for the largest number in the specified tensor. + /// The input .. public static T Min(scoped in ReadOnlyTensorSpan x) - where T : INumber + where T : INumber, IMinMaxValue { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Min); + T result = T.MaxValue; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor Min(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Min(x, y, output); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor output); + TensorOperation.Invoke, T, T>(x, y, output); return output; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Min); + TensorOperation.ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor Min(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - Min(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Min); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion #region MinMagnitude - /// Searches for the number with the smallest magnitude in the specified tensor. - /// The input . + /// Searches for the number with the largest magnitude in the specified tensor. + /// The input .. public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitude); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinMagnitude(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MinMagnitude(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitude); + ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion #region MinMagnitudeNumber - /// Searches for the number with the smallest magnitude in the specified tensor. + /// Searches for the number with the largest magnitude in the specified tensor. /// The input .. public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) where T : INumberBase { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinMagnitudeNumber); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinMagnitudeNumber(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MinMagnitudeNumber(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinMagnitudeNumber); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion #region MinNumber - /// Searches for the smallest number in the specified tensor. - /// The input .. + /// Searches for the largest number in the specified tensor. + /// The input .. public static T MinNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber + where T : INumber, IMinMaxValue { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.MinNumber); + T result = T.MaxValue; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - MinNumber(x, y, output); - return output; + ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.MinNumber); + ValidateCompatibility(in x, in y, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor MinNumber(in ReadOnlyTensorSpan x, T y) where T : INumber { - Tensor output = Tensor.Create(x.Lengths); - MinNumber(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise Minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.MinNumber); + TensorOperation.ValidateCompatibility(in x, in destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4820,9 +4755,9 @@ public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSp public static Tensor Multiply(in ReadOnlyTensorSpan x, T y) where T : IMultiplyOperators, IMultiplicativeIdentity { - Tensor output = Tensor.Create(x.Lengths); - Multiply((ReadOnlyTensorSpan)x, y, output); - return output; + Tensor destination = Tensor.Create(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -4834,18 +4769,9 @@ public static Tensor Multiply(in ReadOnlyTensorSpan x, T y) public static Tensor Multiply(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IMultiplyOperators, IMultiplicativeIdentity { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Multiply((ReadOnlyTensorSpan)x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -4857,9 +4783,8 @@ public static Tensor Multiply(in ReadOnlyTensorSpan x, in ReadOnlyTenso public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IMultiplyOperators, IMultiplicativeIdentity { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.Multiply(span, y, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4873,7 +4798,9 @@ public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpa public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IMultiplyOperators, IMultiplicativeIdentity { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Multiply); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -4883,9 +4810,9 @@ public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpa public static Tensor Negate(in ReadOnlyTensorSpan x) where T : IUnaryNegationOperators { - Tensor output = Tensor.Create(x.Lengths); - Negate(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise negation of each number in the specified tensor. @@ -4894,7 +4821,9 @@ public static Tensor Negate(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Negate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IUnaryNegationOperators { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Negate); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -4906,7 +4835,10 @@ public static ref readonly TensorSpan Negate(scoped in ReadOnlyTensorSpan< public static T Norm(scoped in ReadOnlyTensorSpan x) where T : IRootFunctions { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Norm); + // TODO: TANNER ADVICE + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, T.AdditiveIdentity, ref result); + return T.Sqrt(result); } #endregion @@ -4916,9 +4848,9 @@ public static T Norm(scoped in ReadOnlyTensorSpan x) public static Tensor OnesComplement(in ReadOnlyTensorSpan x) where T : IBitwiseOperators { - Tensor output = Tensor.Create(x.Lengths); - OnesComplement(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise one's complement of numbers in the specified tensor. @@ -4927,7 +4859,9 @@ public static Tensor OnesComplement(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan OnesComplement(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.OnesComplement); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(y, destination); + return ref destination; } #endregion @@ -4937,9 +4871,9 @@ public static ref readonly TensorSpan OnesComplement(scoped in ReadOnlyTen public static Tensor PopCount(in ReadOnlyTensorSpan x) where T : IBinaryInteger { - Tensor output = Tensor.Create(x.Lengths); - PopCount(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise population count of numbers in the specified tensor. @@ -4948,7 +4882,9 @@ public static Tensor PopCount(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan PopCount(scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IBinaryInteger { - return ref TensorPrimitivesHelperSpanInSpanOut(y, destination, TensorPrimitives.PopCount); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(y, destination); + return ref destination; } #endregion @@ -4959,18 +4895,9 @@ public static ref readonly TensorSpan PopCount(scoped in ReadOnlyTensorSpa public static Tensor Pow(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IPowerFunctions { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Pow(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. @@ -4980,7 +4907,9 @@ public static Tensor Pow(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IPowerFunctions { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. @@ -4989,10 +4918,9 @@ public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan public static Tensor Pow(in ReadOnlyTensorSpan x, T y) where T : IPowerFunctions { - Tensor output = Tensor.Create(x.Lengths); - - Pow(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. @@ -5002,7 +4930,9 @@ public static Tensor Pow(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IPowerFunctions { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Pow); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. @@ -5011,10 +4941,9 @@ public static ref readonly TensorSpan Pow(scoped in ReadOnlyTensorSpan public static Tensor Pow(T x, in ReadOnlyTensorSpan y) where T : IPowerFunctions { - Tensor output = Tensor.Create(y.Lengths); - - Pow(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. @@ -5024,7 +4953,9 @@ public static Tensor Pow(T x, in ReadOnlyTensorSpan y) public static ref readonly TensorSpan Pow(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IPowerFunctions { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Pow); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -5034,7 +4965,9 @@ public static ref readonly TensorSpan Pow(T x, scoped in ReadOnlyTensorSpa public static T Product(scoped in ReadOnlyTensorSpan x) where T : IMultiplicativeIdentity, IMultiplyOperators { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Product); + T destination = T.MultiplicativeIdentity; + TensorOperation.Invoke, T, T>(x, ref destination); + return destination; } #endregion @@ -5044,9 +4977,9 @@ public static T Product(scoped in ReadOnlyTensorSpan x) public static Tensor RadiansToDegrees(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - RadiansToDegrees(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. @@ -5055,7 +4988,9 @@ public static Tensor RadiansToDegrees(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan RadiansToDegrees(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.RadiansToDegrees); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5065,9 +5000,9 @@ public static ref readonly TensorSpan RadiansToDegrees(scoped in ReadOnlyT public static Tensor Reciprocal(in ReadOnlyTensorSpan x) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Reciprocal(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise reciprocal of numbers in the specified tensor. @@ -5076,7 +5011,9 @@ public static Tensor Reciprocal(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Reciprocal(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPoint { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Reciprocal); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5087,9 +5024,9 @@ public static ref readonly TensorSpan Reciprocal(scoped in ReadOnlyTensorS public static Tensor RootN(in ReadOnlyTensorSpan x, int n) where T : IRootFunctions { - Tensor output = Tensor.Create(x.Lengths); - RootN(x, n, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, n, destination); + return destination; } /// Computes the element-wise n-th root of the values in the specified tensor. @@ -5099,12 +5036,8 @@ public static Tensor RootN(in ReadOnlyTensorSpan x, int n) public static ref readonly TensorSpan RootN(scoped in ReadOnlyTensorSpan x, int n, in TensorSpan destination) where T : IRootFunctions { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.RootN(span, n, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, n, destination); return ref destination; } #endregion @@ -5117,9 +5050,9 @@ public static ref readonly TensorSpan RootN(scoped in ReadOnlyTensorSpan RotateLeft(in ReadOnlyTensorSpan x, int rotateAmount) where T : IBinaryInteger { - Tensor output = Tensor.Create(x.Lengths); - RotateLeft(x, rotateAmount, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, rotateAmount, destination); + return destination; } /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. @@ -5130,12 +5063,8 @@ public static Tensor RotateLeft(in ReadOnlyTensorSpan x, int rotateAmou public static ref readonly TensorSpan RotateLeft(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) where T : IBinaryInteger { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.RotateLeft(span, rotateAmount, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, rotateAmount, destination); return ref destination; } #endregion @@ -5148,9 +5077,9 @@ public static ref readonly TensorSpan RotateLeft(scoped in ReadOnlyTensorS public static Tensor RotateRight(in ReadOnlyTensorSpan x, int rotateAmount) where T : IBinaryInteger { - Tensor output = Tensor.Create(x.Lengths); - RotateRight(x, rotateAmount, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, rotateAmount, destination); + return destination; } /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. @@ -5161,12 +5090,8 @@ public static Tensor RotateRight(in ReadOnlyTensorSpan x, int rotateAmo public static ref readonly TensorSpan RotateRight(scoped in ReadOnlyTensorSpan x, int rotateAmount, in TensorSpan destination) where T : IBinaryInteger { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.RotateRight(span, rotateAmount, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, rotateAmount, destination); return ref destination; } #endregion @@ -5177,9 +5102,9 @@ public static ref readonly TensorSpan RotateRight(scoped in ReadOnlyTensor public static Tensor Round(in ReadOnlyTensorSpan x) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Round(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise rounding of the numbers in the specified tensor @@ -5188,7 +5113,9 @@ public static Tensor Round(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPoint { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Round); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } /// Computes the element-wise rounding of the numbers in the specified tensor @@ -5198,9 +5125,9 @@ public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan Round(in ReadOnlyTensorSpan x, int digits, MidpointRounding mode) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Round(x, digits, mode, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(digits, mode), destination); + return destination; } /// Computes the element-wise rounding of the numbers in the specified tensor @@ -5211,12 +5138,8 @@ public static Tensor Round(in ReadOnlyTensorSpan x, int digits, Midpoin public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, MidpointRounding mode, in TensorSpan destination) where T : IFloatingPoint { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.Round(span, digits, mode, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(digits, mode), in destination); return ref destination; } @@ -5226,9 +5149,9 @@ public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan Round(in ReadOnlyTensorSpan x, int digits) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Round(x, digits, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(digits, MidpointRounding.ToEven), destination); + return destination; } /// Computes the element-wise rounding of the numbers in the specified tensor @@ -5238,12 +5161,8 @@ public static Tensor Round(in ReadOnlyTensorSpan x, int digits) public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, int digits, in TensorSpan destination) where T : IFloatingPoint { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.Round(span, digits, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(digits, MidpointRounding.ToEven), in destination); return ref destination; } @@ -5253,9 +5172,9 @@ public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan Round(in ReadOnlyTensorSpan x, MidpointRounding mode) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Round(x, mode, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(0, mode), destination); + return destination; } /// Computes the element-wise rounding of the numbers in the specified tensor @@ -5265,12 +5184,8 @@ public static Tensor Round(in ReadOnlyTensorSpan x, MidpointRounding mo public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan x, MidpointRounding mode, in TensorSpan destination) where T : IFloatingPoint { - if (destination._shape.LinearLength < x._shape.LinearLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape.LinearLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - TensorPrimitives.Round(span, mode, ospan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, Tuple, T>(x, Tuple.Create(0, mode), in destination); return ref destination; } #endregion @@ -5281,9 +5196,9 @@ public static ref readonly TensorSpan Round(scoped in ReadOnlyTensorSpan Sigmoid(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - Sigmoid(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. @@ -5292,7 +5207,9 @@ public static Tensor Sigmoid(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Sigmoid(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sigmoid); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5304,9 +5221,9 @@ public static ref readonly TensorSpan Sigmoid(scoped in ReadOnlyTensorSpan public static Tensor Sin(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Sin(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -5317,7 +5234,9 @@ public static Tensor Sin(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Sin(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sin); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5327,9 +5246,9 @@ public static ref readonly TensorSpan Sin(scoped in ReadOnlyTensorSpan public static Tensor Sinh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Sinh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. @@ -5338,7 +5257,9 @@ public static Tensor Sinh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Sinh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sinh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5348,9 +5269,9 @@ public static ref readonly TensorSpan Sinh(scoped in ReadOnlyTensorSpan public static Tensor SinPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - SinPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. @@ -5359,7 +5280,9 @@ public static Tensor SinPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan SinPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SinPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5369,9 +5292,12 @@ public static ref readonly TensorSpan SinPi(scoped in ReadOnlyTensorSpan SoftMax(in ReadOnlyTensorSpan x) where T : IExponentialFunctions { - Tensor output = Tensor.Create(x.Lengths); - SoftMax(x, output); - return output; + T sumExp = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref sumExp); + + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, sumExp, destination); + return destination; } /// Computes the softmax function over the specified non-empty tensor of numbers. @@ -5380,7 +5306,12 @@ public static Tensor SoftMax(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan SoftMax(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IExponentialFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.SoftMax); + T sumExp = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref sumExp); + + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, sumExp, destination); + return ref destination; } #endregion @@ -5392,9 +5323,9 @@ public static ref readonly TensorSpan SoftMax(scoped in ReadOnlyTensorSpan public static Tensor Sqrt(in ReadOnlyTensorSpan x) where T : IRootFunctions { - Tensor output = Tensor.Create(x.Lengths); - Sqrt(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// @@ -5405,7 +5336,9 @@ public static Tensor Sqrt(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Sqrt(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IRootFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Sqrt); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5418,7 +5351,12 @@ public static ref readonly TensorSpan Sqrt(scoped in ReadOnlyTensorSpan public static T StdDev(in ReadOnlyTensorSpan x) where T : IRootFunctions { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.StdDev); + T mean = Average(x); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, mean, ref result); + T variance = result / T.CreateChecked(x.FlattenedLength); + return T.Sqrt(variance); + } #endregion @@ -5431,9 +5369,9 @@ public static T StdDev(in ReadOnlyTensorSpan x) public static Tensor Subtract(in ReadOnlyTensorSpan x, T y) where T : ISubtractionOperators { - Tensor output = Create(x.Lengths); - Subtract(x, y, output); - return output; + Tensor destination = CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -5444,9 +5382,9 @@ public static Tensor Subtract(in ReadOnlyTensorSpan x, T y) public static Tensor Subtract(T x, in ReadOnlyTensorSpan y) where T : ISubtractionOperators { - Tensor output = Create(y.Lengths); - Subtract(x, y, output); - return output; + Tensor destination = CreateUninitialized(y.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -5457,18 +5395,9 @@ public static Tensor Subtract(T x, in ReadOnlyTensorSpan y) public static Tensor Subtract(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : ISubtractionOperators { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Subtract(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -5480,7 +5409,9 @@ public static Tensor Subtract(in ReadOnlyTensorSpan x, in ReadOnlyTenso public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : ISubtractionOperators { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Subtract); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -5492,7 +5423,9 @@ public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpa public static ref readonly TensorSpan Subtract(T x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : ISubtractionOperators { - return ref TensorPrimitivesHelperTInSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); + TensorOperation.ValidateCompatibility(y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -5504,7 +5437,9 @@ public static ref readonly TensorSpan Subtract(T x, scoped in ReadOnlyTens public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : ISubtractionOperators { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Subtract); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } #endregion @@ -5517,7 +5452,9 @@ public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpa public static T Sum(scoped in ReadOnlyTensorSpan x) where T : IAdditionOperators, IAdditiveIdentity { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.Sum); + T destination = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref destination); + return destination; } #endregion @@ -5530,7 +5467,9 @@ public static T Sum(scoped in ReadOnlyTensorSpan x) internal static T SumOfSquares(scoped in ReadOnlyTensorSpan x) where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators { - return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.SumOfSquares); + T result = T.AdditiveIdentity; + TensorOperation.Invoke, T, T>(x, ref result); + return result; } #endregion @@ -5540,9 +5479,9 @@ internal static T SumOfSquares(scoped in ReadOnlyTensorSpan x) public static Tensor Tan(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - Tan(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise tangent of the value in the specified tensor. @@ -5551,7 +5490,9 @@ public static Tensor Tan(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Tan(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tan); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5561,9 +5502,9 @@ public static ref readonly TensorSpan Tan(scoped in ReadOnlyTensorSpan public static Tensor Tanh(in ReadOnlyTensorSpan x) where T : IHyperbolicFunctions { - Tensor output = Tensor.Create(x.Lengths); - Tanh(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. @@ -5572,7 +5513,9 @@ public static Tensor Tanh(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Tanh(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IHyperbolicFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Tanh); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5582,9 +5525,9 @@ public static ref readonly TensorSpan Tanh(scoped in ReadOnlyTensorSpan public static Tensor TanPi(in ReadOnlyTensorSpan x) where T : ITrigonometricFunctions { - Tensor output = Tensor.Create(x.Lengths); - TanPi(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. @@ -5593,7 +5536,9 @@ public static Tensor TanPi(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan TanPi(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : ITrigonometricFunctions { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TanPi); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5603,9 +5548,9 @@ public static ref readonly TensorSpan TanPi(scoped in ReadOnlyTensorSpan TrailingZeroCount(in ReadOnlyTensorSpan x) where T : IBinaryInteger { - Tensor output = Tensor.Create(x.Lengths); - TrailingZeroCount(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise trailing zero count of numbers in the specified tensor. @@ -5614,7 +5559,9 @@ public static Tensor TrailingZeroCount(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan TrailingZeroCount(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IBinaryInteger { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.TrailingZeroCount); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5624,9 +5571,9 @@ public static ref readonly TensorSpan TrailingZeroCount(scoped in ReadOnly public static Tensor Truncate(in ReadOnlyTensorSpan x) where T : IFloatingPoint { - Tensor output = Tensor.Create(x.Lengths); - Truncate(x, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, destination); + return destination; } /// Computes the element-wise truncation of numbers in the specified tensor. @@ -5635,7 +5582,9 @@ public static Tensor Truncate(in ReadOnlyTensorSpan x) public static ref readonly TensorSpan Truncate(scoped in ReadOnlyTensorSpan x, in TensorSpan destination) where T : IFloatingPoint { - return ref TensorPrimitivesHelperSpanInSpanOut(x, destination, TensorPrimitives.Truncate); + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, destination); + return ref destination; } #endregion @@ -5646,18 +5595,9 @@ public static ref readonly TensorSpan Truncate(scoped in ReadOnlyTensorSpa public static Tensor Xor(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : IBitwiseOperators { - Tensor output; - if (x.Lengths.SequenceEqual(y.Lengths)) - { - output = Tensor.Create(x.Lengths); - } - else - { - output = Tensor.Create(GetSmallestBroadcastableLengths(x.Lengths, y.Lengths)); - } - - Xor(x, y, output); - return output; + TensorOperation.ValidateCompatibility(x, y, out Tensor destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// Computes the element-wise XOR of numbers in the specified tensors. @@ -5667,7 +5607,9 @@ public static Tensor Xor(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperTwoSpanInSpanOut(x, y, destination, TensorPrimitives.Xor); + TensorOperation.ValidateCompatibility(x, y, destination); + TensorOperation.Invoke, T, T>(x, y, destination); + return ref destination; } /// @@ -5678,9 +5620,9 @@ public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan public static Tensor Xor(in ReadOnlyTensorSpan x, T y) where T : IBitwiseOperators { - Tensor output = Tensor.Create(x.Lengths); - Xor(x, y, output); - return output; + Tensor destination = Tensor.CreateUninitialized(x.Lengths); + TensorOperation.Invoke, T, T>(x, y, destination); + return destination; } /// @@ -5692,367 +5634,11 @@ public static Tensor Xor(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan Xor(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : IBitwiseOperators { - return ref TensorPrimitivesHelperSpanInTInSpanOut(x, y, destination, TensorPrimitives.Xor); - } - #endregion - - /// - /// Gets the smallest broadcastable lengths for two shapes. - /// - /// The first shape to broadcast. - /// The second shape to broadcast. - /// The smallest lengths these shapes can be broadcast to. - /// The lengths of and are not broadcast compatible. - public static nint[] GetSmallestBroadcastableLengths(ReadOnlySpan shape1, ReadOnlySpan shape2) - { - if (!TensorHelpers.IsBroadcastableTo(shape1, shape2)) - ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible(); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(shape1, shape2.Length); - for (int i = 1; i <= shape1.Length; i++) - { - intermediateShape[^i] = Math.Max(intermediateShape[^i], shape1[^i]); - } - for (int i = 1; i <= shape2.Length; i++) - { - intermediateShape[^i] = Math.Max(intermediateShape[^i], shape2[^i]); - } - - return intermediateShape; - } - - #region TensorPrimitivesHelpers - private delegate void PerformCalculationSpanInSpanOut(ReadOnlySpan input, Span output); - - private delegate void PerformCalculationSpanInTInSpanOut(ReadOnlySpan input, T value, Span output); - - private delegate void PerformCalculationTInSpanInSpanOut(T value, ReadOnlySpan input, Span output); - - private delegate void PerformCalculationTwoSpanInSpanOut(ReadOnlySpan input, ReadOnlySpan inputTwo, Span output); - - private delegate T PerformCalculationTwoSpanInTOut(ReadOnlySpan input, ReadOnlySpan inputTwo); - - private delegate T PerformCalculationSpanInTOut(ReadOnlySpan input); - - private static T TensorPrimitivesHelperSpanInTOut(scoped in ReadOnlyTensorSpan input, PerformCalculationSpanInTOut performCalculation) - { - if (TensorHelpers.IsContiguousAndDense(input)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); - return performCalculation(span); - } - // Flattening needs to happen - else - { - // TODO: Can optimize this to not need to realize the broadcasts - // That will need to be done on a per method basis. - nint flattenedLength = input.FlattenedLength; - T[] flattened = new T[flattenedLength]; - input.FlattenTo(flattened); - return performCalculation(flattened); - } - } - - private static T TensorPrimitivesHelperTwoSpanInTOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, PerformCalculationTwoSpanInTOut performCalculation) - { - // If sizes are the same. - if (TensorHelpers.IsContiguousAndDense(left) && TensorHelpers.IsContiguousAndDense(right) && TensorShape.AreLengthsTheSame(left, right)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left._shape.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right._shape.FlattenedLength); - return performCalculation(span, rspan); - } - // Broadcasting needs to happen. - else - { - // Have a couple different possible cases here. - // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) - // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) - // Because we are returning a single T though we need to actual realize the broadcasts at this point to perform the calculations. - - // TODO: Can optimize this to not need to realize the broadcasts - // That will need to be done on a per method basis. - nint[] newLengths = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); - nint newLength = TensorSpanHelpers.CalculateFlattenedLength(newLengths); - TensorSpan broadcastedLeft = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); - TensorSpan broadcastedRight = new TensorSpan(new T[newLength], newLengths, ReadOnlySpan.Empty); - BroadcastTo(left, broadcastedLeft); - BroadcastTo(right, broadcastedRight); - - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref broadcastedLeft._reference, (int)broadcastedLeft.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref broadcastedRight._reference, (int)broadcastedRight.FlattenedLength); - return performCalculation(span, rspan); - } - } - - private static ref readonly TensorSpan TensorPrimitivesHelperSpanInSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationSpanInSpanOut performCalculation) - { - // Make sure destination has enough memory - if (destination._shape.LinearLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - Span destinationSpan; - ReadOnlySpan inputSpan; - - // Memory is contiguous for both input and destination - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.FlattenedLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape.FlattenedLength); - performCalculation(inputSpan, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(inputSpan, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperSpanInTInSpanOut(scoped in ReadOnlyTensorSpan input, T value, in TensorSpan destination, PerformCalculationSpanInTInSpanOut performCalculation) - { - if (destination._shape.LinearLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - ReadOnlySpan inputSpan; - Span destinationSpan; - - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); - performCalculation(inputSpan, value, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(inputSpan, value, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperTInSpanInSpanOut(T value, scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationTInSpanInSpanOut performCalculation) - { - if (destination._shape.LinearLength < input._shape.FlattenedLength) - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - - // Make sure destination shape works with input shape - TensorSpan slicedDestination = destination.Slice(input._shape.Lengths); - - ReadOnlySpan inputSpan; - Span destinationSpan; - - if (TensorHelpers.IsContiguousAndDense(input) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape.LinearLength); - destinationSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - performCalculation(value, inputSpan, destinationSpan); - } - else - { - scoped Span curIndex; - nint[]? curIndexArray; - if (input.Lengths.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(input.Lengths.Length); - curIndex = curIndexArray.AsSpan(0, input.Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[input.Lengths.Length]; - } - curIndex.Clear(); - - int copiedValues = 0; - nint rowLength = input.Lengths[^1]; - - while (copiedValues < slicedDestination.FlattenedLength) - { - inputSpan = MemoryMarshal.CreateReadOnlySpan(in input[curIndex], (int)rowLength); - destinationSpan = MemoryMarshal.CreateSpan(ref slicedDestination[curIndex], (int)rowLength); - performCalculation(value, inputSpan, destinationSpan); - copiedValues += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(input.Rank - 2, 1, curIndex, input.Lengths); - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } - - return ref destination; - } - - private static ref readonly TensorSpan TensorPrimitivesHelperTwoSpanInSpanOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, in TensorSpan destination, PerformCalculationTwoSpanInSpanOut performCalculation) - { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(left.Lengths, right.Lengths); - - TensorSpan slicedDestination = destination.Slice(newSize); - - // If sizes are the same and memory is contiguous for all tensors - if (TensorShape.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right) && TensorHelpers.IsContiguousAndDense(left) - && TensorHelpers.IsContiguousAndDense(right) && TensorHelpers.IsContiguousAndDense(slicedDestination)) - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, left._shape.LinearLength <= left.FlattenedLength ? (int)left._shape.LinearLength : (int)left.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, right._shape.LinearLength <= right.FlattenedLength ? (int)right._shape.LinearLength : (int)right.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref slicedDestination._reference, (int)slicedDestination._shape.LinearLength); - performCalculation(span, rspan, ospan); - return ref destination; - } - // Broadcasting needs to happen. - else - { - // Have a couple different possible cases here. - // 1 - Both tensors have row contiguous memory (i.e. a 1x5 being broadcast to a 5x5) - // 2 - One tensor has row contiguous memory and the right has column contiguous memory (i.e. a 1x5 and a 5x1) - - ReadOnlyTensorSpan broadcastedLeft = Tensor.LazyBroadcast(left, newSize); - ReadOnlyTensorSpan broadcastedRight = Tensor.LazyBroadcast(right, newSize); - - nint rowLength = newSize[^1]; - Span ospan; - ReadOnlySpan ispan; - Span buffer = new T[rowLength]; - - scoped Span curIndex; - nint[]? curIndexArray; - if (newSize.Length > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(newSize.Length); - curIndex = curIndexArray.AsSpan(0, newSize.Length); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[newSize.Length]; - } - curIndex.Clear(); - - int outputOffset = 0; - // neither row contiguous - if (broadcastedLeft.Strides[^1] == 0 && broadcastedRight.Strides[^1] == 0) - { - Span buffer2 = new T[rowLength]; - - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedLeft[curIndex]); - buffer2.Fill(broadcastedRight[curIndex]); - performCalculation(buffer, buffer2, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // tensor not row contiguous - else if (broadcastedLeft.Strides[^1] == 0) - { - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedLeft[curIndex]); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); - performCalculation(buffer, ispan, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // right not row contiguous - else if (broadcastedRight.Strides[^1] == 0) - { - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - buffer.Fill(broadcastedRight[curIndex]); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); - performCalculation(ispan, buffer, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - // both row contiguous - else - { - Span rspan; - while (outputOffset < slicedDestination.FlattenedLength) - { - ospan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref slicedDestination._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, slicedDestination.Strides, slicedDestination.Lengths)), (int)rowLength); - ispan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedLeft._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedLeft.Strides, broadcastedLeft.Lengths)), (int)rowLength); - rspan = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref broadcastedRight._reference, TensorSpanHelpers.ComputeLinearIndex(curIndex, broadcastedRight.Strides, broadcastedRight.Lengths)), (int)rowLength); - performCalculation(ispan, rspan, ospan); - outputOffset += (int)rowLength; - TensorSpanHelpers.AdjustIndexes(broadcastedLeft.Rank - 2, 1, curIndex, broadcastedLeft.Lengths); - } - } - - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - } + TensorOperation.ValidateCompatibility(x, destination); + TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } - #endregion - #endregion } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 13fa4736f16485..c367c737d95438 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using static System.Numerics.Tensors.TensorOperation; namespace System.Numerics.Tensors { @@ -17,7 +18,7 @@ public static void Invoke(in TensorSpan x) for (nint i = 0; i < x.FlattenedLength; i++) { - linearOffset = x._shape.AdjustToNextIndex(linearOffset, indexes); + linearOffset = x._shape.AdjustToNextIndex(x._shape, linearOffset, indexes); TOperation.Invoke( ref Unsafe.Add(ref x._reference, linearOffset) ); @@ -33,11 +34,12 @@ public static bool Invoke(in ReadOnlyTensorSpan x, in Re scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); for (nint i = 0; i < x.FlattenedLength; i++) { - xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); - yLinearOffset = y._shape.AdjustToNextIndex(yLinearOffset, yIndexes); + xLinearOffset = x._shape.AdjustToNextIndex(destinationShape, xLinearOffset, xIndexes); + yLinearOffset = y._shape.AdjustToNextIndex(destinationShape, yLinearOffset, yIndexes); TOperation.Invoke( in Unsafe.Add(ref x._reference, xLinearOffset), @@ -66,7 +68,7 @@ public static bool Invoke(in ReadOnlyTensorSpan x, TArg for (nint i = 0; i < x.FlattenedLength; i++) { - xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); + xLinearOffset = x._shape.AdjustToNextIndex(x._shape, xLinearOffset, xIndexes); TOperation.Invoke( in Unsafe.Add(ref x._reference, xLinearOffset), @@ -92,7 +94,7 @@ public static void Invoke(in TensorSpan dest for (nint i = 0; i < destination.FlattenedLength; i++) { - linearOffset = destination._shape.AdjustToNextIndex(linearOffset, indexes); + linearOffset = destination._shape.AdjustToNextIndex(destination._shape, linearOffset, indexes); TOperation.Invoke( ref Unsafe.Add(ref destination._reference, linearOffset), scalar @@ -102,226 +104,1987 @@ ref Unsafe.Add(ref destination._reference, linearOffset), rentedBuffer.Dispose(); } - public static void Invoke(in ReadOnlyTensorSpan x, Span destination) + public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(destination._shape, xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destination._shape, destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, ref TResult destination) + where TOperation : TensorOperation.IUnaryReduction_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + + for (nint i = 0; i < x.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(x._shape, xLinearOffset, xIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + ref destination + ); + } + + xRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destination.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(destination._shape, xLinearOffset, xIndexes); + yLinearOffset = y._shape.AdjustToNextIndex(destination._shape, yLinearOffset, yIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destination._shape, destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + in Unsafe.Add(ref y._reference, yLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + yRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, ref TResult result) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); + + nint loopCount = Math.Max(x.FlattenedLength, y.FlattenedLength); + + for (nint i = 0; i < loopCount; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(destinationShape, xLinearOffset, xIndexes); + yLinearOffset = y._shape.AdjustToNextIndex(destinationShape, yLinearOffset, yIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + in Unsafe.Add(ref y._reference, yLinearOffset), + ref result + ); + } + + xRentedBuffer.Dispose(); + yRentedBuffer.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(in ReadOnlyTensorSpan x, TArg y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar => Invoke(in x, y, in destination); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(in ReadOnlyTensorSpan x, int y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Int32 => Invoke(in x, y, in destination); + + public static void Invoke(in ReadOnlyTensorSpan x, T2 y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(destination._shape, xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destination._shape, destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + y, + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(TArg x, in ReadOnlyTensorSpan y, in TensorSpan destination) + where TOperation : TensorOperation.IBinaryOperation_Scalar_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(y.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = y._shape.AdjustToNextIndex(destination._shape, xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToNextIndex(destination._shape, destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + x, + in Unsafe.Add(ref y._reference, xLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, T2 y, ref TResult result) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + + for (nint i = 0; i < x.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(x._shape, xLinearOffset, xIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + y, + ref result + ); + } + + xRentedBuffer.Dispose(); + } + + public static void Invoke(in ReadOnlyTensorSpan x, TArg y, ref TResult result) + where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar => Invoke(in x, y, ref result); + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + { + // can do bidirectional validation + if (!x.Lengths.SequenceEqual(y.Lengths)) + ThrowHelper.ThrowArgument_LengthsNotCompatible(); + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in TensorSpan destination) + { + // x can be broadcast to destination, not vice verse + if (!x.Lengths.SequenceEqual(destination.Lengths)) + ThrowHelper.ThrowArgument_LengthsNotCompatible(); + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) + { + // can do bidirectional validation between x and y, that result can then be broadcast to destination + if (TensorShape.AreCompatible(x._shape, y._shape, true)) + { + if (TensorShape.AreCompatible(x._shape, destination._shape, false)) + { + if (TensorShape.AreCompatible(y._shape, destination._shape, false)) + { + // all three are compatible + return; + } + } + } + ThrowHelper.ThrowArgument_LengthsNotCompatible(); + } + + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, out Tensor destination) + { + // can do bidirectional validation between x and y, that result can then be broadcast to destination + if (TensorShape.AreCompatible(x._shape, y._shape, true)) + { + if (x.Rank > y.Rank) + { + destination = Tensor.CreateUninitialized(x._shape.Lengths); + } + else + { + destination = Tensor.CreateUninitialized(y._shape.Lengths); + } + return; + } + destination = default!; + ThrowHelper.ThrowArgument_LengthsNotCompatible(); + } + + public readonly struct Clear + : IOperation + { + public static void Invoke(ref T destination) + { + destination = default!; + } + + public static void Invoke(Span destination) + { + destination.Clear(); + } + } + + public readonly struct CopyTo + : IUnaryOperation_Tensor + { + public static void Invoke(ref readonly T source, ref T destination) + { + destination = source; + } + + public static void Invoke(ReadOnlySpan source, Span destination) + { + source.CopyTo(destination); + } + } + + public readonly struct Equals + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IEqualityOperators + { + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left == right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left == right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] == right); + } + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = (left[i] == right[i]); + } + } + } + + public readonly struct EqualsAny + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IEqualityOperators + { + // The main loop early exits at the first false condition, so we + // check x != y and returns false on first equal. The consumer will + // then negate whatever the main loop returns as `true` means none + // are equal. + + public static void Invoke(ref readonly T left, T right, ref bool destination) + { + destination = (left != right); + } + + public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + { + destination = (left != right); + } + + public static void Invoke(ReadOnlySpan left, T right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = (left[i] != right); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + + public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + { + Debug.Assert(destination.Length == 1); + bool result = false; + + for (int i = 0; i < destination.Length; i++) + { + result = (left[i] != right[i]); + + if (!result) + { + break; + } + } + + destination[0] = result; + } + } + + #region TensorOperation Primitives + public readonly struct Abs + : IUnaryOperation_Tensor + where T : INumberBase + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Abs(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Abs(x, destination); + } + } + + public readonly struct Acos + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Acos(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Acos(x, destination); + } + } + + public readonly struct Acosh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Acosh(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Acosh(x, destination); + } + } + + public readonly struct AcosPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.AcosPi(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.AcosPi(x, destination); + } + } + + public readonly struct Add + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IAdditionOperators, IAdditiveIdentity + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x + y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Add(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x + y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Add(x, y, destination); + } + } + + public readonly struct Asin + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Asin(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Asin(x, destination); + } + } + + public readonly struct Asinh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Asinh(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Asinh(x, destination); + } + } + + public readonly struct AsinPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.AsinPi(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.AsinPi(x, destination); + } + } + + public readonly struct Atan + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Atan(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Atan(x, destination); + } + } + + public readonly struct Atan2 + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IFloatingPointIeee754 + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Atan2(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = T.Atan2(x[i], y); + } + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Atan2(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Atan2(x, y, destination); + } + } + + public readonly struct Atan2Pi + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IFloatingPointIeee754 + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Atan2Pi(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = T.Atan2Pi(x[i], y); + } + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Atan2Pi(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Atan2Pi(x, y, destination); + } + } + + public readonly struct Atanh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Atanh(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Atanh(x, destination); + } + } + + public readonly struct AtanPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.AtanPi(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.AtanPi(x, destination); + } + } + + public readonly struct BitwiseAnd + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IBitwiseOperators + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x & y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.BitwiseAnd(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x & y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.BitwiseAnd(x, y, destination); + } + } + + public readonly struct BitwiseOr + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IBitwiseOperators + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x | y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.BitwiseOr(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x | y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.BitwiseOr(x, y, destination); + } + } + + public readonly struct Cbrt + : IUnaryOperation_Tensor + where T : IRootFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Cbrt(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Cbrt(x, destination); + } + } + + public readonly struct Ceiling + : IUnaryOperation_Tensor + where T : IFloatingPoint + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Ceiling(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Ceiling(x, destination); + } + } + + public readonly struct ConvertChecked + : IUnaryOperation_Tensor + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + public static void Invoke(ref readonly TFrom x, ref TTo destination) + { + destination = TTo.CreateChecked(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.ConvertChecked(x, destination); + } + } + + public readonly struct ConvertSaturating + : IUnaryOperation_Tensor + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + public static void Invoke(ref readonly TFrom x, ref TTo destination) + { + destination = TTo.CreateSaturating(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.ConvertSaturating(x, destination); + } + } + + public readonly struct ConvertTruncating + : IUnaryOperation_Tensor + where TFrom : IEquatable, IEqualityOperators, INumberBase + where TTo : INumberBase + { + public static void Invoke(ref readonly TFrom x, ref TTo destination) + { + destination = TTo.CreateTruncating(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.ConvertTruncating(x, destination); + } + } + + public readonly struct CopySign + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.CopySign(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.CopySign(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.CopySign(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.CopySign(x, y, destination); + } + } + + public readonly struct Cos + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Cos(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Cos(x, destination); + } + } + + public readonly struct Cosh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Cosh(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Cosh(x, destination); + } + } + + public struct CosineSimilarity + : IBinaryOperation_Tensor_Tensor> + where T : IRootFunctions + { + /// This method effectively computes TensorPrimitives.Dot(x, y) / (.Sqrt(TensorPrimitives.SumOfSquares(x)) * .Sqrt(TensorPrimitives.SumOfSquares(y)). + + public static void Invoke(ref readonly T x, ref readonly T y, ref (T, T, T) destination) + { + destination.Item1 += (x * y); + destination.Item2 += (x * x); + destination.Item3 += (y * y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span<(T, T, T)> destination) + { + destination[0].Item1 += TensorPrimitives.Dot(x, y); + destination[0].Item2 += TensorPrimitives.SumOfSquares(x); + destination[0].Item3 += TensorPrimitives.SumOfSquares(y); + } + } + + public readonly struct CosPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.CosPi(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.CosPi(x, destination); + } + } + + public readonly struct DegreesToRadians + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.DegreesToRadians(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.DegreesToRadians(x, destination); + } + } + + public readonly struct Divide + : IBinaryOperation_Scalar_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IDivisionOperators + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x / y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Divide(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x / y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Divide(x, y, destination); + } + + public static void Invoke(T x, ref readonly T y, ref T destination) + { + destination = x / y; + } + public static void Invoke(T x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Divide(x, y, destination); + } + } + + public struct Dot + : IBinaryOperation_Tensor_Tensor + where T : IAdditionOperators, IAdditiveIdentity, IMultiplicativeIdentity, IMultiplyOperators + { + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination += x * y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + destination[0] += TensorPrimitives.Dot(x, y); + } + } + + public readonly struct Exp + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Exp(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Exp(x, destination); + } + } + + public readonly struct Exp10 + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Exp10(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Exp10(x, destination); + } + } + + public readonly struct Exp10M1 + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Exp10M1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Exp10M1(x, destination); + } + } + + public readonly struct Exp2 + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Exp2(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Exp2(x, destination); + } + } + + public readonly struct Exp2M1 + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Exp2M1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Exp2M1(x, destination); + } + } + + public readonly struct ExpM1 + : IUnaryOperation_Tensor + where T : IExponentialFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.ExpM1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.ExpM1(x, destination); + } + } + + public readonly struct Floor + : IUnaryOperation_Tensor + where T : IFloatingPoint + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Floor(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Floor(x, destination); + } + } + + public readonly struct Hypot + : IBinaryOperation_Tensor_Tensor + where T : IRootFunctions + { + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Hypot(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Hypot(x, y, destination); + } + } + + public readonly struct Ieee754Remainder + : IBinaryOperation_Scalar_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IFloatingPointIeee754 + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Ieee754Remainder(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Ieee754Remainder(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Ieee754Remainder(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Ieee754Remainder(x, y, destination); + } + + public static void Invoke(T x, ref readonly T y, ref T destination) + { + destination = T.Ieee754Remainder(x, y); + } + public static void Invoke(T x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Ieee754Remainder(x, y, destination); + } + } + + public readonly struct ILogB + : IUnaryOperation_Tensor + where T : IFloatingPointIeee754 + { + public static void Invoke(ref readonly T x, ref int destination) + { + destination = T.ILogB(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.ILogB(x, destination); + } + } + + public readonly struct LeadingZeroCount + : IUnaryOperation_Tensor + where T : IBinaryInteger + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.LeadingZeroCount(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.LeadingZeroCount(x, destination); + } + } + + public readonly struct Log + : IUnaryOperation_Tensor, + IBinaryOperation_Tensor_Tensor, + IBinaryOperation_Tensor_Scalar + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Log(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Log(x, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Log(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Log(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Log(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Log(x, y, destination); + } + } + + public readonly struct Log10 + : IUnaryOperation_Tensor + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Log10(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Log10(x, destination); + } + } + + public readonly struct Log10P1 + : IUnaryOperation_Tensor + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Log10P1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Log10P1(x, destination); + } + } + + public readonly struct Log2 + : IUnaryOperation_Tensor + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Log2(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Log2(x, destination); + } + } + + public readonly struct Log2P1 + : IUnaryOperation_Tensor + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Log2P1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Log2P1(x, destination); + } + } + + public readonly struct LogP1 + : IUnaryOperation_Tensor + where T : ILogarithmicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.LogP1(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.LogP1(x, destination); + } + } + + public struct Max + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Max(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.Max(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Max(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Max(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Max(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Max(x, y, destination); + } + } + + public struct MaxMagnitude + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MaxMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MaxMagnitude(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MaxMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MaxMagnitude(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MaxMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MaxMagnitude(x, y, destination); + } + } + + public struct MaxMagnitudeNumber + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumberBase + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MaxMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MaxMagnitudeNumber(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MaxMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MaxMagnitudeNumber(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MaxMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MaxMagnitudeNumber(x, y, destination); + } + } + + public struct MaxNumber + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MaxNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MaxNumber(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MaxNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MaxNumber(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MaxNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MaxNumber(x, y, destination); + } + } + + public struct Min + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Min(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.Min(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Min(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Min(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Min(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Min(x, y, destination); + } + } + + public struct MinMagnitude + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MinMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MinMagnitude(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MinMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MinMagnitude(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MinMagnitude(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MinMagnitude(x, y, destination); + } + } + + public struct MinMagnitudeNumber + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumberBase + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MinMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MinMagnitudeNumber(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MinMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MinMagnitudeNumber(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MinMagnitudeNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MinMagnitudeNumber(x, y, destination); + } + } + + public struct MinNumber + : IUnaryReduction_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : INumber + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.MinNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination = TensorPrimitives.MinNumber(x); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.MinNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.MinNumber(x, y, destination); + } + + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.MinNumber(x, destination); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.MinNumber(x, y, destination); + } + } + + public readonly struct Multiply + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IMultiplyOperators, IMultiplicativeIdentity + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x * y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Multiply(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x * y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Multiply(x, y, destination); + } + } + + public readonly struct Negate + : IUnaryOperation_Tensor + where T : IUnaryNegationOperators + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = -x; + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Negate(x, destination); + } + } + + public readonly struct OnesComplement + : IUnaryOperation_Tensor + where T : IBitwiseOperators + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = ~x; + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.OnesComplement(x, destination); + } + } + + public readonly struct PopCount + : IUnaryOperation_Tensor + where T : IBinaryInteger + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.PopCount(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.PopCount(x, destination); + } + } + + public readonly struct Pow + : IBinaryOperation_Scalar_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IPowerFunctions + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Pow(x, y); + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Pow(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = T.Pow(x, y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Pow(x, y, destination); + } + + public static void Invoke(T x, ref readonly T y, ref T destination) + { + destination = T.Pow(x, y); + } + + public static void Invoke(T x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Pow(x, y, destination); + } + } + + public struct Product + : IUnaryReduction_Tensor + where T : IMultiplicativeIdentity, IMultiplyOperators + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination *= x; + } - for (int i = 0; i < destination.Length; i++) + public static void Invoke(ReadOnlySpan x, ref T destination) { - linearOffset = x._shape.AdjustToNextIndex(linearOffset, indexes); - TOperation.Invoke( - in Unsafe.Add(ref x._reference, linearOffset), - ref destination[i] - ); + destination = TensorPrimitives.Product(x); + } + } + + public readonly struct RadiansToDegrees + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.RadiansToDegrees(x); } - rentedBuffer.Dispose(); + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.RadiansToDegrees(x, destination); + } } - public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) - where TOperation : TensorOperation.IUnaryOperation_Tensor + public readonly struct Reciprocal + : IUnaryOperation_Tensor + where T : IFloatingPoint { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.One / x; + } - for (nint i = 0; i < destination.FlattenedLength; i++) + public static void Invoke(ReadOnlySpan x, Span destination) { - xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); - destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + TensorPrimitives.Reciprocal(x, destination); + } + } - TOperation.Invoke( - in Unsafe.Add(ref x._reference, xLinearOffset), - ref Unsafe.Add(ref destination._reference, destinationLinearOffset) - ); + public readonly struct RootN + : IBinaryOperation_Tensor_Int32 + where T : IRootFunctions + { + public static void Invoke(ref readonly T x, int y, ref T destination) + { + destination = T.RootN(x, y); } - xRentedBuffer.Dispose(); - destinationRentedBuffer.Dispose(); + public static void Invoke(ReadOnlySpan x, int y, Span destination) + { + TensorPrimitives.RootN(x, y, destination); + } } - public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) - where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor + public readonly struct RotateLeft + : IBinaryOperation_Tensor_Int32 + where T : IBinaryInteger { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + public static void Invoke(ref readonly T x, int y, ref T destination) + { + destination = T.RotateLeft(x, y); + } + public static void Invoke(ReadOnlySpan x, int y, Span destination) + { + TensorPrimitives.RotateLeft(x, y, destination); + } + } - for (nint i = 0; i < destination.FlattenedLength; i++) + public readonly struct RotateRight + : IBinaryOperation_Tensor_Int32 + where T : IBinaryInteger + { + public static void Invoke(ref readonly T x, int y, ref T destination) + { + destination = T.RotateRight(x, y); + } + public static void Invoke(ReadOnlySpan x, int y, Span destination) { - xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); - yLinearOffset = y._shape.AdjustToNextIndex(yLinearOffset, yIndexes); - destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + TensorPrimitives.RotateRight(x, y, destination); + } + } - TOperation.Invoke( - in Unsafe.Add(ref x._reference, xLinearOffset), - in Unsafe.Add(ref y._reference, yLinearOffset), - ref Unsafe.Add(ref destination._reference, destinationLinearOffset) - ); + public readonly struct Round + : IUnaryOperation_Tensor, + IBinaryOperation_Tensor_Scalar, T> + where T : IFloatingPoint + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Round(x); } - xRentedBuffer.Dispose(); - yRentedBuffer.Dispose(); - destinationRentedBuffer.Dispose(); + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Round(x, destination); + } + + public static void Invoke(ref readonly T x, Tuple y, ref T destination) + { + destination = T.Round(x, y.Item1, y.Item2); + } + + public static void Invoke(ReadOnlySpan x, Tuple y, Span destination) + { + TensorPrimitives.Round(x, y.Item1, y.Item2, destination); + } } - public static void Invoke(in ReadOnlyTensorSpan x, TArg y, in TensorSpan destination) - where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar + public readonly struct Sigmoid + : IUnaryOperation_Tensor + where T : IExponentialFunctions { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.One / (T.One + T.Exp(-x)); + } - for (nint i = 0; i < destination.FlattenedLength; i++) + public static void Invoke(ReadOnlySpan x, Span destination) { - xLinearOffset = x._shape.AdjustToNextIndex(xLinearOffset, xIndexes); - destinationLinearOffset = destination._shape.AdjustToNextIndex(destinationLinearOffset, destinationIndexes); + TensorPrimitives.Sigmoid(x, destination); + } + } - TOperation.Invoke( - in Unsafe.Add(ref x._reference, xLinearOffset), - y, - ref Unsafe.Add(ref destination._reference, destinationLinearOffset) - ); + public readonly struct Sin + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Sin(x); } - xRentedBuffer.Dispose(); - destinationRentedBuffer.Dispose(); + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Sin(x, destination); + } } - public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in TensorSpan destination) + public readonly struct Sinh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Sinh(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Sinh(x, destination); + } } - public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) + public readonly struct SinPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.SinPi(x); + } + + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.SinPi(x, destination); + } } - public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) + // SoftMax Helper + public readonly struct SumExp + : IUnaryReduction_Tensor + where T : IExponentialFunctions { + public static void Invoke(ref readonly T x, ref T destination) + { + destination += T.Exp(x); + } + public static void Invoke(ReadOnlySpan x, ref T destination) + { + for (int i = 0; i < x.Length; i++) + { + destination += T.Exp(x[i]); + } + } } - public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, out Tensor destination) + public readonly struct SoftMax + : IBinaryOperation_Tensor_Scalar + where T : IExponentialFunctions { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = T.Exp(x) / y; + } + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.Exp(x[i]) / y; + } + } } - public readonly struct Clear - : IOperation + public readonly struct Sqrt + : IUnaryOperation_Tensor + where T : IRootFunctions { - public static void Invoke(ref T destination) + public static void Invoke(ref readonly T x, ref T destination) { - destination = default!; + destination = T.Sqrt(x); } - public static void Invoke(Span destination) + public static void Invoke(ReadOnlySpan x, Span destination) { - destination.Clear(); + TensorPrimitives.Sqrt(x, destination); } } - public readonly struct CopyTo - : IUnaryOperation_Tensor + public readonly struct Subtract + : IBinaryOperation_Scalar_Tensor, + IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : ISubtractionOperators { - public static void Invoke(ref readonly T source, ref T destination) + public static void Invoke(ref readonly T x, T y, ref T destination) { - destination = source; + destination = x - y; } - public static void Invoke(ReadOnlySpan source, Span destination) + public static void Invoke(ReadOnlySpan x, T y, Span destination) { - source.CopyTo(destination); + TensorPrimitives.Subtract(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x - y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Subtract(x, y, destination); + } + + public static void Invoke(T x, ref readonly T y, ref T destination) + { + destination = x - y; + } + + public static void Invoke(T x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Subtract(x, y, destination); } } - public readonly struct Equals - : IBinaryOperation_Tensor_Scalar, - IBinaryOperation_Tensor_Tensor - where T : IEqualityOperators + public struct Sum + : IUnaryReduction_Tensor + where T : IAdditionOperators, IAdditiveIdentity { - public static void Invoke(ref readonly T left, T right, ref bool destination) + public static void Invoke(ref readonly T x, ref T destination) { - destination = (left == right); + destination += x; } - public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + public static void Invoke(ReadOnlySpan x, ref T destination) { - destination = (left == right); + destination += TensorPrimitives.Sum(x); } + } - public static void Invoke(ReadOnlySpan left, T right, Span destination) + public struct SumOfSquaredDifferences + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators, ISubtractionOperators + { + public static void Invoke(ref readonly T x, T y, ref T destination) { - for (int i = 0; i < destination.Length; i++) + destination = (x - y) * (x - y); + } + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + for (int i = 0; i < x.Length; i++) { - destination[i] = (left[i] == right); + destination[i] = (x[i] - y) * (x[i] - y); } } - public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) { - for (int i = 0; i < destination.Length; i++) + destination = (x - y) * (x - y); + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + for (int i = 0; i < x.Length; i++) { - destination[i] = (left[i] == right[i]); + destination[i] = (x[i] - y[i]) * (x[i] - y[i]); } } } - public readonly struct EqualsAny - : IBinaryOperation_Tensor_Scalar, - IBinaryOperation_Tensor_Tensor - where T : IEqualityOperators + public readonly struct SumOfSquares + : IUnaryReduction_Tensor + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators { - // The main loop early exits at the first false condition, so we - // check x != y and returns false on first equal. The consumer will - // then negate whatever the main loop returns as `true` means none - // are equal. + public static void Invoke(ref readonly T x, ref T destination) + { + destination += x * x; + } + public static void Invoke(ReadOnlySpan x, ref T destination) + { + destination += TensorPrimitives.SumOfSquares(x); + } + } - public static void Invoke(ref readonly T left, T right, ref bool destination) + public readonly struct Tan + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) { - destination = (left != right); + destination = T.Tan(x); } - public static void Invoke(ref readonly T left, ref readonly T right, ref bool destination) + public static void Invoke(ReadOnlySpan x, Span destination) { - destination = (left != right); + TensorPrimitives.Tan(x, destination); } + } - public static void Invoke(ReadOnlySpan left, T right, Span destination) + public readonly struct Tanh + : IUnaryOperation_Tensor + where T : IHyperbolicFunctions + { + public static void Invoke(ref readonly T x, ref T destination) { - Debug.Assert(destination.Length == 1); - bool result = false; + destination = T.Tanh(x); + } - for (int i = 0; i < destination.Length; i++) - { - result = (left[i] != right); + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Tanh(x, destination); + } + } - if (!result) - { - break; - } - } + public readonly struct TanPi + : IUnaryOperation_Tensor + where T : ITrigonometricFunctions + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.TanPi(x); + } - destination[0] = result; + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.TanPi(x, destination); } + } - public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span destination) + public readonly struct TrailingZeroCount + : IUnaryOperation_Tensor + where T : IBinaryInteger + { + public static void Invoke(ref readonly T x, ref T destination) { - Debug.Assert(destination.Length == 1); - bool result = false; + destination = T.TrailingZeroCount(x); + } - for (int i = 0; i < destination.Length; i++) - { - result = (left[i] != right[i]); + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.TrailingZeroCount(x, destination); + } + } - if (!result) - { - break; - } - } + public readonly struct Truncate + : IUnaryOperation_Tensor + where T : IFloatingPoint + { + public static void Invoke(ref readonly T x, ref T destination) + { + destination = T.Truncate(x); + } - destination[0] = result; + public static void Invoke(ReadOnlySpan x, Span destination) + { + TensorPrimitives.Truncate(x, destination); + } + } + + public readonly struct Xor + : IBinaryOperation_Tensor_Scalar, + IBinaryOperation_Tensor_Tensor + where T : IBitwiseOperators + { + public static void Invoke(ref readonly T x, T y, ref T destination) + { + destination = x ^ y; + } + + public static void Invoke(ReadOnlySpan x, T y, Span destination) + { + TensorPrimitives.Xor(x, y, destination); + } + + public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) + { + destination = x ^ y; + } + + public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + TensorPrimitives.Xor(x, y, destination); } } + #endregion + public readonly struct Fill : IUnaryOperation_Scalar { @@ -693,9 +2456,25 @@ public static void Invoke(ReadOnlySpan left, ReadOnlySpan right, Span + : IBinaryOperation_Tensor_Scalar + { + } + + public interface IBinaryOperation_Tensor_Int32 + : IBinaryOperation_Tensor_Scalar + { + } + + public interface IBinaryOperation_Tensor_Scalar + { + static abstract void Invoke(ref readonly T1 x, T2 y, ref TResult destination); + static abstract void Invoke(ReadOnlySpan x, T2 y, Span destination); + } + + public interface IBinaryOperation_Scalar_Tensor { - static abstract void Invoke(ref readonly T x, T y, ref TResult destination); - static abstract void Invoke(ReadOnlySpan x, T y, Span destination); + static abstract void Invoke(T1 x, ref readonly T2 y, ref TResult destination); + static abstract void Invoke(T1 x, ReadOnlySpan y, Span destination); } public interface IBinaryOperation_Tensor_Tensor @@ -722,6 +2501,12 @@ public interface IUnaryOperation_Tensor static abstract void Invoke(ReadOnlySpan x, Span destination); } + public interface IUnaryReduction_Tensor + { + static abstract void Invoke(ref readonly T x, ref TResult destination); + static abstract void Invoke(ReadOnlySpan x, ref TResult destination); + } + private ref struct RentedBuffer : IDisposable { private nint[]? _array; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index bcf1bf0add6f75..f2074665987417 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -413,16 +413,17 @@ public ReadOnlySpan Strides public static bool operator !=(in TensorShape left, in TensorShape right) => !(left == right); - public nint AdjustToNextIndex(nint linearOffset, Span indexes) + public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset, Span indexes) { - Debug.Assert(indexes.Length == Rank); + Debug.Assert(indexes.Length >= Rank); + Debug.Assert(indexes.Length == destinationShape.Rank); ReadOnlySpan lengths = Lengths; ReadOnlySpan strides = Strides; - for (int i = 0; i < indexes.Length; i++) + for (int i = 0; i < strides.Length; i++) { - int rankIndex = indexes.Length - (i + 1); + int rankIndex = lengths.Length - (i + 1); nint length = lengths[rankIndex]; nint stride = strides[rankIndex]; @@ -432,17 +433,39 @@ public nint AdjustToNextIndex(nint linearOffset, Span indexes) if (index < length) { - break; + return linearOffset; } indexes[rankIndex] = 0; linearOffset -= (stride * length); } - return linearOffset; + if (indexes.Length != Rank) + { + lengths = destinationShape.Lengths; + for (int i = strides.Length; i < indexes.Length; i++) + { + int rankIndex = lengths.Length - (i + 1); + + nint length = lengths[rankIndex]; + // Strides are always 0 because we are broadcasting at this point in the loop. + + nint index = ++indexes[rankIndex]; + + if (index < length) + { + break; + } + + indexes[rankIndex] = 0; + } + } + + return 0; } - public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2) + // can shape2 turn into shape1 + public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, bool allowBidirectional) { scoped ReadOnlySpan lengths1 = shape1.Lengths; scoped ReadOnlySpan lengths2 = shape2.Lengths; @@ -453,6 +476,11 @@ public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2) { if (rankDelta < 0) { + if (!allowBidirectional) + { + return false; + } + lengths1 = shape2.Lengths; lengths2 = shape1.Lengths; @@ -460,14 +488,21 @@ public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2) Debug.Assert(rankDelta > 0); } - if (lengths1[..rankDelta].ContainsAnyExcept(1)) + lengths1 = lengths1[rankDelta..]; + } + + // if equal or one is 1 + for (int i = 0; i < lengths1.Length; i++) + { + nint length1 = lengths1[i]; + nint length2 = lengths2[i]; + if ((length1 != length2) && (length1 != 1) && (length2 != 1)) { return false; } - lengths1 = lengths1[rankDelta..]; } - return lengths1.SequenceEqual(lengths2); + return true; } public static bool AreLengthsTheSame(in TensorShape shape1, in TensorShape shape2) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 0175606188ecd3..a5a40d937ff0d6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -201,7 +201,7 @@ public void CopyTo(scoped in TensorSpan destination) { if (!TryCopyTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } @@ -219,7 +219,7 @@ public void FlattenTo(scoped Span destination) { if (!TryFlattenTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 9de83c2a471441..6792dfd39cc689 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -157,7 +157,7 @@ public void CopyTo(scoped in TensorSpan destination) { if (!TryCopyTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } @@ -169,7 +169,7 @@ public void FlattenTo(scoped Span destination) { if (!TryFlattenTo(destination)) { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs index acb746289635d8..e920bbb0111bf7 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs @@ -64,12 +64,6 @@ internal static void ThrowIndexOutOfRangeException() throw new IndexOutOfRangeException(); } - [DoesNotReturn] - public static void ThrowArgumentException_DestinationTooShort() - { - throw GetArgumentException(SR.DestinationTooShort); - } - [DoesNotReturn] public static void ThrowArgument_LengthsMustEqualArrayLength() { @@ -122,9 +116,9 @@ public static void ThrowArgument_SetSliceInvalidShapes(string? paramNames) } [DoesNotReturn] - public static void ThrowArgument_LengthsNotBroadcastCompatible() + public static void ThrowArgument_LengthsNotCompatible() { - throw new ArgumentException(SR.ThrowArgument_LengthsNotBroadcastCompatible); + throw new ArgumentException(SR.ThrowArgument_LengthsNotCompatible); } [DoesNotReturn] From 6400ad6d127ba4e802f2b0ced3b806c01c3fe86a Mon Sep 17 00:00:00 2001 From: Michael Sharp <51342856+michaelgsharp@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:36:06 -0600 Subject: [PATCH 04/30] more tensors updates (#26) --- .../System/Numerics/Tensors/netcore/Tensor.cs | 237 +++--------------- .../Tensors/netcore/TensorOperation.cs | 34 ++- .../Numerics/Tensors/netcore/TensorShape.cs | 41 +++ 3 files changed, 104 insertions(+), 208 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index ace77450c42f93..a7fe02a022d96b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -54,12 +54,10 @@ public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, sco /// Thrown when the shapes are not broadcast compatible. public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, scoped ReadOnlySpan lengths) { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, lengths); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - Tensor output = Tensor.CreateUninitialized(intermediate.Lengths); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref output._values[0], (int)output.FlattenedLength)); - return output; + TensorOperation.ValidateCompatibility(source, lengths); + Tensor destination = Tensor.CreateUninitialized(lengths); + TensorOperation.Invoke, T, T>(source, destination); + return destination; } #endregion @@ -71,12 +69,8 @@ public static Tensor Broadcast(scoped in ReadOnlyTensorSpan source, sco /// public static void BroadcastTo(this Tensor source, in TensorSpan destination) { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, T, T>(source, destination); } /// @@ -86,12 +80,8 @@ public static void BroadcastTo(this Tensor source, in TensorSpan destin /// Other to make shapes broadcastable. public static void BroadcastTo(in this TensorSpan source, in TensorSpan destination) { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, T, T>(source, destination); } /// @@ -101,141 +91,8 @@ public static void BroadcastTo(in this TensorSpan source, in TensorSpan /// public static void BroadcastTo(in this ReadOnlyTensorSpan source, in TensorSpan destination) { - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(source.Lengths, destination.Lengths); - if (!destination.Lengths.SequenceEqual(newSize)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - ReadOnlyTensorSpan intermediate = LazyBroadcast(source, newSize); - intermediate.FlattenTo(MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength)); - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlySpan lengths) - { - if (input.Lengths.SequenceEqual(lengths)) - return new TensorSpan(ref input._reference, input._shape.LinearLength, lengths, input.Strides); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); - - if (newSize == input.FlattenedLength) - return Reshape(input, lengths); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, lengths.Length); - nint[] strides = new nint[lengths.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && lengths[i] != 1) || (intermediateShape[i] == 1 && lengths[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - TensorSpan output = new TensorSpan(ref input._reference, input._shape.LinearLength, lengths, strides); - - return output; - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan input, ReadOnlySpan shape) - { - if (input.Lengths.SequenceEqual(shape)) - return new TensorSpan(ref input._reference, input._shape.LinearLength, shape, input.Strides); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - nint newSize = TensorSpanHelpers.CalculateFlattenedLength(shape); - - if (newSize == input.FlattenedLength) - return Reshape(input, shape); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, shape.Length); - nint[] strides = new nint[shape.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && shape[i] != 1) || (intermediateShape[i] == 1 && shape[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - TensorSpan output = new TensorSpan(ref input._reference, input._shape.LinearLength, shape, strides); - - return output; - } - - // Lazy/non-copy broadcasting, internal only for now. - /// - /// Broadcast the data from to the new shape . Creates a new - /// but no memory is allocated. It manipulates the strides to achieve this affect. - /// If the shape of the is not compatible with the new shape, an exception is thrown. - /// - /// Input . - /// of the desired new shape. - /// Thrown when the shapes are not broadcast compatible. - internal static Tensor LazyBroadcast(Tensor input, ReadOnlySpan lengths) - { - if (input.Lengths.SequenceEqual(lengths)) - return new Tensor(input._values, lengths, input._start, isPinned: false); - - if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths)) - ThrowHelper.ThrowArgument_LengthsNotCompatible(); - - nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); - - if (newSize == input.FlattenedLength) - return Reshape(input, lengths); - - nint[] intermediateShape = TensorHelpers.GetIntermediateShape(input.Lengths, lengths.Length); - nint[] strides = new nint[lengths.Length]; - - nint stride = 1; - - for (int i = strides.Length - 1; i >= 0; i--) - { - if ((intermediateShape[i] == 1 && lengths[i] != 1) || (intermediateShape[i] == 1 && lengths[i] == 1)) - strides[i] = 0; - else - { - strides[i] = stride; - stride *= intermediateShape[i]; - } - } - - Tensor output = new Tensor(input._values, input._start, lengths, strides); - - return output; + TensorOperation.ValidateCompatibility(source, destination); + TensorOperation.Invoke, T, T>(source, destination); } #endregion @@ -265,7 +122,7 @@ public static Tensor ConcatenateOnDimension(int dimension, params scoped R // Calculate total space needed. nint totalLength = 0; for (int i = 0; i < tensors.Length; i++) - totalLength += TensorSpanHelpers.CalculateFlattenedLength(tensors[i].Lengths); + totalLength += tensors[i].FlattenedLength; nint sumOfAxis = 0; // If axis != -1, make sure all dimensions except the one to concatenate on match. @@ -333,13 +190,12 @@ public static ref readonly TensorSpan ConcatenateOnDimension(int dimension // Calculate total space needed. nint totalLength = 0; for (int i = 0; i < tensors.Length; i++) - totalLength += TensorSpanHelpers.CalculateFlattenedLength(tensors[i].Lengths); + totalLength += tensors[i].FlattenedLength; - nint sumOfAxis = 0; // If axis != -1, make sure all dimensions except the one to concatenate on match. if (dimension != -1) { - sumOfAxis = tensors[0].Lengths[dimension]; + nint sumOfAxis = tensors[0].Lengths[dimension]; for (int i = 1; i < tensors.Length; i++) { if (tensors[0].Rank != tensors[i].Rank) @@ -364,42 +220,13 @@ public static ref readonly TensorSpan ConcatenateOnDimension(int dimension ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); } Span dstSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)totalLength); - nint valuesCopied = 0; - - scoped Span curIndex; - nint[]? curIndexArray; - - if (tensors[0].Rank > TensorShape.MaxInlineRank) - { - curIndexArray = ArrayPool.Shared.Rent(tensors[0].Rank); - curIndex = curIndexArray.AsSpan(0, tensors[0].Rank); - } - else - { - curIndexArray = null; - curIndex = stackalloc nint[tensors[0].Rank]; - } - curIndex.Clear(); - nint srcIndex; - nint copyLength; - - while (valuesCopied < totalLength) + for (int i = 0; i < tensors.Length; i++) { - for (int i = 0; i < tensors.Length; i++) - { - srcIndex = TensorSpanHelpers.ComputeLinearIndex(curIndex, tensors[i].Strides, tensors[i].Lengths); - copyLength = CalculateCopyLength(tensors[i].Lengths, dimension); - Span srcSpan = MemoryMarshal.CreateSpan(ref tensors[i]._values[srcIndex], (int)copyLength); - TensorSpanHelpers.Memmove(dstSpan, srcSpan, copyLength, valuesCopied); - valuesCopied += copyLength; - } - TensorSpanHelpers.AdjustIndexes(dimension - 1, 1, curIndex, tensors[0].Lengths); + TensorOperation.Invoke, T, T>(tensors[i], dstSpan); + dstSpan = dstSpan.Slice((int)tensors[i].FlattenedLength); } - if (curIndexArray != null) - ArrayPool.Shared.Return(curIndexArray); - return ref destination; } @@ -1803,15 +1630,15 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten /// of the desired new shape. public static Tensor Resize(Tensor tensor, ReadOnlySpan lengths) { - nint newSize = TensorSpanHelpers.CalculateFlattenedLength(lengths); + nint newSize = TensorPrimitives.Product(lengths); T[] values = tensor.IsPinned ? GC.AllocateArray((int)newSize) : (new T[newSize]); - Tensor output = new Tensor(values, lengths, tensor._start, isPinned: false); - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor.AsTensorSpan()._reference, (int)tensor._values.Length); + Tensor output = Tensor.Create(values, 0, lengths, []); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref tensor.AsTensorSpan()._reference, tensor._start), (int)tensor._values.Length - tensor._start); Span ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength); - if (newSize > tensor._values.Length) - TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); + if (newSize >= span.Length) + span.CopyTo(ospan); else - TensorSpanHelpers.Memmove(ospan, span, newSize); + span.Slice(0, ospan.Length).CopyTo(ospan); return output; } @@ -1824,12 +1651,12 @@ public static Tensor Resize(Tensor tensor, ReadOnlySpan lengths) /// Destination with the desired new shape. public static void ResizeTo(scoped in Tensor tensor, in TensorSpan destination) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._values[0], tensor._values.Length); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref tensor.AsTensorSpan()._reference, tensor._start), (int)tensor._values.Length - tensor._start); Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - if (destination._shape.LinearLength > tensor._values.Length) - TensorSpanHelpers.Memmove(ospan, span, tensor._values.Length); + if (ospan.Length >= span.Length) + span.CopyTo(ospan); else - TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + span.Slice(0, ospan.Length).CopyTo(ospan); } /// @@ -1842,10 +1669,10 @@ public static void ResizeTo(scoped in TensorSpan tensor, in TensorSpan { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - if (destination._shape.LinearLength > tensor._shape.LinearLength) - TensorSpanHelpers.Memmove(ospan, span, tensor._shape.LinearLength); + if (ospan.Length >= span.Length) + span.CopyTo(ospan); else - TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + span.Slice(0, ospan.Length).CopyTo(ospan); } /// @@ -1858,10 +1685,10 @@ public static void ResizeTo(scoped in ReadOnlyTensorSpan tensor, in Tensor { ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - if (destination._shape.LinearLength > tensor._shape.LinearLength) - TensorSpanHelpers.Memmove(ospan, span, tensor._shape.LinearLength); + if (ospan.Length >= span.Length) + span.CopyTo(ospan); else - TensorSpanHelpers.Memmove(ospan, span, destination._shape.LinearLength); + span.Slice(0, ospan.Length).CopyTo(ospan); } #endregion diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index c367c737d95438..d986409c1a3f50 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -125,6 +125,27 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) destinationRentedBuffer.Dispose(); } + // For copyto/flattento + public static void Invoke(in ReadOnlyTensorSpan x, in Span destination) + where TOperation : TensorOperation.IUnaryOperation_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + nint destinationIndex = -1; + + for (nint i = 0; i < destination.Length; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(x._shape, xLinearOffset, xIndexes); + destinationIndex++; + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + ref Unsafe.Add(ref destination[0], destinationIndex) + ); + } + + xRentedBuffer.Dispose(); + } + public static void Invoke(in ReadOnlyTensorSpan x, ref TResult destination) where TOperation : TensorOperation.IUnaryReduction_Tensor { @@ -267,17 +288,24 @@ ref result public static void Invoke(in ReadOnlyTensorSpan x, TArg y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar => Invoke(in x, y, ref result); + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlySpan lengths) + { + // x can be broadcast to destination, not vice verse + if (!TensorShape.AreCompatible(lengths, x._shape, true)) + ThrowHelper.ThrowArgument_LengthsNotCompatible(); + } + public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) { - // can do bidirectional validation - if (!x.Lengths.SequenceEqual(y.Lengths)) + // Can be bidirectional validation + if (!TensorShape.AreCompatible(x._shape, y._shape, true)) ThrowHelper.ThrowArgument_LengthsNotCompatible(); } public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in TensorSpan destination) { // x can be broadcast to destination, not vice verse - if (!x.Lengths.SequenceEqual(destination.Lengths)) + if (!TensorShape.AreCompatible(destination._shape, x._shape, false)) ThrowHelper.ThrowArgument_LengthsNotCompatible(); } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index f2074665987417..05f7fa5c937aff 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -505,6 +505,47 @@ public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, b return true; } + // can shape1 turn into lengths + public static bool AreCompatible(in ReadOnlySpan lengths, in TensorShape shape1, bool allowBidirectional) + { + scoped ReadOnlySpan lengths1 = lengths; + scoped ReadOnlySpan lengths2 = shape1.Lengths; + + int rankDelta = lengths1.Length - shape1.Rank; + + if (rankDelta != 0) + { + if (rankDelta < 0) + { + if (!allowBidirectional) + { + return false; + } + + lengths1 = shape1.Lengths; + lengths2 = lengths; + + rankDelta = -rankDelta; + Debug.Assert(rankDelta > 0); + } + + lengths1 = lengths1[rankDelta..]; + } + + // if equal or one is 1 + for (int i = 0; i < lengths1.Length; i++) + { + nint length1 = lengths1[i]; + nint length2 = lengths2[i]; + if ((length1 != length2) && (length1 != 1) && (length2 != 1)) + { + return false; + } + } + + return true; + } + public static bool AreLengthsTheSame(in TensorShape shape1, in TensorShape shape2) { return AreLengthsTheSame(shape1.Lengths, shape2.Lengths); From 80dd6da9b4663d436d2416d3beac8adfbbcfafe9 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 17 Apr 2025 12:14:13 -0700 Subject: [PATCH 05/30] Resolve a few build failures --- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 2 +- .../System/Numerics/Tensors/netcore/Tensor.cs | 38 +++++++++---------- .../Numerics/Tensors/netcore/TensorSpan.cs | 2 +- .../Numerics/Tensors/netcore/Tensor_1.cs | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 4c7aa1f6e054ec..fa2d62efbe6064 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -395,7 +395,7 @@ public bool MoveNext() return false; } - _linearOffset = _span._shape.AdjustToNextIndex(_linearOffset, _indexes); + _linearOffset = _span._shape.AdjustToNextIndex(_span._shape, _linearOffset, _indexes); _itemsEnumerated++; return true; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index a7fe02a022d96b..23ccc9a982585e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -1340,7 +1340,7 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO } else { - T[] values = tensor.IsPinned ? GC.AllocateArray((int)tensor._flattenedLength) : (new T[tensor._flattenedLength]); + T[] values = tensor.IsPinned ? GC.AllocateArray((int)tensor.FlattenedLength) : (new T[tensor.FlattenedLength]); nint[] lengths = new nint[tensor.Rank]; Tensor outTensor; TensorSpan ospan; @@ -1352,7 +1352,7 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO int[] tempPermutation = new int[tensor.Rank]; for (int i = 0; i < tensor.Rank; i++) { - lengths[i] = tensor._lengths[tensor.Rank - 1 - i]; + lengths[i] = tensor.Lengths[tensor.Rank - 1 - i]; tempPermutation[i] = tensor.Rank - 1 - i; } @@ -1393,7 +1393,7 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO permutedIndices = stackalloc nint[outTensor.Rank]; } - for (int i = 0; i < tensor._flattenedLength; i++) + for (int i = 0; i < tensor.FlattenedLength; i++) { TensorHelpers.PermuteIndices(indexes, permutedIndices, permutation); ospan[permutedIndices] = ispan[indexes]; @@ -1477,7 +1477,7 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan(tensor._values, arrLengths, strides, tensor._start); + return new Tensor(tensor._values, tensor._start, arrLengths, strides); } /// @@ -1547,7 +1547,7 @@ public static TensorSpan Reshape(in this TensorSpan tensor, params scop else strides = TensorSpanHelpers.CalculateStrides(arrLengths); - TensorSpan output = new TensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape.LinearLength); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, arrLengths, strides); return output; } @@ -1616,7 +1616,7 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten else strides = TensorSpanHelpers.CalculateStrides(arrLengths); - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, arrLengths, strides, tensor._shape.LinearLength); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, arrLengths, strides); return output; } #endregion @@ -1923,19 +1923,19 @@ public static Tensor[] Split(scoped in ReadOnlyTensorSpan tensor, int s for (int i = 0; i < outputs.Length; i++) { T[] values = new T[(int)totalToCopy]; - outputs[i] = new Tensor(values, newShape, memoryOffset: 0); + outputs[i] = Create(values, start: 0, newShape, strides: []); oIndices.Clear(); iIndices.Clear(); iIndices[(int)dimension] = i; ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); - TensorSpan oslice = outputs[i].AsTensorSpan().Slice(outputs[i]._lengths); + TensorSpan oslice = outputs[i].AsTensorSpan().Slice(outputs[i].Lengths); nint copiedValues = 0; while (copiedValues < totalToCopy) { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref oslice._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, outputs[0].Strides, outputs[0].Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, outputs[i]._lengths); + TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, outputs[i].Lengths); TensorSpanHelpers.AdjustIndexes((int)dimension - 1, 1, iIndices, tensor.Lengths); copiedValues += copyLength; } @@ -2005,7 +2005,7 @@ public static Tensor SqueezeDimension(this Tensor tensor, int dimension strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new Tensor(tensor._values, lengths, strides, tensor._start); + return new Tensor(tensor._values, tensor._start, lengths, strides); } /// @@ -2061,7 +2061,7 @@ public static TensorSpan SqueezeDimension(in this TensorSpan tensor, in strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + return new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); } /// @@ -2117,7 +2117,7 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + return new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); } #endregion @@ -2143,7 +2143,7 @@ public static Tensor StackAlongDimension(int dimension, params ReadOnlySpa for (int i = 1; i < tensors.Length; i++) { - if (!TensorShape.AreLengthsTheSame(tensors[0], tensors[i])) + if (!TensorShape.AreLengthsTheSame(tensors[0].Lengths, tensors[i].Lengths)) ThrowHelper.ThrowArgument_StackShapesNotSame(); } @@ -2181,7 +2181,7 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS for (int i = 1; i < tensors.Length; i++) { - if (!TensorShape.AreLengthsTheSame(tensors[0], tensors[i])) + if (!TensorShape.AreLengthsTheSame(tensors[0].Lengths, tensors[i].Lengths)) ThrowHelper.ThrowArgument_StackShapesNotSame(); } @@ -2345,11 +2345,11 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) if (dimension < 0) dimension = tensor.Rank - dimension; - Span lengths = tensor._lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor._lengths.Length + 1] : - new nint[tensor._lengths.Length + 1]; - tensor._lengths.AsSpan(0, dimension).CopyTo(lengths); - tensor._lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1)); + Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? + stackalloc nint[tensor.Lengths.Length + 1] : + new nint[tensor.Lengths.Length + 1]; + tensor.Lengths.AsSpan(0, dimension).CopyTo(lengths); + tensor.Lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1)); lengths[dimension] = 1; Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index a5a40d937ff0d6..0fd4682632d120 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -311,7 +311,7 @@ public bool MoveNext() return false; } - _linearOffset = _span._shape.AdjustToNextIndex(_linearOffset, _indexes); + _linearOffset = _span._shape.AdjustToNextIndex(_span._shape, _linearOffset, _indexes); _itemsEnumerated++; return true; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 6792dfd39cc689..8db67302e90976 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -361,7 +361,7 @@ public bool MoveNext() return false; } - _linearOffset = _tensor._shape.AdjustToNextIndex(_linearOffset, _indexes); + _linearOffset = _tensor._shape.AdjustToNextIndex(_tensor._shape, _linearOffset, _indexes); _itemsEnumerated++; return true; From 96d683b7b917ec07f84d7181b613e6ecc9531049 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 17 Apr 2025 13:37:28 -0700 Subject: [PATCH 06/30] Ensure SetSlice and ToString are working as expected --- .../System/Numerics/Tensors/netcore/Tensor.cs | 162 +++++++++--------- .../Tensors/netcore/TensorOperation.cs | 1 - .../Numerics/Tensors/netcore/Tensor_1.cs | 2 +- 3 files changed, 83 insertions(+), 82 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 23ccc9a982585e..f85d5b86ba046e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -4,15 +4,12 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Serialization; using System.Text; -using System.Threading.Tasks; -using static System.Numerics.Tensors.TensorOperation; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace System.Numerics.Tensors { @@ -1366,7 +1363,7 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO lengths[i] = tensor.Lengths[dimensions[i]]; permutation = dimensions.ToArray(); } - outTensor = new Tensor(values, tensor._start, lengths, strides: [], tensor._isPinned); + outTensor = new Tensor(values, tensor._start, lengths, strides: []); // TODO: Propagate tensor._isPinned ospan = outTensor.AsTensorSpan(); ispan = tensor.AsTensorSpan(); @@ -1838,8 +1835,7 @@ public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan tensor, /// The ranges you want to set. public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan values, params ReadOnlySpan ranges) { - SetSlice((TensorSpan)tensor, values, ranges); - + tensor.AsTensorSpan().SetSlice(values, ranges); return tensor; } @@ -1851,24 +1847,7 @@ public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan /// The ranges you want to set. public static ref readonly TensorSpan SetSlice(this in TensorSpan tensor, scoped in ReadOnlyTensorSpan values, params scoped ReadOnlySpan ranges) { - TensorSpan srcSpan; - if (ranges == ReadOnlySpan.Empty) - { - if (!TensorHelpers.IsBroadcastableTo(values.Lengths, tensor.Lengths)) - ThrowHelper.ThrowArgument_SetSliceNoRange(nameof(values)); - srcSpan = tensor; - } - else - srcSpan = tensor.Slice(ranges); - - if (!TensorHelpers.IsContiguousAndDense(srcSpan)) - ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); - - if (!TensorHelpers.IsBroadcastableTo(values.Lengths, srcSpan.Lengths)) - ThrowHelper.ThrowArgument_SetSliceInvalidShapes(nameof(values)); - - values.CopyTo(srcSpan); - + values.CopyTo(tensor.Slice(ranges)); return ref tensor; } #endregion @@ -2204,8 +2183,8 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) => - ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) + => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); /// /// Creates a representation of the ."/> @@ -2215,47 +2194,70 @@ public static string ToString(this in TensorSpan tensor, params ReadOnlySp /// Maximum Length of each dimension public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) { + if (maximumLengths.Length != tensor.Rank) + { + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + } + StringBuilder sb = new(); - ToString(in tensor, sb, maximumLengths); + ToString(in tensor, maximumLengths, sb); return sb.ToString(); } - internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBuilder sb, params ReadOnlySpan maximumLengths) + internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, StringBuilder sb, int indentLevel = 0) { - if (maximumLengths.Length != tensor.Rank) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + Debug.Assert(maximumLengths.Length != tensor.Rank); - scoped Span curIndexes; - nint[]? curIndexesArray; - if (tensor.Rank > 6) - { - curIndexesArray = ArrayPool.Shared.Rent(tensor.Rank); - curIndexes = curIndexesArray.AsSpan(0, tensor.Rank); - curIndexes.Clear(); - } - else + sb.Append(' ', indentLevel * 2); + sb.Append('['); + + if (tensor.Rank != 0) { - curIndexesArray = null; - curIndexes = stackalloc nint[tensor.Rank]; - } + nint length = nint.Max(tensor.Lengths[0], maximumLengths[0]); - nint copiedValues = 0; + if (tensor.Rank != 1) + { + string separator = string.Empty; - T[] values = new T[tensor.Lengths[tensor.Rank - 1]]; - while (copiedValues < tensor.FlattenedLength) - { - var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, tensor.Strides, tensor.Lengths)), [tensor.Lengths[tensor.Rank - 1]], [1], tensor.Lengths[tensor.Rank - 1]); - sb.Append('{'); - sp.FlattenTo(values); - sb.Append(string.Join(",", values)); - sb.AppendLine("}"); - - TensorSpanHelpers.AdjustIndexes(tensor.Rank - 2, 1, curIndexes, tensor.Lengths); - copiedValues += tensor.Lengths[tensor.Rank - 1]; - } + for (nint i = 0; i < length; i++) + { + sb.AppendLine(separator); + + TensorShape tmpShape = TensorShape.Create(tensor.Lengths[1..], tensor.Strides[1..]); + ReadOnlyTensorSpan tmpTensor = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]), tmpShape); + ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); + separator = ","; + } + + if (length != tensor.Lengths[0]) + { + sb.AppendLine(separator); + sb.Append(' ', indentLevel * 2); + sb.AppendLine("..."); + } + } + else + { + string separator = " "; + + for (nint i = 0; i < length; i++) + { + sb.Append(separator); + sb.Append(Unsafe.Add(ref tensor._reference, i)); + separator = ", "; + } + + if (length != tensor.Lengths[0]) + { + sb.Append(separator); + sb.Append("..."); + } + + sb.Append(separator); + } + } + sb.Append(']'); } /// @@ -2264,7 +2266,7 @@ internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBui /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); #endregion @@ -2298,7 +2300,7 @@ public static Tensor Transpose(Tensor tensor) /// Destination . public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan destination) { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); } /// @@ -2309,7 +2311,7 @@ public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan des /// Destination . public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan destination) { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); } /// @@ -2348,8 +2350,8 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? stackalloc nint[tensor.Lengths.Length + 1] : new nint[tensor.Lengths.Length + 1]; - tensor.Lengths.AsSpan(0, dimension).CopyTo(lengths); - tensor.Lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1)); + tensor.Lengths[..dimension].CopyTo(lengths); + tensor.Lengths[dimension..].CopyTo(lengths.Slice(dimension + 1)); lengths[dimension] = 1; Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? @@ -2367,7 +2369,7 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new Tensor(tensor._values, lengths, strides, tensor._start); + return new Tensor(tensor._values, tensor._start, lengths, strides); } /// @@ -2404,7 +2406,7 @@ public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimen strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new TensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + return new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); } /// @@ -2441,7 +2443,7 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new ReadOnlyTensorSpan(ref tensor._reference, lengths, strides, tensor._shape.LinearLength); + return new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); } #endregion @@ -4184,7 +4186,7 @@ public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4196,7 +4198,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4219,7 +4221,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in destination); + TensorOperation.ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4242,7 +4244,7 @@ public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4254,7 +4256,7 @@ public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4300,7 +4302,7 @@ public static T MaxNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4312,7 +4314,7 @@ public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4416,7 +4418,7 @@ public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4428,7 +4430,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4451,7 +4453,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in destination); + TensorOperation.ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4474,7 +4476,7 @@ public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4486,7 +4488,7 @@ public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4532,7 +4534,7 @@ public static T MinNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4544,7 +4546,7 @@ public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index d986409c1a3f50..113be55c8a7254 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using static System.Numerics.Tensors.TensorOperation; namespace System.Numerics.Tensors { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 8db67302e90976..c7554d852f0131 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -257,7 +257,7 @@ public string ToString(params ReadOnlySpan maximumLengths) var sb = new StringBuilder($"System.Numerics.Tensors.Tensor<{typeof(T).Name}>[{_shape}]"); sb.AppendLine("{"); - ((ReadOnlyTensorSpan)AsTensorSpan()).ToString(sb, maximumLengths); + Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, sb); sb.AppendLine("}"); return sb.ToString(); From 28e655f9c49ea4a67a94c37674fe8e1bc8832742 Mon Sep 17 00:00:00 2001 From: Michael Sharp <51342856+michaelgsharp@users.noreply.github.com> Date: Mon, 21 Apr 2025 08:06:31 -0600 Subject: [PATCH 07/30] Tensors lastfew (#27) * only couple left * pausing for food * fixed rented buffer * squeeze/unsqueeze * set slice/ split * only 2 left --- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 6 + .../System/Numerics/Tensors/netcore/Tensor.cs | 505 +++++++++--------- .../Tensors/netcore/TensorOperation.cs | 72 ++- .../Numerics/Tensors/netcore/TensorShape.cs | 54 +- .../Numerics/Tensors/netcore/TensorSpan.cs | 6 + .../Numerics/Tensors/netcore/Tensor_1.cs | 11 +- 6 files changed, 357 insertions(+), 297 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index fa2d62efbe6064..3e673cd835b795 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -185,6 +185,12 @@ internal ReadOnlyTensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) + { + _shape = TensorShape.Create(ref data, dataLength, lengths, strides, linearRankOrder); + _reference = ref data; + } + internal ReadOnlyTensorSpan(ref T reference, scoped in TensorShape shape) { _reference = ref reference; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index f85d5b86ba046e..b6225d8d46f4c4 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -9,7 +9,11 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Serialization; using System.Text; +using System.Threading.Tasks; +using static System.Numerics.Tensors.TensorOperation; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace System.Numerics.Tensors { @@ -1337,71 +1341,38 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO } else { - T[] values = tensor.IsPinned ? GC.AllocateArray((int)tensor.FlattenedLength) : (new T[tensor.FlattenedLength]); - nint[] lengths = new nint[tensor.Rank]; + scoped Span newLengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span newStrides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); + scoped Span newLinearOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer linearOrderRentedBuffer); + Tensor outTensor; - TensorSpan ospan; - TensorSpan ispan; - ReadOnlySpan permutation; if (dimensions.IsEmpty) { - int[] tempPermutation = new int[tensor.Rank]; for (int i = 0; i < tensor.Rank; i++) { - lengths[i] = tensor.Lengths[tensor.Rank - 1 - i]; - tempPermutation[i] = tensor.Rank - 1 - i; + newLengths[i] = tensor.Lengths[tensor.Rank - 1 - i]; + newStrides[i] = tensor.Strides[tensor.Rank - 1 - i]; + newLinearOrder[i] = tensor._shape.LinearRankOrder[tensor.Rank - 1 - i]; } - - permutation = tempPermutation; } else { if (dimensions.Length != tensor.Lengths.Length) ThrowHelper.ThrowArgument_PermuteAxisOrder(); - for (int i = 0; i < lengths.Length; i++) - lengths[i] = tensor.Lengths[dimensions[i]]; - permutation = dimensions.ToArray(); - } - outTensor = new Tensor(values, tensor._start, lengths, strides: []); // TODO: Propagate tensor._isPinned - - ospan = outTensor.AsTensorSpan(); - ispan = tensor.AsTensorSpan(); - scoped Span indexes; - nint[]? indicesArray; - scoped Span permutedIndices; - nint[]? permutedIndicesArray; - if (outTensor.Rank > 6) - { - indicesArray = ArrayPool.Shared.Rent(outTensor.Rank); - indexes = indicesArray.AsSpan(0, outTensor.Rank); - indexes.Clear(); - - permutedIndicesArray = ArrayPool.Shared.Rent(outTensor.Rank); - permutedIndices = permutedIndicesArray.AsSpan(0, outTensor.Rank); - permutedIndices.Clear(); - } - else - { - indicesArray = null; - indexes = stackalloc nint[outTensor.Rank]; - permutedIndicesArray = null; - permutedIndices = stackalloc nint[outTensor.Rank]; - } - - for (int i = 0; i < tensor.FlattenedLength; i++) - { - TensorHelpers.PermuteIndices(indexes, permutedIndices, permutation); - ospan[permutedIndices] = ispan[indexes]; - TensorShape.AdjustToNextIndex(indexes, tensor.Lengths); + for (int i = 0; i < dimensions.Length; i++) + { + newLengths[i] = tensor.Lengths[dimensions[i]]; + newStrides[i] = tensor.Strides[dimensions[i]]; + newLinearOrder[i] = tensor._shape.LinearRankOrder[dimensions[i]]; + } } + outTensor = new Tensor(tensor._values, tensor._start, newLengths, newStrides, newLinearOrder); - if (indicesArray != null && permutedIndicesArray != null) - { - ArrayPool.Shared.Return(indicesArray); - ArrayPool.Shared.Return(permutedIndicesArray); - } + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); + linearOrderRentedBuffer.Dispose(); return outTensor; } @@ -1421,12 +1392,12 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan(tensor) && !tensor.Strides.Contains(0)) + if (!tensor._shape.IsContiguousAndDense && !tensor.Strides.Contains(0)) { ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); } - nint[] arrLengths = lengths.ToArray(); + nint[] newLengths = lengths.ToArray(); // Calculate wildcard info. if (lengths.Contains(-1)) { @@ -1440,26 +1411,30 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan origStrides = new List(tensor.Strides.ToArray()); int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) + for (int i = 0; i < newLengths.Length; i++) { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + if (lengthOffset < tensor.Rank && newLengths[i] == tensor.Lengths[lengthOffset]) lengthOffset++; - else if (arrLengths[i] == 1) + else if (newLengths[i] == 1) { if (lengthOffset == tensor.Rank) origStrides.Add(tensor.Strides[lengthOffset - 1]); @@ -1472,9 +1447,9 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan(tensor._values, tensor._start, arrLengths, strides); + return new Tensor(tensor._values, tensor._start, lengths, strides); } /// @@ -1489,12 +1464,12 @@ public static TensorSpan Reshape(in this TensorSpan tensor, params scop if (tensor.Lengths.SequenceEqual(lengths)) return tensor; - if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) + if (!tensor._shape.IsContiguousAndDense && !tensor.Strides.Contains(0)) { ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); } - nint[] arrLengths = lengths.ToArray(); + nint[] newLengths = lengths.ToArray(); // Calculate wildcard info. if (lengths.Contains(-1)) { @@ -1508,28 +1483,33 @@ public static TensorSpan Reshape(in this TensorSpan tensor, params scop tempTotal /= lengths[i]; } } - arrLengths[lengths.IndexOf(-1)] = tempTotal; + newLengths[lengths.IndexOf(-1)] = tempTotal; } - nint tempLinear = TensorSpanHelpers.CalculateFlattenedLength(arrLengths); + nint tempLinear = TensorPrimitives.Product(newLengths); if (tempLinear != tensor.FlattenedLength) ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); nint[] strides; + // If all our strides are 0 we can reshape however we like and keep all new strides at 0 + if (!tensor.Strides.ContainsAnyExcept(0)) + { + strides = new nint[newLengths.Length]; + } // If we contain a 0 stride we can only add dimensions of length 1. - if (tensor.Strides.Contains(0)) + else if (tensor.Strides.Contains(0)) { List origStrides = new List(tensor.Strides.ToArray()); int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) + for (int i = 0; i < newLengths.Length; i++) { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + if (lengthOffset < tensor.Rank && newLengths[i] == tensor.Lengths[lengthOffset]) { lengthOffset++; } - else if (arrLengths[i] == 1) + else if (newLengths[i] == 1) { if (lengthOffset == tensor.Rank) origStrides.Add(tensor.Strides[lengthOffset - 1]); @@ -1542,9 +1522,9 @@ public static TensorSpan Reshape(in this TensorSpan tensor, params scop strides = origStrides.ToArray(); } else - strides = TensorSpanHelpers.CalculateStrides(arrLengths); + strides = []; - TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, arrLengths, strides); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, newLengths, strides); return output; } @@ -1560,12 +1540,12 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten if (tensor.Lengths.SequenceEqual(lengths)) return tensor; - if (!TensorHelpers.IsContiguousAndDense(tensor) && !tensor.Strides.Contains(0)) + if (!tensor._shape.IsContiguousAndDense && !tensor.Strides.Contains(0)) { ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); } - nint[] arrLengths = lengths.ToArray(); + nint[] newLengths = lengths.ToArray(); // Calculate wildcard info. if (lengths.Contains(-1)) { @@ -1579,26 +1559,31 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten tempTotal /= lengths[i]; } } - arrLengths[lengths.IndexOf(-1)] = tempTotal; + newLengths[lengths.IndexOf(-1)] = tempTotal; } - nint tempLinear = TensorSpanHelpers.CalculateFlattenedLength(arrLengths); + nint tempLinear = TensorPrimitives.Product(newLengths); if (tempLinear != tensor.FlattenedLength) ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); nint[] strides; + // If all our strides are 0 we can reshape however we like and keep all new strides at 0 + if (!tensor.Strides.ContainsAnyExcept(0)) + { + strides = new nint[newLengths.Length]; + } // If we contain a 0 stride we can only add dimensions of length 1. - if (tensor.Strides.Contains(0)) + else if (tensor.Strides.Contains(0)) { List origStrides = new List(tensor.Strides.ToArray()); int lengthOffset = 0; - for (int i = 0; i < arrLengths.Length; i++) + for (int i = 0; i < newLengths.Length; i++) { - if (lengthOffset < tensor.Rank && arrLengths[i] == tensor.Lengths[lengthOffset]) + if (lengthOffset < tensor.Rank && newLengths[i] == tensor.Lengths[lengthOffset]) lengthOffset++; - else if (arrLengths[i] == 1) + else if (newLengths[i] == 1) { if (lengthOffset == tensor.Rank) origStrides.Add(tensor.Strides[lengthOffset - 1]); @@ -1611,9 +1596,9 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten strides = origStrides.ToArray(); } else - strides = TensorSpanHelpers.CalculateStrides(arrLengths); + strides = []; - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, arrLengths, strides); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, newLengths, strides); return output; } #endregion @@ -1737,6 +1722,7 @@ public static ref readonly TensorSpan ReverseDimension(scoped in ReadOnlyT { if (dimension == -1) { + Debug.Assert(tensor._shape.LinearLength == destination._shape.LinearLength); nint index = tensor._shape.LinearLength - 1; Span inputSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); @@ -1835,7 +1821,8 @@ public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan tensor, /// The ranges you want to set. public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan values, params ReadOnlySpan ranges) { - tensor.AsTensorSpan().SetSlice(values, ranges); + SetSlice((TensorSpan)tensor, values, ranges); + return tensor; } @@ -1868,64 +1855,28 @@ public static Tensor[] Split(scoped in ReadOnlyTensorSpan tensor, int s Tensor[] outputs = new Tensor[splitCount]; nint totalToCopy = tensor.FlattenedLength / splitCount; - nint copyLength = 1; - for (nint i = dimension; i < tensor.Lengths.Length; i++) - { - copyLength *= tensor.Lengths[(int)i]; - } - copyLength /= splitCount; - nint[] newShape = tensor.Lengths.ToArray(); - newShape[(int)dimension] = newShape[(int)dimension] / splitCount; - scoped Span oIndices; - nint[]? oIndicesArray; - scoped Span iIndices; - nint[]? iIndicesArray; - if (tensor.Rank > 6) - { - oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - oIndices = oIndicesArray.AsSpan(0, tensor.Rank); - oIndices.Clear(); + nint[] newShape = tensor.Lengths.ToArray(); + nint splitLength = newShape[dimension] / splitCount; + newShape[dimension] = splitLength; - iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - iIndices = iIndicesArray.AsSpan(0, tensor.Rank); - iIndices.Clear(); - } - else + scoped Span sliceDims = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + for (int i = 0; i < sliceDims.Length; i++) { - oIndicesArray = null; - oIndices = stackalloc nint[tensor.Rank]; - iIndicesArray = null; - iIndices = stackalloc nint[tensor.Rank]; + sliceDims[i] = NRange.All; } - + nint start = 0; for (int i = 0; i < outputs.Length; i++) { + sliceDims[(int)dimension] = new NRange(start, start + splitLength); T[] values = new T[(int)totalToCopy]; - outputs[i] = Create(values, start: 0, newShape, strides: []); - oIndices.Clear(); - iIndices.Clear(); + outputs[i] = new Tensor(values, 0, newShape, [], tensor._shape.LinearRankOrder); - iIndices[(int)dimension] = i; - ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); - TensorSpan oslice = outputs[i].AsTensorSpan().Slice(outputs[i].Lengths); - - nint copiedValues = 0; - while (copiedValues < totalToCopy) - { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref oslice._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, outputs[0].Strides, outputs[0].Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, outputs[i].Lengths); - TensorSpanHelpers.AdjustIndexes((int)dimension - 1, 1, iIndices, tensor.Lengths); - copiedValues += copyLength; - } - } - - if (oIndicesArray != null && iIndicesArray != null) - { - ArrayPool.Shared.Return(oIndicesArray); - ArrayPool.Shared.Return(iIndicesArray); + tensor.Slice(sliceDims).CopyTo(outputs[i]); + start += splitLength; } + lengthsRentedBuffer.Dispose(); return outputs; } #endregion @@ -1948,24 +1899,27 @@ public static Tensor Squeeze(this Tensor tensor) /// The dimension to remove. public static Tensor SqueezeDimension(this Tensor tensor, int dimension) { - if (dimension >= tensor.Rank) + if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - nint[] lengths; - nint[] strides; + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + int newRank = 0; + int index = 0; - List tempLengths = new List(); if (dimension == -1) { for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } else { @@ -1977,14 +1931,21 @@ public static Tensor SqueezeDimension(this Tensor tensor, int dimension { if (i != dimension) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new Tensor(tensor._values, tensor._start, lengths, strides); + Tensor output = new Tensor(tensor._values, tensor._start, lengths, strides, strideOrder); + + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); + stridesOrderRentedBuffer.Dispose(); + + return output; } /// @@ -2004,24 +1965,27 @@ public static TensorSpan Squeeze(in this TensorSpan tensor) /// The dimension to remove. public static TensorSpan SqueezeDimension(in this TensorSpan tensor, int dimension) { - if (dimension >= tensor.Rank) + if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - nint[] lengths; - nint[] strides; + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + int newRank = 0; + int index = 0; - List tempLengths = new List(); if (dimension == -1) { for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } else { @@ -2033,14 +1997,21 @@ public static TensorSpan SqueezeDimension(in this TensorSpan tensor, in { if (i != dimension) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides, strideOrder); + + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); + stridesOrderRentedBuffer.Dispose(); + + return output; } /// @@ -2060,24 +2031,27 @@ public static ReadOnlyTensorSpan Squeeze(in this ReadOnlyTensorSpan ten /// The dimension to remove. public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSpan tensor, int dimension) { - if (dimension >= tensor.Rank) + if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - nint[] lengths; - nint[] strides; + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + int newRank = 0; + int index = 0; - List tempLengths = new List(); if (dimension == -1) { for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } else { @@ -2089,14 +2063,21 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp { if (i != dimension) { - tempLengths.Add(tensor.Lengths[i]); + lengths[index] = tensor.Lengths[i]; + strides[index] = tensor.Strides[i]; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + newRank++; } } - lengths = tempLengths.ToArray(); - strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides, strideOrder); + + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); + stridesOrderRentedBuffer.Dispose(); + + return output; } #endregion @@ -2122,7 +2103,7 @@ public static Tensor StackAlongDimension(int dimension, params ReadOnlySpa for (int i = 1; i < tensors.Length; i++) { - if (!TensorShape.AreLengthsTheSame(tensors[0].Lengths, tensors[i].Lengths)) + if (!tensors[0].Lengths.SequenceEqual(tensors[i].Lengths)) ThrowHelper.ThrowArgument_StackShapesNotSame(); } @@ -2160,7 +2141,7 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS for (int i = 1; i < tensors.Length; i++) { - if (!TensorShape.AreLengthsTheSame(tensors[0].Lengths, tensors[i].Lengths)) + if (!tensors[0].Lengths.SequenceEqual(tensors[i].Lengths)) ThrowHelper.ThrowArgument_StackShapesNotSame(); } @@ -2183,8 +2164,8 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) - => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); + public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) => + ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); /// /// Creates a representation of the ."/> @@ -2194,70 +2175,47 @@ public static string ToString(this in TensorSpan tensor, params ReadOnlySp /// Maximum Length of each dimension public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) { - if (maximumLengths.Length != tensor.Rank) - { - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); - } - StringBuilder sb = new(); - ToString(in tensor, maximumLengths, sb); + ToString(in tensor, sb, maximumLengths); return sb.ToString(); } - internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, StringBuilder sb, int indentLevel = 0) + internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBuilder sb, params ReadOnlySpan maximumLengths) { - Debug.Assert(maximumLengths.Length != tensor.Rank); - - sb.Append(' ', indentLevel * 2); - sb.Append('['); + if (maximumLengths.Length != tensor.Rank) + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); - if (tensor.Rank != 0) + scoped Span curIndexes; + nint[]? curIndexesArray; + if (tensor.Rank > 6) { - nint length = nint.Max(tensor.Lengths[0], maximumLengths[0]); - - if (tensor.Rank != 1) - { - string separator = string.Empty; - - for (nint i = 0; i < length; i++) - { - sb.AppendLine(separator); - - TensorShape tmpShape = TensorShape.Create(tensor.Lengths[1..], tensor.Strides[1..]); - ReadOnlyTensorSpan tmpTensor = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]), tmpShape); - ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); - - separator = ","; - } - - if (length != tensor.Lengths[0]) - { - sb.AppendLine(separator); - sb.Append(' ', indentLevel * 2); - sb.AppendLine("..."); - } - } - else - { - string separator = " "; + curIndexesArray = ArrayPool.Shared.Rent(tensor.Rank); + curIndexes = curIndexesArray.AsSpan(0, tensor.Rank); + curIndexes.Clear(); + } + else + { + curIndexesArray = null; + curIndexes = stackalloc nint[tensor.Rank]; + } - for (nint i = 0; i < length; i++) - { - sb.Append(separator); - sb.Append(Unsafe.Add(ref tensor._reference, i)); - separator = ", "; - } + nint copiedValues = 0; - if (length != tensor.Lengths[0]) - { - sb.Append(separator); - sb.Append("..."); - } - - sb.Append(separator); - } + T[] values = new T[tensor.Lengths[tensor.Rank - 1]]; + while (copiedValues < tensor.FlattenedLength) + { + var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, tensor.Strides, tensor.Lengths)), [tensor.Lengths[tensor.Rank - 1]], [1], tensor.Lengths[tensor.Rank - 1]); + sb.Append('{'); + sp.FlattenTo(values); + sb.Append(string.Join(",", values)); + sb.AppendLine("}"); + + TensorSpanHelpers.AdjustIndexes(tensor.Rank - 2, 1, curIndexes, tensor.Lengths); + copiedValues += tensor.Lengths[tensor.Rank - 1]; } - sb.Append(']'); + + if (curIndexesArray != null) + ArrayPool.Shared.Return(curIndexesArray); } /// @@ -2266,7 +2224,7 @@ internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpanThe you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); + public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); #endregion @@ -2280,14 +2238,33 @@ public static Tensor Transpose(Tensor tensor) if (tensor.Lengths.Length < 2) ThrowHelper.ThrowArgument_TransposeTooFewDimensions(); - Span dimension = tensor.Rank <= TensorShape.MaxInlineRank ? stackalloc int[tensor.Rank] : new int[tensor.Rank]; - TensorSpanHelpers.FillRange(dimension); + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + + tensor.Lengths.CopyTo(lengths); + tensor.Strides.CopyTo(strides); + tensor._shape.LinearRankOrder.CopyTo(strideOrder); + + nint temp = lengths[^1]; + lengths[^1] = lengths[^2]; + lengths[^2] = temp; + + temp = strides[^1]; + strides[^1] = strides[^2]; + strides[^2] = temp; + + int tempOrder = strideOrder[^1]; + strideOrder[^1] = strideOrder[^2]; + strideOrder[^2] = tempOrder; - int temp = dimension[tensor.Rank - 1]; - dimension[tensor.Rank - 1] = dimension[tensor.Rank - 2]; - dimension[tensor.Rank - 2] = temp; + Tensor output = new Tensor(tensor._values, tensor._start, lengths, strides, strideOrder); - return PermuteDimensions(tensor, dimension); + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); + stridesOrderRentedBuffer.Dispose(); + + return output; } #endregion @@ -2300,7 +2277,7 @@ public static Tensor Transpose(Tensor tensor) /// Destination . public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan destination) { - return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); + return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); } /// @@ -2311,7 +2288,7 @@ public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan des /// Destination . public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan destination) { - return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); + return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); } /// @@ -2322,14 +2299,11 @@ public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan /// Destination . public static bool TryBroadcastTo(in this ReadOnlyTensorSpan tensor, in TensorSpan destination) { - if (!TensorHelpers.IsBroadcastableTo(tensor.Lengths, destination.Lengths)) - return false; - - nint[] newSize = Tensor.GetSmallestBroadcastableLengths(tensor.Lengths, destination.Lengths); - if (!TensorShape.AreLengthsTheSame(destination.Lengths, newSize)) + TensorOperation.ValidateCompatibility(tensor, destination); + if (!TensorShape.AreCompatible(destination._shape, tensor._shape, false)) return false; - LazyBroadcast(tensor, newSize).CopyTo(destination); + BroadcastTo(tensor, destination); return true; } #endregion @@ -2347,11 +2321,10 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) if (dimension < 0) dimension = tensor.Rank - dimension; - Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Lengths.Length + 1] : - new nint[tensor.Lengths.Length + 1]; - tensor.Lengths[..dimension].CopyTo(lengths); - tensor.Lengths[dimension..].CopyTo(lengths.Slice(dimension + 1)); + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + + tensor.Lengths.Slice(0, dimension).CopyTo(lengths); + tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); lengths[dimension] = 1; Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? @@ -2369,7 +2342,9 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new Tensor(tensor._values, tensor._start, lengths, strides); + Tensor output = new Tensor(tensor._values, tensor._start, lengths, strides); + xRentedBuffer.Dispose(); + return output; } /// @@ -2384,9 +2359,8 @@ public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimen if (dimension < 0) dimension = tensor.Rank - dimension; - Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Lengths.Length + 1] : - new nint[tensor.Lengths.Length + 1]; + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + tensor.Lengths.Slice(0, dimension).CopyTo(lengths); tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); lengths[dimension] = 1; @@ -2406,7 +2380,9 @@ public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimen strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + xRentedBuffer.Dispose(); + return output; } /// @@ -2421,9 +2397,8 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t if (dimension < 0) dimension = tensor.Rank - dimension; - Span lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Lengths.Length + 1] : - new nint[tensor.Lengths.Length + 1]; + scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + tensor.Lengths.Slice(0, dimension).CopyTo(lengths); tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); lengths[dimension] = 1; @@ -2443,7 +2418,9 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; } - return new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); + xRentedBuffer.Dispose(); + return output; } #endregion @@ -4186,7 +4163,7 @@ public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4198,7 +4175,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4221,7 +4198,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in destination); + ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4244,7 +4221,7 @@ public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4256,7 +4233,7 @@ public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4302,7 +4279,7 @@ public static T MaxNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4314,7 +4291,7 @@ public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4418,7 +4395,7 @@ public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4430,7 +4407,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4453,7 +4430,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in destination); + ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4476,7 +4453,7 @@ public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4488,7 +4465,7 @@ public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4534,7 +4511,7 @@ public static T MinNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); + ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4546,7 +4523,7 @@ public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - TensorOperation.ValidateCompatibility(in x, in y, in destination); + ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 113be55c8a7254..53f6202e788958 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates; +using static System.Numerics.Tensors.TensorOperation; namespace System.Numerics.Tensors { @@ -13,7 +15,7 @@ internal static class TensorOperation public static void Invoke(in TensorSpan x) where TOperation : TensorOperation.IOperation { - scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -31,8 +33,8 @@ public static bool Invoke(in ReadOnlyTensorSpan x, in Re { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); for (nint i = 0; i < x.FlattenedLength; i++) @@ -63,7 +65,7 @@ public static bool Invoke(in ReadOnlyTensorSpan x, TArg { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -89,7 +91,7 @@ ref result public static void Invoke(in TensorSpan destination, TArg scalar) where TOperation : TensorOperation.IUnaryOperation_Scalar { - scoped Span indexes = RentedBuffer.Create(destination.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(destination.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -106,8 +108,8 @@ ref Unsafe.Add(ref destination._reference, linearOffset), public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -128,7 +130,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in Span destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); nint destinationIndex = -1; for (nint i = 0; i < destination.Length; i++) @@ -148,7 +150,7 @@ ref Unsafe.Add(ref destination[0], destinationIndex) public static void Invoke(in ReadOnlyTensorSpan x, ref TResult destination) where TOperation : TensorOperation.IUnaryReduction_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -166,9 +168,9 @@ ref destination public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(destination.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(destination.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destination.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -191,8 +193,8 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); nint loopCount = Math.Max(x.FlattenedLength, y.FlattenedLength); @@ -224,8 +226,8 @@ public static void Invoke(in ReadOnlyTensorSpan public static void Invoke(in ReadOnlyTensorSpan x, T2 y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -246,8 +248,8 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(TArg x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Scalar_Tensor { - scoped Span xIndexes = RentedBuffer.Create(y.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(y.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -268,7 +270,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, T2 y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -2534,21 +2536,34 @@ public interface IUnaryReduction_Tensor static abstract void Invoke(ReadOnlySpan x, ref TResult destination); } - private ref struct RentedBuffer : IDisposable + internal readonly struct RentedBuffer { - private nint[]? _array; - private TensorShape.InlineBuffer _inline; - - public static Span Create(int rank, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) + public static Span Create(int rank, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) + where T : INumber { + Span output = RentedBuffer.Create(rank, out rentedBuffer); linearOffset = 0; + output[rank - 1] = T.CreateChecked(-1); + return output; + } + + public static Span CreateUninitialized(int rank, [UnscopedRef] out RentedBuffer rentedBuffer) + => RentedBuffer.Create(rank, out rentedBuffer); + } + + internal ref struct RentedBuffer : IDisposable + { + private T[]? _array; + private TensorShape.InlineBuffer _inline; + + public static Span Create(int rank, [UnscopedRef] out RentedBuffer rentedBuffer) + { if (rank > TensorShape.MaxInlineRank) { - rentedBuffer._array = ArrayPool.Shared.Rent(rank); + rentedBuffer._array = ArrayPool.Shared.Rent(rank); Unsafe.SkipInit(out rentedBuffer._inline); - rentedBuffer._array[rank - 1] = -1; return rentedBuffer._array.AsSpan(0, rank); } else @@ -2556,8 +2571,7 @@ public static Span Create(int rank, out nint linearOffset, [UnscopedRef] o rentedBuffer._array = null; rentedBuffer._inline = default; - rentedBuffer._inline[rank - 1] = -1; - return ((Span)rentedBuffer._inline)[..rank]; + return ((Span)rentedBuffer._inline)[..rank]; } } @@ -2565,7 +2579,7 @@ public void Dispose() { if (_array is not null) { - ArrayPool.Shared.Return(_array); + ArrayPool.Shared.Return(_array); _array = null; } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 05f7fa5c937aff..0d9dc7001be549 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -31,6 +31,13 @@ namespace System.Numerics.Tensors // strides and the backing memory form a strict relationship that is validated by the // public constructors. + [Flags] + internal enum TensorFlags : uint + { + None = 0, + IsContiguousAndDense = 1 + } + [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] internal readonly struct TensorShape : IEquatable { @@ -54,7 +61,8 @@ namespace System.Numerics.Tensors private readonly InlineBuffer _inlineStrides; // 4x8 bytes (32) private readonly InlineBuffer _inlineLinearRankOrder; // 4x4 bytes (16) - private readonly int _rank; // 4 bytes + private readonly int _rank; + private readonly TensorFlags _tensorFlags; // 4 bytes private TensorShape(nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) { @@ -136,7 +144,7 @@ ref Unsafe.As(ref metadata[rank * 2]), if (strides.Length == 0) { - // When no strides is specified, we need to computing them based on the given + // When no strides are specified, we need to computing them based on the given // rank order. We use destinationLinearRankOrder here to ensure that we have a // correct order even if no rank order was specified by the user. // @@ -160,6 +168,11 @@ ref Unsafe.As(ref metadata[rank * 2]), destinationLengths[linearRankIndex] = length; destinationStrides[linearRankIndex] = flattenedLength; } + + // When no strides are specified, the way we construct them makes it so that the + // underlying memory is contiguous and dense. This means that we can set the flag + // to indicate that the tensor is contiguous and dense without any further checks. + _tensorFlags |= TensorFlags.IsContiguousAndDense; } else if (strides.Length != lengths.Length) { @@ -298,6 +311,8 @@ ref Unsafe.As(ref metadata[rank * 2]), _rank = rank; } + public bool IsContiguousAndDense => (_tensorFlags & TensorFlags.IsContiguousAndDense) != 0; + public nint FlattenedLength => _flattenedLength; public bool IsEmpty => _flattenedLength == 0; @@ -767,6 +782,36 @@ public static TensorShape Create(T[]? array, int start, scoped ReadOnlySpan(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) + { + if (array is not null) + { + int linearLength = array.Length; + + if ((start < 0) || (start > linearLength)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + linearLength -= start; + + if (linearLength != 0) + { + return new TensorShape(linearLength, lengths, strides, linearRankOrder); + } + } + else if (start != 0) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } + + if ((lengths.Length != 0) || (strides.Length != 0)) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + return default; + } + public static TensorShape Create(ref T reference, nint linearLength) { if (!Unsafe.IsNullRef(ref reference)) @@ -791,12 +836,15 @@ public static TensorShape Create(ref T reference, nint linearLength) } public static TensorShape Create(ref T reference, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => Create(ref reference, linearLength, lengths, strides, linearRankOrder: []); + + public static TensorShape Create(ref T reference, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) { if (!Unsafe.IsNullRef(ref reference)) { if (linearLength != 0) { - return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); + return new TensorShape(linearLength, lengths, strides, linearRankOrder); } } else if (linearLength != 0) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 0fd4682632d120..7059cc061411f6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -120,6 +120,12 @@ internal TensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengt _reference = ref data; } + internal TensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) + { + _shape = TensorShape.Create(ref data, dataLength, lengths, strides, linearRankOrder); + _reference = ref data; + } + internal TensorSpan(ref T reference, scoped in TensorShape shape) { _reference = ref reference; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index c7554d852f0131..bc9f5c771522c0 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -55,6 +55,15 @@ internal Tensor(T[]? array, int start, scoped ReadOnlySpan lengths, scoped _isPinned = false; } + internal Tensor(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) + { + _shape = TensorShape.Create(array, start, lengths, strides, linearRankOrder); + _values = (array is not null) ? array : []; + + _start = start; + _isPinned = false; + } + internal Tensor(T[] array, int start, in TensorShape shape, bool isPinned) { ThrowHelper.ThrowIfArrayTypeMismatch(array); @@ -257,7 +266,7 @@ public string ToString(params ReadOnlySpan maximumLengths) var sb = new StringBuilder($"System.Numerics.Tensors.Tensor<{typeof(T).Name}>[{_shape}]"); sb.AppendLine("{"); - Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, sb); + ((ReadOnlyTensorSpan)AsTensorSpan()).ToString(sb, maximumLengths); sb.AppendLine("}"); return sb.ToString(); From cbae3a181d24986d795d6a31278e01223a947444 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 07:13:31 -0700 Subject: [PATCH 08/30] Minor cleanup of the Tensor files --- .../System/Numerics/Tensors/netcore/Tensor.cs | 169 ++++++++++-------- .../Tensors/netcore/TensorOperation.cs | 2 - .../Numerics/Tensors/netcore/Tensor_1.cs | 2 +- 3 files changed, 95 insertions(+), 78 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index b6225d8d46f4c4..0b44f6daeca885 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -9,11 +9,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Serialization; using System.Text; -using System.Threading.Tasks; -using static System.Numerics.Tensors.TensorOperation; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace System.Numerics.Tensors { @@ -1341,9 +1337,9 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO } else { - scoped Span newLengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); - scoped Span newStrides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); - scoped Span newLinearOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer linearOrderRentedBuffer); + scoped Span newLengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); + scoped Span newStrides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesRentedBuffer); + scoped Span newLinearOrder = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer linearOrderRentedBuffer); Tensor outTensor; @@ -1821,8 +1817,7 @@ public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan tensor, /// The ranges you want to set. public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan values, params ReadOnlySpan ranges) { - SetSlice((TensorSpan)tensor, values, ranges); - + tensor.AsTensorSpan().SetSlice(values, ranges); return tensor; } @@ -1860,7 +1855,7 @@ public static Tensor[] Split(scoped in ReadOnlyTensorSpan tensor, int s nint splitLength = newShape[dimension] / splitCount; newShape[dimension] = splitLength; - scoped Span sliceDims = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); + scoped Span sliceDims = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); for (int i = 0; i < sliceDims.Length; i++) { sliceDims[i] = NRange.All; @@ -1902,9 +1897,9 @@ public static Tensor SqueezeDimension(this Tensor tensor, int dimension if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); - scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); - scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); + scoped Span strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesOrderRentedBuffer); int newRank = 0; int index = 0; @@ -1968,9 +1963,9 @@ public static TensorSpan SqueezeDimension(in this TensorSpan tensor, in if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); - scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); - scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); + scoped Span strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesOrderRentedBuffer); int newRank = 0; int index = 0; @@ -2034,9 +2029,9 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp if (dimension >= tensor.Rank || dimension < -1) ThrowHelper.ThrowArgument_AxisLargerThanRank(); - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); - scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); - scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); + scoped Span strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesOrderRentedBuffer); int newRank = 0; int index = 0; @@ -2164,8 +2159,8 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) => - ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) + => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); /// /// Creates a representation of the ."/> @@ -2175,47 +2170,70 @@ public static string ToString(this in TensorSpan tensor, params ReadOnlySp /// Maximum Length of each dimension public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) { + if (maximumLengths.Length != tensor.Rank) + { + ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + } + StringBuilder sb = new(); - ToString(in tensor, sb, maximumLengths); + ToString(in tensor, maximumLengths, sb); return sb.ToString(); } - internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBuilder sb, params ReadOnlySpan maximumLengths) + internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, StringBuilder sb, int indentLevel = 0) { - if (maximumLengths.Length != tensor.Rank) - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + Debug.Assert(maximumLengths.Length != tensor.Rank); - scoped Span curIndexes; - nint[]? curIndexesArray; - if (tensor.Rank > 6) - { - curIndexesArray = ArrayPool.Shared.Rent(tensor.Rank); - curIndexes = curIndexesArray.AsSpan(0, tensor.Rank); - curIndexes.Clear(); - } - else + sb.Append(' ', indentLevel * 2); + sb.Append('['); + + if (tensor.Rank != 0) { - curIndexesArray = null; - curIndexes = stackalloc nint[tensor.Rank]; - } + nint length = nint.Max(tensor.Lengths[0], maximumLengths[0]); - nint copiedValues = 0; + if (tensor.Rank != 1) + { + string separator = string.Empty; - T[] values = new T[tensor.Lengths[tensor.Rank - 1]]; - while (copiedValues < tensor.FlattenedLength) - { - var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, tensor.Strides, tensor.Lengths)), [tensor.Lengths[tensor.Rank - 1]], [1], tensor.Lengths[tensor.Rank - 1]); - sb.Append('{'); - sp.FlattenTo(values); - sb.Append(string.Join(",", values)); - sb.AppendLine("}"); - - TensorSpanHelpers.AdjustIndexes(tensor.Rank - 2, 1, curIndexes, tensor.Lengths); - copiedValues += tensor.Lengths[tensor.Rank - 1]; - } + for (nint i = 0; i < length; i++) + { + sb.AppendLine(separator); + + TensorShape tmpShape = TensorShape.Create(tensor.Lengths[1..], tensor.Strides[1..]); + ReadOnlyTensorSpan tmpTensor = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]), tmpShape); + ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); + + separator = ","; + } + + if (length != tensor.Lengths[0]) + { + sb.AppendLine(separator); + sb.Append(' ', indentLevel * 2); + sb.AppendLine("..."); + } + } + else + { + string separator = " "; + + for (nint i = 0; i < length; i++) + { + sb.Append(separator); + sb.Append(Unsafe.Add(ref tensor._reference, i)); + separator = ", "; + } + + if (length != tensor.Lengths[0]) + { + sb.Append(separator); + sb.Append("..."); + } - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); + sb.Append(separator); + } + } + sb.Append(']'); } /// @@ -2224,7 +2242,8 @@ internal static void ToString(this in ReadOnlyTensorSpan tensor, StringBui /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) => ((ReadOnlyTensorSpan)tensor).ToString(maximumLengths); + public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) + => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); #endregion @@ -2238,9 +2257,9 @@ public static Tensor Transpose(Tensor tensor) if (tensor.Lengths.Length < 2) ThrowHelper.ThrowArgument_TransposeTooFewDimensions(); - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer lengthsRentedBuffer); - scoped Span strides = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesRentedBuffer); - scoped Span strideOrder = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer stridesOrderRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer lengthsRentedBuffer); + scoped Span strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesRentedBuffer); + scoped Span strideOrder = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer stridesOrderRentedBuffer); tensor.Lengths.CopyTo(lengths); tensor.Strides.CopyTo(strides); @@ -2277,7 +2296,7 @@ public static Tensor Transpose(Tensor tensor) /// Destination . public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan destination) { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); } /// @@ -2288,7 +2307,7 @@ public static bool TryBroadcastTo(this Tensor tensor, in TensorSpan des /// Destination . public static bool TryBroadcastTo(in this TensorSpan tensor, in TensorSpan destination) { - return TryBroadcastTo((ReadOnlyTensorSpan)tensor, destination); + return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination); } /// @@ -2321,7 +2340,7 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); tensor.Lengths.Slice(0, dimension).CopyTo(lengths); tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); @@ -2359,7 +2378,7 @@ public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimen if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); tensor.Lengths.Slice(0, dimension).CopyTo(lengths); tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); @@ -2397,7 +2416,7 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = RentedBuffer.CreateUninitialized(tensor.Rank, out RentedBuffer xRentedBuffer); + scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); tensor.Lengths.Slice(0, dimension).CopyTo(lengths); tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); @@ -4163,7 +4182,7 @@ public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4175,7 +4194,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4198,7 +4217,7 @@ public static Tensor MaxMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in destination); + TensorOperation.ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4221,7 +4240,7 @@ public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4233,7 +4252,7 @@ public static Tensor MaxMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4279,7 +4298,7 @@ public static T MaxNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4291,7 +4310,7 @@ public static Tensor MaxNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4395,7 +4414,7 @@ public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4407,7 +4426,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, in ReadOnlyT public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4430,7 +4449,7 @@ public static Tensor MinMagnitude(in ReadOnlyTensorSpan x, T y) public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTensorSpan x, T y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in destination); + TensorOperation.ValidateCompatibility(in x, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4453,7 +4472,7 @@ public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4465,7 +4484,7 @@ public static Tensor MinMagnitudeNumber(in ReadOnlyTensorSpan x, in Rea public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } @@ -4511,7 +4530,7 @@ public static T MinNumber(scoped in ReadOnlyTensorSpan x) public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) where T : INumber { - ValidateCompatibility(in x, in y, out Tensor destination); + TensorOperation.ValidateCompatibility(in x, in y, out Tensor destination); TensorOperation.Invoke, T, T>(x, y, destination); return destination; } @@ -4523,7 +4542,7 @@ public static Tensor MinNumber(in ReadOnlyTensorSpan x, in ReadOnlyTens public static ref readonly TensorSpan MinNumber(scoped in ReadOnlyTensorSpan x, scoped in ReadOnlyTensorSpan y, in TensorSpan destination) where T : INumber { - ValidateCompatibility(in x, in y, in destination); + TensorOperation.ValidateCompatibility(in x, in y, in destination); TensorOperation.Invoke, T, T>(x, y, destination); return ref destination; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 53f6202e788958..bf93faf6ec4cfa 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -5,8 +5,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Security.Cryptography.X509Certificates; -using static System.Numerics.Tensors.TensorOperation; namespace System.Numerics.Tensors { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index bc9f5c771522c0..da669ec6a27e15 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -266,7 +266,7 @@ public string ToString(params ReadOnlySpan maximumLengths) var sb = new StringBuilder($"System.Numerics.Tensors.Tensor<{typeof(T).Name}>[{_shape}]"); sb.AppendLine("{"); - ((ReadOnlyTensorSpan)AsTensorSpan()).ToString(sb, maximumLengths); + Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, sb); sb.AppendLine("}"); return sb.ToString(); From 5ce0d05c967f23501d846a3f2068646f41f13d08 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 09:23:27 -0700 Subject: [PATCH 09/30] Ensure that tensor tests are building --- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 87 ++++- .../System/Numerics/Tensors/netcore/Tensor.cs | 63 ++-- .../Numerics/Tensors/netcore/TensorShape.cs | 47 +++ .../Numerics/Tensors/netcore/TensorSpan.cs | 53 ++- .../Numerics/Tensors/netcore/Tensor_1.cs | 40 +++ .../tests/ReadOnlyTensorSpanTests.cs | 122 ++++--- .../tests/TensorSpanTests.cs | 157 +++++---- .../tests/TensorTests.cs | 303 +++++++++--------- 8 files changed, 557 insertions(+), 315 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 3e673cd835b795..faaf8c4af9e1b1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.VisualBasic; using static System.Numerics.Tensors.TensorOperation; #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member @@ -42,6 +43,46 @@ public ReadOnlyTensorSpan(T[]? array) : ref Unsafe.NullRef(); } + /// Creates a new tensor over the portion of the target array using the specified lengths. + /// The target array. + /// The lengths of the dimensions. If an empty span is provided, the created tensor will have a single dimension that is the same length as . + /// Returns default when is null. + /// + /// Thrown when one of the following conditions is met: + /// * is null and is not empty + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// + public ReadOnlyTensorSpan(T[]? array, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(array, lengths); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); + } + + /// Creates a new tensor over the portion of the target array beginning at the specified start index and using the specified lengths and strides. + /// The target array. + /// The lengths of the dimensions. If an empty span is provided, the created tensor will have a single dimension that is the same length as . + /// The strides of each dimension. If an empty span is provided, then strides will be automatically calculated from . + /// Returns default when is null. + /// + /// Thrown when one of the following conditions is met: + /// * is null and or is not empty + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// * is not empty and has a length different from + /// * is not empty and contains an element that is negative + /// * is not empty and contains an element that is zero in a non leading position + /// + public ReadOnlyTensorSpan(T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(array, lengths, strides); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); + } + /// Creates a new tensor over the portion of the target array beginning at the specified start index and using the specified lengths and strides. /// The target array. /// The index at which to begin the tensor. @@ -76,6 +117,21 @@ public ReadOnlyTensorSpan(ReadOnlySpan span) _reference = ref reference; } + /// Creates a new tensor span over the target span using the specified lengths. + /// The target span. + /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . + /// + /// Thrown when one of the following conditions is met: + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than .Length + /// + public ReadOnlyTensorSpan(ReadOnlySpan span, scoped ReadOnlySpan lengths) + { + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length, lengths); + _reference = ref reference; + } + /// Creates a new tensor span over the target span using the specified lengths and strides. /// The target span. /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . @@ -145,7 +201,26 @@ public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan start, scoped R [CLSCompliant(false)] public unsafe ReadOnlyTensorSpan(T* data, nint dataLength) { - _shape = TensorShape.Create(data, dataLength); + _shape = TensorShape.Create(data, dataLength); + _reference = ref Unsafe.AsRef(data); + } + + /// Creates a new tensor span over the target unmanaged buffer using the specified lengths. + /// The pointer to the start of the target unmanaged buffer. + /// The number of elements the target unmanaged buffer contains. + /// The lengths of the dimensions. If an empty span is provided, the created tensor span will have a single dimension that is the same length as . + /// Returns default when is null. + /// + /// Thrown when one of the following conditions is met: + /// * is null and is not zero + /// * is null and + /// * is not empty and contains an element that is either zero or negative + /// * is not empty and has a flattened length greater than + /// + [CLSCompliant(false)] + public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(data, dataLength, lengths); _reference = ref Unsafe.AsRef(data); } @@ -168,7 +243,7 @@ public unsafe ReadOnlyTensorSpan(T* data, nint dataLength) [CLSCompliant(false)] public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - _shape = TensorShape.Create(data, dataLength, lengths, strides); + _shape = TensorShape.Create(data, dataLength, lengths, strides); _reference = ref Unsafe.AsRef(data); } @@ -179,6 +254,12 @@ internal ReadOnlyTensorSpan(ref T data, nint dataLength) _reference = ref data; } + internal ReadOnlyTensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(ref data, dataLength, lengths); + _reference = ref data; + } + internal ReadOnlyTensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { _shape = TensorShape.Create(ref data, dataLength, lengths, strides); @@ -218,6 +299,8 @@ public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] /// public nint FlattenedLength => _shape.FlattenedLength; + internal bool IsContiguousAndDense => _shape.IsContiguousAndDense; + /// public bool IsEmpty => _shape.IsEmpty; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 0b44f6daeca885..f34b6a950951c6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -19,13 +19,32 @@ public static partial class Tensor { #region AsTensorSpan /// - public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array) => new ReadOnlyTensorSpan(array); + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array) + => new ReadOnlyTensorSpan(array); + + /// + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, scoped ReadOnlySpan lengths) + => new ReadOnlyTensorSpan(array, lengths); + + /// + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new ReadOnlyTensorSpan(array, lengths, strides); /// - public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new ReadOnlyTensorSpan(array, start, lengths, strides); + public static ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new ReadOnlyTensorSpan(array, start, lengths, strides); /// - public static TensorSpan AsTensorSpan(this T[]? array) => new TensorSpan(array); + public static TensorSpan AsTensorSpan(this T[]? array) + => new TensorSpan(array); + + /// + public static TensorSpan AsTensorSpan(this T[]? array, scoped ReadOnlySpan lengths) + => new TensorSpan(array, lengths); + + /// + public static TensorSpan AsTensorSpan(this T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new TensorSpan(array, lengths, strides); /// public static TensorSpan AsTensorSpan(this T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new TensorSpan(array, start, lengths, strides); @@ -257,14 +276,21 @@ public static Tensor Create(scoped ReadOnlySpan lengths, scoped Read public static Tensor Create(T[] array) => new Tensor(array); + /// + /// A new tensor that uses as its backing buffer and with the specified . + public static Tensor Create(T[] array, scoped ReadOnlySpan lengths) + => new Tensor(array, lengths); + + /// + /// A new tensor that uses as its backing buffer and with the specified and . + public static Tensor Create(T[] array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + => new Tensor(array, lengths, strides); + /// /// A new tensor that uses as its backing buffer and with the specified and . public static Tensor Create(T[] array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new Tensor(array, start, lengths, strides); - /// Creates a tensor that contains elements copied from the specified enumerable. - /// The enumerable whose elements are copied to the new tensor. - /// true to pin the underlying buffer. The default is false. /// A new tensor that contains elements copied from . public static Tensor Create(IEnumerable enumerable, bool pinned = false) { @@ -284,12 +310,13 @@ public static Tensor Create(IEnumerable enumerable, bool pinned = false } } - /// Creates a tensor that contains elements copied from the specified enumerable. - /// The enumerable whose elements are copied to the new tensor. - /// The lengths of each dimension. - /// The strides of each dimension. - /// true to pin the underlying buffer. The default is false. - /// A new tensor that contains elements copied from . + /// + /// A new tensor that contains elements copied from and with the specified . + public static Tensor Create(IEnumerable enumerable, scoped ReadOnlySpan lengths, bool pinned = false) + => Create(enumerable, lengths, strides: [], pinned); + + /// + /// A new tensor that contains elements copied from and with the specified and . public static Tensor Create(IEnumerable enumerable, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned = false) { if (pinned) @@ -304,7 +331,7 @@ public static Tensor Create(IEnumerable enumerable, scoped ReadOnlySpan else { T[] array = enumerable.ToArray(); - return Create(array, start: 0, lengths, strides); + return Create(array, lengths, strides); } } @@ -360,7 +387,7 @@ public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths { TensorShape shape = TensorShape.Create(lengths, strides: []); T[] array = GC.AllocateUninitializedArray(checked((int)(shape.LinearLength)), pinned); - return new Tensor(array, start: 0, in shape, pinned); + return new Tensor(array, in shape, pinned); } /// @@ -368,7 +395,7 @@ public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths { TensorShape shape = TensorShape.Create(lengths, strides); T[] values = GC.AllocateUninitializedArray(checked((int)(shape.LinearLength)), pinned); - return new Tensor(values, start: 0, in shape, pinned); + return new Tensor(values, in shape, pinned); } #endregion @@ -1388,7 +1415,7 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan Reshape(in this TensorSpan tensor, params scop if (tensor.Lengths.SequenceEqual(lengths)) return tensor; - if (!tensor._shape.IsContiguousAndDense && !tensor.Strides.Contains(0)) + if (!tensor.IsContiguousAndDense && !tensor.Strides.Contains(0)) { ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); } @@ -1536,7 +1563,7 @@ public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan ten if (tensor.Lengths.SequenceEqual(lengths)) return tensor; - if (!tensor._shape.IsContiguousAndDense && !tensor.Strides.Contains(0)) + if (!tensor.IsContiguousAndDense && !tensor.Strides.Contains(0)) { ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense(); } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 0d9dc7001be549..63b967c96adab6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -728,6 +728,9 @@ public static TensorShape Create(Array? array, scoped ReadOnlySpan start, s return default; } + public static TensorShape Create(scoped ReadOnlySpan lengths) + => new TensorShape(linearLength: -1, lengths, strides: [], linearRankOrder: []); + public static TensorShape Create(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => new TensorShape(linearLength: -1, lengths, strides, linearRankOrder: []); @@ -752,6 +755,44 @@ public static TensorShape Create(T[]? array) return default; } + public static TensorShape Create(T[]? array, scoped ReadOnlySpan lengths) + { + if (array is not null) + { + int linearLength = array.Length; + + if (linearLength != 0) + { + return new TensorShape(linearLength, lengths, strides: [], linearRankOrder: []); + } + } + + if (lengths.Length != 0) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + return default; + } + + public static TensorShape Create(T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + if (array is not null) + { + int linearLength = array.Length; + + if (linearLength != 0) + { + return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); + } + } + + if ((lengths.Length != 0) || (strides.Length != 0)) + { + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + return default; + } + public static TensorShape Create(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { if (array is not null) @@ -835,6 +876,9 @@ public static TensorShape Create(ref T reference, nint linearLength) return default; } + public static TensorShape Create(ref T reference, nint linearLength, scoped ReadOnlySpan lengths) + => Create(ref reference, linearLength, lengths, strides: [], linearRankOrder: []); + public static TensorShape Create(ref T reference, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => Create(ref reference, linearLength, lengths, strides, linearRankOrder: []); @@ -862,6 +906,9 @@ public static TensorShape Create(ref T reference, nint linearLength, scoped R public static unsafe TensorShape Create(T* address, nint linearLength) => Create(ref Unsafe.AsRef(address), linearLength); + public static unsafe TensorShape Create(T* address, nint linearLength, scoped ReadOnlySpan lengths) + => Create(ref Unsafe.AsRef(address), linearLength, lengths, strides: []); + public static unsafe TensorShape Create(T* address, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) => Create(ref Unsafe.AsRef(address), linearLength, lengths, strides); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 7059cc061411f6..a404aa20b59736 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.VisualBasic; using static System.Numerics.Tensors.TensorOperation; #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member @@ -40,6 +41,30 @@ public TensorSpan(T[]? array) : ref Unsafe.NullRef(); } + /// + /// is covariant and its type is not exactly T[]. + public TensorSpan(T[]? array, scoped ReadOnlySpan lengths) + { + ThrowHelper.ThrowIfArrayTypeMismatch(array); + + _shape = TensorShape.Create(array, lengths); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); + } + + /// + /// is covariant and its type is not exactly T[]. + public TensorSpan(T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + ThrowHelper.ThrowIfArrayTypeMismatch(array); + + _shape = TensorShape.Create(array, lengths, strides); + _reference = ref (array is not null) + ? ref MemoryMarshal.GetArrayDataReference(array) + : ref Unsafe.NullRef(); + } + /// /// is covariant and its type is not exactly T[]. public TensorSpan(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) @@ -60,6 +85,14 @@ public TensorSpan(Span span) _reference = ref reference; } + /// + public TensorSpan(Span span, scoped ReadOnlySpan lengths) + { + ref T reference = ref MemoryMarshal.GetReference(span); + _shape = TensorShape.Create(ref reference, span.Length, lengths); + _reference = ref reference; + } + /// public TensorSpan(Span span, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { @@ -96,7 +129,15 @@ public TensorSpan(Array? array, scoped ReadOnlySpan start, scoped ReadOnlyS [CLSCompliant(false)] public unsafe TensorSpan(T* data, nint dataLength) { - _shape = TensorShape.Create(data, dataLength); + _shape = TensorShape.Create(data, dataLength); + _reference = ref Unsafe.AsRef(data); + } + + /// + [CLSCompliant(false)] + public unsafe TensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(data, dataLength, lengths); _reference = ref Unsafe.AsRef(data); } @@ -104,7 +145,7 @@ public unsafe TensorSpan(T* data, nint dataLength) [CLSCompliant(false)] public unsafe TensorSpan(T* data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { - _shape = TensorShape.Create(data, dataLength, lengths, strides); + _shape = TensorShape.Create(data, dataLength, lengths, strides); _reference = ref Unsafe.AsRef(data); } @@ -114,6 +155,12 @@ internal TensorSpan(ref T data, nint dataLength) _reference = ref data; } + internal TensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(ref data, dataLength, lengths); + _reference = ref data; + } + internal TensorSpan(ref T data, nint dataLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { _shape = TensorShape.Create(ref data, dataLength, lengths, strides); @@ -154,6 +201,8 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] /// public nint FlattenedLength => _shape.FlattenedLength; + internal bool IsContiguousAndDense => _shape.IsContiguousAndDense; + /// public bool IsEmpty => _shape.IsEmpty; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index da669ec6a27e15..b7cc96aadf9abe 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -28,6 +28,15 @@ public sealed class Tensor : ITensor, T> internal readonly int _start; internal readonly bool _isPinned; + internal Tensor(scoped ReadOnlySpan lengths, bool pinned) + { + _shape = TensorShape.Create(lengths); + _values = GC.AllocateArray(checked((int)(_shape.LinearLength)), pinned); + + _start = 0; + _isPinned = pinned; + } + internal Tensor(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) { _shape = TensorShape.Create(lengths, strides); @@ -46,6 +55,24 @@ internal Tensor(T[]? array) _isPinned = false; } + internal Tensor(T[]? array, scoped ReadOnlySpan lengths) + { + _shape = TensorShape.Create(array, lengths); + _values = (array is not null) ? array : []; + + _start = 0; + _isPinned = false; + } + + internal Tensor(T[]? array, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) + { + _shape = TensorShape.Create(array, lengths, strides); + _values = (array is not null) ? array : []; + + _start = 0; + _isPinned = false; + } + internal Tensor(T[]? array, int start, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides) { _shape = TensorShape.Create(array, start, lengths, strides); @@ -64,6 +91,17 @@ internal Tensor(T[]? array, int start, scoped ReadOnlySpan lengths, scoped _isPinned = false; } + internal Tensor(T[] array, in TensorShape shape, bool isPinned) + { + ThrowHelper.ThrowIfArrayTypeMismatch(array); + + _shape = shape; + _values = array; + + _start = 0; + _isPinned = isPinned; + } + internal Tensor(T[] array, int start, in TensorShape shape, bool isPinned) { ThrowHelper.ThrowIfArrayTypeMismatch(array); @@ -106,6 +144,8 @@ public Tensor this[params ReadOnlySpan ranges] /// public nint FlattenedLength => _shape.FlattenedLength; + internal bool IsContiguousAndDense => _shape.IsContiguousAndDense; + /// public bool IsEmpty => _shape.IsEmpty; diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index d88ccfb9a7fbe6..37de472c886cc2 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -5,8 +5,6 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace System.Numerics.Tensors.Tests @@ -206,25 +204,25 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(94, spanInt[1, 0]); Assert.Equal(94, spanInt[1, 1]); - //Make sure it works with NIndex - spanInt = new ReadOnlyTensorSpan(a, (NIndex[])[1, 1], [2, 2], [0, 0]); - Assert.Equal(2, spanInt.Rank); - Assert.Equal(2, spanInt.Lengths[0]); - Assert.Equal(2, spanInt.Lengths[1]); - Assert.Equal(94, spanInt[0, 0]); - Assert.Equal(94, spanInt[0, 1]); - Assert.Equal(94, spanInt[1, 0]); - Assert.Equal(94, spanInt[1, 1]); - - //Make sure it works with NIndex - spanInt = new ReadOnlyTensorSpan(a, (NIndex[])[^1, ^1], [2, 2], [0, 0]); - Assert.Equal(2, spanInt.Rank); - Assert.Equal(2, spanInt.Lengths[0]); - Assert.Equal(2, spanInt.Lengths[1]); - Assert.Equal(94, spanInt[0, 0]); - Assert.Equal(94, spanInt[0, 1]); - Assert.Equal(94, spanInt[1, 0]); - Assert.Equal(94, spanInt[1, 1]); + // // TODO: Make sure it works with NIndex + // spanInt = new ReadOnlyTensorSpan(a, (NIndex[])[1, 1], [2, 2], [0, 0]); + // Assert.Equal(2, spanInt.Rank); + // Assert.Equal(2, spanInt.Lengths[0]); + // Assert.Equal(2, spanInt.Lengths[1]); + // Assert.Equal(94, spanInt[0, 0]); + // Assert.Equal(94, spanInt[0, 1]); + // Assert.Equal(94, spanInt[1, 0]); + // Assert.Equal(94, spanInt[1, 1]); + + // // TODO: Make sure it works with NIndex + // spanInt = new ReadOnlyTensorSpan(a, (NIndex[])[^1, ^1], [2, 2], [0, 0]); + // Assert.Equal(2, spanInt.Rank); + // Assert.Equal(2, spanInt.Lengths[0]); + // Assert.Equal(2, spanInt.Lengths[1]); + // Assert.Equal(94, spanInt[0, 0]); + // Assert.Equal(94, spanInt[0, 1]); + // Assert.Equal(94, spanInt[1, 0]); + // Assert.Equal(94, spanInt[1, 1]); } [Fact] @@ -275,7 +273,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(0, spanInt.Strides[0]); // Make sure 2D array works - spanInt = new ReadOnlyTensorSpan(a, new Index(0), [2,2], default); + spanInt = new ReadOnlyTensorSpan(a, 0, [2,2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -285,24 +283,24 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1,1]); // Make sure can use only some of the array - spanInt = new ReadOnlyTensorSpan(a, new Index(0), [1, 2], default); + spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(0), [1, 2], default); + var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 1]; }); Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(0), [1, 2], default); + var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 0]; }); // Make sure Index offset works correctly - spanInt = new ReadOnlyTensorSpan(a, new Index(1), [1, 2], default); + spanInt = new ReadOnlyTensorSpan(a, 1, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -310,7 +308,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(-93, spanInt[0, 1]); // Make sure Index offset works correctly - spanInt = new ReadOnlyTensorSpan(a, new Index(2), [1, 2], default); + spanInt = new ReadOnlyTensorSpan(a, 2, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -319,11 +317,11 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() // Make sure we catch that there aren't enough elements in the array for the lengths Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(3), [1, 2], default); + var spanInt = new ReadOnlyTensorSpan(a, 3, [1, 2], default); }); // Make sure 2D array works with basic strides - spanInt = new ReadOnlyTensorSpan(a, new Index(0), [2, 2], [2, 1]); + spanInt = new ReadOnlyTensorSpan(a, 0, [2, 2], [2, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -333,7 +331,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1, 1]); // Make sure 2D array works with stride of 0 to loop over first 2 elements again - spanInt = new ReadOnlyTensorSpan(a, new Index(0), [2, 2], [0, 1]); + spanInt = new ReadOnlyTensorSpan(a, 0, [2, 2], [0, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -343,7 +341,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(92, spanInt[1, 1]); // Make sure 2D array works with stride of 0 and initial offset to loop over last 2 elements again - spanInt = new ReadOnlyTensorSpan(a, new Index(2), [2, 2], [0, 1]); + spanInt = new ReadOnlyTensorSpan(a, 2, [2, 2], [0, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -353,7 +351,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1, 1]); // Make sure 2D array works with strides of all 0 and initial offset to loop over last element again - spanInt = new ReadOnlyTensorSpan(a, new Index(3), [2, 2], [0, 0]); + spanInt = new ReadOnlyTensorSpan(a, 3, [2, 2], [0, 0]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -364,18 +362,18 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() // Make sure strides can't be negative Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(3), [1, 2], [-1, 0]); + var spanInt = new ReadOnlyTensorSpan(a, 3, [1, 2], [-1, 0]); }); Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(3), [1, 2], [0, -1]); + var spanInt = new ReadOnlyTensorSpan(a, 3, [1, 2], [0, -1]); }); // Make sure lengths can't be negative Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(3), [-1, 2], []); + var spanInt = new ReadOnlyTensorSpan(a, 3, [-1, 2], []); }); Assert.Throws(() => { - var spanInt = new ReadOnlyTensorSpan(a, new Index(3), [1, -2], []); + var spanInt = new ReadOnlyTensorSpan(a, 3, [1, -2], []); }); // Make sure 2D array works with strides to hit element 0,0,2,2 @@ -768,7 +766,7 @@ public static void ReadOnlyTensorSpanLargeDimensionsTests() { int[] a = { 91, 92, -93, 94, 95, -96 }; int[] results = new int[6]; - ReadOnlyTensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); + ReadOnlyTensorSpan spanInt = a.AsTensorSpan([1, 1, 1, 1, 1, 6]); Assert.Equal(6, spanInt.Rank); Assert.Equal(6, spanInt.Lengths.Length); @@ -796,7 +794,7 @@ public static void ReadOnlyTensorSpanLargeDimensionsTests() a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; results = new int[12]; - spanInt = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + spanInt = a.AsTensorSpan([1, 2, 2, 1, 1, 3]); Assert.Equal(6, spanInt.Lengths.Length); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -832,7 +830,7 @@ public static void IntArrayAsReadOnlyTensorSpan() { int[] a = { 91, 92, -93, 94 }; int[] results = new int[4]; - ReadOnlyTensorSpan spanInt = a.AsTensorSpan(4); + ReadOnlyTensorSpan spanInt = a.AsTensorSpan([4]); Assert.Equal(1, spanInt.Rank); Assert.Equal(1, spanInt.Lengths.Length); @@ -850,7 +848,7 @@ public static void IntArrayAsReadOnlyTensorSpan() a[1] = 92; a[2] = -93; a[3] = 94; - spanInt = a.AsTensorSpan(2, 2); + spanInt = a.AsTensorSpan([2, 2]); spanInt.FlattenTo(results); Assert.Equal(a, results); Assert.Equal(2, spanInt.Rank); @@ -871,8 +869,8 @@ public static void ReadOnlyTensorSpanCopyTest() { int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; - ReadOnlyTensorSpan leftSpan = leftData.AsTensorSpan(3, 3); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + ReadOnlyTensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); leftSpan.CopyTo(rightSpan); var leftEnum = leftSpan.GetEnumerator(); var rightEnum = rightSpan.GetEnumerator(); @@ -890,16 +888,16 @@ public static void ReadOnlyTensorSpanCopyTest() { leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; rightData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - TensorSpan leftSpan = leftData.AsTensorSpan(9); - TensorSpan tensor = rightData.AsTensorSpan(rightData.Length); + TensorSpan leftSpan = leftData.AsTensorSpan([9]); + TensorSpan tensor = rightData.AsTensorSpan([rightData.Length]); leftSpan.CopyTo(tensor); } ); leftData = [.. Enumerable.Range(0, 27)]; rightData = [.. Enumerable.Range(0, 27)]; - leftSpan = leftData.AsTensorSpan(3, 3, 3); - rightSpan = rightData.AsTensorSpan(3, 3, 3); + leftSpan = leftData.AsTensorSpan([3, 3, 3]); + rightSpan = rightData.AsTensorSpan([3, 3, 3]); leftSpan.CopyTo(rightSpan); while (leftEnum.MoveNext() && rightEnum.MoveNext()) @@ -909,7 +907,7 @@ public static void ReadOnlyTensorSpanCopyTest() Assert.Throws(() => { - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); l.CopyTo(r); }); @@ -920,8 +918,8 @@ public static void ReadOnlyTensorSpanTryCopyTest() { int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; - ReadOnlyTensorSpan leftSpan = leftData.AsTensorSpan(3, 3); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + ReadOnlyTensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); var success = leftSpan.TryCopyTo(rightSpan); Assert.True(success); var leftEnum = leftSpan.GetEnumerator(); @@ -937,15 +935,15 @@ public static void ReadOnlyTensorSpanTryCopyTest() leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; rightData = new int[15]; - leftSpan = leftData.AsTensorSpan(9); - rightSpan = rightData.AsTensorSpan(15); + leftSpan = leftData.AsTensorSpan([9]); + rightSpan = rightData.AsTensorSpan([15]); success = leftSpan.TryCopyTo(rightSpan); Assert.False(success); leftData = [.. Enumerable.Range(0, 27)]; rightData = [.. Enumerable.Range(0, 27)]; - leftSpan = leftData.AsTensorSpan(3, 3, 3); - rightSpan = rightData.AsTensorSpan(3, 3, 3); + leftSpan = leftData.AsTensorSpan([3, 3, 3]); + rightSpan = rightData.AsTensorSpan([3, 3, 3]); success = leftSpan.TryCopyTo(rightSpan); Assert.True(success); @@ -954,7 +952,7 @@ public static void ReadOnlyTensorSpanTryCopyTest() Assert.Equal(leftEnum.Current, rightEnum.Current); } - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); success = l.TryCopyTo(r); Assert.False(success); @@ -1014,11 +1012,11 @@ public static void ReadOnlyTensorSpanSliceTest() int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] results = new int[9]; - ReadOnlyTensorSpan spanInt = a.AsTensorSpan(3, 3); + ReadOnlyTensorSpan spanInt = a.AsTensorSpan([3, 3]); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(0..1)); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(1..2)); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(0..1, 5..6)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1, 5..6)); var sp = spanInt.Slice(1..3, 1..3); Assert.Equal(5, sp[0, 0]); @@ -1058,7 +1056,7 @@ public static void ReadOnlyTensorSpanSliceTest() sp = spanInt.Slice(0..1, 0..1); Assert.Equal(1, sp[0, 0]); - Assert.Throws(() => a.AsTensorSpan(3, 3).Slice(0..1, 0..1)[0, 1]); + Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); slice = [1]; results = new int[1]; sp.FlattenTo(results); @@ -1087,7 +1085,7 @@ public static void ReadOnlyTensorSpanSliceTest() } int[] numbers = [.. Enumerable.Range(0, 27)]; - spanInt = numbers.AsTensorSpan(3, 3, 3); + spanInt = numbers.AsTensorSpan([3, 3, 3]); sp = spanInt.Slice(1..2, 1..2, 1..2); Assert.Equal(13, sp[0, 0, 0]); slice = [13]; @@ -1122,7 +1120,7 @@ public static void ReadOnlyTensorSpanSliceTest() } numbers = [.. Enumerable.Range(0, 16)]; - spanInt = numbers.AsTensorSpan(2, 2, 2, 2); + spanInt = numbers.AsTensorSpan([2, 2, 2, 2]); sp = spanInt.Slice(1..2, 0..2, 1..2, 0..2); Assert.Equal(10, sp[0, 0, 0, 0]); Assert.Equal(11, sp[0, 0, 0, 1]); @@ -1144,7 +1142,7 @@ public static void ReadOnlyTensorSpanSliceTest() public static void LongArrayAsReadOnlyTensorSpan() { long[] b = { 91, -92, 93, 94, -95 }; - ReadOnlyTensorSpan spanLong = b.AsTensorSpan(5); + ReadOnlyTensorSpan spanLong = b.AsTensorSpan([5]); Assert.Equal(91, spanLong[0]); Assert.Equal(-92, spanLong[1]); Assert.Equal(93, spanLong[2]); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 0d0fc167b6bfcd..8df9f81a516ad4 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using Xunit; @@ -137,7 +136,7 @@ public void TensorExtensionsSpanInSpanOut(TensorPrimitivesSpanInSpanO x.FlattenTo(sliceData); expectedOutput = new TOut[sliceFlattenedLength]; - if (TensorHelpers.IsContiguousAndDense(x)) + if (x.IsContiguousAndDense) { tensorPrimitivesOperation((ReadOnlySpan)sliceData, expectedOutput); } @@ -177,7 +176,7 @@ public void TensorExtensionsSpanInSpanOut(TensorPrimitivesSpanInSpanO x.FlattenTo(sliceData); expectedOutput = new TOut[sliceFlattenedLength]; - if (TensorHelpers.IsContiguousAndDense(x)) + if (x.IsContiguousAndDense) { tensorPrimitivesOperation((ReadOnlySpan)sliceData, expectedOutput); } @@ -360,7 +359,7 @@ public void TensorExtensionsTwoSpanInSpanOut(TensorPrimitivesTwoSpanInSpanOut x.Slice(sliceLengths).BroadcastTo(x); x.FlattenTo(data1); - if (TensorHelpers.IsContiguousAndDense(x.Slice(sliceLengths)) && TensorHelpers.IsContiguousAndDense(y)) + if (x.Slice(sliceLengths).IsContiguousAndDense && y.IsContiguousAndDense) { tensorPrimitivesOperation((ReadOnlySpan)data1, data2, expectedOutput); } @@ -396,7 +395,7 @@ public void TensorExtensionsTwoSpanInSpanOut(TensorPrimitivesTwoSpanInSpanOut y.Slice(sliceLengths).BroadcastTo(y); y.FlattenTo(data2); - if (TensorHelpers.IsContiguousAndDense(x) && TensorHelpers.IsContiguousAndDense(y.Slice(sliceLengths))) + if (x.IsContiguousAndDense && y.Slice(sliceLengths).IsContiguousAndDense) { tensorPrimitivesOperation((ReadOnlySpan)data1, data2, expectedOutput); } @@ -436,7 +435,7 @@ public void TensorExtensionsTwoSpanInSpanOut(TensorPrimitivesTwoSpanInSpanOut x.Slice(sliceLengths).FlattenTo(sliceData1); y.Slice(sliceLengths).FlattenTo(sliceData2); - if (TensorHelpers.IsContiguousAndDense(x.Slice(sliceLengths)) && TensorHelpers.IsContiguousAndDense(y.Slice(sliceLengths))) + if (x.Slice(sliceLengths).IsContiguousAndDense && y.Slice(sliceLengths).IsContiguousAndDense) { tensorPrimitivesOperation((ReadOnlySpan)sliceData1, sliceData2, expectedOutput); } @@ -551,7 +550,7 @@ public static unsafe void TensorSpanSetSliceTests() { var ab = new TensorSpan(array: [0d, 1, 2, 3, 0d, 1, 2, 3]); // [0, 1, 2, 3] var b = ab.Reshape(lengths: new IntPtr[] { 2, 2, 2 }); // [[0, 1], [2, 3]] - var c = b.Slice(ranges: new NRange[] { 1.., 1..2, ..1 }); // [[0], [2]] + var c = b.Slice(new NRange[] { 1.., 1..2, ..1 }); // [[0], [2]] c.Reshape(lengths: new IntPtr[] { 1, 2, 1 }); }); @@ -790,25 +789,25 @@ public static void TensorSpanSystemArrayConstructorTests() Assert.Equal(94, spanInt[1, 0]); Assert.Equal(94, spanInt[1, 1]); - //Make sure it works with NIndex - spanInt = new TensorSpan(a, (NIndex[])[1, 1], [2, 2], [0, 0]); - Assert.Equal(2, spanInt.Rank); - Assert.Equal(2, spanInt.Lengths[0]); - Assert.Equal(2, spanInt.Lengths[1]); - Assert.Equal(94, spanInt[0, 0]); - Assert.Equal(94, spanInt[0, 1]); - Assert.Equal(94, spanInt[1, 0]); - Assert.Equal(94, spanInt[1, 1]); - - //Make sure it works with NIndex - spanInt = new TensorSpan(a, (NIndex[])[^1, ^1], [2, 2], [0, 0]); - Assert.Equal(2, spanInt.Rank); - Assert.Equal(2, spanInt.Lengths[0]); - Assert.Equal(2, spanInt.Lengths[1]); - Assert.Equal(94, spanInt[0, 0]); - Assert.Equal(94, spanInt[0, 1]); - Assert.Equal(94, spanInt[1, 0]); - Assert.Equal(94, spanInt[1, 1]); + // // TODO: Make sure it works with NIndex + // spanInt = new TensorSpan(a, [0, 0], (NIndex[])[1, 1], [2, 2]); + // Assert.Equal(2, spanInt.Rank); + // Assert.Equal(2, spanInt.Lengths[0]); + // Assert.Equal(2, spanInt.Lengths[1]); + // Assert.Equal(94, spanInt[0, 0]); + // Assert.Equal(94, spanInt[0, 1]); + // Assert.Equal(94, spanInt[1, 0]); + // Assert.Equal(94, spanInt[1, 1]); + + // // TODO: Make sure it works with NIndex + // spanInt = new TensorSpan(a, [0, 0], (NIndex[])[^1, ^1], [2, 2]); + // Assert.Equal(2, spanInt.Rank); + // Assert.Equal(2, spanInt.Lengths[0]); + // Assert.Equal(2, spanInt.Lengths[1]); + // Assert.Equal(94, spanInt[0, 0]); + // Assert.Equal(94, spanInt[0, 1]); + // Assert.Equal(94, spanInt[1, 0]); + // Assert.Equal(94, spanInt[1, 1]); } [Fact] @@ -859,7 +858,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(0, spanInt.Strides[0]); // Make sure 2D array works - spanInt = new TensorSpan(a, new Index(0), [2, 2], default); + spanInt = new TensorSpan(a, 0, [2, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -869,24 +868,24 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1, 1]); // Make sure can use only some of the array - spanInt = new TensorSpan(a, new Index(0), [1, 2], default); + spanInt = new TensorSpan(a, 0, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(0), [1, 2], default); + var spanInt = new TensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 1]; }); Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(0), [1, 2], default); + var spanInt = new TensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 0]; }); // Make sure Index offset works correctly - spanInt = new TensorSpan(a, new Index(1), [1, 2], default); + spanInt = new TensorSpan(a, 1, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -894,7 +893,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(-93, spanInt[0, 1]); // Make sure Index offset works correctly - spanInt = new TensorSpan(a, new Index(2), [1, 2], default); + spanInt = new TensorSpan(a, 2, [1, 2], default); Assert.Equal(2, spanInt.Rank); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -903,11 +902,11 @@ public static void TensorSpanArrayConstructorTests() // Make sure we catch that there aren't enough elements in the array for the lengths Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(3), [1, 2], default); + var spanInt = new TensorSpan(a, 3, [1, 2], default); }); // Make sure 2D array works with basic strides - spanInt = new TensorSpan(a, new Index(0), [2, 2], [2, 1]); + spanInt = new TensorSpan(a, 0, [2, 2], [2, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -917,7 +916,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1, 1]); // Make sure 2D array works with stride of 0 to loop over first 2 elements again - spanInt = new TensorSpan(a, new Index(0), [2, 2], [0, 1]); + spanInt = new TensorSpan(a, 0, [2, 2], [0, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -927,7 +926,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(92, spanInt[1, 1]); // Make sure 2D array works with stride of 0 and initial offset to loop over last 2 elements again - spanInt = new TensorSpan(a, new Index(2), [2, 2], [0, 1]); + spanInt = new TensorSpan(a, 2, [2, 2], [0, 1]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -937,7 +936,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[1, 1]); // Make sure 2D array works with strides of all 0 and initial offset to loop over last element again - spanInt = new TensorSpan(a, new Index(3), [2, 2], [0, 0]); + spanInt = new TensorSpan(a, 3, [2, 2], [0, 0]); Assert.Equal(2, spanInt.Rank); Assert.Equal(2, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -948,18 +947,18 @@ public static void TensorSpanArrayConstructorTests() // Make sure strides can't be negative Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(3), [1, 2], [-1, 0]); + var spanInt = new TensorSpan(a, 3, [1, 2], [-1, 0]); }); Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(3), [1, 2], [0, -1]); + var spanInt = new TensorSpan(a, 3, [1, 2], [0, -1]); }); // Make sure lengths can't be negative Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(3), [-1, 2], []); + var spanInt = new TensorSpan(a, 3, [-1, 2], []); }); Assert.Throws(() => { - var spanInt = new TensorSpan(a, new Index(3), [1, -2], []); + var spanInt = new TensorSpan(a, 3, [1, -2], []); }); // Make sure 2D array works with strides to hit element 0,0,2,2 @@ -1351,7 +1350,7 @@ public static void TensorSpanLargeDimensionsTests() { int[] a = { 91, 92, -93, 94, 95, -96 }; int[] results = new int[6]; - TensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); + TensorSpan spanInt = a.AsTensorSpan([1, 1, 1, 1, 1, 6]); Assert.Equal(6, spanInt.Rank); Assert.Equal(6, spanInt.Lengths.Length); @@ -1379,7 +1378,7 @@ public static void TensorSpanLargeDimensionsTests() a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; results = new int[12]; - spanInt = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + spanInt = a.AsTensorSpan([1, 2, 2, 1, 1, 3]); Assert.Equal(6, spanInt.Lengths.Length); Assert.Equal(1, spanInt.Lengths[0]); Assert.Equal(2, spanInt.Lengths[1]); @@ -1415,7 +1414,7 @@ public static void IntArrayAsTensorSpan() { int[] a = { 91, 92, -93, 94 }; int[] results = new int[4]; - TensorSpan spanInt = a.AsTensorSpan(4); + TensorSpan spanInt = a.AsTensorSpan([4]); Assert.Equal(1, spanInt.Rank); Assert.Equal(1, spanInt.Lengths.Length); @@ -1442,7 +1441,7 @@ public static void IntArrayAsTensorSpan() a[1] = 92; a[2] = -93; a[3] = 94; - spanInt = a.AsTensorSpan(2, 2); + spanInt = a.AsTensorSpan([2, 2]); spanInt.FlattenTo(results); Assert.Equal(a, results); Assert.Equal(2, spanInt.Rank); @@ -1472,7 +1471,7 @@ public static void IntArrayAsTensorSpan() public static void TensorSpanFillTest() { int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - TensorSpan spanInt = a.AsTensorSpan(3, 3); + TensorSpan spanInt = a.AsTensorSpan([3, 3]); spanInt.Fill(-1); var enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1495,7 +1494,7 @@ public static void TensorSpanFillTest() } a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - spanInt = a.AsTensorSpan(9); + spanInt = a.AsTensorSpan([9]); spanInt.Fill(-1); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1504,7 +1503,7 @@ public static void TensorSpanFillTest() } a = [.. Enumerable.Range(0, 27)]; - spanInt = a.AsTensorSpan(3,3,3); + spanInt = a.AsTensorSpan([3,3,3]); spanInt.Fill(-1); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1513,7 +1512,7 @@ public static void TensorSpanFillTest() } a = [.. Enumerable.Range(0, 12)]; - spanInt = a.AsTensorSpan(3, 2, 2); + spanInt = a.AsTensorSpan([3, 2, 2]); spanInt.Fill(-1); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1522,7 +1521,7 @@ public static void TensorSpanFillTest() } a = [.. Enumerable.Range(0, 16)]; - spanInt = a.AsTensorSpan(2,2,2,2); + spanInt = a.AsTensorSpan([2,2,2,2]); spanInt.Fill(-1); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1531,7 +1530,7 @@ public static void TensorSpanFillTest() } a = [.. Enumerable.Range(0, 24)]; - spanInt = a.AsTensorSpan(3, 2, 2, 2); + spanInt = a.AsTensorSpan([3, 2, 2, 2]); spanInt.Fill(-1); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1544,7 +1543,7 @@ public static void TensorSpanFillTest() public static void TensorSpanClearTest() { int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - TensorSpan spanInt = a.AsTensorSpan(3, 3); + TensorSpan spanInt = a.AsTensorSpan([3, 3]); var slice = spanInt.Slice(0..2, 0..2); slice.Clear(); @@ -1573,7 +1572,7 @@ public static void TensorSpanClearTest() } a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - spanInt = a.AsTensorSpan(9); + spanInt = a.AsTensorSpan([9]); slice = spanInt.Slice(0..1); slice.Clear(); Assert.Equal(0, slice[0]); @@ -1598,7 +1597,7 @@ public static void TensorSpanClearTest() } a = [.. Enumerable.Range(0, 27)]; - spanInt = a.AsTensorSpan(3, 3, 3); + spanInt = a.AsTensorSpan([3, 3, 3]); spanInt.Clear(); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1607,7 +1606,7 @@ public static void TensorSpanClearTest() } a = [.. Enumerable.Range(0, 12)]; - spanInt = a.AsTensorSpan(3, 2, 2); + spanInt = a.AsTensorSpan([3, 2, 2]); spanInt.Clear(); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1616,7 +1615,7 @@ public static void TensorSpanClearTest() } a = [.. Enumerable.Range(0, 16)]; - spanInt = a.AsTensorSpan(2, 2, 2, 2); + spanInt = a.AsTensorSpan([2, 2, 2, 2]); spanInt.Clear(); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1625,7 +1624,7 @@ public static void TensorSpanClearTest() } a = [.. Enumerable.Range(0, 24)]; - spanInt = a.AsTensorSpan(3, 2, 2, 2); + spanInt = a.AsTensorSpan([3, 2, 2, 2]); spanInt.Clear(); enumerator = spanInt.GetEnumerator(); while (enumerator.MoveNext()) @@ -1639,8 +1638,8 @@ public static void TensorSpanCopyTest() { int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; - TensorSpan leftSpan = leftData.AsTensorSpan(3, 3); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + TensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); leftSpan.CopyTo(rightSpan); var leftEnum = leftSpan.GetEnumerator(); var rightEnum = rightSpan.GetEnumerator(); @@ -1658,16 +1657,16 @@ public static void TensorSpanCopyTest() { leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; rightData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - TensorSpan leftSpan = leftData.AsTensorSpan(9); - TensorSpan tensor = rightData.AsTensorSpan(rightData.Length); + TensorSpan leftSpan = leftData.AsTensorSpan([9]); + TensorSpan tensor = rightData.AsTensorSpan([rightData.Length]); leftSpan.CopyTo(tensor); } ); leftData = [.. Enumerable.Range(0, 27)]; rightData = [.. Enumerable.Range(0, 27)]; - leftSpan = leftData.AsTensorSpan(3, 3, 3); - rightSpan = rightData.AsTensorSpan(3, 3, 3); + leftSpan = leftData.AsTensorSpan([3, 3, 3]); + rightSpan = rightData.AsTensorSpan([3, 3, 3]); leftSpan.CopyTo(rightSpan); while (leftEnum.MoveNext() && rightEnum.MoveNext()) @@ -1677,7 +1676,7 @@ public static void TensorSpanCopyTest() Assert.Throws(() => { - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); l.CopyTo(r); }); @@ -1688,8 +1687,8 @@ public static void TensorSpanTryCopyTest() { int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; - TensorSpan leftSpan = leftData.AsTensorSpan(3, 3); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + TensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); var success = leftSpan.TryCopyTo(rightSpan); Assert.True(success); var leftEnum = leftSpan.GetEnumerator(); @@ -1705,16 +1704,16 @@ public static void TensorSpanTryCopyTest() leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; rightData = new int[15]; - leftSpan = leftData.AsTensorSpan(9); - rightSpan = rightData.AsTensorSpan(15); + leftSpan = leftData.AsTensorSpan([9]); + rightSpan = rightData.AsTensorSpan([15]); success = leftSpan.TryCopyTo(rightSpan); Assert.False(success); leftData = [.. Enumerable.Range(0, 27)]; rightData = [.. Enumerable.Range(0, 27)]; - leftSpan = leftData.AsTensorSpan(3, 3, 3); - rightSpan = rightData.AsTensorSpan(3, 3, 3); + leftSpan = leftData.AsTensorSpan([3, 3, 3]); + rightSpan = rightData.AsTensorSpan([3, 3, 3]); success = leftSpan.TryCopyTo(rightSpan); Assert.True(success); @@ -1723,7 +1722,7 @@ public static void TensorSpanTryCopyTest() Assert.Equal(leftEnum.Current, rightEnum.Current); } - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); success = l.TryCopyTo(r); Assert.False(success); @@ -1783,11 +1782,11 @@ public static void TensorSpanSliceTest() int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] results = new int[9]; - TensorSpan spanInt = a.AsTensorSpan(3, 3); + TensorSpan spanInt = a.AsTensorSpan([3, 3]); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(0..1)); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(1..2)); - Assert.Throws(() => a.AsTensorSpan(2, 3).Slice(0..1, 5..6)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1, 5..6)); var sp = spanInt.Slice(1..3, 1..3); Assert.Equal(5, sp[0, 0]); @@ -1827,7 +1826,7 @@ public static void TensorSpanSliceTest() sp = spanInt.Slice(0..1, 0..1); Assert.Equal(1, sp[0, 0]); - Assert.Throws(() => a.AsTensorSpan(3, 3).Slice(0..1, 0..1)[0, 1]); + Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); slice = [1]; results = new int[1]; sp.FlattenTo(results); @@ -1856,7 +1855,7 @@ public static void TensorSpanSliceTest() } int[] numbers = [.. Enumerable.Range(0, 27)]; - spanInt = numbers.AsTensorSpan(3, 3, 3); + spanInt = numbers.AsTensorSpan([3, 3, 3]); sp = spanInt.Slice(1..2, 1..2, 1..2); Assert.Equal(13, sp[0, 0, 0]); slice = [13]; @@ -1891,7 +1890,7 @@ public static void TensorSpanSliceTest() } numbers = [.. Enumerable.Range(0, 16)]; - spanInt = numbers.AsTensorSpan(2, 2, 2, 2); + spanInt = numbers.AsTensorSpan([2, 2, 2, 2]); sp = spanInt.Slice(1..2, 0..2, 1..2, 0..2); Assert.Equal(10, sp[0,0,0,0]); Assert.Equal(11, sp[0,0,0,1]); @@ -1913,7 +1912,7 @@ public static void TensorSpanSliceTest() public static void LongArrayAsTensorSpan() { long[] b = { 91, -92, 93, 94, -95 }; - TensorSpan spanLong = b.AsTensorSpan(5); + TensorSpan spanLong = b.AsTensorSpan([5]); Assert.Equal(91, spanLong[0]); Assert.Equal(-92, spanLong[1]); Assert.Equal(93, spanLong[2]); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index fce9d8f68f474f..0796cd416d9fdf 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Runtime.InteropServices; using Xunit; -using static System.Numerics.Tensors.Tests.TensorTests; namespace System.Numerics.Tensors.Tests { @@ -241,7 +240,7 @@ public static void TensorLargeDimensionsTests() { int[] a = { 91, 92, -93, 94, 95, -96 }; int[] results = new int[6]; - Tensor tensor = Tensor.Create(a,[1, 1, 1, 1, 1, 6]); + Tensor tensor = Tensor.Create(a, lengths: [1, 1, 1, 1, 1, 6]); Assert.Equal(6, tensor.Rank); Assert.Equal(6, tensor.Lengths.Length); @@ -269,7 +268,7 @@ public static void TensorLargeDimensionsTests() a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; results = new int[12]; - tensor = Tensor.Create(a, [1, 2, 2, 1, 1, 3]); + tensor = Tensor.Create(a, lengths: [1, 2, 2, 1, 1, 3]); Assert.Equal(6, tensor.Lengths.Length); Assert.Equal(1, tensor.Lengths[0]); Assert.Equal(2, tensor.Lengths[1]); @@ -588,20 +587,20 @@ public static void TensorCosineSimilarityTests() float[] a = [0, 0, 0, 1, 1, 1]; float[] b = [1, 0, 0, 1, 1, 0]; - Tensor left = Tensor.Create(a, [2,3]); - Tensor right = Tensor.Create(b, [2,3]); + Tensor left = Tensor.Create(a, lengths: [2,3]); + Tensor right = Tensor.Create(b, lengths: [2,3]); - Tensor result = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan(), right); - Assert.Equal(2, result.Rank); - Assert.Equal(2, result.Lengths[0]); - Assert.Equal(2, result.Lengths[1]); + Tensor result = Tensor.Create(a, lengths: [2, 1]); + result[0, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([0..1, 0..]), right[0..1, 0..]); + result[1, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([0..1, 0..]), right[0..1, 0..]); - Assert.Equal(float.NaN, result[0, 0]); - Assert.Equal(float.NaN, result[0, 1]); + Assert.Equal(2, result.Rank); + Assert.Equal(2, result.Lengths[0]); + Assert.Equal(1, result.Lengths[1]); - Assert.Equal(0.57735, result[1, 0], .00001); - Assert.Equal(0.81649, result[1, 1], .00001); + Assert.Equal(0.57735, result[0, 0], .00001); + Assert.Equal(0.81649, result[1, 0], .00001); } //[Fact] @@ -681,8 +680,8 @@ public static void TensorCosineSimilarityTests() [Fact] public static void TensorMultiplyTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 3), default); - Tensor t1 = Tensor.Create(Enumerable.Range(0, 3), [3, 1]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 3)); + Tensor t1 = Tensor.Create(Enumerable.Range(0, 3), lengths: [3, 1]); Tensor t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); Assert.Equal([3,3], t2.Lengths); @@ -709,7 +708,7 @@ public static void TensorMultiplyTests() Assert.Equal(2, t2[2, 1]); Assert.Equal(4, t2[2, 2]); - t1 = Tensor.Create(Enumerable.Range(0, 9), [3, 3]); + t1 = Tensor.Create(Enumerable.Range(0, 9), lengths: [3, 3]); t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); Assert.Equal([3, 3], t2.Lengths); @@ -727,7 +726,7 @@ public static void TensorMultiplyTests() [Fact] public static void TensorBroadcastTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 3), [1, 3, 1, 1, 1]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 3), lengths: [1, 3, 1, 1, 1]); Tensor t1 = Tensor.Broadcast(t0, [1, 3, 1, 2, 1]); Assert.Equal([1, 3, 1, 2, 1], t1.Lengths); @@ -749,8 +748,8 @@ public static void TensorBroadcastTests() Assert.Equal(2, t1[0, 2, 0, 0, 0]); Assert.Equal(2, t1[0, 2, 1, 0, 0]); - t0 = Tensor.Create(Enumerable.Range(0, 3), [1, 3]); - t1 = Tensor.Create(Enumerable.Range(0, 3), [3, 1]); + t0 = Tensor.Create(Enumerable.Range(0, 3), lengths: [1, 3]); + t1 = Tensor.Create(Enumerable.Range(0, 3), lengths: [3, 1]); var t2 = Tensor.Broadcast(t0, [3, 3]); Assert.Equal([3, 3], t2.Lengths); @@ -764,7 +763,7 @@ public static void TensorBroadcastTests() Assert.Equal(1, t2[2, 1]); Assert.Equal(2, t2[2, 2]); - t1 = Tensor.Create(Enumerable.Range(0, 3), [3, 1]); + t1 = Tensor.Create(Enumerable.Range(0, 3), lengths: [3, 1]); t2 = Tensor.Broadcast(t1, [3, 3]); Assert.Equal([3, 3], t2.Lengths); @@ -810,7 +809,7 @@ public static void TensorBroadcastTests() [Fact] public static void TensorResizeTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), [2, 2, 2]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), lengths: [2, 2, 2]); var t1 = Tensor.Resize(t0, [1]); Assert.Equal([1], t1.Lengths); Assert.Equal(0, t1[0]); @@ -858,7 +857,7 @@ public static void TensorResizeTests() [Fact] public static void TensorSplitTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), [2, 2, 2]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), lengths: [2, 2, 2]); var t1 = Tensor.Split(t0, 2, 0); Assert.Equal([1, 2, 2], t1[0].Lengths); Assert.Equal([1, 2, 2], t1[1].Lengths); @@ -899,7 +898,7 @@ public static void TensorSplitTests() [Fact] public static void TensorReverseTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), [2, 2, 2]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 8), lengths: [2, 2, 2]); var t1 = Tensor.Reverse(t0); Assert.Equal(7, t1[0, 0, 0]); Assert.Equal(6, t1[0, 0, 1]); @@ -944,8 +943,8 @@ public static void TensorReverseTests() [Fact] public static void TensorSetSliceTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 10), [2, 5]); - Tensor t1 = Tensor.Create(Enumerable.Range(10, 10), [2, 5]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); + Tensor t1 = Tensor.Create(Enumerable.Range(10, 10), lengths: [2, 5]); Tensor.SetSlice(t0, t1); Assert.Equal(10, t0[0, 0]); @@ -959,8 +958,8 @@ public static void TensorSetSliceTests() Assert.Equal(18, t0[1, 3]); Assert.Equal(19, t0[1, 4]); - t0 = Tensor.Create(Enumerable.Range(0, 10), [2, 5]); - t1 = Tensor.Create(Enumerable.Range(10, 5), [1, 5]); + t0 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); + t1 = Tensor.Create(Enumerable.Range(10, 5), lengths: [1, 5]); t0.SetSlice(t1, 0..1, ..); Assert.Equal(10, t0[0, 0]); @@ -974,8 +973,8 @@ public static void TensorSetSliceTests() Assert.Equal(8, t0[1, 3]); Assert.Equal(9, t0[1, 4]); - t0 = Tensor.Create(Enumerable.Range(0, 10), [2, 5]); - t1 = Tensor.Create(Enumerable.Range(10, 5), [1, 5]); + t0 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); + t1 = Tensor.Create(Enumerable.Range(10, 5), lengths: [1, 5]); Tensor.SetSlice(t0, t1, 1..2, ..); Assert.Equal(0, t0[0, 0]); @@ -993,8 +992,8 @@ public static void TensorSetSliceTests() [Fact] public static void TensorStackTests() { - Tensor t0 = Tensor.Create(Enumerable.Range(0, 10), [2, 5]); - Tensor t1 = Tensor.Create(Enumerable.Range(0, 10), [2, 5]); + Tensor t0 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); + Tensor t1 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); var resultTensor = Tensor.Stack([t0, t1]); Assert.Equal(3, resultTensor.Rank); @@ -1078,8 +1077,8 @@ public static void TensorStackTests() Assert.Equal(9, resultTensor[1, 4, 1]); // stacking 2x2 tensors along dimension 1 - Tensor v1 = Tensor.Create([1, 2, 3, 4], [2, 2]); - Tensor v2 = Tensor.Create([10, 20, 30, 40], [2, 2]); + Tensor v1 = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + Tensor v2 = Tensor.Create([10, 20, 30, 40], lengths: [2, 2]); resultTensor = Tensor.StackAlongDimension(1, [v1, v2]); @@ -1122,7 +1121,7 @@ public static void TensorStackTests() [Fact] public static void TensorStdDevTests() { - Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); Assert.Equal(StdDev([0, 1, 2, 3]), Tensor.StdDev(t0), .1); @@ -1140,7 +1139,7 @@ public static void TensorStdDevTests() public static void TensorSumTests() { float[] values = new float[] { 1, 2, 3, 4, 5, 6 }; - Tensor t0 = Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3]); + Tensor t0 = Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, lengths: [2, 3]); float sum = Tensor.Sum(t0); Assert.Equal(21, sum); @@ -1169,11 +1168,11 @@ public static void TensorSumTests() sum = Tensor.Sum(t1); Assert.Equal(9, sum); - Assert.Throws(()=> new Tensor(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], -1)); - Assert.Throws(()=> new Tensor(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 100)); - Assert.Throws(()=> new Tensor(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MinValue)); - Assert.Throws(()=> new Tensor(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MaxValue)); - Assert.Throws(()=> new Tensor(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 2)); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: -1, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 100, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MinValue, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MaxValue, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 2, lengths: [2, 3], strides: [])); } public static float StdDev(float[] values) @@ -1190,7 +1189,7 @@ public static float StdDev(float[] values) [Fact] public static void TensorMeanTests() { - Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); Assert.Equal(Mean([0, 1, 2, 3]), Tensor.Average(t0), .1); } @@ -1208,8 +1207,8 @@ public static float Mean(float[] values) [Fact] public static void TensorConcatenateTests() { - Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); - Tensor t1 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); + Tensor t1 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); var resultTensor = Tensor.Concatenate([t0, t1]); Assert.Equal(2, resultTensor.Rank); @@ -1249,7 +1248,7 @@ public static void TensorConcatenateTests() Assert.Equal(2, resultTensor[6]); Assert.Equal(3, resultTensor[7]); - Tensor t2 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t2 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); resultTensor = Tensor.Concatenate([t0, t1, t2]); Assert.Equal(2, resultTensor.Rank); @@ -1303,9 +1302,9 @@ public static void TensorConcatenateTests() Assert.Equal(2, resultTensor[1, 4]); Assert.Equal(3, resultTensor[1, 5]); - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 3, 2]); - t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 3, 2]); - t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), [2, 2, 2]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 3, 2]); + t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 3, 2]); + t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), lengths: [2, 2, 2]); Assert.Throws(() => Tensor.ConcatenateOnDimension(0, [t0, t1, t2])); Assert.Throws(() => Tensor.ConcatenateOnDimension(2, [t0, t1, t2])); Assert.Throws(() => Tensor.ConcatenateOnDimension(5, [t0, t1, t2])); @@ -1330,9 +1329,9 @@ public static void TensorConcatenateTests() Helpers.AdjustIndices(resultTensor.Rank - 1, 1, ref indices, resultTensor.Lengths); } - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 2, 3]); - t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 2, 3]); - t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), [2, 2, 2]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); + t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); + t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), lengths: [2, 2, 2]); Assert.Throws(() => Tensor.Concatenate([t0, t1, t2])); Assert.Throws(() => Tensor.ConcatenateOnDimension(1, [t0, t1, t2])); resultTensor = Tensor.ConcatenateOnDimension(2, [t0, t1, t2]); @@ -1349,9 +1348,9 @@ public static void TensorConcatenateTests() Helpers.AdjustIndices(resultTensor.Rank - 1, 1, ref indices, resultTensor.Lengths); } - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [3, 2, 2]); - t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [3, 2, 2]); - t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), [2, 2, 2]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [3, 2, 2]); + t1 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [3, 2, 2]); + t2 = Tensor.Create((Enumerable.Range(0, 8).Select(i => (float)i)), lengths: [2, 2, 2]); Assert.Throws(() => Tensor.ConcatenateOnDimension(1, [t0, t1, t2])); Assert.Throws(() => Tensor.ConcatenateOnDimension(2, [t0, t1, t2])); resultTensor = Tensor.ConcatenateOnDimension(0, [t0, t1, t2]); @@ -1372,7 +1371,7 @@ public static void TensorConcatenateTests() [Fact] public static void TensorTransposeTests() { - Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); var t1 = Tensor.PermuteDimensions(t0); Assert.Equal(0, t1[0, 0]); @@ -1380,7 +1379,7 @@ public static void TensorTransposeTests() Assert.Equal(1, t1[1, 0]); Assert.Equal(3, t1[1, 1]); - t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), [2, 3]); + t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), lengths: [2, 3]); t1 = Tensor.PermuteDimensions(t0); Assert.Equal(3, t1.Lengths[0]); @@ -1392,7 +1391,7 @@ public static void TensorTransposeTests() Assert.Equal(2, t1[2, 0]); Assert.Equal(5, t1[2, 1]); - t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), [1, 2, 3]); + t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), lengths: [1, 2, 3]); t1 = Tensor.PermuteDimensions(t0); Assert.Equal(3, t1.Lengths[0]); @@ -1405,7 +1404,7 @@ public static void TensorTransposeTests() Assert.Equal(2, t1[2, 0, 0]); Assert.Equal(5, t1[2, 1, 0]); - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 2, 3]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); t1 = Tensor.PermuteDimensions(t0); Assert.Equal(3, t1.Lengths[0]); @@ -1424,7 +1423,7 @@ public static void TensorTransposeTests() Assert.Equal(5, t1[2, 1, 0]); Assert.Equal(11, t1[2, 1, 1]); - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 2, 3]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); t1 = Tensor.PermuteDimensions(t0, 1, 2, 0); Assert.Equal(2, t1.Lengths[0]); @@ -1447,7 +1446,7 @@ public static void TensorTransposeTests() [Fact] public static void TensorPermuteTests() { - Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); + Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); var t1 = Tensor.Transpose(t0); Assert.Equal(0, t1[0, 0]); @@ -1455,7 +1454,7 @@ public static void TensorPermuteTests() Assert.Equal(1, t1[1, 0]); Assert.Equal(3, t1[1, 1]); - t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), [2, 2, 3]); + t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); t1 = Tensor.Transpose(t0); Assert.Equal(2, t1.Lengths[0]); @@ -1479,7 +1478,7 @@ public static void TensorPermuteTests() public static void IntArrayAsTensor() { int[] a = [91, 92, -93, 94]; - TensorSpan t1 = a.AsTensorSpan(4); + TensorSpan t1 = a.AsTensorSpan(); nint[] dims = [4]; var tensor = Tensor.CreateUninitialized(dims.AsSpan(), false); t1.CopyTo(tensor); @@ -1508,7 +1507,7 @@ public static void IntArrayAsTensor() a[1] = 92; a[2] = -93; a[3] = 94; - t1 = a.AsTensorSpan(2, 2); + t1 = a.AsTensorSpan([2, 2]); dims = [2, 2]; tensor = Tensor.CreateUninitialized(dims.AsSpan(), false); t1.CopyTo(tensor); @@ -1613,7 +1612,7 @@ public static void TensorFillTest() public static void TensorClearTest() { int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - TensorSpan t1 = a.AsTensorSpan(3, 3); + TensorSpan t1 = a.AsTensorSpan([3, 3]); var tensor = Tensor.CreateUninitialized([3, 3], false); t1.CopyTo(tensor); var slice = tensor.Slice(0..2, 0..2); @@ -1643,7 +1642,7 @@ public static void TensorClearTest() } a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - t1 = a.AsTensorSpan(9); + t1 = a.AsTensorSpan([9]); tensor = Tensor.CreateUninitialized([9], false); t1.CopyTo(tensor); slice = tensor.Slice(0..1); @@ -1670,7 +1669,7 @@ public static void TensorClearTest() } a = [.. Enumerable.Range(0, 27)]; - t1 = a.AsTensorSpan(3, 3, 3); + t1 = a.AsTensorSpan([3, 3, 3]); tensor = Tensor.CreateUninitialized([3, 3, 3], false); t1.CopyTo(tensor); tensor.Clear(); @@ -1681,7 +1680,7 @@ public static void TensorClearTest() } a = [.. Enumerable.Range(0, 12)]; - t1 = a.AsTensorSpan(3, 2, 2); + t1 = a.AsTensorSpan([3, 2, 2]); tensor = Tensor.CreateUninitialized([3, 2, 2], false); t1.CopyTo(tensor); tensor.Clear(); @@ -1692,7 +1691,7 @@ public static void TensorClearTest() } a = [.. Enumerable.Range(0, 16)]; - t1 = a.AsTensorSpan(2, 2, 2, 2); + t1 = a.AsTensorSpan([2, 2, 2, 2]); tensor = Tensor.CreateUninitialized([2, 2, 2, 2], false); t1.CopyTo(tensor); tensor.Clear(); @@ -1703,7 +1702,7 @@ public static void TensorClearTest() } a = [.. Enumerable.Range(0, 24)]; - t1 = a.AsTensorSpan(3, 2, 2, 2); + t1 = a.AsTensorSpan([3, 2, 2, 2]); tensor = Tensor.CreateUninitialized([3, 2, 2, 2], false); t1.CopyTo(tensor); tensor.Clear(); @@ -1715,7 +1714,7 @@ public static void TensorClearTest() // Make sure clearing a slice of a SPan doesn't clear the whole thing. a = [.. Enumerable.Range(0, 9)]; - t1 = a.AsTensorSpan(3, 3); + t1 = a.AsTensorSpan([3, 3]); var spanSlice = t1.Slice(0..1, 0..3); spanSlice.Clear(); var spanEnumerator = spanSlice.GetEnumerator(); @@ -1736,7 +1735,7 @@ public static void TensorClearTest() // Make sure clearing a slice from the middle of a SPan doesn't clear the whole thing. a = [.. Enumerable.Range(0, 9)]; - t1 = a.AsTensorSpan(3, 3); + t1 = a.AsTensorSpan([3, 3]); spanSlice = t1.Slice(1..2, 0..3); spanSlice.Clear(); spanEnumerator = spanSlice.GetEnumerator(); @@ -1757,7 +1756,7 @@ public static void TensorClearTest() // Make sure clearing a slice from the end of a SPan doesn't clear the whole thing. a = [.. Enumerable.Range(0, 9)]; - t1 = a.AsTensorSpan(3, 3); + t1 = a.AsTensorSpan([3, 3]); spanSlice = t1.Slice(2..3, 0..3); spanSlice.Clear(); spanEnumerator = spanSlice.GetEnumerator(); @@ -1778,7 +1777,7 @@ public static void TensorClearTest() // Make sure it works with reference types. object[] o = [new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object()]; - TensorSpan spanObj = o.AsTensorSpan(3, 3); + TensorSpan spanObj = o.AsTensorSpan([3, 3]); spanObj.Clear(); var oSpanEnumerator = spanObj.GetEnumerator(); @@ -1789,7 +1788,7 @@ public static void TensorClearTest() // Make sure clearing a slice of a SPan with references it doesn't clear the whole thing. o = [new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object()]; - spanObj = o.AsTensorSpan(3, 3); + spanObj = o.AsTensorSpan([3, 3]); var oSpanSlice = spanObj.Slice(0..1, 0..3); oSpanSlice.Clear(); oSpanEnumerator = oSpanSlice.GetEnumerator(); @@ -1810,7 +1809,7 @@ public static void TensorClearTest() // Make sure clearing a slice of a SPan with references it doesn't clear the whole thing. o = [new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object()]; - spanObj = o.AsTensorSpan(3, 3); + spanObj = o.AsTensorSpan([3, 3]); oSpanSlice = spanObj.Slice(1..2, 0..3); oSpanSlice.Clear(); oSpanEnumerator = oSpanSlice.GetEnumerator(); @@ -1831,7 +1830,7 @@ public static void TensorClearTest() // Make sure clearing a slice of a SPan with references it doesn't clear the whole thing. o = [new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object(), new object()]; - spanObj = o.AsTensorSpan(3, 3); + spanObj = o.AsTensorSpan([3, 3]); oSpanSlice = spanObj.Slice(2..3, 0..3); oSpanSlice.Clear(); oSpanEnumerator = oSpanSlice.GetEnumerator(); @@ -1857,9 +1856,9 @@ public static void TensorCopyTest() int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; nint[] dims = [3, 3]; - TensorSpan leftSpan = leftData.AsTensorSpan(3, 3); + TensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); var tensor = Tensor.CreateUninitialized(dims.AsSpan(), false); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); leftSpan.CopyTo(tensor); var leftEnum = leftSpan.GetEnumerator(); var tensorEnum = tensor.GetEnumerator(); @@ -1885,7 +1884,7 @@ public static void TensorCopyTest() { leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; dims = [15]; - TensorSpan leftSpan = leftData.AsTensorSpan(9); + TensorSpan leftSpan = leftData.AsTensorSpan([9]); tensor = Tensor.Create(dims.AsSpan(), false); leftSpan.CopyTo(tensor); } @@ -1893,7 +1892,7 @@ public static void TensorCopyTest() Assert.Throws(() => { - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); l.CopyTo(r); }); @@ -1904,10 +1903,10 @@ public static void TensorTryCopyTest() { int[] leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; int[] rightData = new int[9]; - TensorSpan leftSpan = leftData.AsTensorSpan(3, 3); + TensorSpan leftSpan = leftData.AsTensorSpan([3, 3]); nint[] dims = [3, 3]; var tensor = Tensor.CreateUninitialized(dims.AsSpan(), false); - TensorSpan rightSpan = rightData.AsTensorSpan(3, 3); + TensorSpan rightSpan = rightData.AsTensorSpan([3, 3]); var success = leftSpan.TryCopyTo(tensor); Assert.True(success); success = tensor.TryCopyTo(rightSpan); @@ -1927,13 +1926,13 @@ public static void TensorTryCopyTest() leftData = [1, 2, 3, 4, 5, 6, 7, 8, 9]; dims = [15]; - leftSpan = leftData.AsTensorSpan(9); + leftSpan = leftData.AsTensorSpan([9]); tensor = Tensor.Create(dims.AsSpan(), false); success = leftSpan.TryCopyTo(tensor); Assert.False(success); leftData = [.. Enumerable.Range(0, 27)]; - var l = leftData.AsTensorSpan(3, 3, 3); + var l = leftData.AsTensorSpan([3, 3, 3]); dims = [2, 2]; tensor = Tensor.Create(dims.AsSpan(), false); var r = new TensorSpan(); @@ -1955,7 +1954,7 @@ public static void TensorSliceTest() //Assert.Throws(() => tensor.Slice(0..1)); //Assert.Throws(() => tensor.Slice(1..2)); //Assert.Throws(() => tensor.Slice(0..1, 5..6)); - var intSpan = a.AsTensorSpan(3, 3); + var intSpan = a.AsTensorSpan([3, 3]); intSpan.CopyTo(tensor.AsTensorSpan()); var sp = tensor.Slice(1..3, 1..3); @@ -1992,7 +1991,7 @@ public static void TensorSliceTest() sp = tensor.Slice(0..1, 0..1); Assert.Equal(1, sp[0, 0]); - Assert.Throws(() => a.AsTensorSpan(3, 3).Slice(0..1, 0..1)[0, 1]); + Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); slice = [1]; Assert.Equal(slice, sp.ToArray()); enumerator = sp.GetEnumerator(); @@ -2017,7 +2016,7 @@ public static void TensorSliceTest() } int[] numbers = [.. Enumerable.Range(0, 27)]; - intSpan = numbers.AsTensorSpan(3, 3, 3); + intSpan = numbers.AsTensorSpan([3, 3, 3]); tensor = Tensor.CreateUninitialized([3, 3, 3], false); intSpan.CopyTo(tensor.AsTensorSpan()); sp = tensor.Slice(1..2, 1..2, 1..2); @@ -2050,7 +2049,7 @@ public static void TensorSliceTest() } numbers = [.. Enumerable.Range(0, 16)]; - intSpan = numbers.AsTensorSpan(2, 2, 2, 2); + intSpan = numbers.AsTensorSpan([2, 2, 2, 2]); tensor = Tensor.CreateUninitialized([2, 2, 2, 2], false); intSpan.CopyTo(tensor.AsTensorSpan()); sp = tensor.Slice(1..2, 0..2, 1..2, 0..2); @@ -2265,15 +2264,15 @@ public static void TensorUnsqueezeTest() [Fact] public void TensorGreaterThanTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); Tensor result = Tensor.GreaterThan(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.Equal(new bool[] { false, false, true }, result.ToArray()); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThan(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2291,15 +2290,15 @@ public void TensorGreaterThanTest() [Fact] public void TensorGreaterThanOrEqualTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); Tensor result = Tensor.GreaterThanOrEqual(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.Equal(new bool[] { false, true, true }, result.ToArray()); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThanOrEqual(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2317,14 +2316,14 @@ public void TensorGreaterThanOrEqualTest() [Fact] public void TensorGreaterThanAllTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.GreaterThanAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThanAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); @@ -2345,14 +2344,14 @@ public void TensorGreaterThanAllTest() [Fact] public void TensorGreaterThanOrEqualAllTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.GreaterThanOrEqualAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThanOrEqualAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); @@ -2373,14 +2372,14 @@ public void TensorGreaterThanOrEqualAllTest() [Fact] public void TensorGreaterThanAnyTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.GreaterThanAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThanAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); @@ -2404,14 +2403,14 @@ public void TensorGreaterThanAnyTest() [Fact] public void TensorGreaterThanOrEqualAnyTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.GreaterThanOrEqualAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.GreaterThanOrEqualAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); @@ -2435,15 +2434,15 @@ public void TensorGreaterThanOrEqualAnyTest() [Fact] public void TensorLessThanTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); Tensor result = Tensor.LessThan(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.Equal(new bool[] { true, false, false }, result.ToArray()); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThan(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2461,15 +2460,15 @@ public void TensorLessThanTest() [Fact] public void TensorLessThanOrEqualTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); Tensor result = Tensor.LessThanOrEqual(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.Equal(new bool[] { true, true, false }, result.ToArray()); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThanOrEqual(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2487,14 +2486,14 @@ public void TensorLessThanOrEqualTest() [Fact] public void TensorLessThanAllTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.LessThanAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThanAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); @@ -2515,14 +2514,14 @@ public void TensorLessThanAllTest() [Fact] public void TensorLessThanAnyTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.LessThanAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThanAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); @@ -2546,14 +2545,14 @@ public void TensorLessThanAnyTest() [Fact] public void TensorLessThanOrEqualAllTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.LessThanOrEqualAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThanOrEqualAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); @@ -2577,14 +2576,14 @@ public void TensorLessThanOrEqualAllTest() [Fact] public void TensorLessThanOrEqualAnyTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3], false); - Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, [3], false); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 2, 2, 2 }, lengths: [3]); bool result = Tensor.LessThanOrEqualAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, [2, 2], false); - tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, [2, 2], false); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4 }, lengths: [2, 2]); + tensor2 = Tensor.Create(new int[] { 0, 2, 2, 3 }, lengths: [2, 2]); result = Tensor.LessThanOrEqualAny(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); @@ -2608,8 +2607,8 @@ public void TensorLessThanOrEqualAnyTest() [Fact] public void TensorEqualsTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3]); - Tensor tensor2 = Tensor.Create(new int[] { 1, 2, 3 }, [3]); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); Tensor result = Tensor.Equals(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2627,8 +2626,8 @@ public void TensorEqualsTest() Assert.Equal(new bool[] { false, false, true }, result.ToArray()); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4, 5, 6 }, [2, 3]); - tensor2 = Tensor.Create(new int[] { 4, 5, 6 }, [3]); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4, 5, 6 }, lengths: [2, 3]); + tensor2 = Tensor.Create(new int[] { 4, 5, 6 }, lengths: [3]); result = Tensor.Equals(tensor1.AsReadOnlyTensorSpan(), tensor2); @@ -2638,8 +2637,8 @@ public void TensorEqualsTest() [Fact] public void TensorEqualsAllTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3]); - Tensor tensor2 = Tensor.Create(new int[] { 1, 2, 3 }, [3]); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor tensor2 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); bool result = Tensor.EqualsAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); @@ -2653,17 +2652,17 @@ public void TensorEqualsAllTest() result = Tensor.EqualsAll(tensor1.AsReadOnlyTensorSpan(), 3); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 1, 1, 1 }, [3]); + tensor1 = Tensor.Create(new int[] { 1, 1, 1 }, lengths: [3]); result = Tensor.EqualsAll(tensor1.AsReadOnlyTensorSpan(), 1); Assert.True(result); - tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4, 5, 6 }, [2, 3]); - tensor2 = Tensor.Create(new int[] { 4, 5, 6 }, [3]); + tensor1 = Tensor.Create(new int[] { 1, 2, 3, 4, 5, 6 }, lengths: [2, 3]); + tensor2 = Tensor.Create(new int[] { 4, 5, 6 }, lengths: [3]); result = Tensor.EqualsAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.False(result); - tensor1 = Tensor.Create(new int[] { 4, 5, 6, 4, 5, 6 }, [2, 3]); + tensor1 = Tensor.Create(new int[] { 4, 5, 6, 4, 5, 6 }, lengths: [2, 3]); result = Tensor.EqualsAll(tensor1.AsReadOnlyTensorSpan(), tensor2); Assert.True(result); } @@ -2671,9 +2670,9 @@ public void TensorEqualsAllTest() [Fact] public void TensorFilteredUpdateTest() { - Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, [3]); - Tensor filter = Tensor.Create(new bool[] { true, false, false }, [3]); - Tensor replace = Tensor.Create(new int[] { -1, -1, -1 }, [3]); + Tensor tensor1 = Tensor.Create(new int[] { 1, 2, 3 }, lengths: [3]); + Tensor filter = Tensor.Create(new bool[] { true, false, false }, lengths: [3]); + Tensor replace = Tensor.Create(new int[] { -1, -1, -1 }, lengths: [3]); Tensor.FilteredUpdate(tensor1.AsTensorSpan(), filter, 2); Assert.Equal(new int[] { 2, 2, 3 }, tensor1.ToArray()); @@ -2681,7 +2680,7 @@ public void TensorFilteredUpdateTest() Tensor.FilteredUpdate(tensor1.AsTensorSpan(), filter, replace); Assert.Equal(new int[] { -1, 2, 3 }, tensor1.ToArray()); - filter = Tensor.Create(new bool[] { true, true, true}, [3]); + filter = Tensor.Create(new bool[] { true, true, true}, lengths: [3]); Tensor.FilteredUpdate(tensor1.AsTensorSpan(), filter, replace); Assert.Equal(new int[] { -1, -1, -1 }, tensor1.ToArray()); } @@ -2689,7 +2688,7 @@ public void TensorFilteredUpdateTest() [Fact] public void TensorObjectFillTests() { - ITensor tensor = (ITensor)new Tensor(new int[4], new nint[] { 2, 2 }, 0); + ITensor tensor = (ITensor)new Tensor(new int[4], new nint[] { 2, 2 }); tensor.Fill(5); Assert.Equal(5, tensor[0, 0]); @@ -2710,7 +2709,7 @@ public void TensorObjectFillTests() [Fact] public void TensorObjectIndexerTests() { - ITensor tensor = new Tensor(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0); + ITensor tensor = new Tensor(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }); Assert.Equal(1, tensor[new nint[] { 0, 0 }]); Assert.Equal(2, tensor[new nint[] { 0, 1 }]); @@ -2741,7 +2740,7 @@ public void TensorObjectIndexerTests() [Fact] public void TensorGetPinnedHandleTests() { - Tensor tensor = new Tensor(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0); + Tensor tensor = new Tensor(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }); using MemoryHandle handle = tensor.GetPinnedHandle(); unsafe From daeaa5810a35e94717c6fbb98887081ebd4cf339 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 11:10:20 -0700 Subject: [PATCH 10/30] Resolving various build failures due to API compatibility --- .../ref/System.Numerics.Tensors.netcore.cs | 149 +++++++++++------- .../src/CompatibilitySuppressions.xml | 9 +- .../Tensors/netcore/IReadOnlyTensor_1.cs | 28 ++-- .../Numerics/Tensors/netcore/ITensor_1.cs | 18 +-- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 36 +++-- .../System/Numerics/Tensors/netcore/Tensor.cs | 94 +++++++---- .../Numerics/Tensors/netcore/TensorSpan.cs | 42 +++-- .../Numerics/Tensors/netcore/Tensor_1.cs | 37 +++-- .../tests/TensorTests.cs | 10 +- 9 files changed, 276 insertions(+), 147 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index f1130b7bbd1e2b..ceaedd2ce8d8fc 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -4,6 +4,8 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ +using System.Collections; + namespace System.Buffers { [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5001", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] @@ -78,15 +80,15 @@ public partial interface IReadOnlyTensor : System.Collections.Generic. TSelf this[params scoped System.ReadOnlySpan ranges] { get; } new T this[params scoped System.ReadOnlySpan indexes] { get; } System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(); - System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndex); - System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan range); - System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan start); + System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes); + System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan ranges); + System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes); void CopyTo(scoped System.Numerics.Tensors.TensorSpan destination); void FlattenTo(scoped System.Span destination); ref readonly T GetPinnableReference(); - TSelf Slice(params scoped System.ReadOnlySpan startIndex); - TSelf Slice(params scoped System.ReadOnlySpan range); - TSelf Slice(params scoped System.ReadOnlySpan start); + TSelf Slice(params scoped System.ReadOnlySpan startIndexes); + TSelf Slice(params scoped System.ReadOnlySpan ranges); + TSelf Slice(params scoped System.ReadOnlySpan startIndexes); bool TryCopyTo(scoped System.Numerics.Tensors.TensorSpan destination); bool TryFlattenTo(scoped System.Span destination); } @@ -106,9 +108,9 @@ public partial interface ITensor : System.Collections.Generic.IEnumera new TSelf this[params scoped System.ReadOnlySpan ranges] { get; set; } new T this[params scoped System.ReadOnlySpan indexes] { get; set; } System.Numerics.Tensors.TensorSpan AsTensorSpan(); - System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndex); - System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan range); - System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan start); + System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndexes); + System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan ranges); + System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndexes); static abstract TSelf Create(scoped System.ReadOnlySpan lengths, bool pinned = false); static abstract TSelf Create(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false); static abstract TSelf CreateUninitialized(scoped System.ReadOnlySpan lengths, bool pinned = false); @@ -122,16 +124,19 @@ public readonly ref partial struct ReadOnlyTensorSpan private readonly object _dummy; private readonly int _dummyPrimitive; public ReadOnlyTensorSpan(System.Array? array) { throw null; } - public ReadOnlyTensorSpan(System.Array? array, scoped System.ReadOnlySpan startIndex, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public ReadOnlyTensorSpan(System.Array? array, scoped System.ReadOnlySpan start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public ReadOnlyTensorSpan(System.ReadOnlySpan span) { throw null; } + public ReadOnlyTensorSpan(System.ReadOnlySpan span, scoped System.ReadOnlySpan lengths) { throw null; } public ReadOnlyTensorSpan(System.ReadOnlySpan span, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } [System.CLSCompliantAttribute(false)] public unsafe ReadOnlyTensorSpan(T* data, nint dataLength) { throw null; } [System.CLSCompliantAttribute(false)] + public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped System.ReadOnlySpan lengths) { throw null; } + [System.CLSCompliantAttribute(false)] public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public ReadOnlyTensorSpan(T[]? array) { throw null; } - public ReadOnlyTensorSpan(T[]? array, System.Index startIndex, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public ReadOnlyTensorSpan(T[]? array, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public ReadOnlyTensorSpan(T[]? array, scoped System.ReadOnlySpan lengths) { throw null; } public ReadOnlyTensorSpan(T[]? array, int start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public static System.Numerics.Tensors.ReadOnlyTensorSpan Empty { get { throw null; } } public nint FlattenedLength { get { throw null; } } @@ -162,18 +167,23 @@ public void CopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { } public static bool operator ==(System.Numerics.Tensors.ReadOnlyTensorSpan left, System.Numerics.Tensors.ReadOnlyTensorSpan right) { throw null; } public static implicit operator System.Numerics.Tensors.ReadOnlyTensorSpan (T[]? array) { throw null; } public static bool operator !=(System.Numerics.Tensors.ReadOnlyTensorSpan left, System.Numerics.Tensors.ReadOnlyTensorSpan right) { throw null; } - public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan indexes) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public override string ToString() { throw null; } public bool TryCopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } public void FlattenTo(scoped System.Span destination) { throw null; } - public ref partial struct Enumerator + public ref partial struct Enumerator : System.Collections.Generic.IEnumerator { private object _dummy; private int _dummyPrimitive; - public ref readonly T Current { get { throw null; } } + public readonly ref readonly T Current { get { throw null; } } public bool MoveNext() { throw null; } + public void Reset() { throw null; } + void System.IDisposable.Dispose() { throw null; } + readonly object? System.Collections.IEnumerator.Current { get { throw null; } } + readonly T System.Collections.Generic.IEnumerator.Current { get { throw null; } } } } [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5001", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] @@ -197,8 +207,14 @@ public static partial class Tensor public static ref readonly System.Numerics.Tensors.TensorSpan AsinPi(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static System.Numerics.Tensors.Tensor Asin(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Asin(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } - public static System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, params scoped System.ReadOnlySpan lengths) { throw null; } - public static System.Numerics.Tensors.TensorSpan AsTensorSpan(this T[]? array, params scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array) { throw null; } + public static System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public static System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(this T[]? array, int start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public static System.Numerics.Tensors.TensorSpan AsTensorSpan(this T[]? array) { throw null; } + public static System.Numerics.Tensors.TensorSpan AsTensorSpan(this T[]? array, scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.TensorSpan AsTensorSpan(this T[]? array, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public static System.Numerics.Tensors.TensorSpan AsTensorSpan(this T[]? array, int start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public static System.Numerics.Tensors.Tensor Atan2Pi(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IFloatingPointIeee754 { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Atan2Pi(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IFloatingPointIeee754 { throw null; } public static System.Numerics.Tensors.Tensor Atan2Pi(in System.Numerics.Tensors.ReadOnlyTensorSpan x, T y) where T : System.Numerics.IFloatingPointIeee754 { throw null; } @@ -251,24 +267,26 @@ public static void BroadcastTo(this System.Numerics.Tensors.Tensor source, public static ref readonly System.Numerics.Tensors.TensorSpan CopySign(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, T sign, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.INumber { throw null; } public static System.Numerics.Tensors.Tensor Cosh(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IHyperbolicFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Cosh(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IHyperbolicFunctions { throw null; } - public static System.Numerics.Tensors.Tensor CosineSimilarity(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IRootFunctions { throw null; } - public static ref readonly System.Numerics.Tensors.TensorSpan CosineSimilarity(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IRootFunctions { throw null; } + public static T CosineSimilarity(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IRootFunctions { throw null; } public static System.Numerics.Tensors.Tensor CosPi(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan CosPi(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static System.Numerics.Tensors.Tensor Cos(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Cos(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } - public static System.Numerics.Tensors.Tensor CreateAndFillGaussianNormalDistribution(System.Random random, params scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } - public static System.Numerics.Tensors.Tensor CreateAndFillGaussianNormalDistribution(params scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } - public static System.Numerics.Tensors.Tensor CreateAndFillUniformDistribution(System.Random random, params scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } - public static System.Numerics.Tensors.Tensor CreateAndFillUniformDistribution(params scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } + public static System.Numerics.Tensors.Tensor CreateAndFillGaussianNormalDistribution(System.Random random, scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } + public static System.Numerics.Tensors.Tensor CreateAndFillGaussianNormalDistribution(scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } + public static System.Numerics.Tensors.Tensor CreateAndFillUniformDistribution(System.Random random, scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } + public static System.Numerics.Tensors.Tensor CreateAndFillUniformDistribution(scoped System.ReadOnlySpan lengths) where T : System.Numerics.IFloatingPoint { throw null; } public static System.Numerics.Tensors.Tensor CreateUninitialized(scoped System.ReadOnlySpan lengths, bool pinned = false) { throw null; } public static System.Numerics.Tensors.Tensor CreateUninitialized(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false) { throw null; } - public static System.Numerics.Tensors.Tensor Create(System.Collections.Generic.IEnumerable values, scoped System.ReadOnlySpan lengths, bool pinned = false) { throw null; } - public static System.Numerics.Tensors.Tensor Create(System.Collections.Generic.IEnumerable values, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false) { throw null; } + public static System.Numerics.Tensors.Tensor Create(System.Collections.Generic.IEnumerable enumerable, bool pinned = false) { throw null; } + public static System.Numerics.Tensors.Tensor Create(System.Collections.Generic.IEnumerable enumerable, scoped System.ReadOnlySpan lengths, bool pinned = false) { throw null; } + public static System.Numerics.Tensors.Tensor Create(System.Collections.Generic.IEnumerable enumerable, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false) { throw null; } public static System.Numerics.Tensors.Tensor Create(scoped System.ReadOnlySpan lengths, bool pinned = false) { throw null; } public static System.Numerics.Tensors.Tensor Create(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false) { throw null; } - public static System.Numerics.Tensors.Tensor Create(T[] values, scoped System.ReadOnlySpan lengths, bool pinned = false) { throw null; } - public static System.Numerics.Tensors.Tensor Create(T[] values, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned = false) { throw null; } + public static System.Numerics.Tensors.Tensor Create(T[] array) { throw null; } + public static System.Numerics.Tensors.Tensor Create(T[] array, scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.Tensor Create(T[] array, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public static System.Numerics.Tensors.Tensor Create(T[] array, int start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public static System.Numerics.Tensors.Tensor DegreesToRadians(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan DegreesToRadians(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static T Distance(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IRootFunctions { throw null; } @@ -305,7 +323,6 @@ public static void BroadcastTo(this System.Numerics.Tensors.Tensor source, public static ref readonly System.Numerics.Tensors.TensorSpan FilteredUpdate(this in System.Numerics.Tensors.TensorSpan tensor, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan filter, T value) { throw null; } public static System.Numerics.Tensors.Tensor Floor(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IFloatingPoint { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Floor(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IFloatingPoint { throw null; } - public static nint[] GetSmallestBroadcastableLengths(System.ReadOnlySpan shape1, System.ReadOnlySpan shape2) { throw null; } public static bool GreaterThanAll(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IComparisonOperators { throw null; } public static bool GreaterThanAll(in System.Numerics.Tensors.ReadOnlyTensorSpan x, T y) where T : System.Numerics.IComparisonOperators { throw null; } public static bool GreaterThanAll(T x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IComparisonOperators { throw null; } @@ -435,7 +452,7 @@ public static void BroadcastTo(this System.Numerics.Tensors.Tensor source, public static T Norm(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IRootFunctions { throw null; } public static System.Numerics.Tensors.Tensor OnesComplement(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IBitwiseOperators { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan OnesComplement(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IBitwiseOperators { throw null; } - public static System.Numerics.Tensors.Tensor PermuteDimensions(this System.Numerics.Tensors.Tensor tensor, params scoped System.ReadOnlySpan dimensions) { throw null; } + public static System.Numerics.Tensors.Tensor PermuteDimensions(this System.Numerics.Tensors.Tensor tensor, scoped System.ReadOnlySpan dimensions) { throw null; } public static System.Numerics.Tensors.Tensor PopCount(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IBinaryInteger { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan PopCount(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Tensors.Tensor Pow(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.IPowerFunctions { throw null; } @@ -449,9 +466,9 @@ public static void BroadcastTo(this System.Numerics.Tensors.Tensor source, public static ref readonly System.Numerics.Tensors.TensorSpan RadiansToDegrees(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static System.Numerics.Tensors.Tensor Reciprocal(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IFloatingPoint { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Reciprocal(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IFloatingPoint { throw null; } - public static System.Numerics.Tensors.ReadOnlyTensorSpan Reshape(this in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, params scoped System.ReadOnlySpan lengths) { throw null; } - public static System.Numerics.Tensors.TensorSpan Reshape(this in System.Numerics.Tensors.TensorSpan tensor, params scoped System.ReadOnlySpan lengths) { throw null; } - public static System.Numerics.Tensors.Tensor Reshape(this System.Numerics.Tensors.Tensor tensor, params scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.ReadOnlyTensorSpan Reshape(this in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.TensorSpan Reshape(this in System.Numerics.Tensors.TensorSpan tensor, scoped System.ReadOnlySpan lengths) { throw null; } + public static System.Numerics.Tensors.Tensor Reshape(this System.Numerics.Tensors.Tensor tensor, scoped System.ReadOnlySpan lengths) { throw null; } public static void ResizeTo(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, in System.Numerics.Tensors.TensorSpan destination) { } public static void ResizeTo(scoped in System.Numerics.Tensors.TensorSpan tensor, in System.Numerics.Tensors.TensorSpan destination) { } public static void ResizeTo(scoped in System.Numerics.Tensors.Tensor tensor, in System.Numerics.Tensors.TensorSpan destination) { } @@ -515,9 +532,9 @@ public static void ResizeTo(scoped in System.Numerics.Tensors.Tensor tenso public static ref readonly System.Numerics.Tensors.TensorSpan TanPi(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static System.Numerics.Tensors.Tensor Tan(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Tan(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } - public static string ToString(this in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, params scoped System.ReadOnlySpan maximumLengths) { throw null; } - public static string ToString(this in System.Numerics.Tensors.TensorSpan tensor, params scoped System.ReadOnlySpan maximumLengths) { throw null; } - public static string ToString(this System.Numerics.Tensors.Tensor tensor, params scoped System.ReadOnlySpan maximumLengths) { throw null; } + public static string ToString(this in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, scoped System.ReadOnlySpan maximumLengths) { throw null; } + public static string ToString(this in System.Numerics.Tensors.TensorSpan tensor, scoped System.ReadOnlySpan maximumLengths) { throw null; } + public static string ToString(this System.Numerics.Tensors.Tensor tensor, scoped System.ReadOnlySpan maximumLengths) { throw null; } public static System.Numerics.Tensors.Tensor TrailingZeroCount(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IBinaryInteger { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan TrailingZeroCount(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Tensors.Tensor Transpose(System.Numerics.Tensors.Tensor tensor) { throw null; } @@ -774,16 +791,19 @@ public readonly ref partial struct TensorSpan private readonly object _dummy; private readonly int _dummyPrimitive; public TensorSpan(System.Array? array) { throw null; } - public TensorSpan(System.Array? array, scoped System.ReadOnlySpan startIndex, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public TensorSpan(System.Array? array, scoped System.ReadOnlySpan start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public TensorSpan(System.Span span) { throw null; } + public TensorSpan(System.Span span, scoped System.ReadOnlySpan lengths) { throw null; } public TensorSpan(System.Span span, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } [System.CLSCompliantAttribute(false)] public unsafe TensorSpan(T* data, nint dataLength) { throw null; } [System.CLSCompliantAttribute(false)] + public unsafe TensorSpan(T* data, nint dataLength, scoped System.ReadOnlySpan lengths) { throw null; } + [System.CLSCompliantAttribute(false)] public unsafe TensorSpan(T* data, nint dataLength, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public TensorSpan(T[]? array) { throw null; } - public TensorSpan(T[]? array, System.Index startIndex, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } + public TensorSpan(T[]? array, scoped System.ReadOnlySpan lengths) { throw null; } + public TensorSpan(T[]? array, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public TensorSpan(T[]? array, int start, scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides) { throw null; } public static System.Numerics.Tensors.TensorSpan Empty { get { throw null; } } public nint FlattenedLength { get { throw null; } } @@ -796,6 +816,10 @@ public readonly ref partial struct TensorSpan public int Rank { get { throw null; } } [System.Diagnostics.CodeAnalysis.UnscopedRefAttribute] public System.ReadOnlySpan Strides { get { throw null; } } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan() { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } public void Clear() { } public void CopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -814,20 +838,25 @@ public void FlattenTo(scoped System.Span destination) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public ref T GetPinnableReference() { throw null; } public static bool operator ==(System.Numerics.Tensors.TensorSpan left, System.Numerics.Tensors.TensorSpan right) { throw null; } - public static implicit operator System.Numerics.Tensors.ReadOnlyTensorSpan (System.Numerics.Tensors.TensorSpan span) { throw null; } + public static implicit operator System.Numerics.Tensors.ReadOnlyTensorSpan (System.Numerics.Tensors.TensorSpan tensor) { throw null; } public static implicit operator System.Numerics.Tensors.TensorSpan (T[]? array) { throw null; } public static bool operator !=(System.Numerics.Tensors.TensorSpan left, System.Numerics.Tensors.TensorSpan right) { throw null; } - public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan indexes) { throw null; } + public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public override string ToString() { throw null; } public bool TryCopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } - public ref partial struct Enumerator + public ref partial struct Enumerator : System.Collections.Generic.IEnumerator { private object _dummy; private int _dummyPrimitive; - public ref T Current { get { throw null; } } + public readonly ref T Current { get { throw null; } } public bool MoveNext() { throw null; } + public void Reset() { throw null; } + void System.IDisposable.Dispose() { throw null; } + readonly object? System.Collections.IEnumerator.Current { get { throw null; } } + readonly T System.Collections.Generic.IEnumerator.Current { get { throw null; } } } } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5001", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] @@ -838,7 +867,6 @@ internal Tensor() { } public nint FlattenedLength { get { throw null; } } public bool IsEmpty { get { throw null; } } public bool IsPinned { get { throw null; } } - public System.Numerics.Tensors.Tensor this[System.Numerics.Tensors.Tensor filter] { get { throw null; } } public ref T this[params scoped System.ReadOnlySpan indexes] { get { throw null; } } public System.Numerics.Tensors.Tensor this[params scoped System.ReadOnlySpan ranges] { get { throw null; } set { } } public ref T this[params scoped System.ReadOnlySpan indexes] { get { throw null; } } @@ -858,39 +886,50 @@ internal Tensor() { } T System.Numerics.Tensors.ITensor, T>.this[params scoped System.ReadOnlySpan indexes] { get { throw null; } set { } } T System.Numerics.Tensors.ITensor, T>.this[params scoped System.ReadOnlySpan indexes] { get { throw null; } set { } } public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan() { throw null; } - public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndex) { throw null; } - public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan start) { throw null; } - public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan start) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } public System.Numerics.Tensors.TensorSpan AsTensorSpan() { throw null; } - public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndex) { throw null; } - public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan start) { throw null; } - public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan start) { throw null; } + public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } + public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.TensorSpan AsTensorSpan(params scoped System.ReadOnlySpan startIndexes) { throw null; } public void Clear() { } public void CopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { } - public void Fill(object value) { } public void Fill(T value) { } public void FlattenTo(scoped System.Span destination) { } - public System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public Enumerator GetEnumerator() { throw null; } public override int GetHashCode() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public ref T GetPinnableReference() { throw null; } public System.Buffers.MemoryHandle GetPinnedHandle() { throw null; } - public static implicit operator System.Numerics.Tensors.ReadOnlyTensorSpan (System.Numerics.Tensors.Tensor value) { throw null; } - public static implicit operator System.Numerics.Tensors.TensorSpan (System.Numerics.Tensors.Tensor value) { throw null; } + public static implicit operator System.Numerics.Tensors.ReadOnlyTensorSpan (System.Numerics.Tensors.Tensor tensor) { throw null; } + public static implicit operator System.Numerics.Tensors.TensorSpan (System.Numerics.Tensors.Tensor tensor) { throw null; } public static implicit operator System.Numerics.Tensors.Tensor (T[] array) { throw null; } - public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan startIndex) { throw null; } - public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan start) { throw null; } - public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan start) { throw null; } + public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } + public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan ranges) { throw null; } + public System.Numerics.Tensors.Tensor Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] ref readonly T System.Numerics.Tensors.IReadOnlyTensor, T>.GetPinnableReference() { throw null; } + void System.Numerics.Tensors.ITensor.Fill(object value) { } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.Create(scoped System.ReadOnlySpan lengths, bool pinned) { throw null; } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.Create(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned) { throw null; } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.CreateUninitialized(scoped System.ReadOnlySpan lengths, bool pinned) { throw null; } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.CreateUninitialized(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned) { throw null; } - public string ToString(params scoped System.ReadOnlySpan maximumLengths) { throw null; } + public string ToString(scoped System.ReadOnlySpan maximumLengths) { throw null; } public bool TryCopyTo(scoped System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } + public partial struct Enumerator : System.Collections.Generic.IEnumerator + { + private object _dummy; + private int _dummyPrimitive; + public readonly ref T Current { get { throw null; } } + public bool MoveNext() { throw null; } + public void Reset() { throw null; } + void System.IDisposable.Dispose() { throw null; } + readonly object? System.Collections.IEnumerator.Current { get { throw null; } } + readonly T System.Collections.Generic.IEnumerator.Current { get { throw null; } } + } } } diff --git a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml index 06a5854b5aba3c..335f33bb28ad0e 100644 --- a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml @@ -281,4 +281,11 @@ lib/net9.0/System.Numerics.Tensors.dll true - \ No newline at end of file + + CP0021 + M:System.Numerics.Tensors.Tensor.StdDev``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@)``0:T:System.Numerics.IRootFunctions{``0} + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs index bd1024bcb74aea..022140ed33aa72 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/IReadOnlyTensor_1.cs @@ -38,17 +38,17 @@ public interface IReadOnlyTensor : IReadOnlyTensor, IEnumerable ReadOnlyTensorSpan AsReadOnlyTensorSpan(); /// Creates a new readonly tensor span over a portion of the tensor starting at a specified position to the end of the tensor. - /// The initial index from which the tensor will be converted. + /// The initial indexes from which the tensor will be converted. /// The readonly tensor span representation of the tensor. - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start); + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes); /// - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex); + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes); /// Creates a new readonly tensor span over a portion of the tensor defined by the specified range. - /// The range of the tensor to convert. + /// The ranges of the tensor to convert. /// The readonly tensor span representation of the tensor. - ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range); + ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan ranges); /// Copies the contents of the tensor into a destination tensor span. /// The destination tensor span. @@ -68,19 +68,19 @@ public interface IReadOnlyTensor : IReadOnlyTensor, IEnumerable ref readonly T GetPinnableReference(); /// Forms a slice out of the current tensor that begins at a specified index. - /// The indexes at which to begin the slice. - /// A tensor that consists of all elements of the current tensor from to the end of the tensor. - /// is greater than the number of items in the tensor. - TSelf Slice(params scoped ReadOnlySpan start); + /// The indexes at which to begin the slice. + /// A tensor that consists of all elements of the current tensor from to the end of the tensor. + /// is greater than the number of items in the tensor. + TSelf Slice(params scoped ReadOnlySpan startIndexes); /// - TSelf Slice(params scoped ReadOnlySpan startIndex); + TSelf Slice(params scoped ReadOnlySpan startIndexes); /// Gets a slice out of the current tensor that contains a specified range. - /// The range of which to slice. - /// A tensor that consists of all elements of the current tensor in . - /// is larger than the tensor. - TSelf Slice(params scoped ReadOnlySpan range); + /// The range of which to slice. + /// A tensor that consists of all elements of the current tensor in . + /// is larger than the tensor. + TSelf Slice(params scoped ReadOnlySpan ranges); /// Attempts to copy the contents of this tensor into a destination tensor span and returns a value to indicate whether or not the operation succeeded. /// The target of the copy operation. diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs index 998bfb138bacca..8be1445275e3fb 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ITensor_1.cs @@ -62,27 +62,27 @@ public interface ITensor : ITensor, IReadOnlyTensor new ref T this[params scoped ReadOnlySpan indexes] { get; } /// Gets or sets a slice out of the current tensor that contains a specified range. - /// The range of which to slice. - /// A tensor that consists of all elements of the current tensor in . - /// is larger than the tensor. - new TSelf this[params scoped ReadOnlySpan range] { get; set; } + /// The range of which to slice. + /// A tensor that consists of all elements of the current tensor in . + /// is larger than the tensor. + new TSelf this[params scoped ReadOnlySpan ranges] { get; set; } /// Creates a new tensor span over the tensor. /// The tensor span representation of the tensor. TensorSpan AsTensorSpan(); /// Creates a new tensor span over a portion of the tensor starting at a specified position to the end of the tensor. - /// The initial index from which the tensor will be converted. + /// The initial indexes from which the tensor will be converted. /// The tensor span representation of the tensor. - TensorSpan AsTensorSpan(params scoped ReadOnlySpan start); + TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndexes); /// - TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex); + TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndexes); /// Creates a new tensor span over a portion of the tensor defined by the specified range. - /// The range of the tensor to convert. + /// The ranges of the tensor to convert. /// The tensor span representation of the tensor. - TensorSpan AsTensorSpan(params scoped ReadOnlySpan range); + TensorSpan AsTensorSpan(params scoped ReadOnlySpan ranges); /// void Fill(T value); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index faaf8c4af9e1b1..772c1994aa4bf7 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Microsoft.VisualBasic; -using static System.Numerics.Tensors.TensorOperation; #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member @@ -401,9 +401,9 @@ public ref readonly T GetPinnableReference() } /// - public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan start) + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(start, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); return new ReadOnlyTensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -411,9 +411,9 @@ ref Unsafe.Add(ref _reference, linearOffset), } /// - public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan startIndex) + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); return new ReadOnlyTensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -421,9 +421,9 @@ ref Unsafe.Add(ref _reference, linearOffset), } /// - public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan range) + public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) { - TensorShape shape = _shape.Slice(range, out nint linearOffset); + TensorShape shape = _shape.Slice(ranges, out nint linearOffset); return new ReadOnlyTensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -457,7 +457,7 @@ public bool TryFlattenTo(scoped Span destination) } /// Enumerates the elements of a tensor span. - public ref struct Enumerator + public ref struct Enumerator : IEnumerator { private readonly ReadOnlyTensorSpan _span; private nint[] _indexes; @@ -497,6 +497,24 @@ public void Reset() _linearOffset = 0; _itemsEnumerated = -1; } + + // + // IDisposable + // + + void IDisposable.Dispose() { } + + // + // IEnumerator + // + + readonly object? IEnumerator.Current => Current; + + // + // IEnumerator + // + + readonly T IEnumerator.Current => Current; } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index f34b6a950951c6..f18aa5cb1d69e8 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -339,7 +339,7 @@ public static Tensor Create(IEnumerable enumerable, scoped ReadOnlySpan /// Creates a and initializes it with random data in a gaussian normal distribution. /// /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillGaussianNormalDistribution(params scoped ReadOnlySpan lengths) + public static Tensor CreateAndFillGaussianNormalDistribution(scoped ReadOnlySpan lengths) where T : IFloatingPoint { return CreateAndFillGaussianNormalDistribution(Random.Shared, lengths); @@ -350,7 +350,7 @@ public static Tensor CreateAndFillGaussianNormalDistribution(params scoped /// /// /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillGaussianNormalDistribution(Random random, params scoped ReadOnlySpan lengths) + public static Tensor CreateAndFillGaussianNormalDistribution(Random random, scoped ReadOnlySpan lengths) where T : IFloatingPoint { Tensor tensor = CreateUninitialized(lengths); @@ -363,7 +363,7 @@ public static Tensor CreateAndFillGaussianNormalDistribution(Random random /// Creates a and initializes it with random data uniformly distributed. /// /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillUniformDistribution(params scoped ReadOnlySpan lengths) + public static Tensor CreateAndFillUniformDistribution(scoped ReadOnlySpan lengths) where T : IFloatingPoint { return CreateAndFillUniformDistribution(Random.Shared, lengths); @@ -374,7 +374,7 @@ public static Tensor CreateAndFillUniformDistribution(params scoped ReadOn /// /// /// A indicating the lengths of each dimension. - public static Tensor CreateAndFillUniformDistribution(Random random, params scoped ReadOnlySpan lengths) + public static Tensor CreateAndFillUniformDistribution(Random random, scoped ReadOnlySpan lengths) where T : IFloatingPoint { Tensor tensor = CreateUninitialized(lengths); @@ -1356,7 +1356,7 @@ public static bool LessThanOrEqualAny(T x, in ReadOnlyTensorSpan y) /// /// Input /// with the new axis ordering. - public static Tensor PermuteDimensions(this Tensor tensor, params ReadOnlySpan dimensions) + public static Tensor PermuteDimensions(this Tensor tensor, ReadOnlySpan dimensions) { if (tensor.Rank == 1) { @@ -1410,7 +1410,7 @@ public static Tensor PermuteDimensions(this Tensor tensor, params ReadO /// /// you want to reshape. /// with the new dimensions. - public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan lengths) + public static Tensor Reshape(this Tensor tensor, ReadOnlySpan lengths) { if (tensor.Lengths.SequenceEqual(lengths)) return tensor; @@ -1482,7 +1482,7 @@ public static Tensor Reshape(this Tensor tensor, params ReadOnlySpan /// you want to reshape. /// with the new dimensions. - public static TensorSpan Reshape(in this TensorSpan tensor, params scoped ReadOnlySpan lengths) + public static TensorSpan Reshape(in this TensorSpan tensor, scoped ReadOnlySpan lengths) { if (tensor.Lengths.SequenceEqual(lengths)) return tensor; @@ -1558,7 +1558,7 @@ public static TensorSpan Reshape(in this TensorSpan tensor, params scop /// /// you want to reshape. /// with the new dimensions. - public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan tensor, params scoped ReadOnlySpan lengths) + public static ReadOnlyTensorSpan Reshape(in this ReadOnlyTensorSpan tensor, scoped ReadOnlySpan lengths) { if (tensor.Lengths.SequenceEqual(lengths)) return tensor; @@ -1792,9 +1792,9 @@ public static ref readonly TensorSpan ReverseDimension(scoped in ReadOnlyT while (copiedValues < tensor.FlattenedLength) { - TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destination._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, tensor.Strides, tensor.Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, tensor.Lengths); - TensorSpanHelpers.AdjustIndexesDown((int)dimension, 1, iIndices, tensor.Lengths); + // TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destination._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, tensor.Strides, tensor.Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); + // TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, tensor.Lengths); + // TensorSpanHelpers.AdjustIndexesDown((int)dimension, 1, iIndices, tensor.Lengths); copiedValues += copyLength; } @@ -2186,7 +2186,7 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS /// The you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this in TensorSpan tensor, params ReadOnlySpan maximumLengths) + public static string ToString(this in TensorSpan tensor, ReadOnlySpan maximumLengths) => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); /// @@ -2195,7 +2195,7 @@ public static string ToString(this in TensorSpan tensor, params ReadOnlySp /// /// The you want to represent as a string. /// Maximum Length of each dimension - public static string ToString(this in ReadOnlyTensorSpan tensor, params ReadOnlySpan maximumLengths) + public static string ToString(this in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths) { if (maximumLengths.Length != tensor.Rank) { @@ -2269,7 +2269,7 @@ internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpanThe you want to represent as a string. /// Maximum Length of each dimension /// A representation of the - public static string ToString(this Tensor tensor, params ReadOnlySpan maximumLengths) + public static string ToString(this Tensor tensor, ReadOnlySpan maximumLengths) => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); #endregion @@ -4138,9 +4138,14 @@ public static ref readonly TensorSpan LogP1(scoped in ReadOnlyTensorSpanSearches for the largest number in the specified tensor. /// The input .. public static T Max(scoped in ReadOnlyTensorSpan x) - where T : INumber, IMinMaxValue + where T : INumber { - T result = T.MinValue; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4149,7 +4154,7 @@ public static T Max(scoped in ReadOnlyTensorSpan x) /// The first tensor, represented as a span. /// The second tensor, represented as a span. public static Tensor Max(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y) - where T : INumber, IMinMaxValue + where T : INumber { TensorOperation.ValidateCompatibility(in x, in y, out Tensor output); TensorOperation.Invoke, T, T>(x, y, output); @@ -4198,7 +4203,12 @@ public static ref readonly TensorSpan Max(scoped in ReadOnlyTensorSpan public static T MaxMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { - T result = T.AdditiveIdentity; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4256,7 +4266,12 @@ public static ref readonly TensorSpan MaxMagnitude(scoped in ReadOnlyTenso public static T MaxMagnitudeNumber(scoped in ReadOnlyTensorSpan x) where T : INumberBase { - T result = T.AdditiveIdentity; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4312,9 +4327,14 @@ public static ref readonly TensorSpan MaxMagnitudeNumber(scoped in ReadOnl /// Searches for the largest number in the specified tensor. /// The input .. public static T MaxNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber, IMinMaxValue + where T : INumber { - T result = T.MinValue; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4370,9 +4390,14 @@ public static ref readonly TensorSpan MaxNumber(scoped in ReadOnlyTensorSp /// Searches for the largest number in the specified tensor. /// The input .. public static T Min(scoped in ReadOnlyTensorSpan x) - where T : INumber, IMinMaxValue + where T : INumber { - T result = T.MaxValue; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4430,7 +4455,12 @@ public static ref readonly TensorSpan Min(scoped in ReadOnlyTensorSpan public static T MinMagnitude(scoped in ReadOnlyTensorSpan x) where T : INumber { - T result = T.AdditiveIdentity; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4488,7 +4518,12 @@ public static ref readonly TensorSpan MinMagnitude(scoped in ReadOnlyTenso public static T MinMagnitudeNumber(scoped in ReadOnlyTensorSpan x) where T : INumberBase { - T result = T.AdditiveIdentity; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } @@ -4544,9 +4579,14 @@ public static ref readonly TensorSpan MinMagnitudeNumber(scoped in ReadOnl /// Searches for the largest number in the specified tensor. /// The input .. public static T MinNumber(scoped in ReadOnlyTensorSpan x) - where T : INumber, IMinMaxValue + where T : INumber { - T result = T.MaxValue; + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + T result = x._reference; TensorOperation.Invoke, T, T>(x, ref result); return result; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index a404aa20b59736..5c1a2bc0738ecd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Microsoft.VisualBasic; -using static System.Numerics.Tensors.TensorOperation; #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member @@ -240,13 +240,13 @@ public static implicit operator ReadOnlyTensorSpan(scoped in TensorSpan te public ReadOnlyTensorSpan AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan(ref _reference, in _shape); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsReadOnlyTensorSpan().Slice(start); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes) => AsReadOnlyTensorSpan().Slice(startIndexes); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex) => AsReadOnlyTensorSpan().Slice(startIndex); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes) => AsReadOnlyTensorSpan().Slice(startIndexes); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range) => AsReadOnlyTensorSpan().Slice(range); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan ranges) => AsReadOnlyTensorSpan().Slice(ranges); /// public void Clear() => TensorOperation.Invoke, T>(this); @@ -298,9 +298,9 @@ public ref T GetPinnableReference() } /// - public TensorSpan Slice(params scoped ReadOnlySpan start) + public TensorSpan Slice(params scoped ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(start, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); return new TensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -308,9 +308,9 @@ ref Unsafe.Add(ref _reference, linearOffset), } /// - public TensorSpan Slice(params scoped ReadOnlySpan startIndex) + public TensorSpan Slice(params scoped ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); return new TensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -318,9 +318,9 @@ ref Unsafe.Add(ref _reference, linearOffset), } /// - public TensorSpan Slice(params scoped ReadOnlySpan range) + public TensorSpan Slice(params scoped ReadOnlySpan ranges) { - TensorShape shape = _shape.Slice(range, out nint linearOffset); + TensorShape shape = _shape.Slice(ranges, out nint linearOffset); return new TensorSpan( ref Unsafe.Add(ref _reference, linearOffset), shape @@ -337,7 +337,7 @@ ref Unsafe.Add(ref _reference, linearOffset), public bool TryFlattenTo(scoped Span destination) => AsReadOnlyTensorSpan().TryFlattenTo(destination); /// Enumerates the elements of a tensor span. - public ref struct Enumerator + public ref struct Enumerator : IEnumerator { private readonly TensorSpan _span; private nint[] _indexes; @@ -381,6 +381,24 @@ public void Reset() _linearOffset = 0; _itemsEnumerated = -1; } + + // + // IDisposable + // + + void IDisposable.Dispose() { } + + // + // IEnumerator + // + + readonly object? IEnumerator.Current => Current; + + // + // IEnumerator + // + + readonly T IEnumerator.Current => Current; } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index b7cc96aadf9abe..3a42bcdaf3f58e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -178,25 +178,25 @@ public Tensor this[params ReadOnlySpan ranges] public ReadOnlyTensorSpan AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), in _shape); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan start) => AsReadOnlyTensorSpan().Slice(start); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes) => AsReadOnlyTensorSpan().Slice(startIndexes); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndex) => AsReadOnlyTensorSpan().Slice(startIndex); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan startIndexes) => AsReadOnlyTensorSpan().Slice(startIndexes); /// - public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan range) => AsReadOnlyTensorSpan().Slice(range); + public ReadOnlyTensorSpan AsReadOnlyTensorSpan(params scoped ReadOnlySpan ranges) => AsReadOnlyTensorSpan().Slice(ranges); /// public TensorSpan AsTensorSpan() => new TensorSpan(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), in _shape); /// - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan start) => AsTensorSpan().Slice(start); + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndexes) => AsTensorSpan().Slice(startIndexes); /// - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndex) => AsTensorSpan().Slice(startIndex); + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan startIndexes) => AsTensorSpan().Slice(startIndexes); /// - public TensorSpan AsTensorSpan(params scoped ReadOnlySpan range) => AsTensorSpan().Slice(range); + public TensorSpan AsTensorSpan(params scoped ReadOnlySpan ranges) => AsTensorSpan().Slice(ranges); /// public unsafe void Clear() => AsTensorSpan().Clear(); @@ -223,7 +223,7 @@ public void FlattenTo(scoped Span destination) } /// Gets an enumerator for the readonly tensor. - public IEnumerator GetEnumerator() => new Enumerator(this); + public Enumerator GetEnumerator() => new Enumerator(this); /// [EditorBrowsable(EditorBrowsableState.Never)] @@ -243,9 +243,9 @@ public unsafe MemoryHandle GetPinnedHandle() } /// - public Tensor Slice(params ReadOnlySpan start) + public Tensor Slice(params ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(start, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. Debug.Assert((int)(linearOffset) == linearOffset); @@ -259,9 +259,9 @@ public Tensor Slice(params ReadOnlySpan start) } /// - public Tensor Slice(params ReadOnlySpan startIndex) + public Tensor Slice(params ReadOnlySpan startIndexes) { - TensorShape shape = _shape.Slice(startIndex, out nint linearOffset); + TensorShape shape = _shape.Slice(startIndexes, out nint linearOffset); // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. Debug.Assert((int)(linearOffset) == linearOffset); @@ -275,9 +275,9 @@ public Tensor Slice(params ReadOnlySpan startIndex) } /// - public Tensor Slice(params ReadOnlySpan range) + public Tensor Slice(params ReadOnlySpan ranges) { - TensorShape shape = _shape.Slice(range, out nint linearOffset); + TensorShape shape = _shape.Slice(ranges, out nint linearOffset); // The source tensor can have no more than int.MaxValue elements so linearOffset will always be in range of int. Debug.Assert((int)(linearOffset) == linearOffset); @@ -383,7 +383,8 @@ public string ToString(params ReadOnlySpan maximumLengths) static Tensor ITensor, T>.CreateUninitialized(scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, bool pinned) => Tensor.Create(lengths, strides, pinned); - private struct Enumerator : IEnumerator + /// Enumerates the elements of a tensor. + public struct Enumerator : IEnumerator { private readonly Tensor _tensor; private nint[] _indexes; @@ -400,7 +401,7 @@ internal Enumerator(Tensor tensor) } /// - public readonly T Current => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_tensor._values), _linearOffset); + public readonly ref T Current => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_tensor._values), _linearOffset); /// public bool MoveNext() @@ -435,6 +436,12 @@ readonly void IDisposable.Dispose() { } // readonly object? IEnumerator.Current => Current; + + // + // IEnumerator + // + + readonly T IEnumerator.Current => Current; } } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 0796cd416d9fdf..2eba1f2e912b66 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -1372,7 +1372,7 @@ public static void TensorConcatenateTests() public static void TensorTransposeTests() { Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), lengths: [2, 2]); - var t1 = Tensor.PermuteDimensions(t0); + var t1 = t0.PermuteDimensions([]); Assert.Equal(0, t1[0, 0]); Assert.Equal(2, t1[0, 1]); @@ -1380,7 +1380,7 @@ public static void TensorTransposeTests() Assert.Equal(3, t1[1, 1]); t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), lengths: [2, 3]); - t1 = Tensor.PermuteDimensions(t0); + t1 = t0.PermuteDimensions([]); Assert.Equal(3, t1.Lengths[0]); Assert.Equal(2, t1.Lengths[1]); @@ -1392,7 +1392,7 @@ public static void TensorTransposeTests() Assert.Equal(5, t1[2, 1]); t0 = Tensor.Create((Enumerable.Range(0, 6).Select(i => (float)i)), lengths: [1, 2, 3]); - t1 = Tensor.PermuteDimensions(t0); + t1 = t0.PermuteDimensions([]); Assert.Equal(3, t1.Lengths[0]); Assert.Equal(2, t1.Lengths[1]); @@ -1405,7 +1405,7 @@ public static void TensorTransposeTests() Assert.Equal(5, t1[2, 1, 0]); t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); - t1 = Tensor.PermuteDimensions(t0); + t1 = t0.PermuteDimensions([]); Assert.Equal(3, t1.Lengths[0]); Assert.Equal(2, t1.Lengths[1]); @@ -1424,7 +1424,7 @@ public static void TensorTransposeTests() Assert.Equal(11, t1[2, 1, 1]); t0 = Tensor.Create((Enumerable.Range(0, 12).Select(i => (float)i)), lengths: [2, 2, 3]); - t1 = Tensor.PermuteDimensions(t0, 1, 2, 0); + t1 = t0.PermuteDimensions([1, 2, 0]); Assert.Equal(2, t1.Lengths[0]); Assert.Equal(3, t1.Lengths[1]); From 0b3476e0f58142641849146998257faa855d8748 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 11:21:10 -0700 Subject: [PATCH 11/30] Ensure flattendLength is adjusted after the stride is set for that dimension --- .../src/System/Numerics/Tensors/netcore/TensorShape.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 63b967c96adab6..546a2632371b88 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -163,10 +163,10 @@ ref Unsafe.As(ref metadata[rank * 2]), ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); } - flattenedLength = checked(flattenedLength * length); - destinationLengths[linearRankIndex] = length; destinationStrides[linearRankIndex] = flattenedLength; + + flattenedLength = checked(flattenedLength * length); } // When no strides are specified, the way we construct them makes it so that the @@ -221,11 +221,10 @@ ref Unsafe.As(ref metadata[rank * 2]), break; } - flattenedLength = checked(flattenedLength * length); - destinationLengths[linearRankIndex] = length; destinationStrides[linearRankIndex] = flattenedLength; + flattenedLength = checked(flattenedLength * length); i++; } @@ -252,11 +251,10 @@ ref Unsafe.As(ref metadata[rank * 2]), nint stride = strides[linearRankIndex]; ArgumentOutOfRangeException.ThrowIfNotEqual(stride, 0); - flattenedLength = checked(flattenedLength * length); - destinationLengths[linearRankIndex] = length; destinationStrides[linearRankIndex] = 0; + flattenedLength = checked(flattenedLength * length); i++; } } From 5afc6327967d50360ba18216fbb6a4abb28cb7f4 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 11:26:31 -0700 Subject: [PATCH 12/30] Ensure that we set linearLength if -1 is passed in when strides is empty --- .../src/System/Numerics/Tensors/netcore/TensorShape.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 546a2632371b88..a130468ad397ef 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -169,6 +169,15 @@ ref Unsafe.As(ref metadata[rank * 2]), flattenedLength = checked(flattenedLength * length); } + if (linearLength == -1) + { + linearLength = flattenedLength; + } + else + { + ArgumentOutOfRangeException.ThrowIfNotEqual(linearLength, flattenedLength); + } + // When no strides are specified, the way we construct them makes it so that the // underlying memory is contiguous and dense. This means that we can set the flag // to indicate that the tensor is contiguous and dense without any further checks. From 5e8d9247f697203d828aba65c117451ce0f2dd12 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 11:54:22 -0700 Subject: [PATCH 13/30] Ensure that the first index is correct --- .../Tensors/netcore/TensorOperation.cs | 46 +++++++++---------- .../Numerics/Tensors/netcore/TensorShape.cs | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index bf93faf6ec4cfa..af05d025eeea64 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -13,7 +13,7 @@ internal static class TensorOperation public static void Invoke(in TensorSpan x) where TOperation : TensorOperation.IOperation { - scoped Span indexes = RentedBuffer.Create(x.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(x._shape, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -31,8 +31,8 @@ public static bool Invoke(in ReadOnlyTensorSpan x, in Re { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); for (nint i = 0; i < x.FlattenedLength; i++) @@ -63,7 +63,7 @@ public static bool Invoke(in ReadOnlyTensorSpan x, TArg { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -89,7 +89,7 @@ ref result public static void Invoke(in TensorSpan destination, TArg scalar) where TOperation : TensorOperation.IUnaryOperation_Scalar { - scoped Span indexes = RentedBuffer.Create(destination.Rank, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(destination._shape, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -106,8 +106,8 @@ ref Unsafe.Add(ref destination._reference, linearOffset), public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -128,7 +128,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in Span destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); nint destinationIndex = -1; for (nint i = 0; i < destination.Length; i++) @@ -148,7 +148,7 @@ ref Unsafe.Add(ref destination[0], destinationIndex) public static void Invoke(in ReadOnlyTensorSpan x, ref TResult destination) where TOperation : TensorOperation.IUnaryReduction_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -166,9 +166,9 @@ ref destination public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(destination.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(destination.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destination._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -191,8 +191,8 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y.Rank, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(y._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); nint loopCount = Math.Max(x.FlattenedLength, y.FlattenedLength); @@ -224,8 +224,8 @@ public static void Invoke(in ReadOnlyTensorSpan public static void Invoke(in ReadOnlyTensorSpan x, T2 y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -246,8 +246,8 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(TArg x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Scalar_Tensor { - scoped Span xIndexes = RentedBuffer.Create(y.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(y._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -268,7 +268,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, T2 y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x.Rank, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -2536,13 +2536,13 @@ public interface IUnaryReduction_Tensor internal readonly struct RentedBuffer { - public static Span Create(int rank, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) + public static Span Create(in TensorShape shape, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) where T : INumber { - Span output = RentedBuffer.Create(rank, out rentedBuffer); - linearOffset = 0; + Span output = RentedBuffer.Create(shape.Rank, out rentedBuffer); + linearOffset = -shape.Strides[^1]; - output[rank - 1] = T.CreateChecked(-1); + output[shape.Rank - 1] = T.CreateChecked(-1); return output; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index a130468ad397ef..99e44e72ddf784 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -483,7 +483,7 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset } } - return 0; + return -strides[^1]; } // can shape2 turn into shape1 From 52ac342e8c6c4dfbb811f7d9ff40087a43bb3519 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 13:18:06 -0700 Subject: [PATCH 14/30] Cleanup to ensure iteration and construction initializes correctly --- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 8 +- .../Tensors/netcore/TensorOperation.cs | 4 +- .../Numerics/Tensors/netcore/TensorShape.cs | 281 +++++++++--------- .../Numerics/Tensors/netcore/TensorSpan.cs | 4 +- .../Numerics/Tensors/netcore/Tensor_1.cs | 8 +- .../tests/ReadOnlyTensorSpanTests.cs | 10 +- 6 files changed, 156 insertions(+), 159 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 772c1994aa4bf7..7a01a023710fa4 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -469,7 +469,9 @@ internal Enumerator(ReadOnlyTensorSpan span) _span = span; _indexes = new nint[span.Rank]; - _linearOffset = 0; + _indexes[^1] = -1; + + _linearOffset = 0 - (!span.IsEmpty ? span.Strides[^1] : 0); _itemsEnumerated = -1; } @@ -494,7 +496,9 @@ public bool MoveNext() public void Reset() { Array.Clear(_indexes); - _linearOffset = 0; + _indexes[^1] = -1; + + _linearOffset = 0 - (!_span.IsEmpty ? _span.Strides[^1] : 0); _itemsEnumerated = -1; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index af05d025eeea64..2b9a3c9d3163a4 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -2540,9 +2540,9 @@ public static Span Create(in TensorShape shape, out nint linearOffset, [Un where T : INumber { Span output = RentedBuffer.Create(shape.Rank, out rentedBuffer); - linearOffset = -shape.Strides[^1]; + linearOffset = !shape.IsEmpty ? (-shape.Strides[^1]) : 0; - output[shape.Rank - 1] = T.CreateChecked(-1); + output[^1] = T.CreateChecked(-1); return output; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 99e44e72ddf784..4e96f29ee0cc3e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -169,13 +169,17 @@ ref Unsafe.As(ref metadata[rank * 2]), flattenedLength = checked(flattenedLength * length); } + // Once we've finished computing everything physically present in the input + // we need to ensure that the linearLength is greater than the flattenedLength + // as this ensures that lengths is in range of the backing buffer + if (linearLength == -1) { linearLength = flattenedLength; } else { - ArgumentOutOfRangeException.ThrowIfNotEqual(linearLength, flattenedLength); + ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, flattenedLength); } // When no strides are specified, the way we construct them makes it so that the @@ -204,6 +208,8 @@ ref Unsafe.As(ref metadata[rank * 2]), int i = 0; + nint minimumLinearLength = 1; + while (i < destinationLinearRankOrder.Length) { int rankIndex = destinationLinearRankOrder.Length - (i + 1); @@ -230,21 +236,37 @@ ref Unsafe.As(ref metadata[rank * 2]), break; } + if (stride < minimumLinearLength) + { + // The next stride needs to be at least as big as the + // previous stride times the dimension length, otherwise + // the linear rank ordering is incorrect + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + destinationLengths[linearRankIndex] = length; - destinationStrides[linearRankIndex] = flattenedLength; + destinationStrides[linearRankIndex] = stride; + minimumLinearLength = checked(stride * length); flattenedLength = checked(flattenedLength * length); + i++; } + // Once we've finished computing everything physically present in the input + // we need to ensure that the linearLength is greater than the flattenedLength + // and the minimumLinearLength. The first case ensures that lengths is in range + // of the backing buffer while the second case ensures that strides is in range. + if (linearLength == -1) { linearLength = flattenedLength; } else { - ArgumentOutOfRangeException.ThrowIfNotEqual(linearLength, flattenedLength); + ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, flattenedLength); } + ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); while (i < destinationLinearRankOrder.Length) { @@ -268,7 +290,7 @@ ref Unsafe.As(ref metadata[rank * 2]), } } - Debug.Assert((flattenedLength % linearLength) == 0); + Debug.Assert((linearLength == 0) || ((flattenedLength % linearLength) == 0)); _flattenedLength = flattenedLength; _linearLength = linearLength; @@ -278,7 +300,7 @@ ref Unsafe.As(ref metadata[rank * 2]), private TensorShape(nint flattenedLength, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder, int rank) { - Debug.Assert((flattenedLength % linearLength) == 0); + Debug.Assert((linearLength == 0) || ((flattenedLength % linearLength) == 0)); Debug.Assert(lengths.Length == rank); Debug.Assert(strides.Length == rank); @@ -583,46 +605,42 @@ public static TensorShape Create(Array? array) if (array is not null) { nint linearLength = (nint)array.LongLength; + int rank = array.Rank; - if (linearLength != 0) - { - int rank = array.Rank; - - nint[]? lengthsArray = null; - InlineBuffer lengthsBuffer; - scoped Span lengths; + nint[]? lengthsArray = null; + InlineBuffer lengthsBuffer; + scoped Span lengths; - if (rank > MaxInlineRank) - { - lengthsArray = ArrayPool.Shared.Rent(rank); - lengths = lengthsArray.AsSpan(0, rank); - } - else - { - Unsafe.SkipInit(out lengthsBuffer); - lengths = ((Span)lengthsBuffer)[..rank]; - } + if (rank > MaxInlineRank) + { + lengthsArray = ArrayPool.Shared.Rent(rank); + lengths = lengthsArray.AsSpan(0, rank); + } + else + { + Unsafe.SkipInit(out lengthsBuffer); + lengths = ((Span)lengthsBuffer)[..rank]; + } - for (int i = 0; i < rank; i++) - { - lengths[i] = array.GetLength(i); - } + for (int i = 0; i < rank; i++) + { + lengths[i] = array.GetLength(i); + } - TensorShape result = new TensorShape( - flattenedLength: linearLength, - linearLength, - lengths, - strides: [], - linearRankOrder: [], - rank - ); + TensorShape result = new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths, + strides: [], + linearRankOrder: [], + rank + ); - if (lengthsArray is not null) - { - ArrayPool.Shared.Return(lengthsArray); - } - return result; + if (lengthsArray is not null) + { + ArrayPool.Shared.Return(lengthsArray); } + return result; } return default; } @@ -646,81 +664,78 @@ public static TensorShape Create(Array? array, scoped ReadOnlySpan start, s nint linearLength = (nint)array.LongLength; - if (linearLength != 0) + if (lengths.Length == 0) { - if (lengths.Length == 0) + // When no lengths are specified we need to retrieve them from the array + // since that has the expected shape. We don't need to validate the strides + // however as that will be done by the TensorShape constructor. + + if (rank > MaxInlineRank) { - // When no lengths are specified we need to retrieve them from the array - // since that has the expected shape. We don't need to validate the strides - // however as that will be done by the TensorShape constructor. + intermediateLengthsArray = ArrayPool.Shared.Rent(rank); + intermediateLengths = intermediateLengthsArray.AsSpan(0, rank); + } + else + { + Unsafe.SkipInit(out intermediateLengthsBuffer); + intermediateLengths = ((Span)intermediateLengthsBuffer)[..rank]; + } - if (rank > MaxInlineRank) - { - intermediateLengthsArray = ArrayPool.Shared.Rent(rank); - intermediateLengths = intermediateLengthsArray.AsSpan(0, rank); - } - else - { - Unsafe.SkipInit(out intermediateLengthsBuffer); - intermediateLengths = ((Span)intermediateLengthsBuffer)[..rank]; - } + for (int i = 0; i < rank; i++) + { + intermediateLengths[i] = array.GetLength(i); + } + + lengths = intermediateLengths; + } - for (int i = 0; i < rank; i++) + if (start.Length != 0) + { + // In the case a starting index is specified, we need to compute the linear + // index that is the actual starting position. Additionally, if no lengths + // were specified we want to adjust the lengths computed from the array to + // ensure they remain correct. However, we don't validate or adjust the lengths + // if they were user specified as we expect them to already be correct. This + // is because we allow users to do a "reshape" as part of construction and so + // the lengths and strides can mismatch what the underlying multidimensional + // array may have itself. + + nint stride = 1; + + for (int i = 0; i < start.Length; i++) + { + int index = start.Length - (i + 1); + int offset = start[index]; + int length = array.GetLength(index); + + if ((offset < 0) || (offset > length)) { - intermediateLengths[i] = array.GetLength(i); + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); } - lengths = intermediateLengths; - } + computedOffset += (offset * stride); + stride *= length; - if (start.Length != 0) - { - // In the case a starting index is specified, we need to compute the linear - // index that is the actual starting position. Additionally, if no lengths - // were specified we want to adjust the lengths computed from the array to - // ensure they remain correct. However, we don't validate or adjust the lengths - // if they were user specified as we expect them to already be correct. This - // is because we allow users to do a "reshape" as part of construction and so - // the lengths and strides can mismatch what the underlying multidimensional - // array may have itself. - - nint stride = 1; - - for (int i = 0; i < start.Length; i++) + if (intermediateLengths.Length != 0) { - int index = start.Length - (i + 1); - int offset = start[index]; - int length = array.GetLength(index); - - if ((offset < 0) || (offset > length)) - { - ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); - } - - computedOffset += (offset * stride); - stride *= length; - - if (intermediateLengths.Length != 0) - { - intermediateLengths[index] -= offset; - } + intermediateLengths[index] -= offset; } } + } - if ((computedOffset < 0) || (computedOffset > linearLength)) - { - ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); - } + if ((computedOffset < 0) || (computedOffset > linearLength)) + { + ThrowHelper.ThrowArgument_StartIndexOutOfBounds(); + } - TensorShape result = new TensorShape(linearLength - computedOffset, lengths, strides, linearRankOrder: []); + TensorShape result = new TensorShape(linearLength - computedOffset, lengths, strides, linearRankOrder: []); - if (intermediateLengthsArray is not null) - { - ArrayPool.Shared.Return(intermediateLengthsArray); - } - linearOffset = computedOffset; - return result; + if (intermediateLengthsArray is not null) + { + ArrayPool.Shared.Return(intermediateLengthsArray); } + linearOffset = computedOffset; + return result; } else if (start.Length != 0) { @@ -746,18 +761,14 @@ public static TensorShape Create(T[]? array) if (array is not null) { int linearLength = array.Length; - - if (linearLength != 0) - { - return new TensorShape( - flattenedLength: linearLength, - linearLength, - lengths: [linearLength], - strides: [1], - linearRankOrder: [0], - rank: 1 - ); - } + return new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths: [linearLength], + strides: [1], + linearRankOrder: [0], + rank: 1 + ); } return default; } @@ -767,11 +778,7 @@ public static TensorShape Create(T[]? array, scoped ReadOnlySpan length if (array is not null) { int linearLength = array.Length; - - if (linearLength != 0) - { - return new TensorShape(linearLength, lengths, strides: [], linearRankOrder: []); - } + return new TensorShape(linearLength, lengths, strides: [], linearRankOrder: []); } if (lengths.Length != 0) @@ -786,11 +793,7 @@ public static TensorShape Create(T[]? array, scoped ReadOnlySpan length if (array is not null) { int linearLength = array.Length; - - if (linearLength != 0) - { - return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); - } + return new TensorShape(linearLength, lengths, strides, linearRankOrder: []); } if ((lengths.Length != 0) || (strides.Length != 0)) @@ -812,11 +815,7 @@ public static TensorShape Create(T[]? array, int start, scoped ReadOnlySpan(T[]? array, int start, scoped ReadOnlySpan(ref T reference, nint linearLength) { if (!Unsafe.IsNullRef(ref reference)) { - if (linearLength != 0) - { - return new TensorShape( - flattenedLength: linearLength, - linearLength, - lengths: [linearLength], - strides: [1], - linearRankOrder: [0], - rank: 1 - ); - } + return new TensorShape( + flattenedLength: linearLength, + linearLength, + lengths: [linearLength], + strides: [1], + linearRankOrder: [0], + rank: 1 + ); } else if (linearLength != 0) { @@ -893,10 +885,7 @@ public static TensorShape Create(ref T reference, nint linearLength, scoped R { if (!Unsafe.IsNullRef(ref reference)) { - if (linearLength != 0) - { - return new TensorShape(linearLength, lengths, strides, linearRankOrder); - } + return new TensorShape(linearLength, lengths, strides, linearRankOrder); } else if (linearLength != 0) { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 5c1a2bc0738ecd..135b8af7bfb130 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -351,7 +351,7 @@ internal Enumerator(TensorSpan span) _indexes[^1] = -1; - _linearOffset = 0; + _linearOffset = 0 - (!span.IsEmpty ? span.Strides[^1] : 0); _itemsEnumerated = -1; } @@ -378,7 +378,7 @@ public void Reset() Array.Clear(_indexes); _indexes[^1] = -1; - _linearOffset = 0; + _linearOffset = 0 - (!_span.IsEmpty ? _span.Strides[^1] : 0); _itemsEnumerated = -1; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 3a42bcdaf3f58e..aaf0a4414bd139 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -396,7 +396,9 @@ internal Enumerator(Tensor tensor) _tensor = tensor; _indexes = new nint[tensor.Rank]; - _linearOffset = tensor._start; + _indexes[^1] = -1; + + _linearOffset = tensor._start - (!tensor.IsEmpty ? tensor.Strides[^1] : 0); _itemsEnumerated = -1; } @@ -421,7 +423,9 @@ public bool MoveNext() public void Reset() { Array.Clear(_indexes); - _linearOffset = _tensor._start; + _indexes[^1] = -1; + + _linearOffset = _tensor._start - (!_tensor.IsEmpty ? _tensor.Strides[^1] : 0); _itemsEnumerated = -1; } diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 37de472c886cc2..e1d893b565bd4c 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -229,9 +229,9 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() public static void ReadOnlyTensorSpanArrayConstructorTests() { // Make sure exception is thrown if lengths and strides would let you go past the end of the array - Assert.Throws(() => new TensorSpan(new double[0], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); - Assert.Throws(() => new TensorSpan(new double[1], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); - Assert.Throws(() => new TensorSpan(new double[2], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 2 })); + Assert.Throws(() => new TensorSpan(new double[0], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); + Assert.Throws(() => new TensorSpan(new double[1], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); + Assert.Throws(() => new TensorSpan(new double[2], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 2 })); // Make sure basic T[] constructor works int[] a = { 91, 92, -93, 94 }; @@ -257,7 +257,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(1, spanInt.Strides[0]); // Make sure it still throws on index 0 Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); @@ -571,7 +571,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() fixed (int* p = b) { spanInt = new ReadOnlyTensorSpan(p, 0); - Assert.Equal(1, spanInt.Rank); + Assert.Equal(0, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); Assert.Equal(0, spanInt.Strides[0]); From f9c29f9ccbaaba3400203049d36d4cb5f6092a6c Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 14:07:03 -0700 Subject: [PATCH 15/30] Ensure that broadcasting is allowed to be in any stride position --- .../Tensors/netcore/ReadOnlyTensorSpan_1.cs | 4 +- .../Tensors/netcore/TensorOperation.cs | 49 +++++----- .../Numerics/Tensors/netcore/TensorShape.cs | 97 +++++++------------ .../Numerics/Tensors/netcore/TensorSpan.cs | 4 +- .../Numerics/Tensors/netcore/Tensor_1.cs | 4 +- .../src/System/ThrowHelper.cs | 11 ++- .../tests/ReadOnlyTensorSpanTests.cs | 62 ++++++------ 7 files changed, 107 insertions(+), 124 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 7a01a023710fa4..36452121c96ebd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -472,7 +472,7 @@ internal Enumerator(ReadOnlyTensorSpan span) _indexes[^1] = -1; _linearOffset = 0 - (!span.IsEmpty ? span.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } /// Gets the element at the current position of the enumerator. @@ -499,7 +499,7 @@ public void Reset() _indexes[^1] = -1; _linearOffset = 0 - (!_span.IsEmpty ? _span.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } // diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 2b9a3c9d3163a4..7c5d1833fe7278 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -13,7 +13,7 @@ internal static class TensorOperation public static void Invoke(in TensorSpan x) where TOperation : TensorOperation.IOperation { - scoped Span indexes = RentedBuffer.Create(x._shape, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(x.Rank, x.Strides, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -31,9 +31,9 @@ public static bool Invoke(in ReadOnlyTensorSpan x, in Re { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); + ref readonly TensorShape destinationShape = ref ((x._shape.FlattenedLength > y._shape.FlattenedLength) ? ref x._shape : ref y._shape); + scoped Span xIndexes = RentedBuffer.Create(destinationShape.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destinationShape.Rank, y.Strides, out nint yLinearOffset, out RentedBuffer yRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -63,7 +63,7 @@ public static bool Invoke(in ReadOnlyTensorSpan x, TArg { bool result = false; - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -89,7 +89,7 @@ ref result public static void Invoke(in TensorSpan destination, TArg scalar) where TOperation : TensorOperation.IUnaryOperation_Scalar { - scoped Span indexes = RentedBuffer.Create(destination._shape, out nint linearOffset, out RentedBuffer rentedBuffer); + scoped Span indexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint linearOffset, out RentedBuffer rentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -106,8 +106,8 @@ ref Unsafe.Add(ref destination._reference, linearOffset), public static void Invoke(in ReadOnlyTensorSpan x, in TensorSpan destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -128,7 +128,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in Span destination) where TOperation : TensorOperation.IUnaryOperation_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); nint destinationIndex = -1; for (nint i = 0; i < destination.Length; i++) @@ -148,7 +148,7 @@ ref Unsafe.Add(ref destination[0], destinationIndex) public static void Invoke(in ReadOnlyTensorSpan x, ref TResult destination) where TOperation : TensorOperation.IUnaryReduction_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -166,9 +166,9 @@ ref destination public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(destination._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(destination._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destination.Rank, y.Strides, out nint yLinearOffset, out RentedBuffer yRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -191,9 +191,10 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, in ReadOnlyTensorSpan y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Tensor { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span yIndexes = RentedBuffer.Create(y._shape, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - ref readonly TensorShape destinationShape = ref (x._shape.FlattenedLength > y._shape.FlattenedLength ? ref x._shape : ref y._shape); + ref readonly TensorShape destinationShape = ref ((x._shape.FlattenedLength > y._shape.FlattenedLength) ? ref x._shape : ref y._shape); + + scoped Span xIndexes = RentedBuffer.Create(destinationShape.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span yIndexes = RentedBuffer.Create(destinationShape.Rank, y.Strides, out nint yLinearOffset, out RentedBuffer yRentedBuffer); nint loopCount = Math.Max(x.FlattenedLength, y.FlattenedLength); @@ -224,8 +225,8 @@ public static void Invoke(in ReadOnlyTensorSpan public static void Invoke(in ReadOnlyTensorSpan x, T2 y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -246,8 +247,8 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(TArg x, in ReadOnlyTensorSpan y, in TensorSpan destination) where TOperation : TensorOperation.IBinaryOperation_Scalar_Tensor { - scoped Span xIndexes = RentedBuffer.Create(y._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); - scoped Span destinationIndexes = RentedBuffer.Create(destination._shape, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, y.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint destinationLinearOffset, out RentedBuffer destinationRentedBuffer); for (nint i = 0; i < destination.FlattenedLength; i++) { @@ -268,7 +269,7 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) public static void Invoke(in ReadOnlyTensorSpan x, T2 y, ref TResult result) where TOperation : TensorOperation.IBinaryOperation_Tensor_Scalar { - scoped Span xIndexes = RentedBuffer.Create(x._shape, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span xIndexes = RentedBuffer.Create(x.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); for (nint i = 0; i < x.FlattenedLength; i++) { @@ -2536,11 +2537,11 @@ public interface IUnaryReduction_Tensor internal readonly struct RentedBuffer { - public static Span Create(in TensorShape shape, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) + public static Span Create(int rank, ReadOnlySpan strides, out nint linearOffset, [UnscopedRef] out RentedBuffer rentedBuffer) where T : INumber { - Span output = RentedBuffer.Create(shape.Rank, out rentedBuffer); - linearOffset = !shape.IsEmpty ? (-shape.Strides[^1]) : 0; + Span output = RentedBuffer.Create(rank, out rentedBuffer); + linearOffset = 0 - (!strides.IsEmpty ? strides[^1] : 0); output[^1] = T.CreateChecked(-1); return output; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 4e96f29ee0cc3e..3c3908f7d3fb63 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -35,7 +35,8 @@ namespace System.Numerics.Tensors internal enum TensorFlags : uint { None = 0, - IsContiguousAndDense = 1 + IsContiguousAndDense = (1 << 0), + IsBroadcast = (1 << 1), } [Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)] @@ -73,6 +74,7 @@ private TensorShape(nint linearLength, scoped ReadOnlySpan lengths, scoped lengths = [linearLength]; rank = 1; } + Debug.Assert(rank >= 1); scoped Span destinationLengths; scoped Span destinationStrides; @@ -158,9 +160,9 @@ ref Unsafe.As(ref metadata[rank * 2]), int linearRankIndex = destinationLinearRankOrder[rankIndex]; nint length = lengths[linearRankIndex]; - if (length <= 0) + if (length < 0) { - ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); + ThrowHelper.ThrowArgument_LengthIsNegative(); } destinationLengths[linearRankIndex] = length; @@ -169,18 +171,16 @@ ref Unsafe.As(ref metadata[rank * 2]), flattenedLength = checked(flattenedLength * length); } - // Once we've finished computing everything physically present in the input - // we need to ensure that the linearLength is greater than the flattenedLength - // as this ensures that lengths is in range of the backing buffer + // This path cannot have any broadcasting, so once we've finished computing + // everything physically present in the input we need to ensure that the + // linearLength is greater than or equal to the flattenedLength which ensures + // that lengths is in range of the backing buffer. - if (linearLength == -1) - { - linearLength = flattenedLength; - } - else + if (linearLength != -1) { ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, flattenedLength); } + linearLength = flattenedLength; // When no strides are specified, the way we construct them makes it so that the // underlying memory is contiguous and dense. This means that we can set the flag @@ -207,7 +207,6 @@ ref Unsafe.As(ref metadata[rank * 2]), // aren't actually stored in memory. int i = 0; - nint minimumLinearLength = 1; while (i < destinationLinearRankOrder.Length) @@ -216,9 +215,9 @@ ref Unsafe.As(ref metadata[rank * 2]), int linearRankIndex = destinationLinearRankOrder[rankIndex]; nint length = lengths[linearRankIndex]; - if (length <= 0) + if (length < 0) { - ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); + ThrowHelper.ThrowArgument_LengthIsNegative(); } nint stride = strides[linearRankIndex]; @@ -228,66 +227,40 @@ ref Unsafe.As(ref metadata[rank * 2]), ThrowHelper.ThrowArgument_StrideIsNegative(); } - if (stride == 0) + if (stride != 0) { - // We end up handling i twice due to the break here - // but this shouldn't be significant and makes it - // easier to ensure that the flattened length is correct. - break; - } + if (stride < minimumLinearLength) + { + // The next stride needs to be at least as big as the + // previous stride times the dimension length, otherwise + // the linear rank ordering is incorrect + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } - if (stride < minimumLinearLength) + minimumLinearLength = checked(stride * length); + } + else { - // The next stride needs to be at least as big as the - // previous stride times the dimension length, otherwise - // the linear rank ordering is incorrect - ThrowHelper.ThrowArgument_InvalidTensorShape(); + _tensorFlags |= TensorFlags.IsBroadcast; } destinationLengths[linearRankIndex] = length; destinationStrides[linearRankIndex] = stride; - minimumLinearLength = checked(stride * length); flattenedLength = checked(flattenedLength * length); - i++; } - // Once we've finished computing everything physically present in the input - // we need to ensure that the linearLength is greater than the flattenedLength - // and the minimumLinearLength. The first case ensures that lengths is in range - // of the backing buffer while the second case ensures that strides is in range. - - if (linearLength == -1) - { - linearLength = flattenedLength; - } - else - { - ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, flattenedLength); - } - ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); + // This path can have broadcasting, so once we've finished computing + // everything physically present in the input we need to ensure that the + // linearLength is greater than or equal to the minimumLinearLength which + // ensures that lengths and strides are in range of the backing buffer. - while (i < destinationLinearRankOrder.Length) + if (linearLength != -1) { - int rankIndex = destinationLinearRankOrder.Length - (i + 1); - int linearRankIndex = destinationLinearRankOrder[rankIndex]; - nint length = lengths[linearRankIndex]; - - if (length <= 0) - { - ThrowHelper.ThrowArgument_LengthIsNegativeOrZero(); - } - - nint stride = strides[linearRankIndex]; - ArgumentOutOfRangeException.ThrowIfNotEqual(stride, 0); - - destinationLengths[linearRankIndex] = length; - destinationStrides[linearRankIndex] = 0; - - flattenedLength = checked(flattenedLength * length); - i++; + ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); } + linearLength = minimumLinearLength; } Debug.Assert((linearLength == 0) || ((flattenedLength % linearLength) == 0)); @@ -340,6 +313,8 @@ ref Unsafe.As(ref metadata[rank * 2]), _rank = rank; } + public bool IsBroadcast => (_tensorFlags & TensorFlags.IsBroadcast) != 0; + public bool IsContiguousAndDense => (_tensorFlags & TensorFlags.IsContiguousAndDense) != 0; public nint FlattenedLength => _flattenedLength; @@ -628,12 +603,10 @@ public static TensorShape Create(Array? array) } TensorShape result = new TensorShape( - flattenedLength: linearLength, linearLength, lengths, strides: [], - linearRankOrder: [], - rank + linearRankOrder: [] ); if (lengthsArray is not null) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 135b8af7bfb130..093a922f2f3789 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -352,7 +352,7 @@ internal Enumerator(TensorSpan span) _indexes[^1] = -1; _linearOffset = 0 - (!span.IsEmpty ? span.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } /// Gets the element at the current position of the enumerator. @@ -379,7 +379,7 @@ public void Reset() _indexes[^1] = -1; _linearOffset = 0 - (!_span.IsEmpty ? _span.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } // diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index aaf0a4414bd139..f0cce74714ae20 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -399,7 +399,7 @@ internal Enumerator(Tensor tensor) _indexes[^1] = -1; _linearOffset = tensor._start - (!tensor.IsEmpty ? tensor.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } /// @@ -426,7 +426,7 @@ public void Reset() _indexes[^1] = -1; _linearOffset = _tensor._start - (!_tensor.IsEmpty ? _tensor.Strides[^1] : 0); - _itemsEnumerated = -1; + _itemsEnumerated = 0; } // diff --git a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs index e920bbb0111bf7..0037e18a9dd4b5 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace System { @@ -29,6 +30,14 @@ public static void ThrowArgument_InputAndDestinationSpanMustNotOverlap() => throw new ArgumentException(SR.Argument_InputAndDestinationSpanMustNotOverlap, "destination"); public static void ThrowIfArrayTypeMismatch(Array? array) + { + if ((array is not null) && (array.GetType() != typeof(T[]))) + { + ThrowArrayTypeMismatchException(); + } + } + + public static void ThrowIfArrayTypeMismatch(T[]? array) { if ((array is not null) && !typeof(T).IsValueType && (array.GetType() != typeof(T[]))) { @@ -71,7 +80,7 @@ public static void ThrowArgument_LengthsMustEqualArrayLength() } [DoesNotReturn] - public static void ThrowArgument_LengthIsNegativeOrZero() + public static void ThrowArgument_LengthIsNegative() { throw new ArgumentOutOfRangeException(); } diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index e1d893b565bd4c..667f996a1f61f3 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -48,9 +48,9 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(0, spanInt.Lengths[1]); Assert.Equal(0, spanInt.FlattenedLength); Assert.Equal(0, spanInt.Strides[0]); - Assert.Equal(0, spanInt.Strides[1]); + Assert.Equal(1, spanInt.Strides[1]); // Make sure it still throws on index 0, 0 - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); var x = spanInt[0, 0]; }); @@ -72,25 +72,25 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[0, -1]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[-1, 0]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[1, 0]; @@ -189,7 +189,7 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[], [2, 2], [1, 1]); }); @@ -259,7 +259,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(0, spanInt.FlattenedLength); Assert.Equal(1, spanInt.Strides[0]); // Make sure it still throws on index 0 - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); var x = spanInt[0]; }); @@ -270,7 +270,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(1, spanInt.Strides[0]); // Make sure 2D array works spanInt = new ReadOnlyTensorSpan(a, 0, [2,2], default); @@ -289,12 +289,12 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 0]; }); @@ -316,7 +316,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 3, [1, 2], default); }); @@ -387,7 +387,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 0, [2, 2], [1, 1]); }); } @@ -409,12 +409,12 @@ public static void ReadOnlyTensorSpanSpanConstructorTests() // Should be a Tensor with 0 elements but Rank 1 with dimension 0 length 0 Span b = []; spanInt = new ReadOnlyTensorSpan(b); - Assert.Equal(1, spanInt.Rank); - Assert.Equal(0, spanInt.Lengths[0]); + Assert.Equal(0, spanInt.Rank); + Assert.Equal(0, spanInt.Lengths.Length); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides.Length); // Make sure it still throws on index 0 - Assert.Throws(() => { + Assert.Throws(() => { Span b = []; var spanInt = new ReadOnlyTensorSpan(b); var x = spanInt[0]; @@ -437,13 +437,13 @@ public static void ReadOnlyTensorSpanSpanConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a, [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a, [1, 2], default); var x = spanInt[1, 0]; @@ -466,7 +466,7 @@ public static void ReadOnlyTensorSpanSpanConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a.Slice(3), [1, 2], default); }); @@ -542,7 +542,7 @@ public static void ReadOnlyTensorSpanSpanConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a, [2, 2], [1, 1]); }); @@ -572,11 +572,11 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() { spanInt = new ReadOnlyTensorSpan(p, 0); Assert.Equal(0, spanInt.Rank); - Assert.Equal(0, spanInt.Lengths[0]); + Assert.Equal(0, spanInt.Lengths.Length); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides.Length); // Make sure it still throws on index 0 - Assert.Throws(() => + Assert.Throws(() => { Span b = []; fixed (int* p = b) @@ -607,7 +607,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -617,7 +617,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() } }); - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -644,7 +644,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -750,7 +750,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -1014,8 +1014,8 @@ public static void ReadOnlyTensorSpanSliceTest() int[] results = new int[9]; ReadOnlyTensorSpan spanInt = a.AsTensorSpan([3, 3]); - Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); - Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1, 5..6)); var sp = spanInt.Slice(1..3, 1..3); @@ -1056,7 +1056,7 @@ public static void ReadOnlyTensorSpanSliceTest() sp = spanInt.Slice(0..1, 0..1); Assert.Equal(1, sp[0, 0]); - Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); + Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); slice = [1]; results = new int[1]; sp.FlattenTo(results); From aa745bb1b8f9e6a777082da7240b07fa36fa6a17 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 15:19:32 -0700 Subject: [PATCH 16/30] Have AreCompatible handle empty shapes --- .../Numerics/Tensors/netcore/TensorShape.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 3c3908f7d3fb63..06d53271a6d516 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -510,11 +510,25 @@ public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, b lengths1 = lengths1[rankDelta..]; } - // if equal or one is 1 + // We need both to be empty if either is empty + if (shape1.IsEmpty) + { + return shape2.IsEmpty; + } + else if (shape2.IsEmpty) + { + return false; + } + + // we need the lengths to be equal or one of them to be 1 for (int i = 0; i < lengths1.Length; i++) { nint length1 = lengths1[i]; + Debug.Assert(length1 != 0); + nint length2 = lengths2[i]; + Debug.Assert(length2 != 0); + if ((length1 != length2) && (length1 != 1) && (length2 != 1)) { return false; From d757a6c2762be3723d91ce6e4d3fb05b93b964d1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 15:37:20 -0700 Subject: [PATCH 17/30] Ensure IndexOutOfRangeException is thrown for invalid indexes --- .../Numerics/Tensors/netcore/TensorShape.cs | 6 ++--- .../src/System/ThrowHelper.cs | 2 +- .../tests/ReadOnlyTensorSpanTests.cs | 26 +++++++++---------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 06d53271a6d516..19ed639efc7bcd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -1023,9 +1023,8 @@ public static nint GetOffset(ReadOnlySpan indexes, int rankIndex, nint pre if ((offset < 0) || (offset >= previousLength)) { - ThrowHelper.ThrowArgumentOutOfRangeException(); + ThrowHelper.ThrowIndexOutOfRangeException(); } - return offset; } @@ -1044,9 +1043,8 @@ public static nint GetOffset(ReadOnlySpan indexes, int rankIndex, nint p if ((offset < 0) || (offset >= previousLength)) { - ThrowHelper.ThrowArgumentOutOfRangeException(); + ThrowHelper.ThrowIndexOutOfRangeException(); } - return offset; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs index 0037e18a9dd4b5..81338921a5810d 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs @@ -31,7 +31,7 @@ public static void ThrowArgument_InputAndDestinationSpanMustNotOverlap() => public static void ThrowIfArrayTypeMismatch(Array? array) { - if ((array is not null) && (array.GetType() != typeof(T[]))) + if ((array is not null) && (array.GetType().GetElementType() != typeof(T))) { ThrowArrayTypeMismatchException(); } diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 667f996a1f61f3..2cb890eaae9708 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -50,7 +50,7 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(0, spanInt.Strides[0]); Assert.Equal(1, spanInt.Strides[1]); // Make sure it still throws on index 0, 0 - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); var x = spanInt[0, 0]; }); @@ -72,25 +72,25 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[0, -1]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[-1, 0]; }); - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, (int[])[0, 0], [1, 2], default); var x = spanInt[1, 0]; @@ -259,7 +259,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(0, spanInt.FlattenedLength); Assert.Equal(1, spanInt.Strides[0]); // Make sure it still throws on index 0 - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); var x = spanInt[0]; }); @@ -289,12 +289,12 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(a, 0, [1, 2], default); var x = spanInt[1, 0]; }); @@ -437,13 +437,13 @@ public static void ReadOnlyTensorSpanSpanConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a, [1, 2], default); var x = spanInt[1, 1]; }); - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new ReadOnlyTensorSpan(a, [1, 2], default); var x = spanInt[1, 0]; @@ -607,7 +607,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() Assert.Equal(2, spanInt.Lengths[1]); Assert.Equal(91, spanInt[0, 0]); Assert.Equal(92, spanInt[0, 1]); - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -617,7 +617,7 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() } }); - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -1056,7 +1056,7 @@ public static void ReadOnlyTensorSpanSliceTest() sp = spanInt.Slice(0..1, 0..1); Assert.Equal(1, sp[0, 0]); - Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); + Assert.Throws(() => a.AsTensorSpan([3, 3]).Slice(0..1, 0..1)[0, 1]); slice = [1]; results = new int[1]; sp.FlattenTo(results); From 455a42a7e5912e4e560db0f63e591a4022a5dc6e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 21 Apr 2025 16:13:39 -0700 Subject: [PATCH 18/30] Ensure that the stride is set to 0 when the length of a dimension is 1, so embedded broadcasting works --- .../Numerics/Tensors/netcore/TensorShape.cs | 74 ++++++++++--------- .../tests/ReadOnlyTensorSpanTests.cs | 20 ++--- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 19ed639efc7bcd..1033e46d8c513a 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -143,6 +143,7 @@ ref Unsafe.As(ref metadata[rank * 2]), } nint flattenedLength = 1; + nint minimumLinearLength = 0; if (strides.Length == 0) { @@ -165,27 +166,23 @@ ref Unsafe.As(ref metadata[rank * 2]), ThrowHelper.ThrowArgument_LengthIsNegative(); } - destinationLengths[linearRankIndex] = length; - destinationStrides[linearRankIndex] = flattenedLength; + nint stride = flattenedLength; - flattenedLength = checked(flattenedLength * length); - } + if (length > 1) + { + minimumLinearLength = checked(stride * length); + } + else + { + stride = 0; + _tensorFlags |= TensorFlags.IsBroadcast; + } - // This path cannot have any broadcasting, so once we've finished computing - // everything physically present in the input we need to ensure that the - // linearLength is greater than or equal to the flattenedLength which ensures - // that lengths is in range of the backing buffer. + destinationLengths[linearRankIndex] = length; + destinationStrides[linearRankIndex] = stride; - if (linearLength != -1) - { - ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, flattenedLength); + flattenedLength = checked(flattenedLength * length); } - linearLength = flattenedLength; - - // When no strides are specified, the way we construct them makes it so that the - // underlying memory is contiguous and dense. This means that we can set the flag - // to indicate that the tensor is contiguous and dense without any further checks. - _tensorFlags |= TensorFlags.IsContiguousAndDense; } else if (strides.Length != lengths.Length) { @@ -206,10 +203,7 @@ ref Unsafe.As(ref metadata[rank * 2]), // n+1. This makes it convenient to support implicit broadcasting where higher dimensions // aren't actually stored in memory. - int i = 0; - nint minimumLinearLength = 1; - - while (i < destinationLinearRankOrder.Length) + for (int i = 0; i < destinationLinearRankOrder.Length; i++) { int rankIndex = destinationLinearRankOrder.Length - (i + 1); int linearRankIndex = destinationLinearRankOrder[rankIndex]; @@ -237,6 +231,14 @@ ref Unsafe.As(ref metadata[rank * 2]), ThrowHelper.ThrowArgument_InvalidTensorShape(); } + if (length <= 1) + { + // We require the stride to be zero if the dimension length + // is 0 or 1, as this is necessary for indexing with broadcast to + // work as expected. + ThrowHelper.ThrowArgument_InvalidTensorShape(); + } + minimumLinearLength = checked(stride * length); } else @@ -248,25 +250,31 @@ ref Unsafe.As(ref metadata[rank * 2]), destinationStrides[linearRankIndex] = stride; flattenedLength = checked(flattenedLength * length); - i++; } + } - // This path can have broadcasting, so once we've finished computing - // everything physically present in the input we need to ensure that the - // linearLength is greater than or equal to the minimumLinearLength which - // ensures that lengths and strides are in range of the backing buffer. + // Once we've finished computing everything physically present in the input + // we need to ensure that the linearLength is greater than or equal to the + // minimumLinearLength so that lengths is in range of the backing buffer and + // so that we can support broadcasting for anything that was length 1. - if (linearLength != -1) - { - ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); - } - linearLength = minimumLinearLength; + if (linearLength != -1) + { + ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); } - Debug.Assert((linearLength == 0) || ((flattenedLength % linearLength) == 0)); + if (flattenedLength == minimumLinearLength) + { + // When the flattenedLength and minimumLinearLength are the same, then the + // underlying buffer is "contiguous and dense" which allows various other + // optimizations to be utilized when processing the tensor. + _tensorFlags |= TensorFlags.IsContiguousAndDense; + } + + Debug.Assert((minimumLinearLength == 0) || ((flattenedLength % minimumLinearLength) == 0)); _flattenedLength = flattenedLength; - _linearLength = linearLength; + _linearLength = minimumLinearLength; _rank = rank; } diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 2cb890eaae9708..1a3b5fd8825a68 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -48,7 +48,7 @@ public static void ReadOnlyTensorSpanSystemArrayConstructorTests() Assert.Equal(0, spanInt.Lengths[1]); Assert.Equal(0, spanInt.FlattenedLength); Assert.Equal(0, spanInt.Strides[0]); - Assert.Equal(1, spanInt.Strides[1]); + Assert.Equal(0, spanInt.Strides[1]); // Make sure it still throws on index 0, 0 Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); @@ -270,7 +270,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(1, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[0]); // Make sure 2D array works spanInt = new ReadOnlyTensorSpan(a, 0, [2,2], default); @@ -777,11 +777,11 @@ public static void ReadOnlyTensorSpanLargeDimensionsTests() Assert.Equal(1, spanInt.Lengths[4]); Assert.Equal(6, spanInt.Lengths[5]); Assert.Equal(6, spanInt.Strides.Length); - Assert.Equal(6, spanInt.Strides[0]); - Assert.Equal(6, spanInt.Strides[1]); - Assert.Equal(6, spanInt.Strides[2]); - Assert.Equal(6, spanInt.Strides[3]); - Assert.Equal(6, spanInt.Strides[4]); + Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[1]); + Assert.Equal(0, spanInt.Strides[2]); + Assert.Equal(0, spanInt.Strides[3]); + Assert.Equal(0, spanInt.Strides[4]); Assert.Equal(1, spanInt.Strides[5]); Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); @@ -803,11 +803,11 @@ public static void ReadOnlyTensorSpanLargeDimensionsTests() Assert.Equal(1, spanInt.Lengths[4]); Assert.Equal(3, spanInt.Lengths[5]); Assert.Equal(6, spanInt.Strides.Length); - Assert.Equal(12, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[0]); Assert.Equal(6, spanInt.Strides[1]); Assert.Equal(3, spanInt.Strides[2]); - Assert.Equal(3, spanInt.Strides[3]); - Assert.Equal(3, spanInt.Strides[4]); + Assert.Equal(0, spanInt.Strides[3]); + Assert.Equal(0, spanInt.Strides[4]); Assert.Equal(1, spanInt.Strides[5]); Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); From aea4bf58032a5bd853f0fe67baef3398c4496663 Mon Sep 17 00:00:00 2001 From: Michael Sharp <51342856+michaelgsharp@users.noreply.github.com> Date: Tue, 22 Apr 2025 07:14:08 -0600 Subject: [PATCH 19/30] Fixing Broadcasting Loop (#29) * Fixing Broadcasting Loop * fixes from pr coments * squeeze fixed * unsqueeze * set slice * more tensor fies --- .../System/Numerics/Tensors/netcore/Tensor.cs | 172 ++++++++++++------ .../Tensors/netcore/TensorOperation.cs | 6 +- .../Numerics/Tensors/netcore/TensorShape.cs | 20 +- .../Numerics/Tensors/netcore/Tensor_1.cs | 4 +- .../tests/TensorTests.cs | 76 ++++---- 5 files changed, 175 insertions(+), 103 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index f18aa5cb1d69e8..be128658eddaee 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -1932,16 +1932,26 @@ public static Tensor SqueezeDimension(this Tensor tensor, int dimension if (dimension == -1) { + int removalCount = tensor.Lengths.Count(1); + int removedIndex = 0; + Span removed = TensorOperation.RentedBuffer.CreateUninitialized(removalCount, out TensorOperation.RentedBuffer removedRentedBuffer); + for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed[removedIndex++] = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); + removedRentedBuffer.Dispose(); } else { @@ -1949,19 +1959,25 @@ public static Tensor SqueezeDimension(this Tensor tensor, int dimension { ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); } + int removed = default; for (int i = 0; i < tensor.Lengths.Length; i++) { if (i != dimension) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); } - Tensor output = new Tensor(tensor._values, tensor._start, lengths, strides, strideOrder); + Tensor output = new Tensor(tensor._values, tensor._start, lengths[..newRank], strides[..newRank], strideOrder[..newRank]); lengthsRentedBuffer.Dispose(); stridesRentedBuffer.Dispose(); @@ -1998,16 +2014,26 @@ public static TensorSpan SqueezeDimension(in this TensorSpan tensor, in if (dimension == -1) { + int removalCount = tensor.Lengths.Count(1); + int removedIndex = 0; + Span removed = TensorOperation.RentedBuffer.CreateUninitialized(removalCount, out TensorOperation.RentedBuffer removedRentedBuffer); + for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed[removedIndex++] = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); + removedRentedBuffer.Dispose(); } else { @@ -2015,19 +2041,25 @@ public static TensorSpan SqueezeDimension(in this TensorSpan tensor, in { ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); } + int removed = default; for (int i = 0; i < tensor.Lengths.Length; i++) { if (i != dimension) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); } - TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides, strideOrder); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths[..newRank], strides[..newRank], strideOrder[..newRank]); lengthsRentedBuffer.Dispose(); stridesRentedBuffer.Dispose(); @@ -2064,16 +2096,26 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp if (dimension == -1) { + int removalCount = tensor.Lengths.Count(1); + int removedIndex = 0; + Span removed = TensorOperation.RentedBuffer.CreateUninitialized(removalCount, out TensorOperation.RentedBuffer removedRentedBuffer); + for (int i = 0; i < tensor.Lengths.Length; i++) { if (tensor.Lengths[i] != 1) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed[removedIndex++] = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); + removedRentedBuffer.Dispose(); } else { @@ -2081,19 +2123,25 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp { ThrowHelper.ThrowArgument_InvalidSqueezeAxis(); } + int removed = default; for (int i = 0; i < tensor.Lengths.Length; i++) { if (i != dimension) { lengths[index] = tensor.Lengths[i]; strides[index] = tensor.Strides[i]; - strideOrder[index++] = tensor._shape.LinearRankOrder[i]; newRank++; + strideOrder[index++] = tensor._shape.LinearRankOrder[i]; + } + else + { + removed = tensor._shape.LinearRankOrder[i]; } } + SqueezeHelper(removed, strideOrder); } - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides, strideOrder); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths[..newRank], strides[..newRank], strideOrder[..newRank]); lengthsRentedBuffer.Dispose(); stridesRentedBuffer.Dispose(); @@ -2101,6 +2149,31 @@ public static ReadOnlyTensorSpan SqueezeDimension(in this ReadOnlyTensorSp return output; } + + internal static void SqueezeHelper(scoped in Span removed, scoped in Span strideOrder) + { + for (int i = 0; i < strideOrder.Length; i++) + { + for (int j = removed.Length - 1; j >= 0; j--) + { + if (strideOrder[i] > removed[j]) + { + strideOrder[i]--; + } + } + } + } + + internal static void SqueezeHelper(int removed, scoped in Span strideOrder) + { + for (int i = 0; i < strideOrder.Length; i++) + { + if (strideOrder[i] > removed) + { + strideOrder[i]--; + } + } + } #endregion #region Stack @@ -2367,29 +2440,28 @@ public static Tensor Unsqueeze(this Tensor tensor, int dimension) if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); + scoped Span newLengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer lengthsRentedBuffer); - tensor.Lengths.Slice(0, dimension).CopyTo(lengths); - tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; + tensor.Lengths.Slice(0, dimension).CopyTo(newLengths); + tensor.Lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1)); + newLengths[dimension] = 1; - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; + Span newStrides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer stridesRentedBuffer); if (dimension == tensor.Rank) { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; + tensor.Strides.CopyTo(newStrides); + newStrides[dimension] = 0; } else { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + tensor.Strides.Slice(0, dimension).CopyTo(newStrides); + tensor.Strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1)); + newStrides[dimension] = 0; } - Tensor output = new Tensor(tensor._values, tensor._start, lengths, strides); - xRentedBuffer.Dispose(); + Tensor output = new Tensor(tensor._values, tensor._start, newLengths, newStrides); + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); return output; } @@ -2405,29 +2477,28 @@ public static TensorSpan Unsqueeze(in this TensorSpan tensor, int dimen if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); + scoped Span newLengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer lengthsRentedBuffer); - tensor.Lengths.Slice(0, dimension).CopyTo(lengths); - tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; + tensor.Lengths.Slice(0, dimension).CopyTo(newLengths); + tensor.Lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1)); + newLengths[dimension] = 1; - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; + Span newStrides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer stridesRentedBuffer); if (dimension == tensor.Rank) { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; + tensor.Strides.CopyTo(newStrides); + newStrides[dimension] = 0; } else { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + tensor.Strides.Slice(0, dimension).CopyTo(newStrides); + tensor.Strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1)); + newStrides[dimension] = 0; } - TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); - xRentedBuffer.Dispose(); + TensorSpan output = new TensorSpan(ref tensor._reference, tensor._shape.LinearLength, newLengths, newStrides); + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); return output; } @@ -2443,29 +2514,28 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan t if (dimension < 0) dimension = tensor.Rank - dimension; - scoped Span lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer xRentedBuffer); + scoped Span newLengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer lengthsRentedBuffer); - tensor.Lengths.Slice(0, dimension).CopyTo(lengths); - tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1)); - lengths[dimension] = 1; + tensor.Lengths.Slice(0, dimension).CopyTo(newLengths); + tensor.Lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1)); + newLengths[dimension] = 1; - Span strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ? - stackalloc nint[tensor.Strides.Length + 1] : - new nint[tensor.Strides.Length + 1]; + Span newStrides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank + 1, out TensorOperation.RentedBuffer stridesRentedBuffer); if (dimension == tensor.Rank) { - tensor.Strides.CopyTo(strides); - strides[dimension] = tensor.Strides[dimension - 1]; + tensor.Strides.CopyTo(newStrides); + newStrides[dimension] = 0; } else { - tensor.Strides.Slice(0, dimension).CopyTo(strides); - tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1)); - strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension]; + tensor.Strides.Slice(0, dimension).CopyTo(newStrides); + tensor.Strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1)); + newStrides[dimension] = 0; } - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, lengths, strides); - xRentedBuffer.Dispose(); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref tensor._reference, tensor._shape.LinearLength, newLengths, newStrides); + lengthsRentedBuffer.Dispose(); + stridesRentedBuffer.Dispose(); return output; } #endregion diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 7c5d1833fe7278..cb7d42159f231e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -35,7 +35,7 @@ public static bool Invoke(in ReadOnlyTensorSpan x, in Re scoped Span xIndexes = RentedBuffer.Create(destinationShape.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); scoped Span yIndexes = RentedBuffer.Create(destinationShape.Rank, y.Strides, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - for (nint i = 0; i < x.FlattenedLength; i++) + for (nint i = 0; i < destinationShape.FlattenedLength; i++) { xLinearOffset = x._shape.AdjustToNextIndex(destinationShape, xLinearOffset, xIndexes); yLinearOffset = y._shape.AdjustToNextIndex(destinationShape, yLinearOffset, yIndexes); @@ -1972,7 +1972,7 @@ public struct SumOfSquaredDifferences { public static void Invoke(ref readonly T x, T y, ref T destination) { - destination = (x - y) * (x - y); + destination += (x - y) * (x - y); } public static void Invoke(ReadOnlySpan x, T y, Span destination) { @@ -1984,7 +1984,7 @@ public static void Invoke(ReadOnlySpan x, T y, Span destination) public static void Invoke(ref readonly T x, ref readonly T y, ref T destination) { - destination = (x - y) * (x - y); + destination += (x - y) * (x - y); } public static void Invoke(ReadOnlySpan x, ReadOnlySpan y, Span destination) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 1033e46d8c513a..2a21b93a2c9cc5 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -71,7 +71,7 @@ private TensorShape(nint linearLength, scoped ReadOnlySpan lengths, scoped if (rank == 0) { - lengths = [linearLength]; + lengths = [0]; rank = 1; } Debug.Assert(rank >= 1); @@ -281,7 +281,7 @@ ref Unsafe.As(ref metadata[rank * 2]), private TensorShape(nint flattenedLength, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder, int rank) { - Debug.Assert((linearLength == 0) || ((flattenedLength % linearLength) == 0)); + Debug.Assert((linearLength == 0) || (linearLength >= flattenedLength)); Debug.Assert(lengths.Length == rank); Debug.Assert(strides.Length == rank); @@ -451,11 +451,12 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset for (int i = 0; i < strides.Length; i++) { int rankIndex = lengths.Length - (i + 1); + int destinationRankIndex = destinationShape.Lengths.Length - (i + 1); nint length = lengths[rankIndex]; nint stride = strides[rankIndex]; - nint index = ++indexes[rankIndex]; + nint index = ++indexes[destinationRankIndex]; linearOffset += stride; if (index < length) @@ -463,7 +464,7 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset return linearOffset; } - indexes[rankIndex] = 0; + indexes[destinationRankIndex] = 0; linearOffset -= (stride * length); } @@ -481,7 +482,11 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset if (index < length) { - break; + // If we just break here we end up returning -strides[^1] as the linear offset. + // If strides[^1] is 1, then we return -1 which is not correct and it ends the outer loop early. + // By returning the linear offset here we ensure that the outer loop continues and handles the + // broadcast case correctly. + return linearOffset; } indexes[rankIndex] = 0; @@ -974,8 +979,7 @@ public TensorShape Slice(ReadOnlySpan state, out nint // assume that the previousShape is already valid and the new shape // will strictly be the same size or smaller. - nint flattenedLength = 0; - nint linearLength = 0; + nint flattenedLength = 1; nint computedOffset = 0; for (int i = 0; i < state.Length; i++) @@ -995,7 +999,7 @@ public TensorShape Slice(ReadOnlySpan state, out nint TensorShape result = new TensorShape( flattenedLength, - linearLength, + LinearLength - computedOffset, intermediateLengths, strides, linearRankOrder, diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index f0cce74714ae20..6ab7024fed00cd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -125,13 +125,13 @@ private Tensor() /// public ref T this[params scoped ReadOnlySpan indexes] { - get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _shape.GetLinearOffset(indexes)); + get => ref Unsafe.Add(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), _shape.GetLinearOffset(indexes)); } /// public ref T this[params scoped ReadOnlySpan indexes] { - get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _shape.GetLinearOffset(indexes)); + get => ref Unsafe.Add(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), _shape.GetLinearOffset(indexes)); } /// diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 2eba1f2e912b66..16a2f1fa6af4f7 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -251,11 +251,11 @@ public static void TensorLargeDimensionsTests() Assert.Equal(1, tensor.Lengths[4]); Assert.Equal(6, tensor.Lengths[5]); Assert.Equal(6, tensor.Strides.Length); - Assert.Equal(6, tensor.Strides[0]); - Assert.Equal(6, tensor.Strides[1]); - Assert.Equal(6, tensor.Strides[2]); - Assert.Equal(6, tensor.Strides[3]); - Assert.Equal(6, tensor.Strides[4]); + Assert.Equal(0, tensor.Strides[0]); + Assert.Equal(0, tensor.Strides[1]); + Assert.Equal(0, tensor.Strides[2]); + Assert.Equal(0, tensor.Strides[3]); + Assert.Equal(0, tensor.Strides[4]); Assert.Equal(1, tensor.Strides[5]); Assert.Equal(91, tensor[0, 0, 0, 0, 0, 0]); Assert.Equal(92, tensor[0, 0, 0, 0, 0, 1]); @@ -277,11 +277,11 @@ public static void TensorLargeDimensionsTests() Assert.Equal(1, tensor.Lengths[4]); Assert.Equal(3, tensor.Lengths[5]); Assert.Equal(6, tensor.Strides.Length); - Assert.Equal(12, tensor.Strides[0]); + Assert.Equal(0, tensor.Strides[0]); Assert.Equal(6, tensor.Strides[1]); Assert.Equal(3, tensor.Strides[2]); - Assert.Equal(3, tensor.Strides[3]); - Assert.Equal(3, tensor.Strides[4]); + Assert.Equal(0, tensor.Strides[3]); + Assert.Equal(0, tensor.Strides[4]); Assert.Equal(1, tensor.Strides[5]); Assert.Equal(91, tensor[0, 0, 0, 0, 0, 0]); Assert.Equal(92, tensor[0, 0, 0, 0, 0, 1]); @@ -308,11 +308,11 @@ public static void TensorFactoryCreateUninitializedTests() Assert.Equal(1, t1.Lengths.Length); Assert.Equal(1, t1.Lengths[0]); Assert.Equal(1, t1.Strides.Length); - Assert.Equal(1, t1.Strides[0]); + Assert.Equal(0, t1.Strides[0]); Assert.False(t1.IsPinned); // Make sure can't index too many dimensions - Assert.Throws(() => + Assert.Throws(() => { var x = t1[1, 1]; }); @@ -366,7 +366,7 @@ public static void TensorFactoryCreateUninitializedTests() Assert.Equal(1, t1.Lengths.Length); Assert.Equal(1, t1.Lengths[0]); Assert.Equal(1, t1.Strides.Length); - Assert.Equal(1, t1.Strides[0]); + Assert.Equal(0, t1.Strides[0]); Assert.True(t1.IsPinned); // Make sure 2D array works with basic strides @@ -427,7 +427,7 @@ public static void TensorFactoryCreateUninitializedTests() Assert.Equal(t1[1, 0], t1[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { var t1 = Tensor.CreateUninitialized([2, 2], [1, 1], false); }); } @@ -441,12 +441,12 @@ public static void TensorFactoryCreateTests() Assert.Equal(1, t1.Lengths.Length); Assert.Equal(1, t1.Lengths[0]); Assert.Equal(1, t1.Strides.Length); - Assert.Equal(1, t1.Strides[0]); + Assert.Equal(0, t1.Strides[0]); Assert.False(t1.IsPinned); Assert.Equal(0, t1[0]); // Make sure can't index too many dimensions - Assert.Throws(() => + Assert.Throws(() => { var x = t1[1, 1]; }); @@ -487,11 +487,9 @@ public static void TensorFactoryCreateTests() // Null should behave like empty array since there is no "null" span. t1 = Tensor.Create(null); - Assert.Equal(1, t1.Rank); - Assert.Equal(1, t1.Lengths.Length); - Assert.Equal(0, t1.Lengths[0]); - Assert.Equal(1, t1.Strides.Length); - Assert.Equal(0, t1.Strides[0]); + Assert.Equal(0, t1.Rank); + Assert.Equal(0, t1.Lengths.Length); + Assert.Equal(0, t1.Strides.Length); Assert.False(t1.IsPinned); // Make sure pinned works @@ -500,7 +498,7 @@ public static void TensorFactoryCreateTests() Assert.Equal(1, t1.Lengths.Length); Assert.Equal(1, t1.Lengths[0]); Assert.Equal(1, t1.Strides.Length); - Assert.Equal(1, t1.Strides[0]); + Assert.Equal(0, t1.Strides[0]); Assert.True(t1.IsPinned); Assert.Equal(0, t1[0]); @@ -592,14 +590,14 @@ public static void TensorCosineSimilarityTests() Tensor result = Tensor.Create(a, lengths: [2, 1]); - result[0, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([0..1, 0..]), right[0..1, 0..]); - result[1, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([0..1, 0..]), right[0..1, 0..]); + result[0, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([0..1, 0.. ]), right[0..1, 0..]); + result[1, 0] = Tensor.CosineSimilarity(left.AsReadOnlyTensorSpan([1.., 0..]), right[1.., 0..]); Assert.Equal(2, result.Rank); Assert.Equal(2, result.Lengths[0]); Assert.Equal(1, result.Lengths[1]); - Assert.Equal(0.57735, result[0, 0], .00001); + Assert.Equal(float.NaN, result[0, 0]); Assert.Equal(0.81649, result[1, 0], .00001); } @@ -945,7 +943,7 @@ public static void TensorSetSliceTests() { Tensor t0 = Tensor.Create(Enumerable.Range(0, 10), lengths: [2, 5]); Tensor t1 = Tensor.Create(Enumerable.Range(10, 10), lengths: [2, 5]); - Tensor.SetSlice(t0, t1); + Tensor.SetSlice(t0, t1, .., ..); Assert.Equal(10, t0[0, 0]); Assert.Equal(11, t0[0, 1]); @@ -1168,11 +1166,11 @@ public static void TensorSumTests() sum = Tensor.Sum(t1); Assert.Equal(9, sum); - Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: -1, lengths: [2, 3], strides: [])); - Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 100, lengths: [2, 3], strides: [])); - Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MinValue, lengths: [2, 3], strides: [])); - Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MaxValue, lengths: [2, 3], strides: [])); - Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 2, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: -1, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 100, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MinValue, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: int.MaxValue, lengths: [2, 3], strides: [])); + Assert.Throws(()=> Tensor.Create(new float[] { 1, 2, 3, 4, 5, 6 }, start: 2, lengths: [2, 3], strides: [])); } public static float StdDev(float[] values) @@ -1890,7 +1888,7 @@ public static void TensorCopyTest() } ); - Assert.Throws(() => + Assert.Throws(() => { var l = leftData.AsTensorSpan([3, 3, 3]); var r = new TensorSpan(); @@ -2207,7 +2205,7 @@ public static void TensorUnsqueezeTest() Assert.Equal(0, tensor.Strides[0]); Assert.Equal(0, tensor.Strides[1]); - tensor = Tensor.Create([2], false); + tensor = Tensor.Create((ReadOnlySpan)[2], false); Assert.Equal(1, tensor.Rank); Assert.Equal(2, tensor.Lengths[0]); Assert.Equal(1, tensor.Strides[0]); @@ -2216,7 +2214,7 @@ public static void TensorUnsqueezeTest() Assert.Equal(2, tensor.Rank); Assert.Equal(1, tensor.Lengths[0]); Assert.Equal(2, tensor.Lengths[1]); - Assert.Equal(2, tensor.Strides[0]); + Assert.Equal(0, tensor.Strides[0]); Assert.Equal(1, tensor.Strides[1]); tensor = Tensor.Unsqueeze(tensor, 0); @@ -2224,8 +2222,8 @@ public static void TensorUnsqueezeTest() Assert.Equal(1, tensor.Lengths[0]); Assert.Equal(1, tensor.Lengths[1]); Assert.Equal(2, tensor.Lengths[2]); - Assert.Equal(2, tensor.Strides[0]); - Assert.Equal(2, tensor.Strides[1]); + Assert.Equal(0, tensor.Strides[0]); + Assert.Equal(0, tensor.Strides[1]); Assert.Equal(1, tensor.Strides[2]); tensor = Tensor.Unsqueeze(tensor, 0); @@ -2234,12 +2232,12 @@ public static void TensorUnsqueezeTest() Assert.Equal(1, tensor.Lengths[1]); Assert.Equal(1, tensor.Lengths[2]); Assert.Equal(2, tensor.Lengths[3]); - Assert.Equal(2, tensor.Strides[0]); - Assert.Equal(2, tensor.Strides[1]); - Assert.Equal(2, tensor.Strides[2]); + Assert.Equal(0, tensor.Strides[0]); + Assert.Equal(0, tensor.Strides[1]); + Assert.Equal(0, tensor.Strides[2]); Assert.Equal(1, tensor.Strides[3]); - tensor = Tensor.Create([2], false); + tensor = Tensor.Create((ReadOnlySpan)[2], false); Assert.Equal(1, tensor.Rank); Assert.Equal(2, tensor.Lengths[0]); @@ -2248,7 +2246,7 @@ public static void TensorUnsqueezeTest() Assert.Equal(2, tensor.Lengths[0]); Assert.Equal(1, tensor.Lengths[1]); - tensor = Tensor.Create([2], false); + tensor = Tensor.Create((ReadOnlySpan)[2], false); Assert.Equal(1, tensor.Rank); Assert.Equal(2, tensor.Lengths[0]); From 46681010e7edaaa0cd6ba6a839edc7e369125406 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 22 Apr 2025 11:29:12 -0700 Subject: [PATCH 20/30] Ensure that minimumLinearLength is actually the minimum --- .../Tensors/netcore/TensorOperation.cs | 4 +- .../Numerics/Tensors/netcore/TensorShape.cs | 167 +++++++++++++----- .../Numerics/Tensors/netcore/Tensor_1.cs | 10 +- 3 files changed, 128 insertions(+), 53 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index cb7d42159f231e..bdb34cb4393f20 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -196,9 +196,7 @@ public static void Invoke(in ReadOnlyTensorSpan scoped Span xIndexes = RentedBuffer.Create(destinationShape.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); scoped Span yIndexes = RentedBuffer.Create(destinationShape.Rank, y.Strides, out nint yLinearOffset, out RentedBuffer yRentedBuffer); - nint loopCount = Math.Max(x.FlattenedLength, y.FlattenedLength); - - for (nint i = 0; i < loopCount; i++) + for (nint i = 0; i < destinationShape.FlattenedLength; i++) { xLinearOffset = x._shape.AdjustToNextIndex(destinationShape, xLinearOffset, xIndexes); yLinearOffset = y._shape.AdjustToNextIndex(destinationShape, yLinearOffset, yIndexes); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 2a21b93a2c9cc5..deea273c3e2734 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -21,9 +21,10 @@ namespace System.Numerics.Tensors // key properties. The flattened length is the total number of elements represented // by the tensor, however due to implicit broadcasting this may be greater than the // amount of memory that actually backs the tensor. While the linear length is the - // backing storage size that is present. This gives us the following invariants: - // * linearLength <= flattenedLength - // * (flattenedLength % linearLength) == 0 + // backing storage size that is present. We can also have arbitrary strides, such as + // lengths: [4], strides: [2]. In this case the flattenedLength = 4, while the + // linearLength: 8. We can also have a slice of a tensor, such as lengths: [2, 2], + // strides: [3, 1] (which could have been taken from a 3x3 contiguous and dense tensor). // // These invariants allow us to safely and efficiently index into the backing storage // as we know that memory functionally wraps around after linearLength elements. It @@ -63,7 +64,7 @@ internal enum TensorFlags : uint private readonly InlineBuffer _inlineLinearRankOrder; // 4x4 bytes (16) private readonly int _rank; - private readonly TensorFlags _tensorFlags; // 4 bytes + private readonly TensorFlags _flags; // 4 bytes private TensorShape(nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder) { @@ -143,7 +144,7 @@ ref Unsafe.As(ref metadata[rank * 2]), } nint flattenedLength = 1; - nint minimumLinearLength = 0; + nint maximumLinearIndex = 0; if (strides.Length == 0) { @@ -159,6 +160,7 @@ ref Unsafe.As(ref metadata[rank * 2]), { int rankIndex = destinationLinearRankOrder.Length - (i + 1); int linearRankIndex = destinationLinearRankOrder[rankIndex]; + nint length = lengths[linearRankIndex]; if (length < 0) @@ -170,12 +172,12 @@ ref Unsafe.As(ref metadata[rank * 2]), if (length > 1) { - minimumLinearLength = checked(stride * length); + maximumLinearIndex = checked(maximumLinearIndex + ((length - 1) * stride)); } else { stride = 0; - _tensorFlags |= TensorFlags.IsBroadcast; + _flags |= TensorFlags.IsBroadcast; } destinationLengths[linearRankIndex] = length; @@ -203,10 +205,13 @@ ref Unsafe.As(ref metadata[rank * 2]), // n+1. This makes it convenient to support implicit broadcasting where higher dimensions // aren't actually stored in memory. + nint minimumNonZeroStride = 0; + for (int i = 0; i < destinationLinearRankOrder.Length; i++) { int rankIndex = destinationLinearRankOrder.Length - (i + 1); int linearRankIndex = destinationLinearRankOrder[rankIndex]; + nint length = lengths[linearRankIndex]; if (length < 0) @@ -223,7 +228,7 @@ ref Unsafe.As(ref metadata[rank * 2]), if (stride != 0) { - if (stride < minimumLinearLength) + if (stride < minimumNonZeroStride) { // The next stride needs to be at least as big as the // previous stride times the dimension length, otherwise @@ -239,11 +244,12 @@ ref Unsafe.As(ref metadata[rank * 2]), ThrowHelper.ThrowArgument_InvalidTensorShape(); } - minimumLinearLength = checked(stride * length); + minimumNonZeroStride = checked(length * stride); + maximumLinearIndex = checked(maximumLinearIndex + (minimumNonZeroStride - stride)); } else { - _tensorFlags |= TensorFlags.IsBroadcast; + _flags |= TensorFlags.IsBroadcast; } destinationLengths[linearRankIndex] = length; @@ -258,31 +264,31 @@ ref Unsafe.As(ref metadata[rank * 2]), // minimumLinearLength so that lengths is in range of the backing buffer and // so that we can support broadcasting for anything that was length 1. + nint minimumLinearLength = (flattenedLength != 0) ? (maximumLinearIndex + 1) : 0; + if (linearLength != -1) { ArgumentOutOfRangeException.ThrowIfLessThan(linearLength, minimumLinearLength); } + _flattenedLength = flattenedLength; + _linearLength = minimumLinearLength; + + _rank = rank; + if (flattenedLength == minimumLinearLength) { // When the flattenedLength and minimumLinearLength are the same, then the // underlying buffer is "contiguous and dense" which allows various other // optimizations to be utilized when processing the tensor. - _tensorFlags |= TensorFlags.IsContiguousAndDense; + _flags |= TensorFlags.IsContiguousAndDense; } - Debug.Assert((minimumLinearLength == 0) || ((flattenedLength % minimumLinearLength) == 0)); - - _flattenedLength = flattenedLength; - _linearLength = minimumLinearLength; - - _rank = rank; + ValidateState(); } - private TensorShape(nint flattenedLength, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder, int rank) + private TensorShape(nint flattenedLength, nint linearLength, scoped ReadOnlySpan lengths, scoped ReadOnlySpan strides, scoped ReadOnlySpan linearRankOrder, int rank, TensorFlags flags) { - Debug.Assert((linearLength == 0) || (linearLength >= flattenedLength)); - Debug.Assert(lengths.Length == rank); Debug.Assert(strides.Length == rank); Debug.Assert(linearRankOrder.Length == rank); @@ -319,11 +325,14 @@ ref Unsafe.As(ref metadata[rank * 2]), linearRankOrder.CopyTo(destinationLinearRankOrder); _rank = rank; + _flags = flags; + + ValidateState(); } - public bool IsBroadcast => (_tensorFlags & TensorFlags.IsBroadcast) != 0; + public bool IsBroadcast => (_flags & TensorFlags.IsBroadcast) != 0; - public bool IsContiguousAndDense => (_tensorFlags & TensorFlags.IsContiguousAndDense) != 0; + public bool IsContiguousAndDense => (_flags & TensorFlags.IsContiguousAndDense) != 0; public nint FlattenedLength => _flattenedLength; @@ -448,6 +457,8 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset ReadOnlySpan lengths = Lengths; ReadOnlySpan strides = Strides; + ReadOnlySpan destinationLengths = destinationShape.Lengths; + for (int i = 0; i < strides.Length; i++) { int rankIndex = lengths.Length - (i + 1); @@ -459,8 +470,19 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset nint index = ++indexes[destinationRankIndex]; linearOffset += stride; - if (index < length) + // We can have a scenario such as lengths: [1], destinationLengths: [2] in + // which case we still need to keep incrementing the index but without + // adjusting the linearOffset + + if (index < destinationLengths[destinationRankIndex]) { + if (index >= length) + { + // We should only be here if we were broadcast + Debug.Assert((length == 1) && (stride == 0)); + } + + Debug.Assert(GetLinearOffset(indexes[(destinationShape.Rank - Rank)..]) == linearOffset); return linearOffset; } @@ -470,30 +492,30 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset if (indexes.Length != Rank) { - lengths = destinationShape.Lengths; - for (int i = strides.Length; i < indexes.Length; i++) + for (int i = strides.Length; i < destinationLengths.Length; i++) { - int rankIndex = lengths.Length - (i + 1); + int rankIndex = destinationLengths.Length - (i + 1); - nint length = lengths[rankIndex]; + nint length = destinationLengths[rankIndex]; // Strides are always 0 because we are broadcasting at this point in the loop. nint index = ++indexes[rankIndex]; if (index < length) { - // If we just break here we end up returning -strides[^1] as the linear offset. - // If strides[^1] is 1, then we return -1 which is not correct and it ends the outer loop early. - // By returning the linear offset here we ensure that the outer loop continues and handles the - // broadcast case correctly. - return linearOffset; + // For any indexes that exist in the destinationShape but not + // in the srcShape we will only increment them if all lower + // indexes were 0. This means we're starting over at the beginning + // of the srcShape and the linearOffset must be 0. + break; } indexes[rankIndex] = 0; } } - return -strides[^1]; + Debug.Assert(GetLinearOffset(indexes[(destinationShape.Rank - Rank)..]) == 0); + return 0; } // can shape2 turn into shape1 @@ -761,13 +783,15 @@ public static TensorShape Create(T[]? array) if (array is not null) { int linearLength = array.Length; + return new TensorShape( flattenedLength: linearLength, linearLength, lengths: [linearLength], strides: [1], linearRankOrder: [0], - rank: 1 + rank: 1, + TensorFlags.IsContiguousAndDense ); } return default; @@ -865,7 +889,8 @@ public static TensorShape Create(ref T reference, nint linearLength) lengths: [linearLength], strides: [1], linearRankOrder: [0], - rank: 1 + rank: 1, + TensorFlags.IsContiguousAndDense ); } else if (linearLength != 0) @@ -915,6 +940,13 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() => base.GetHashCode(); + [Conditional("DEBUG")] + private void ValidateState() + { + Debug.Assert(IsContiguousAndDense == (FlattenedLength == LinearLength)); + Debug.Assert(IsBroadcast == Strides.Contains(0)); + } + public nint GetLinearOffset(ReadOnlySpan state) where TGetOffsetAndLength : IGetOffsetAndLength { @@ -950,24 +982,34 @@ public TensorShape Slice(ReadOnlySpan state, out nint InlineBuffer intermediateLengthsBuffer; scoped Span intermediateLengths; + nint[]? intermediateStridesArray = null; + InlineBuffer intermediateStridesBuffer; + scoped Span intermediateStrides; + if (rank > MaxInlineRank) { intermediateLengthsArray = ArrayPool.Shared.Rent(rank); intermediateLengths = intermediateLengthsArray.AsSpan(0, rank); + + intermediateStridesArray = ArrayPool.Shared.Rent(rank); + intermediateStrides = intermediateStridesArray.AsSpan(0, rank); } else { Unsafe.SkipInit(out intermediateLengthsBuffer); intermediateLengths = ((Span)intermediateLengthsBuffer)[..rank]; + + Unsafe.SkipInit(out intermediateStridesBuffer); + intermediateStrides = ((Span)intermediateStridesBuffer)[..rank]; } ReadOnlySpan previousLengths = Lengths; - ReadOnlySpan strides = Strides; + ReadOnlySpan previousStrides = Strides; ReadOnlySpan linearRankOrder = LinearRankOrder; if ((state.Length != previousLengths.Length) || (state.Length != linearRankOrder.Length) || - (state.Length != strides.Length)) + (state.Length != previousStrides.Length)) { ThrowHelper.ThrowArgumentOutOfRangeException(); } @@ -979,31 +1021,59 @@ public TensorShape Slice(ReadOnlySpan state, out nint // assume that the previousShape is already valid and the new shape // will strictly be the same size or smaller. + TensorFlags flags = TensorFlags.None; + nint flattenedLength = 1; + nint maximumLinearIndex = 0; + nint computedOffset = 0; - for (int i = 0; i < state.Length; i++) + for (int i = 0; i < linearRankOrder.Length; i++) { - int rankIndex = state.Length - (i + 1); + int rankIndex = linearRankOrder.Length - (i + 1); int linearRankIndex = linearRankOrder[rankIndex]; nint previousLength = previousLengths[linearRankIndex]; - nint stride = strides[linearRankIndex]; + nint previousStride = previousStrides[linearRankIndex]; (nint offset, nint length) = TGetOffsetAndLength.GetOffsetAndLength(state, linearRankIndex, previousLength); - flattenedLength *= length; + nint stride = (length > 1) ? previousStride : 0; + + if (stride != 0) + { + maximumLinearIndex += ((length - 1) * stride); + } + else + { + flags |= TensorFlags.IsBroadcast; + } intermediateLengths[linearRankIndex] = length; - computedOffset += (offset * stride); + intermediateStrides[linearRankIndex] = stride; + + computedOffset += (offset * previousStride); + flattenedLength *= length; + } + + // We've computed the maximum linear index based on the strides + // so the minimum length must be one higher than that value. + + nint minimumLinearLength = (flattenedLength != 0) ? (maximumLinearIndex + 1) : flattenedLength; + Debug.Assert(minimumLinearLength <= _linearLength); + + if (flattenedLength == minimumLinearLength) + { + flags |= TensorFlags.IsContiguousAndDense; } TensorShape result = new TensorShape( flattenedLength, - LinearLength - computedOffset, + minimumLinearLength, intermediateLengths, - strides, + intermediateStrides, linearRankOrder, - rank + rank, + flags ); if (intermediateLengthsArray is not null) @@ -1011,7 +1081,14 @@ public TensorShape Slice(ReadOnlySpan state, out nint ArrayPool.Shared.Return(intermediateLengthsArray); } + if (intermediateStridesArray is not null) + { + ArrayPool.Shared.Return(intermediateStridesArray); + } + + Debug.Assert(computedOffset == GetLinearOffset(state)); linearOffset = computedOffset; + return result; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 6ab7024fed00cd..a2417ae57f1afe 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -125,13 +125,13 @@ private Tensor() /// public ref T this[params scoped ReadOnlySpan indexes] { - get => ref Unsafe.Add(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), _shape.GetLinearOffset(indexes)); + get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start + _shape.GetLinearOffset(indexes)); } /// public ref T this[params scoped ReadOnlySpan indexes] { - get => ref Unsafe.Add(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start), _shape.GetLinearOffset(indexes)); + get => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _start + _shape.GetLinearOffset(indexes)); } /// @@ -252,7 +252,7 @@ public Tensor Slice(params ReadOnlySpan startIndexes) return new Tensor( _values, - (int)(linearOffset), + (int)(_start + linearOffset), in shape, _isPinned ); @@ -268,7 +268,7 @@ public Tensor Slice(params ReadOnlySpan startIndexes) return new Tensor( _values, - (int)(linearOffset), + (int)(_start + linearOffset), in shape, _isPinned ); @@ -284,7 +284,7 @@ public Tensor Slice(params ReadOnlySpan ranges) return new Tensor( _values, - (int)(linearOffset), + (int)(_start + linearOffset), in shape, _isPinned ); From f846271beb183d6bbf0a6cbce7ed03539dc05665 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 22 Apr 2025 11:47:43 -0700 Subject: [PATCH 21/30] Ensure the rented buffer is cleared --- .../src/System/Numerics/Tensors/netcore/TensorOperation.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index bdb34cb4393f20..4ee76d5d2dc733 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -2561,7 +2561,9 @@ public static Span Create(int rank, [UnscopedRef] out RentedBuffer rentedB rentedBuffer._array = ArrayPool.Shared.Rent(rank); Unsafe.SkipInit(out rentedBuffer._inline); - return rentedBuffer._array.AsSpan(0, rank); + Span resultBuffer = rentedBuffer._array.AsSpan(0, rank); + resultBuffer.Clear(); + return resultBuffer; } else { From 7dd1669691ae28653eabfc309d60fa132f761d4d Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 22 Apr 2025 12:40:40 -0700 Subject: [PATCH 22/30] Fix the AreCompatible checks --- .../System/Numerics/Tensors/netcore/Tensor.cs | 9 +- .../Tensors/netcore/TensorOperation.cs | 2 +- .../Numerics/Tensors/netcore/TensorShape.cs | 128 +++++++++++------- .../tests/TensorSpanTests.cs | 58 ++++---- 4 files changed, 117 insertions(+), 80 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index be128658eddaee..5ba751741cbc6c 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -1856,7 +1856,14 @@ public static Tensor SetSlice(this Tensor tensor, in ReadOnlyTensorSpan /// The ranges you want to set. public static ref readonly TensorSpan SetSlice(this in TensorSpan tensor, scoped in ReadOnlyTensorSpan values, params scoped ReadOnlySpan ranges) { - values.CopyTo(tensor.Slice(ranges)); + if (ranges.IsEmpty) + { + values.CopyTo(tensor); + } + else + { + values.CopyTo(tensor.Slice(ranges)); + } return ref tensor; } #endregion diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index 4ee76d5d2dc733..efdea92bdcb0aa 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -289,7 +289,7 @@ public static void Invoke(in ReadOnlyTensorSpan public static void ValidateCompatibility(in ReadOnlyTensorSpan x, in ReadOnlySpan lengths) { // x can be broadcast to destination, not vice verse - if (!TensorShape.AreCompatible(lengths, x._shape, true)) + if (!TensorShape.AreCompatible(lengths, x._shape)) ThrowHelper.ThrowArgument_LengthsNotCompatible(); } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index deea273c3e2734..706a0ebe4746ab 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -6,6 +6,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Microsoft.VisualBasic; namespace System.Numerics.Tensors { @@ -481,8 +483,6 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset // We should only be here if we were broadcast Debug.Assert((length == 1) && (stride == 0)); } - - Debug.Assert(GetLinearOffset(indexes[(destinationShape.Rank - Rank)..]) == linearOffset); return linearOffset; } @@ -513,39 +513,31 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset indexes[rankIndex] = 0; } } - - Debug.Assert(GetLinearOffset(indexes[(destinationShape.Rank - Rank)..]) == 0); return 0; } - // can shape2 turn into shape1 + // Answer the question: Can shape2 turn into shape1 or vice-versa if allowBidirectional? public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, bool allowBidirectional) { - scoped ReadOnlySpan lengths1 = shape1.Lengths; - scoped ReadOnlySpan lengths2 = shape2.Lengths; - int rankDelta = shape1.Rank - shape2.Rank; - if (rankDelta != 0) + if (rankDelta < 0) { - if (rankDelta < 0) + if (!allowBidirectional) { - if (!allowBidirectional) - { - return false; - } - - lengths1 = shape2.Lengths; - lengths2 = shape1.Lengths; - - rankDelta = -rankDelta; - Debug.Assert(rankDelta > 0); + return false; } - lengths1 = lengths1[rankDelta..]; + ref readonly TensorShape tmpShape = ref shape1; + shape1 = ref shape2; + shape2 = ref tmpShape; + + rankDelta = -rankDelta; + Debug.Assert(rankDelta > 0); } // We need both to be empty if either is empty + if (shape1.IsEmpty) { return shape2.IsEmpty; @@ -555,60 +547,98 @@ public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, b return false; } - // we need the lengths to be equal or one of them to be 1 + // We need the lengths to be equal, length2 to be 1, or + // length1 to be 1 and be doing a bidirectional check. + + ReadOnlySpan lengths1 = shape1.Lengths[rankDelta..]; + ReadOnlySpan lengths2 = shape2.Lengths; + for (int i = 0; i < lengths1.Length; i++) { nint length1 = lengths1[i]; - Debug.Assert(length1 != 0); - nint length2 = lengths2[i]; - Debug.Assert(length2 != 0); - if ((length1 != length2) && (length1 != 1) && (length2 != 1)) + if (length1 == length2) { - return false; + continue; + } + else if ((length1 == 1) && allowBidirectional) + { + continue; + } + else if (length2 == 1) + { + continue; } - } - return true; - } + return false; + } - // can shape1 turn into lengths - public static bool AreCompatible(in ReadOnlySpan lengths, in TensorShape shape1, bool allowBidirectional) - { - scoped ReadOnlySpan lengths1 = lengths; - scoped ReadOnlySpan lengths2 = shape1.Lengths; + if (!allowBidirectional) + { + // When we aren't bidirectionally compatible, then we + // need to ensure that if stride1 is 0, then stride2 + // is also zero; otherwise we cannot safely operate. - int rankDelta = lengths1.Length - shape1.Rank; + ReadOnlySpan strides1 = shape1.Strides[rankDelta..]; + ReadOnlySpan strides2 = shape2.Strides; - if (rankDelta != 0) - { - if (rankDelta < 0) + for (int i = 0; i < strides1.Length; i++) { - if (!allowBidirectional) + nint stride1 = strides1[i]; + nint stride2 = strides2[i]; + + if ((stride1 == 0) && (stride2 != 0)) { return false; } + } + } + return true; + } - lengths1 = shape1.Lengths; - lengths2 = lengths; + // Answer the question: Can shape2 turn into shape1Lengths + public static bool AreCompatible(in ReadOnlySpan shape1Lengths, in TensorShape shape2) + { + int rankDelta = shape1Lengths.Length - shape2.Rank; - rankDelta = -rankDelta; - Debug.Assert(rankDelta > 0); - } + if (rankDelta < 0) + { + return false; + } - lengths1 = lengths1[rankDelta..]; + // We need both to be empty if either is empty + + if (shape1Lengths.IsEmpty) + { + return shape2.IsEmpty; } + else if (shape2.IsEmpty) + { + return false; + } + + // We need the lengths to be equal, length2 to be 1, or + // length1 to be 1 and be doing a bidirectional check. + + ReadOnlySpan lengths1 = shape1Lengths[rankDelta..]; + ReadOnlySpan lengths2 = shape2.Lengths; - // if equal or one is 1 for (int i = 0; i < lengths1.Length; i++) { nint length1 = lengths1[i]; nint length2 = lengths2[i]; - if ((length1 != length2) && (length1 != 1) && (length2 != 1)) + + if (length1 == length2) { - return false; + continue; + } + else if (length2 == 1) + { + continue; } + + return false; } return true; diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 8df9f81a516ad4..3dbf1f499ac0f3 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -774,7 +774,7 @@ public static void TensorSpanSystemArrayConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => + Assert.Throws(() => { var spanInt = new TensorSpan(a, (int[])[], [2, 2], [1, 1]); }); @@ -814,9 +814,9 @@ public static void TensorSpanSystemArrayConstructorTests() public static void TensorSpanArrayConstructorTests() { // Make sure exception is thrown if lengths and strides would let you go past the end of the array - Assert.Throws(() => new TensorSpan(new double[0], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); - Assert.Throws(() => new TensorSpan(new double[1], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); - Assert.Throws(() => new TensorSpan(new double[2], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 2 })); + Assert.Throws(() => new TensorSpan(new double[0], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); + Assert.Throws(() => new TensorSpan(new double[1], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 1 })); + Assert.Throws(() => new TensorSpan(new double[2], lengths: new IntPtr[] { 2 }, strides: new IntPtr[] { 2 })); // Make sure basic T[] constructor works int[] a = { 91, 92, -93, 94 }; @@ -842,7 +842,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(1, spanInt.Strides[0]); // Make sure it still throws on index 0 Assert.Throws(() => { var spanInt = new TensorSpan(b); @@ -901,7 +901,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new TensorSpan(a, 3, [1, 2], default); }); @@ -972,7 +972,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { var spanInt = new TensorSpan(a, 0, [2, 2], [1, 1]); }); } @@ -994,12 +994,12 @@ public static void TensorSpanSpanConstructorTests() // Should be a Tensor with 0 elements but Rank 1 with dimension 0 length 0 Span b = []; spanInt = new TensorSpan(b); - Assert.Equal(1, spanInt.Rank); - Assert.Equal(0, spanInt.Lengths[0]); + Assert.Equal(0, spanInt.Rank); + Assert.Equal(0, spanInt.Lengths.Length); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides.Length); // Make sure it still throws on index 0 - Assert.Throws(() => { + Assert.Throws(() => { Span b = []; var spanInt = new TensorSpan(b); var x = spanInt[0]; @@ -1051,7 +1051,7 @@ public static void TensorSpanSpanConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new TensorSpan(a.Slice(3), [1, 2], default); }); @@ -1127,7 +1127,7 @@ public static void TensorSpanSpanConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { Span a = [91, 92, -93, 94]; var spanInt = new TensorSpan(a, [2, 2], [1, 1]); }); @@ -1156,12 +1156,12 @@ public static unsafe void TensorSpanPointerConstructorTests() fixed (int* p = b) { spanInt = new TensorSpan(p, 0); - Assert.Equal(1, spanInt.Rank); - Assert.Equal(0, spanInt.Lengths[0]); + Assert.Equal(0, spanInt.Rank); + Assert.Equal(0, spanInt.Lengths.Length); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides.Length); // Make sure it still throws on index 0 - Assert.Throws(() => + Assert.Throws(() => { Span b = []; fixed (int* p = b) @@ -1228,7 +1228,7 @@ public static unsafe void TensorSpanPointerConstructorTests() Assert.Equal(94, spanInt[0, 1]); // Make sure we catch that there aren't enough elements in the array for the lengths - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -1334,7 +1334,7 @@ public static unsafe void TensorSpanPointerConstructorTests() Assert.Equal(-93, spanInt[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => + Assert.Throws(() => { Span a = [91, 92, -93, 94]; fixed (int* p = a) @@ -1361,11 +1361,11 @@ public static void TensorSpanLargeDimensionsTests() Assert.Equal(1, spanInt.Lengths[4]); Assert.Equal(6, spanInt.Lengths[5]); Assert.Equal(6, spanInt.Strides.Length); - Assert.Equal(6, spanInt.Strides[0]); - Assert.Equal(6, spanInt.Strides[1]); - Assert.Equal(6, spanInt.Strides[2]); - Assert.Equal(6, spanInt.Strides[3]); - Assert.Equal(6, spanInt.Strides[4]); + Assert.Equal(0, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[1]); + Assert.Equal(0, spanInt.Strides[2]); + Assert.Equal(0, spanInt.Strides[3]); + Assert.Equal(0, spanInt.Strides[4]); Assert.Equal(1, spanInt.Strides[5]); Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); @@ -1387,11 +1387,11 @@ public static void TensorSpanLargeDimensionsTests() Assert.Equal(1, spanInt.Lengths[4]); Assert.Equal(3, spanInt.Lengths[5]); Assert.Equal(6, spanInt.Strides.Length); - Assert.Equal(12, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[0]); Assert.Equal(6, spanInt.Strides[1]); Assert.Equal(3, spanInt.Strides[2]); - Assert.Equal(3, spanInt.Strides[3]); - Assert.Equal(3, spanInt.Strides[4]); + Assert.Equal(0, spanInt.Strides[3]); + Assert.Equal(0, spanInt.Strides[4]); Assert.Equal(1, spanInt.Strides[5]); Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); @@ -1784,8 +1784,8 @@ public static void TensorSpanSliceTest() int[] results = new int[9]; TensorSpan spanInt = a.AsTensorSpan([3, 3]); - Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); - Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1)); + Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(1..2)); Assert.Throws(() => a.AsTensorSpan([2, 3]).Slice(0..1, 5..6)); var sp = spanInt.Slice(1..3, 1..3); From 2d5f2d8e3c4a70c15dbd1697dfd22100cd11ef70 Mon Sep 17 00:00:00 2001 From: Michael Sharp <51342856+michaelgsharp@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:15:10 -0600 Subject: [PATCH 23/30] Tensor finishing (#30) * stack working * more tensor tests working * fix factory create tests --- .../System/Numerics/Tensors/netcore/Tensor.cs | 54 ++++- .../tests/TensorTests.cs | 228 ++++++++++-------- 2 files changed, 181 insertions(+), 101 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 5ba751741cbc6c..67815090313059 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -237,15 +237,60 @@ public static ref readonly TensorSpan ConcatenateOnDimension(int dimension } Span dstSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)totalLength); - for (int i = 0; i < tensors.Length; i++) + if (dimension == 0 || dimension == -1) { - TensorOperation.Invoke, T, T>(tensors[i], dstSpan); - dstSpan = dstSpan.Slice((int)tensors[i].FlattenedLength); + for (int i = 0; i < tensors.Length; i++) + { + TensorOperation.Invoke, T, T>(tensors[i], dstSpan); + dstSpan = dstSpan.Slice((int)tensors[i].FlattenedLength); + } } + else + { + Span ranges = TensorOperation.RentedBuffer.CreateUninitialized(destination.Rank, out TensorOperation.RentedBuffer rentedBuffer); + for (int i = 0; i < dimension; i++) + { + ranges[i] = 0..1; + } + for (int i = dimension; i < destination.Rank; i++) + { + ranges[i] = ..; + } + bool hasMore = true; + while (hasMore) + { + for (int i = 0; i < tensors.Length; i++) + { + Tensor slice = tensors[i].Slice(ranges); + TensorOperation.Invoke, T, T>(slice, dstSpan); + dstSpan = dstSpan.Slice((int)slice.FlattenedLength); + } + hasMore = IncrementIndexes(ranges, dimension, destination.Lengths); + } + rentedBuffer.Dispose(); + } return ref destination; } + private static bool IncrementIndexes(Span ranges, int dimension, ReadOnlySpan lengths) + { + NRange curRange = ranges[dimension - 1]; + ranges[dimension - 1] = new NRange(curRange.Start.Value + 1, curRange.End.Value + 1); + + for (int i = dimension - 1; i >= 0; i--) + { + if (ranges[i].Start.Value >= lengths[i]) + { + ranges[i] = 0..1; + if (i == 0) + return false; + ranges[i - 1] = new NRange(ranges[i - 1].Start.Value + 1, ranges[i - 1].End.Value + 1); + } + } + return true; + } + private static nint CalculateCopyLength(ReadOnlySpan lengths, int startingAxis) { // When starting axis is -1 we want all the data at once same as if starting axis is 0 @@ -4804,9 +4849,8 @@ public static ref readonly TensorSpan Negate(scoped in ReadOnlyTensorSpan< public static T Norm(scoped in ReadOnlyTensorSpan x) where T : IRootFunctions { - // TODO: TANNER ADVICE T result = T.AdditiveIdentity; - TensorOperation.Invoke, T, T>(x, T.AdditiveIdentity, ref result); + TensorOperation.Invoke, T, T>(x, ref result); return T.Sqrt(result); } #endregion diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 16a2f1fa6af4f7..a2e0f203f2b6b6 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -13,7 +13,7 @@ public class TensorTests { #region TensorPrimitivesForwardsTests private void FillTensor(Span span) - where T : INumberBase + where T : INumberBase, IComparisonOperators { for (int i = 0; i < span.Length; i++) { @@ -35,57 +35,57 @@ private static nint CalculateTotalLength(ReadOnlySpan lengths) } public delegate Tensor PerformSpanInSpanOut(in ReadOnlyTensorSpan input); - public delegate void PerformCalculationSpanInSpanOut(ReadOnlySpan input, Span output); + public delegate T PerformCalculationSpanInSpanOut(T input); public static IEnumerable SpanInSpanOutData() { - yield return Create(TensorPrimitives.Abs, Tensor.Abs); - yield return Create(TensorPrimitives.Acos, Tensor.Acos); - yield return Create(TensorPrimitives.Acosh, Tensor.Acosh); - yield return Create(TensorPrimitives.AcosPi, Tensor.AcosPi); - yield return Create(TensorPrimitives.Asin, Tensor.Asin); - yield return Create(TensorPrimitives.Asinh, Tensor.Asinh); - yield return Create(TensorPrimitives.AsinPi, Tensor.AsinPi); - yield return Create(TensorPrimitives.Atan, Tensor.Atan); - yield return Create(TensorPrimitives.Atanh, Tensor.Atanh); - yield return Create(TensorPrimitives.AtanPi, Tensor.AtanPi); - yield return Create(TensorPrimitives.Cbrt, Tensor.Cbrt); - yield return Create(TensorPrimitives.Ceiling, Tensor.Ceiling); - yield return Create(TensorPrimitives.Cos, Tensor.Cos); - yield return Create(TensorPrimitives.Cosh, Tensor.Cosh); - yield return Create(TensorPrimitives.CosPi, Tensor.CosPi); - yield return Create(TensorPrimitives.DegreesToRadians, Tensor.DegreesToRadians); - yield return Create(TensorPrimitives.Exp, Tensor.Exp); - yield return Create(TensorPrimitives.Exp10, Tensor.Exp10); - yield return Create(TensorPrimitives.Exp10M1, Tensor.Exp10M1); - yield return Create(TensorPrimitives.Exp2, Tensor.Exp2); - yield return Create(TensorPrimitives.Exp2M1, Tensor.Exp2M1); - yield return Create(TensorPrimitives.ExpM1, Tensor.ExpM1); - yield return Create(TensorPrimitives.Floor, Tensor.Floor); - yield return Create(TensorPrimitives.LeadingZeroCount, Tensor.LeadingZeroCount); - yield return Create(TensorPrimitives.LeadingZeroCount, Tensor.LeadingZeroCount); - yield return Create(TensorPrimitives.Log, Tensor.Log); - yield return Create(TensorPrimitives.Log10, Tensor.Log10); - yield return Create(TensorPrimitives.Log10P1, Tensor.Log10P1); - yield return Create(TensorPrimitives.Log2, Tensor.Log2); - yield return Create(TensorPrimitives.Log2P1, Tensor.Log2P1); - yield return Create(TensorPrimitives.LogP1, Tensor.LogP1); - yield return Create(TensorPrimitives.Negate, Tensor.Negate); - yield return Create(TensorPrimitives.OnesComplement, Tensor.OnesComplement); - yield return Create(TensorPrimitives.PopCount, Tensor.PopCount); - yield return Create(TensorPrimitives.RadiansToDegrees, Tensor.RadiansToDegrees); - yield return Create(TensorPrimitives.Reciprocal, Tensor.Reciprocal); - yield return Create(TensorPrimitives.Round, Tensor.Round); - yield return Create(TensorPrimitives.Sigmoid, Tensor.Sigmoid); - yield return Create(TensorPrimitives.Sin, Tensor.Sin); - yield return Create(TensorPrimitives.Sinh, Tensor.Sinh); - yield return Create(TensorPrimitives.SinPi, Tensor.SinPi); - yield return Create(TensorPrimitives.SoftMax, Tensor.SoftMax); - yield return Create(TensorPrimitives.Sqrt, Tensor.Sqrt); - yield return Create(TensorPrimitives.Tan, Tensor.Tan); - yield return Create(TensorPrimitives.Tanh, Tensor.Tanh); - yield return Create(TensorPrimitives.TanPi, Tensor.TanPi); - yield return Create(TensorPrimitives.Truncate, Tensor.Truncate); + yield return Create(float.Abs, Tensor.Abs); + yield return Create(float.Acos, Tensor.Acos); + yield return Create(float.Acosh, Tensor.Acosh); + yield return Create(float.AcosPi, Tensor.AcosPi); + yield return Create(float.Asin, Tensor.Asin); + yield return Create(float.Asinh, Tensor.Asinh); + yield return Create(float.AsinPi, Tensor.AsinPi); + yield return Create(float.Atan, Tensor.Atan); + yield return Create(float.Atanh, Tensor.Atanh); + yield return Create(float.AtanPi, Tensor.AtanPi); + yield return Create(float.Cbrt, Tensor.Cbrt); + yield return Create(float.Ceiling, Tensor.Ceiling); + yield return Create(float.Cos, Tensor.Cos); + yield return Create(float.Cosh, Tensor.Cosh); + yield return Create(float.CosPi, Tensor.CosPi); + yield return Create(float.DegreesToRadians, Tensor.DegreesToRadians); + yield return Create(float.Exp, Tensor.Exp); + yield return Create(float.Exp10, Tensor.Exp10); + yield return Create(float.Exp10M1, Tensor.Exp10M1); + yield return Create(float.Exp2, Tensor.Exp2); + yield return Create(float.Exp2M1, Tensor.Exp2M1); + yield return Create(float.ExpM1, Tensor.ExpM1); + yield return Create(float.Floor, Tensor.Floor); + yield return Create(int.LeadingZeroCount, Tensor.LeadingZeroCount); + yield return Create(int.LeadingZeroCount, Tensor.LeadingZeroCount); + yield return Create(float.Log, Tensor.Log); + yield return Create(float.Log10, Tensor.Log10); + yield return Create(float.Log10P1, Tensor.Log10P1); + yield return Create(float.Log2, Tensor.Log2); + yield return Create(float.Log2P1, Tensor.Log2P1); + yield return Create(float.LogP1, Tensor.LogP1); + yield return Create(f => -f, Tensor.Negate); + yield return Create(f => ~f, Tensor.OnesComplement); + yield return Create(int.PopCount, Tensor.PopCount); + yield return Create(float.RadiansToDegrees, Tensor.RadiansToDegrees); + yield return Create( f => 1 / f, Tensor.Reciprocal); + yield return Create(float.Round, Tensor.Round); + //yield return Create(float.Sigmoid, Tensor.Sigmoid); + yield return Create(float.Sin, Tensor.Sin); + yield return Create(float.Sinh, Tensor.Sinh); + yield return Create(float.SinPi, Tensor.SinPi); + //yield return Create(float.SoftMax, Tensor.SoftMax); + yield return Create(float.Sqrt, Tensor.Sqrt); + yield return Create(float.Tan, Tensor.Tan); + yield return Create(float.Tanh, Tensor.Tanh); + yield return Create(float.TanPi, Tensor.TanPi); + yield return Create(float.Truncate, Tensor.Truncate); static object[] Create(PerformCalculationSpanInSpanOut tensorPrimitivesMethod, PerformSpanInSpanOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -93,7 +93,7 @@ static object[] Create(PerformCalculationSpanInSpanOut tensorPrimitivesMet [Theory, MemberData(nameof(SpanInSpanOutData))] public void TensorExtensionsSpanInSpanOut(PerformCalculationSpanInSpanOut tensorPrimitivesOperation, PerformSpanInSpanOut tensorOperation) - where T: INumberBase + where T: INumberBase, IComparisonOperators { Assert.All(Helpers.TensorShapes, tensorLength => { @@ -103,16 +103,15 @@ public void TensorExtensionsSpanInSpanOut(PerformCalculationSpanInSpanOut FillTensor(data); Tensor x = Tensor.Create(data, tensorLength, []); - tensorPrimitivesOperation((ReadOnlySpan)data, expectedOutput); + Tensor results = tensorOperation(x); Assert.Equal(tensorLength, results.Lengths); - nint[] startingIndex = new nint[tensorLength.Length]; - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref results[startingIndex], (int)length); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref results.GetPinnableReference(), (int)length); for (int i = 0; i < data.Length; i++) { - Assert.Equal(expectedOutput[i], span[i]); + Assert.Equal(tensorPrimitivesOperation(data[i]), span[i]); } }); } @@ -127,9 +126,33 @@ public static IEnumerable SpanInFloatOutData() yield return Create(TensorPrimitives.Min, Tensor.Min); yield return Create(TensorPrimitives.MinMagnitude, Tensor.MinMagnitude); yield return Create(TensorPrimitives.MinNumber, Tensor.MinNumber); - yield return Create(TensorPrimitives.Norm, Tensor.Norm); - yield return Create(TensorPrimitives.Product, Tensor.Product); - yield return Create(TensorPrimitives.Sum, Tensor.Sum); + yield return Create(x => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum += x[i] * x[i]; + } + return float.Sqrt(sum); + }, Tensor.Norm); + yield return Create(x => + { + float sum = 1; + for (int i = 0; i < x.Length; i++) + { + sum *= x[i]; + } + return sum; + }, Tensor.Product); + yield return Create(x => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum+= x[i]; + } + return sum; + }, Tensor.Sum); static object[] Create(PerformCalculationSpanInTOut tensorPrimitivesMethod, PerformSpanInTOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -137,7 +160,7 @@ static object[] Create(PerformCalculationSpanInTOut tensorPrimitivesMethod [Theory, MemberData(nameof(SpanInFloatOutData))] public void TensorExtensionsSpanInTOut(PerformCalculationSpanInTOut tensorPrimitivesOperation, PerformSpanInTOut tensorOperation) - where T : INumberBase + where T : INumberBase, IComparisonOperators { Assert.All(Helpers.TensorShapes, tensorLength => { @@ -174,7 +197,7 @@ static object[] Create(PerformCalculationTwoSpanInSpanOut tensorPrimitives [Theory, MemberData(nameof(TwoSpanInSpanOutData))] public void TensorExtensionsTwoSpanInSpanOut(PerformCalculationTwoSpanInSpanOut tensorPrimitivesOperation, PerformTwoSpanInSpanOut tensorOperation) - where T: INumberBase + where T: INumberBase, IComparisonOperators { Assert.All(Helpers.TensorShapes, tensorLength => { @@ -205,8 +228,24 @@ public void TensorExtensionsTwoSpanInSpanOut(PerformCalculationTwoSpanInSpanO public delegate T PerformCalculationTwoSpanInFloatOut(ReadOnlySpan input, ReadOnlySpan inputTwo); public static IEnumerable TwoSpanInFloatOutData() { - yield return Create(TensorPrimitives.Distance, Tensor.Distance); - yield return Create(TensorPrimitives.Dot, Tensor.Dot); + yield return Create((x, y) => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum += (x[i] - y[i]) * (x[i] - y[i]); + } + return float.Sqrt(sum); + }, Tensor.Distance); + yield return Create((x, y) => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum += x[i] * y[i]; + } + return sum; + }, Tensor.Dot); static object[] Create(PerformCalculationTwoSpanInFloatOut tensorPrimitivesMethod, PerformTwoSpanInFloatOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -214,7 +253,7 @@ static object[] Create(PerformCalculationTwoSpanInFloatOut tensorPrimitive [Theory, MemberData(nameof(TwoSpanInFloatOutData))] public void TensorExtensionsTwoSpanInFloatOut(PerformCalculationTwoSpanInFloatOut tensorPrimitivesOperation, PerformTwoSpanInFloatOut tensorOperation) - where T: INumberBase + where T: INumberBase, IComparisonOperators { Assert.All(Helpers.TensorShapes, tensorLength => { @@ -493,7 +532,7 @@ public static void TensorFactoryCreateTests() Assert.False(t1.IsPinned); // Make sure pinned works - t1 = Tensor.Create([1], true); + t1 = Tensor.Create([(nint)1], true); Assert.Equal(1, t1.Rank); Assert.Equal(1, t1.Lengths.Length); Assert.Equal(1, t1.Lengths[0]); @@ -555,12 +594,10 @@ public static void TensorFactoryCreateTests() // Make sure lengths can't be negative Assert.Throws(() => { - Span a = [91, 92, -93, 94]; - var t1 = Tensor.Create([-1, 2], false); + var t1 = Tensor.Create([-1, (nint)2], false); }); Assert.Throws(() => { - Span a = [91, 92, -93, 94]; - var t1 = Tensor.Create([1, -2], false); + var t1 = Tensor.Create([(nint)1, -2], false); }); // Make sure 2D array works with strides to hit element 0,0,2,2 @@ -574,7 +611,7 @@ public static void TensorFactoryCreateTests() Assert.Equal(-93, t1[1, 1]); // Make sure you can't overlap elements using strides - Assert.Throws(() => { + Assert.Throws(() => { var t1 = Tensor.Create((Span)[2, 2], [1, 1], false); }); } @@ -680,34 +717,35 @@ public static void TensorMultiplyTests() { Tensor t0 = Tensor.Create(Enumerable.Range(0, 3)); Tensor t1 = Tensor.Create(Enumerable.Range(0, 3), lengths: [3, 1]); - Tensor t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); - - Assert.Equal([3,3], t2.Lengths); - Assert.Equal(0, t2[0, 0]); - Assert.Equal(0, t2[0, 1]); - Assert.Equal(0, t2[0, 2]); - Assert.Equal(0, t2[1, 0]); - Assert.Equal(1, t2[1, 1]); - Assert.Equal(2, t2[1, 2]); - Assert.Equal(0, t2[2, 0]); - Assert.Equal(2, t2[2, 1]); - Assert.Equal(4, t2[2, 2]); - - t2 = Tensor.Multiply(t1.AsReadOnlyTensorSpan(), t0); - Assert.Equal([3, 3], t2.Lengths); - Assert.Equal(0, t2[0, 0]); - Assert.Equal(0, t2[0, 1]); - Assert.Equal(0, t2[0, 2]); - Assert.Equal(0, t2[1, 0]); - Assert.Equal(1, t2[1, 1]); - Assert.Equal(2, t2[1, 2]); - Assert.Equal(0, t2[2, 0]); - Assert.Equal(2, t2[2, 1]); - Assert.Equal(4, t2[2, 2]); + // TODO: This double broadcast will be added back in the future + //Tensor t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); + //Assert.Equal([3,3], t2.Lengths); + //Assert.Equal(0, t2[0, 0]); + //Assert.Equal(0, t2[0, 1]); + //Assert.Equal(0, t2[0, 2]); + //Assert.Equal(0, t2[1, 0]); + //Assert.Equal(1, t2[1, 1]); + //Assert.Equal(2, t2[1, 2]); + //Assert.Equal(0, t2[2, 0]); + //Assert.Equal(2, t2[2, 1]); + //Assert.Equal(4, t2[2, 2]); + + //t2 = Tensor.Multiply(t1.AsReadOnlyTensorSpan(), t0); + + //Assert.Equal([3, 3], t2.Lengths); + //Assert.Equal(0, t2[0, 0]); + //Assert.Equal(0, t2[0, 1]); + //Assert.Equal(0, t2[0, 2]); + //Assert.Equal(0, t2[1, 0]); + //Assert.Equal(1, t2[1, 1]); + //Assert.Equal(2, t2[1, 2]); + //Assert.Equal(0, t2[2, 0]); + //Assert.Equal(2, t2[2, 1]); + //Assert.Equal(4, t2[2, 2]); t1 = Tensor.Create(Enumerable.Range(0, 9), lengths: [3, 3]); - t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); + Tensor t2 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t1); Assert.Equal([3, 3], t2.Lengths); Assert.Equal(0, t2[0, 0]); @@ -1095,9 +1133,7 @@ public static void TensorStackTests() Assert.Equal(30, resultTensor[1, 1, 0]); Assert.Equal(40, resultTensor[1, 1, 1]); - resultTensor = Tensor.StackAlongDimension(0, [v1, v2]); - - Tensor resultTensor2 = Tensor.Create([2, 2, 2]); + Tensor resultTensor2 = Tensor.Create([(nint)2, 2, 2]); Tensor.StackAlongDimension([v1, v2], resultTensor2, 1); Assert.Equal(3, resultTensor2.Rank); From 6a7456ef5c3afd4926d7769f9ad3c173d0940bd7 Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Tue, 22 Apr 2025 16:40:32 -0600 Subject: [PATCH 24/30] only2 tests left --- .../Tensors/netcore/TensorOperation.cs | 4 +- .../Numerics/Tensors/netcore/TensorShape.cs | 4 + .../tests/TensorSpanTests.cs | 359 ++++++------------ 3 files changed, 120 insertions(+), 247 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index efdea92bdcb0aa..fe8b7f749bc702 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -312,9 +312,9 @@ public static void ValidateCompatibility(in ReadOnlyTensorSpan(indexes[(destinationShape.Rank - Rank)..]) == linearOffset); return linearOffset; } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 3dbf1f499ac0f3..b4616cd4f0d77d 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Runtime.InteropServices; using Xunit; @@ -34,61 +35,61 @@ private static nint CalculateTotalLength(ReadOnlySpan lengths) return totalLength; } - public delegate void TensorPrimitivesSpanInSpanOut(ReadOnlySpan input, Span output); + public delegate TOut TensorPrimitivesSpanInSpanOut(TIn input); public delegate ref readonly TensorSpan TensorSpanInSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination); public static IEnumerable SpanInSpanOutData() { - yield return Create(TensorPrimitives.Abs, Tensor.Abs); - yield return Create(TensorPrimitives.Acos, Tensor.Acos); - yield return Create(TensorPrimitives.Acosh, Tensor.Acosh); - yield return Create(TensorPrimitives.AcosPi, Tensor.AcosPi); - yield return Create(TensorPrimitives.Asin, Tensor.Asin); - yield return Create(TensorPrimitives.Asinh, Tensor.Asinh); - yield return Create(TensorPrimitives.AsinPi, Tensor.AsinPi); - yield return Create(TensorPrimitives.Atan, Tensor.Atan); - yield return Create(TensorPrimitives.Atanh, Tensor.Atanh); - yield return Create(TensorPrimitives.AtanPi, Tensor.AtanPi); - yield return Create(TensorPrimitives.Cbrt, Tensor.Cbrt); - yield return Create(TensorPrimitives.Ceiling, Tensor.Ceiling); - yield return Create(TensorPrimitives.Cos, Tensor.Cos); - yield return Create(TensorPrimitives.Cosh, Tensor.Cosh); - yield return Create(TensorPrimitives.CosPi, Tensor.CosPi); - yield return Create(TensorPrimitives.DegreesToRadians, Tensor.DegreesToRadians); - yield return Create(TensorPrimitives.Exp, Tensor.Exp); - yield return Create(TensorPrimitives.Exp10, Tensor.Exp10); - yield return Create(TensorPrimitives.Exp10M1, Tensor.Exp10M1); - yield return Create(TensorPrimitives.Exp2, Tensor.Exp2); - yield return Create(TensorPrimitives.Exp2M1, Tensor.Exp2M1); - yield return Create(TensorPrimitives.ExpM1, Tensor.ExpM1); - yield return Create(TensorPrimitives.Floor, Tensor.Floor); - yield return Create(TensorPrimitives.LeadingZeroCount, Tensor.LeadingZeroCount); - yield return Create(TensorPrimitives.Log, Tensor.Log); - yield return Create(TensorPrimitives.Log10, Tensor.Log10); - yield return Create(TensorPrimitives.Log10P1, Tensor.Log10P1); - yield return Create(TensorPrimitives.Log2, Tensor.Log2); - yield return Create(TensorPrimitives.Log2P1, Tensor.Log2P1); - yield return Create(TensorPrimitives.LogP1, Tensor.LogP1); - yield return Create(TensorPrimitives.Negate, Tensor.Negate); - yield return Create(TensorPrimitives.OnesComplement, Tensor.OnesComplement); - yield return Create(TensorPrimitives.PopCount, Tensor.PopCount); - yield return Create(TensorPrimitives.RadiansToDegrees, Tensor.RadiansToDegrees); - yield return Create(TensorPrimitives.Reciprocal, Tensor.Reciprocal); - yield return Create(TensorPrimitives.Round, Tensor.Round); - yield return Create(TensorPrimitives.Sigmoid, Tensor.Sigmoid); - yield return Create(TensorPrimitives.Sin, Tensor.Sin); - yield return Create(TensorPrimitives.Sinh, Tensor.Sinh); - yield return Create(TensorPrimitives.SinPi, Tensor.SinPi); - yield return Create(TensorPrimitives.SoftMax, Tensor.SoftMax); - yield return Create(TensorPrimitives.Sqrt, Tensor.Sqrt); - yield return Create(TensorPrimitives.Tan, Tensor.Tan); - yield return Create(TensorPrimitives.Tanh, Tensor.Tanh); - yield return Create(TensorPrimitives.TanPi, Tensor.TanPi); - yield return Create(TensorPrimitives.Truncate, Tensor.Truncate); - yield return Create(TensorPrimitives.ILogB, Tensor.ILogB); - yield return Create(TensorPrimitives.ConvertChecked, Tensor.ConvertChecked); - yield return Create(TensorPrimitives.ConvertSaturating, Tensor.ConvertSaturating); - yield return Create(TensorPrimitives.ConvertTruncating, Tensor.ConvertTruncating); + yield return Create(float.Abs, Tensor.Abs); + yield return Create(float.Acos, Tensor.Acos); + yield return Create(float.Acosh, Tensor.Acosh); + yield return Create(float.AcosPi, Tensor.AcosPi); + yield return Create(float.Asin, Tensor.Asin); + yield return Create(float.Asinh, Tensor.Asinh); + yield return Create(float.AsinPi, Tensor.AsinPi); + yield return Create(float.Atan, Tensor.Atan); + yield return Create(float.Atanh, Tensor.Atanh); + yield return Create(float.AtanPi, Tensor.AtanPi); + yield return Create(float.Cbrt, Tensor.Cbrt); + yield return Create(float.Ceiling, Tensor.Ceiling); + yield return Create(float.Cos, Tensor.Cos); + yield return Create(float.Cosh, Tensor.Cosh); + yield return Create(float.CosPi, Tensor.CosPi); + yield return Create(float.DegreesToRadians, Tensor.DegreesToRadians); + yield return Create(float.Exp, Tensor.Exp); + yield return Create(float.Exp10, Tensor.Exp10); + yield return Create(float.Exp10M1, Tensor.Exp10M1); + yield return Create(float.Exp2, Tensor.Exp2); + yield return Create(float.Exp2M1, Tensor.Exp2M1); + yield return Create(float.ExpM1, Tensor.ExpM1); + yield return Create(float.Floor, Tensor.Floor); + yield return Create(int.LeadingZeroCount, Tensor.LeadingZeroCount); + yield return Create(float.Log, Tensor.Log); + yield return Create(float.Log10, Tensor.Log10); + yield return Create(float.Log10P1, Tensor.Log10P1); + yield return Create(float.Log2, Tensor.Log2); + yield return Create(float.Log2P1, Tensor.Log2P1); + yield return Create(float.LogP1, Tensor.LogP1); + yield return Create(x => -x, Tensor.Negate); + yield return Create(x => ~x, Tensor.OnesComplement); + yield return Create(int.PopCount, Tensor.PopCount); + yield return Create(float.RadiansToDegrees, Tensor.RadiansToDegrees); + yield return Create(f => 1 / f, Tensor.Reciprocal); + yield return Create(float.Round, Tensor.Round); + //yield return Create(float.Sigmoid, Tensor.Sigmoid); + yield return Create(float.Sin, Tensor.Sin); + yield return Create(float.Sinh, Tensor.Sinh); + yield return Create(float.SinPi, Tensor.SinPi); + //yield return Create(float.SoftMax, Tensor.SoftMax); + yield return Create(float.Sqrt, Tensor.Sqrt); + yield return Create(float.Tan, Tensor.Tan); + yield return Create(float.Tanh, Tensor.Tanh); + yield return Create(float.TanPi, Tensor.TanPi); + yield return Create(float.Truncate, Tensor.Truncate); + yield return Create(float.ILogB, Tensor.ILogB); + yield return Create(x => Expression.Lambda>(Expression.ConvertChecked(Expression.Constant(x), typeof(int))).Compile()(), Tensor.ConvertChecked); + yield return Create(x => (int)x, Tensor.ConvertSaturating); + yield return Create(x => (int)MathF.Truncate(x), Tensor.ConvertTruncating); static object[] Create(TensorPrimitivesSpanInSpanOut tensorPrimitivesMethod, TensorSpanInSpanOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -105,26 +106,21 @@ public void TensorExtensionsSpanInSpanOut(TensorPrimitivesSpanInSpanO TIn[] data = new TIn[length]; TOut[] data2 = new TOut[length]; - TOut[] expectedOutput = new TOut[length]; FillTensor(data); TensorSpan x = Tensor.Create(data, tensorLength, []); TensorSpan destination = Tensor.Create(data2, tensorLength, []); - tensorPrimitivesOperation((ReadOnlySpan)data, expectedOutput); TensorSpan tensorResults = tensorOperation(x, destination); Assert.Equal(tensorLength, tensorResults.Lengths); nint[] startingIndex = new nint[tensorLength.Length]; // the "Return" value - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensorResults[startingIndex], (int)length); - // the "destination" value - ReadOnlySpan destSpan = MemoryMarshal.CreateSpan(ref destination[startingIndex], (int)length); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref tensorResults.GetPinnableReference(), (int)length); for (int i = 0; i < data.Length; i++) { - Assert.Equal(expectedOutput[i], span[i]); - Assert.Equal(expectedOutput[i], destSpan[i]); + Assert.Equal(tensorPrimitivesOperation(data[i]), span[i]); } // Now test if the source is sliced to be smaller then the destination that the destination is also sliced @@ -133,79 +129,23 @@ public void TensorExtensionsSpanInSpanOut(TensorPrimitivesSpanInSpanO nint sliceFlattenedLength = CalculateTotalLength(Helpers.TensorSliceShapes[index]); x = x.Slice(sliceLengths); TIn[] sliceData = new TIn[sliceFlattenedLength]; - x.FlattenTo(sliceData); - expectedOutput = new TOut[sliceFlattenedLength]; - - if (x.IsContiguousAndDense) - { - tensorPrimitivesOperation((ReadOnlySpan)sliceData, expectedOutput); - } - else - { - int rowLength = (int)Helpers.TensorSliceShapes[index][^1]; - for (int i = 0; i < sliceData.Length; i+= rowLength) - { - tensorPrimitivesOperation(((ReadOnlySpan)sliceData).Slice(i, rowLength), ((Span)expectedOutput).Slice(i, rowLength)); - } - - } - - tensorResults = tensorOperation(x, destination); - - // tensorResults lengths will still be the original tensorLength and not equal to the sliced length since that happened internally/automatically - Assert.Equal(tensorLength, tensorResults.Lengths); - - TensorSpan.Enumerator destEnum = destination.Slice(sliceLengths).GetEnumerator(); - TensorSpan.Enumerator tensorResultsEnum = tensorResults.Slice(sliceLengths).GetEnumerator(); - bool destEnumMove; - bool tensorResultsEnumMove; - - for (int i = 0; i < expectedOutput.Length; i++) - { - destEnumMove = destEnum.MoveNext(); - tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - - Assert.True(destEnumMove); - Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); - } // Now test if the source and destination are sliced (so neither is continuous) it works correctly. destination = destination.Slice(sliceLengths); x.FlattenTo(sliceData); - expectedOutput = new TOut[sliceFlattenedLength]; - - if (x.IsContiguousAndDense) - { - tensorPrimitivesOperation((ReadOnlySpan)sliceData, expectedOutput); - } - else - { - int rowLength = (int)Helpers.TensorSliceShapes[index][^1]; - for (int i = 0; i < sliceData.Length; i += rowLength) - { - tensorPrimitivesOperation(((ReadOnlySpan)sliceData).Slice(i, rowLength), ((Span)expectedOutput).Slice(i, rowLength)); - } - - } tensorResults = tensorOperation(x, destination); Assert.Equal(Helpers.TensorSliceShapes[index], tensorResults.Lengths); - destEnum = destination.GetEnumerator(); - tensorResultsEnum = tensorResults.GetEnumerator(); - - for (int i = 0; i < expectedOutput.Length; i++) + TensorSpan.Enumerator tensorResultsEnum = tensorResults.GetEnumerator(); + bool tensorResultsEnumMove; + for (int i = 0; i < sliceData.Length; i++) { - destEnumMove = destEnum.MoveNext(); tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - Assert.True(destEnumMove); Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); + Assert.Equal(tensorPrimitivesOperation(sliceData[i]), tensorResultsEnum.Current); } }); } @@ -220,9 +160,33 @@ public static IEnumerable SpanInFloatOutData() yield return Create(TensorPrimitives.Min, Tensor.Min); yield return Create(TensorPrimitives.MinMagnitude, Tensor.MinMagnitude); yield return Create(TensorPrimitives.MinNumber, Tensor.MinNumber); - yield return Create(TensorPrimitives.Norm, Tensor.Norm); - yield return Create(TensorPrimitives.Product, Tensor.Product); - yield return Create(TensorPrimitives.Sum, Tensor.Sum); + yield return Create(x => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum += x[i] * x[i]; + } + return float.Sqrt(sum); + }, Tensor.Norm); + yield return Create(x => + { + float sum = 1; + for (int i = 0; i < x.Length; i++) + { + sum *= x[i]; + } + return sum; + }, Tensor.Product); + yield return Create(x => + { + float sum = 0; + for (int i = 0; i < x.Length; i++) + { + sum += x[i]; + } + return sum; + }, Tensor.Sum); static object[] Create(TensorPrimitivesSpanInTOut tensorPrimitivesMethod, TensorSpanInTOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -268,21 +232,21 @@ public void TensorExtensionsSpanInTOut(TensorPrimitivesSpanInTOut tensorPr }); } - public delegate void TensorPrimitivesTwoSpanInSpanOut(ReadOnlySpan input, ReadOnlySpan inputTwo, Span output); + public delegate T TensorPrimitivesTwoSpanInSpanOut(T input, T inputTwo); public delegate ref readonly TensorSpan TensorTwoSpanInSpanOut(scoped in ReadOnlyTensorSpan input, scoped in ReadOnlyTensorSpan inputTwo, in TensorSpan destination); public delegate ref readonly TensorSpan TensorTwoSpanInSpanOutInPlace(in TensorSpan input, scoped in ReadOnlyTensorSpan inputTwo); public static IEnumerable TwoSpanInSpanOutData() { - yield return Create(TensorPrimitives.Add, Tensor.Add); - yield return Create(TensorPrimitives.Atan2, Tensor.Atan2); - yield return Create(TensorPrimitives.Atan2Pi, Tensor.Atan2Pi); - yield return Create(TensorPrimitives.CopySign, Tensor.CopySign); - yield return Create(TensorPrimitives.Divide, Tensor.Divide); - yield return Create(TensorPrimitives.Hypot, Tensor.Hypot); - yield return Create(TensorPrimitives.Ieee754Remainder, Tensor.Ieee754Remainder); - yield return Create(TensorPrimitives.Multiply, Tensor.Multiply); - yield return Create(TensorPrimitives.Pow, Tensor.Pow); - yield return Create(TensorPrimitives.Subtract, Tensor.Subtract); + yield return Create((x, y) => x + y, Tensor.Add); + yield return Create((x, y) => float.Atan2(x, y), Tensor.Atan2); + yield return Create((x, y) => float.Atan2Pi(x, y), Tensor.Atan2Pi); + yield return Create((x, y) => float.CopySign(x, y), Tensor.CopySign); + yield return Create((x, y) => x / y, Tensor.Divide); + yield return Create((x, y) => float.Hypot(x, y), Tensor.Hypot); + yield return Create((x, y) => float.Ieee754Remainder(x, y), Tensor.Ieee754Remainder); + yield return Create((x, y) => x * y, Tensor.Multiply); + yield return Create((x, y) => float.Pow(x, y), Tensor.Pow); + yield return Create((x, y) => x - y, Tensor.Subtract); static object[] Create(TensorPrimitivesTwoSpanInSpanOut tensorPrimitivesMethod, TensorTwoSpanInSpanOut tensorOperation) => new object[] { tensorPrimitivesMethod, tensorOperation }; @@ -308,162 +272,67 @@ public void TensorExtensionsTwoSpanInSpanOut(TensorPrimitivesTwoSpanInSpanOut TensorSpan x = Tensor.Create(data1, tensorLengths, []); TensorSpan y = Tensor.Create(data2, tensorLengths, []); TensorSpan destination = Tensor.Create(destData, tensorLengths, []); - tensorPrimitivesOperation((ReadOnlySpan)data1, data2, expectedOutput); TensorSpan results = tensorOperation(x, y, destination); Assert.Equal(tensorLengths, results.Lengths); nint[] startingIndex = new nint[tensorLengths.Length]; // the "Return" value - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref results[startingIndex], (int)length); - // the "destination" value - ReadOnlySpan destSpan = MemoryMarshal.CreateSpan(ref destination[startingIndex], (int)length); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref results.GetPinnableReference(), (int)length); for (int i = 0; i < data1.Length; i++) { - Assert.Equal(expectedOutput[i], span[i]); - Assert.Equal(expectedOutput[i], destSpan[i]); + Assert.Equal(tensorPrimitivesOperation(data1[i], data2[i]), span[i]); } - // Now test when both sources are exact sizes but destination is too large and gets sliced internally. - nint[] tempLengths = tensorLengths.Select(i => i + 1).ToArray(); - T[] tempDestData = new T[CalculateTotalLength(tempLengths)]; - destination = Tensor.Create(tempDestData, tempLengths, []); - results = tensorOperation(x, y, destination); - - // Since the slice was internal the result lengths will be the extra large size. - Assert.Equal(tempLengths, results.Lengths); - startingIndex = new nint[tensorLengths.Length]; - - TensorSpan.Enumerator destEnum = destination.Slice(tensorLengths).GetEnumerator(); - TensorSpan.Enumerator tensorResultsEnum = results.Slice(tensorLengths).GetEnumerator(); - bool destEnumMove; - bool tensorResultsEnumMove; - - for (int i = 0; i < expectedOutput.Length; i++) - { - destEnumMove = destEnum.MoveNext(); - tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - - Assert.True(destEnumMove); - Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); - } // Now test if the first source is sliced to be smaller than the second (but is broadcast compatible) that broadcasting happens). int rowLength = (int)Helpers.TensorSliceShapesForBroadcast[index][^1]; NRange[] sliceLengths = Helpers.TensorSliceShapesForBroadcast[index].Select(i => new NRange(0, i)).ToArray(); nint sliceFlattenedLength = CalculateTotalLength(Helpers.TensorSliceShapesForBroadcast[index]); - destination = destination.Slice(tensorLengths); + //destination = destination.Slice(sliceLengths); x.Slice(sliceLengths).BroadcastTo(x); x.FlattenTo(data1); - if (x.Slice(sliceLengths).IsContiguousAndDense && y.IsContiguousAndDense) - { - tensorPrimitivesOperation((ReadOnlySpan)data1, data2, expectedOutput); - } - else - { - for (int i = 0; i < data1.Length; i += rowLength) - { - tensorPrimitivesOperation(((ReadOnlySpan)data1).Slice(i, rowLength), ((ReadOnlySpan)data2).Slice(i, rowLength), ((Span)expectedOutput).Slice(i, rowLength)); - } - - } - results = tensorOperation(x.Slice(sliceLengths), y, destination); // results lengths will still be the original tensorLength Assert.Equal(tensorLengths, results.Lengths); - destEnum = destination.GetEnumerator(); - tensorResultsEnum = results.GetEnumerator(); + TensorSpan.Enumerator tensorResultsEnum = results.GetEnumerator(); + TensorSpan.Enumerator xEnum = x.GetEnumerator(); + TensorSpan.Enumerator yEnum = y.GetEnumerator(); + bool tensorResultsEnumMove; - for (int i = 0; i < expectedOutput.Length; i++) + for (int i = 0; i < results.FlattenedLength; i++) { - destEnumMove = destEnum.MoveNext(); tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - - Assert.True(destEnumMove); + xEnum.MoveNext(); + yEnum.MoveNext(); Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); + + Assert.Equal(tensorPrimitivesOperation(xEnum.Current, yEnum.Current), tensorResultsEnum.Current); } // Now test if the second source is sliced to be smaller than the first (but is broadcast compatible) that broadcasting happens). y.Slice(sliceLengths).BroadcastTo(y); y.FlattenTo(data2); - if (x.IsContiguousAndDense && y.Slice(sliceLengths).IsContiguousAndDense) - { - tensorPrimitivesOperation((ReadOnlySpan)data1, data2, expectedOutput); - } - else - { - for (int i = 0; i < data2.Length; i += rowLength) - { - tensorPrimitivesOperation(((ReadOnlySpan)data1).Slice(i, rowLength), ((ReadOnlySpan)data2).Slice(i, rowLength), ((Span)expectedOutput).Slice(i, rowLength)); - } - - } - results = tensorOperation(x, y.Slice(sliceLengths), destination); // results lengths will still be the original tensorLength Assert.Equal(tensorLengths, results.Lengths); - destEnum = destination.GetEnumerator(); tensorResultsEnum = results.GetEnumerator(); - - for (int i = 0; i < expectedOutput.Length; i++) - { - destEnumMove = destEnum.MoveNext(); - tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - - Assert.True(destEnumMove); - Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); - } - - // Now test if both sources are sliced to be smaller than the destination that the destination will be sliced automatically - T[] sliceData1 = new T[sliceFlattenedLength]; - T[] sliceData2 = new T[sliceFlattenedLength]; - expectedOutput = new T[sliceFlattenedLength]; - - x.Slice(sliceLengths).FlattenTo(sliceData1); - y.Slice(sliceLengths).FlattenTo(sliceData2); - - if (x.Slice(sliceLengths).IsContiguousAndDense && y.Slice(sliceLengths).IsContiguousAndDense) - { - tensorPrimitivesOperation((ReadOnlySpan)sliceData1, sliceData2, expectedOutput); - } - else + xEnum = x.GetEnumerator(); + yEnum = y.GetEnumerator(); + for (int i = 0; i < results.FlattenedLength; i++) { - for (int i = 0; i < sliceData1.Length; i += rowLength) - { - tensorPrimitivesOperation(((ReadOnlySpan)sliceData1).Slice(i, rowLength), ((ReadOnlySpan)sliceData2).Slice(i, rowLength), ((Span)expectedOutput).Slice(i, rowLength)); - } - - } - - results = tensorOperation(x.Slice(sliceLengths), y.Slice(sliceLengths), destination); - - Assert.Equal(tensorLengths, results.Lengths); - - destEnum = destination.Slice(sliceLengths).GetEnumerator(); - tensorResultsEnum = results.Slice(sliceLengths).GetEnumerator(); - - for (int i = 0; i < expectedOutput.Length; i++) - { - destEnumMove = destEnum.MoveNext(); tensorResultsEnumMove = tensorResultsEnum.MoveNext(); - - Assert.True(destEnumMove); + xEnum.MoveNext(); + yEnum.MoveNext(); Assert.True(tensorResultsEnumMove); - Assert.Equal(expectedOutput[i], destEnum.Current); - Assert.Equal(expectedOutput[i], tensorResultsEnum.Current); + Assert.Equal(tensorPrimitivesOperation(xEnum.Current, yEnum.Current), tensorResultsEnum.Current); } }); } From a6b462184a502334c19047b2d08c0c735c79d55d Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 22 Apr 2025 20:04:52 -0700 Subject: [PATCH 25/30] Update src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs --- .../src/System/Numerics/Tensors/netcore/TensorShape.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 0eb676e30271b8..706a0ebe4746ab 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -483,10 +483,6 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset // We should only be here if we were broadcast Debug.Assert((length == 1) && (stride == 0)); } - - // this needs to be linearOffset - src._start, but src is not being passed in currently. Commenting out for now. - // It also causes problems with broadcasting. - //Debug.Assert(GetLinearOffset(indexes[(destinationShape.Rank - Rank)..]) == linearOffset); return linearOffset; } From 07d7653b6fa29f2f102a84d4e5e3395e2896d4f1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 22 Apr 2025 20:21:08 -0700 Subject: [PATCH 26/30] Update compatibility suppressions --- .../src/CompatibilitySuppressions.xml | 569 +++++++++++++++++- 1 file changed, 561 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml index 335f33bb28ad0e..9fda67ddf4a768 100644 --- a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml @@ -127,6 +127,300 @@ lib/netstandard2.0/System.Numerics.Tensors.dll true + + CP0002 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.#ctor(`0[],System.Index,System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.#ctor(System.Array,System.ReadOnlySpan{System.Buffers.NIndex},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.CosineSimilarity``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.TensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.CosineSimilarity``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.Create``1(``0[],System.ReadOnlySpan{System.IntPtr},System.Boolean) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.Create``1(``0[],System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr},System.Boolean) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.GetSmallestBroadcastableLengths(System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMax``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMaxMagnitude``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMin``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMinMagnitude``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor`1.get_Item(System.Numerics.Tensors.Tensor{System.Boolean}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor`1.GetEnumerator + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.TensorSpan`1.#ctor(`0[],System.Index,System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.TensorSpan`1.#ctor(System.Array,System.ReadOnlySpan{System.Buffers.NIndex},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.#ctor(`0[],System.Index,System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.#ctor(System.Array,System.ReadOnlySpan{System.Buffers.NIndex},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.CosineSimilarity``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.TensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.CosineSimilarity``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.Create``1(``0[],System.ReadOnlySpan{System.IntPtr},System.Boolean) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.Create``1(``0[],System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr},System.Boolean) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.GetSmallestBroadcastableLengths(System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMax``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMaxMagnitude``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMin``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.IndexOfMinMagnitude``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor`1.get_Item(System.Numerics.Tensors.Tensor{System.Boolean}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor`1.GetEnumerator + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.TensorSpan`1.#ctor(`0[],System.Index,System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.TensorSpan`1.#ctor(System.Array,System.ReadOnlySpan{System.Buffers.NIndex},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor.Create``1(System.Collections.Generic.IEnumerable{``0},System.ReadOnlySpan{System.IntPtr},System.Boolean)$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor.Create``1(System.Collections.Generic.IEnumerable{``0},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr},System.Boolean)$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + CP0017 M:System.Numerics.Tensors.Tensor.GreaterThanOrEqualAll``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,``0)$0 @@ -190,6 +484,181 @@ lib/net8.0/System.Numerics.Tensors.dll true + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.op_Implicit(System.Numerics.Tensors.Tensor{`0})~System.Numerics.Tensors.ReadOnlyTensorSpan{`0}$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.op_Implicit(System.Numerics.Tensors.Tensor{`0})~System.Numerics.Tensors.TensorSpan{`0}$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.IntPtr})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.TensorSpan`1.op_Implicit(System.Numerics.Tensors.TensorSpan{`0})~System.Numerics.Tensors.ReadOnlyTensorSpan{`0}$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.TensorSpan`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.IReadOnlyTensor`2.Slice(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ITensor`2.AsTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.ReadOnlyTensorSpan`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor.Create``1(System.Collections.Generic.IEnumerable{``0},System.ReadOnlySpan{System.IntPtr},System.Boolean)$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor.Create``1(System.Collections.Generic.IEnumerable{``0},System.ReadOnlySpan{System.IntPtr},System.ReadOnlySpan{System.IntPtr},System.Boolean)$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + CP0017 M:System.Numerics.Tensors.Tensor.GreaterThanOrEqualAll``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,``0)$0 @@ -253,6 +722,97 @@ lib/net9.0/System.Numerics.Tensors.dll true + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsReadOnlyTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.AsTensorSpan(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.op_Implicit(System.Numerics.Tensors.Tensor{`0})~System.Numerics.Tensors.ReadOnlyTensorSpan{`0}$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.op_Implicit(System.Numerics.Tensors.Tensor{`0})~System.Numerics.Tensors.TensorSpan{`0}$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.Buffers.NRange})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.Tensor`1.Slice(System.ReadOnlySpan{System.IntPtr})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.TensorSpan`1.op_Implicit(System.Numerics.Tensors.TensorSpan{`0})~System.Numerics.Tensors.ReadOnlyTensorSpan{`0}$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0017 + M:System.Numerics.Tensors.TensorSpan`1.Slice(System.ReadOnlySpan{System.Buffers.NIndex})$0 + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + CP0021 M:System.Numerics.Tensors.Tensor.Average``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@)``0:T:System.Numerics.INumberBase{``0} @@ -281,11 +841,4 @@ lib/net9.0/System.Numerics.Tensors.dll true - - CP0021 - M:System.Numerics.Tensors.Tensor.StdDev``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@)``0:T:System.Numerics.IRootFunctions{``0} - lib/net9.0/System.Numerics.Tensors.dll - lib/net9.0/System.Numerics.Tensors.dll - true - - + \ No newline at end of file From 5dcebe313c790fcba2eada3b701509a69c5e7c0d Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Wed, 23 Apr 2025 01:42:57 -0600 Subject: [PATCH 27/30] transpose working --- .../src/System/Numerics/Tensors/netcore/TensorShape.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 706a0ebe4746ab..af5b6756bade89 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -208,11 +209,16 @@ ref Unsafe.As(ref metadata[rank * 2]), // aren't actually stored in memory. nint minimumNonZeroStride = 0; + var sortedWithIndex = destinationLinearRankOrder.ToArray() + .Select((value, index) => new { Value = value, Index = index }) + .OrderBy(x => x.Value) + .ToArray(); + int[] sortedOrder = sortedWithIndex.Select(x => x.Index).ToArray(); for (int i = 0; i < destinationLinearRankOrder.Length; i++) { int rankIndex = destinationLinearRankOrder.Length - (i + 1); - int linearRankIndex = destinationLinearRankOrder[rankIndex]; + int linearRankIndex = sortedOrder[rankIndex]; nint length = lengths[linearRankIndex]; From 1eb7021f20ed16113520f72a1fbce71b78039d87 Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Wed, 23 Apr 2025 02:58:33 -0600 Subject: [PATCH 28/30] reverse working --- .../System/Numerics/Tensors/netcore/Tensor.cs | 65 +++++-------------- .../Tensors/netcore/TensorOperation.cs | 28 ++++++++ .../Numerics/Tensors/netcore/TensorShape.cs | 65 +++++++++++++++++++ 3 files changed, 109 insertions(+), 49 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 67815090313059..aaf9612ae0a0b3 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -1788,66 +1788,33 @@ public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan /// dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor. public static ref readonly TensorSpan ReverseDimension(scoped in ReadOnlyTensorSpan tensor, in TensorSpan destination, int dimension) { + // When the dimension is -1, its just a straight reverse copy. if (dimension == -1) { - Debug.Assert(tensor._shape.LinearLength == destination._shape.LinearLength); - nint index = tensor._shape.LinearLength - 1; - Span inputSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape.LinearLength); - Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape.LinearLength); - for (int i = 0; i <= tensor._shape.LinearLength / 2; i++) - { - outputSpan[i] = inputSpan[(int)index]; - outputSpan[(int)index--] = inputSpan[i]; - } + TensorOperation.ValidateCompatibility(tensor, destination); + TensorOperation.ReverseInvoke, T, T>(tensor, destination); } + // With any other dimension, we need to copy the data in reverse order based on the provided dimension. else { - nint copyLength = 1; - for (nint i = dimension; i < tensor.Lengths.Length; i++) - { - copyLength *= tensor.Lengths[(int)i]; - } - copyLength /= tensor.Lengths[(int)dimension]; - - scoped Span oIndices; - nint[]? oIndicesArray; - scoped Span iIndices; - nint[]? iIndicesArray; - if (tensor.Rank > 6) - { - oIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - oIndices = oIndicesArray.AsSpan(0, tensor.Rank); - oIndices.Clear(); - - iIndicesArray = ArrayPool.Shared.Rent(tensor.Rank); - iIndices = iIndicesArray.AsSpan(0, tensor.Rank); - iIndices.Clear(); - } - else - { - oIndicesArray = null; - oIndices = stackalloc nint[tensor.Rank]; - iIndicesArray = null; - iIndices = stackalloc nint[tensor.Rank]; - } - - iIndices[(int)dimension] = tensor.Lengths[(int)dimension] - 1; - nint copiedValues = 0; - ReadOnlyTensorSpan islice = tensor.Slice(tensor.Lengths); + TensorOperation.ValidateCompatibility(tensor, destination); + Span srcIndexes = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer srcIndexesRentedBuffer); + Span dstIndexes = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer dstIndexesRentedBuffer); - while (copiedValues < tensor.FlattenedLength) + for (int i = 0; i < srcIndexes.Length; i++) { - // TensorSpanHelpers.Memmove(ref Unsafe.Add(ref destination._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, tensor.Strides, tensor.Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); - // TensorSpanHelpers.AdjustIndexes((int)dimension, 1, oIndices, tensor.Lengths); - // TensorSpanHelpers.AdjustIndexesDown((int)dimension, 1, iIndices, tensor.Lengths); - copiedValues += copyLength; + srcIndexes[i] = NRange.All; + dstIndexes[i] = NRange.All; } - if (oIndicesArray != null && iIndicesArray != null) + for (int i = (int)tensor.Lengths[dimension]; i > 0; i--) { - ArrayPool.Shared.Return(oIndicesArray); - ArrayPool.Shared.Return(iIndicesArray); + srcIndexes[dimension] = new NRange(i - 1, i); + dstIndexes[dimension] = new NRange(tensor.Lengths[dimension] - i, tensor.Lengths[dimension] - i + 1); + TensorOperation.Invoke, T, T>(tensor.Slice(srcIndexes), destination.Slice(dstIndexes)); } + srcIndexesRentedBuffer.Dispose(); + dstIndexesRentedBuffer.Dispose(); } return ref destination; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs index fe8b7f749bc702..d372602f2fa429 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorOperation.cs @@ -124,6 +124,34 @@ ref Unsafe.Add(ref destination._reference, destinationLinearOffset) destinationRentedBuffer.Dispose(); } + public static void ReverseInvoke(in ReadOnlyTensorSpan x, in TensorSpan destination) + where TOperation : TensorOperation.IUnaryOperation_Tensor + { + scoped Span xIndexes = RentedBuffer.Create(destination.Rank, x.Strides, out nint xLinearOffset, out RentedBuffer xRentedBuffer); + scoped Span destinationIndexes = RentedBuffer.Create(destination.Rank, destination.Strides, out nint _, out RentedBuffer destinationRentedBuffer); + + destinationIndexes[0] = destination.Lengths[0]; + for (int i = 1; i < destinationIndexes.Length; i++) + { + destinationIndexes[i] = destination.Lengths[i] - 1; + } + nint destinationLinearOffset = destination._shape.LinearLength; + + for (nint i = 0; i < destination.FlattenedLength; i++) + { + xLinearOffset = x._shape.AdjustToNextIndex(destination._shape, xLinearOffset, xIndexes); + destinationLinearOffset = destination._shape.AdjustToPreviousIndex(destination._shape, destinationLinearOffset, destinationIndexes); + + TOperation.Invoke( + in Unsafe.Add(ref x._reference, xLinearOffset), + ref Unsafe.Add(ref destination._reference, destinationLinearOffset) + ); + } + + xRentedBuffer.Dispose(); + destinationRentedBuffer.Dispose(); + } + // For copyto/flattento public static void Invoke(in ReadOnlyTensorSpan x, in Span destination) where TOperation : TensorOperation.IUnaryOperation_Tensor diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index af5b6756bade89..9cf04855e93d5d 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -522,6 +522,71 @@ public nint AdjustToNextIndex(in TensorShape destinationShape, nint linearOffset return 0; } + public nint AdjustToPreviousIndex(in TensorShape destinationShape, nint linearOffset, Span indexes) + { + Debug.Assert(indexes.Length >= Rank); + Debug.Assert(indexes.Length == destinationShape.Rank); + + ReadOnlySpan lengths = Lengths; + ReadOnlySpan strides = Strides; + + ReadOnlySpan destinationLengths = destinationShape.Lengths; + + for (int i = 0; i < strides.Length; i++) + { + int rankIndex = lengths.Length - (i + 1); + int destinationRankIndex = destinationShape.Lengths.Length - (i + 1); + + nint length = lengths[rankIndex]; + nint stride = strides[rankIndex]; + + nint index = --indexes[destinationRankIndex]; + linearOffset -= stride; + + // We can have a scenario such as lengths: [1], destinationLengths: [2] in + // which case we still need to keep incrementing the index but without + // adjusting the linearOffset + + if (index >= 0)//destinationLengths[destinationRankIndex]) + { + if (index >= length) + { + // We should only be here if we were broadcast + Debug.Assert((length == 1) && (stride == 0)); + } + return linearOffset; + } + + indexes[destinationRankIndex] = lengths[rankIndex]; + linearOffset += (stride * length); + } + + if (indexes.Length != Rank) + { + for (int i = destinationLengths.Length - 1; i >= strides.Length; i--) + { + int rankIndex = destinationLengths.Length - (i + 1); + + nint length = destinationLengths[rankIndex]; + // Strides are always 0 because we are broadcasting at this point in the loop. + + nint index = ++indexes[rankIndex]; + + if (index < length) + { + // For any indexes that exist in the destinationShape but not + // in the srcShape we will only increment them if all lower + // indexes were 0. This means we're starting over at the beginning + // of the srcShape and the linearOffset must be 0. + break; + } + + indexes[rankIndex] = 0; + } + } + return 0; + } + // Answer the question: Can shape2 turn into shape1 or vice-versa if allowBidirectional? public static bool AreCompatible(in TensorShape shape1, in TensorShape shape2, bool allowBidirectional) { From 4fa8d348dd38fd104eef26515d89dbcf6ad88bd2 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 23 Apr 2025 07:33:09 -0700 Subject: [PATCH 29/30] Revert the unnecessary sln changes --- .../System.Numerics.Tensors.sln | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln b/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln index 2549fa59ae1328..cb5cf60398d544 100644 --- a/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln +++ b/src/libraries/System.Numerics.Tensors/System.Numerics.Tensors.sln @@ -1,8 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.14.35906.104 -MinimumVisualStudioVersion = 10.0.40219.1 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{9F20CEA1-2216-4432-BBBD-F01E05D17F23}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Numerics", "..\Microsoft.Bcl.Numerics\src\Microsoft.Bcl.Numerics.csproj", "{1578185F-C4FA-4866-936B-E62AAEDD03B7}" @@ -37,11 +33,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{7AC4B2C7-A55 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{841A2FA4-A95F-4612-A8B9-AD2EF769BC71}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{A21C99E7-E22B-470E-BF48-56B00AFE3D34}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{A21C99E7-E22B-470E-BF48-56B00AFE3D34}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{25B37C75-C737-4AE8-9260-74A79870C8B8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{25B37C75-C737-4AE8-9260-74A79870C8B8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9482D7C5-F37C-40FC-B057-A16C1ED1C121}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{9482D7C5-F37C-40FC-B057-A16C1ED1C121}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B}" EndProject @@ -109,27 +105,23 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {9F20CEA1-2216-4432-BBBD-F01E05D17F23} = {DE94CA7D-BB10-4865-85A6-6B694631247F} - {1578185F-C4FA-4866-936B-E62AAEDD03B7} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} - {21CB448A-3882-4337-B416-D1A3E0BCFFC5} = {7AC4B2C7-A55C-4C4F-9B02-77F5CBFFF4AB} - {848DD000-3D22-4A25-A9D9-05AFF857A116} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} {46AD9423-D8C3-44BB-A201-1CCCAB4C6DAF} = {DE94CA7D-BB10-4865-85A6-6B694631247F} {4AF6A02D-82C8-4898-9EDF-01F107C25061} = {DE94CA7D-BB10-4865-85A6-6B694631247F} + {1578185F-C4FA-4866-936B-E62AAEDD03B7} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} + {848DD000-3D22-4A25-A9D9-05AFF857A116} = {DF0561A1-3AB8-4B51-AFB4-392EE1DD6247} + {21CB448A-3882-4337-B416-D1A3E0BCFFC5} = {7AC4B2C7-A55C-4C4F-9B02-77F5CBFFF4AB} {D9283CC0-07E1-417A-B73C-223F3EB7A277} = {841A2FA4-A95F-4612-A8B9-AD2EF769BC71} {DB954E01-898A-4FE2-A3AA-180D041AB08F} = {841A2FA4-A95F-4612-A8B9-AD2EF769BC71} {04FC0651-B9D0-448A-A28B-11B1D4A897F4} = {A21C99E7-E22B-470E-BF48-56B00AFE3D34} {683A7D28-CC55-4375-848D-E659075ECEE4} = {A21C99E7-E22B-470E-BF48-56B00AFE3D34} + {A21C99E7-E22B-470E-BF48-56B00AFE3D34} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} {1CBEAEA8-2CA1-4B07-9930-35A785205852} = {25B37C75-C737-4AE8-9260-74A79870C8B8} {BA7828B1-7953-47A0-AE5A-E22B501C4BD0} = {25B37C75-C737-4AE8-9260-74A79870C8B8} - {57E57290-3A6A-43F8-8764-D4DC8151F89C} = {9482D7C5-F37C-40FC-B057-A16C1ED1C121} - {A21C99E7-E22B-470E-BF48-56B00AFE3D34} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} {25B37C75-C737-4AE8-9260-74A79870C8B8} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} + {57E57290-3A6A-43F8-8764-D4DC8151F89C} = {9482D7C5-F37C-40FC-B057-A16C1ED1C121} {9482D7C5-F37C-40FC-B057-A16C1ED1C121} = {F9C2AAB1-C7B0-4E43-BB18-4FB16F6E272B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {10A5F2C3-5230-4916-9D4D-BBDB94851037} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{683a7d28-cc55-4375-848d-e659075ecee4}*SharedItemsImports = 5 - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{ba7828b1-7953-47a0-ae5a-e22b501c4bd0}*SharedItemsImports = 5 - EndGlobalSection EndGlobal From 5a110c424ddab08c82ff39442db4047c61a73666 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 23 Apr 2025 07:37:35 -0700 Subject: [PATCH 30/30] Remove an unnecessary using --- .../ref/System.Numerics.Tensors.netcore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index ceaedd2ce8d8fc..241a65c5da5067 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -4,8 +4,6 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ -using System.Collections; - namespace System.Buffers { [System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5001", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]