// 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;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Numerics.Tensors
{
/// <summary>Provides methods for tensor operations.</summary>
public static partial class Tensor
{
#region AsTensorSpan
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[])" />
public static ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan<T>(this T[]? array)
=> new ReadOnlyTensorSpan<T>(array);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], ReadOnlySpan{nint})" />
public static ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan<T>(this T[]? array, scoped ReadOnlySpan<nint> lengths)
=> new ReadOnlyTensorSpan<T>(array, lengths);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], ReadOnlySpan{nint}, ReadOnlySpan{nint})" />
public static ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan<T>(this T[]? array, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
=> new ReadOnlyTensorSpan<T>(array, lengths, strides);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], int, ReadOnlySpan{nint}, ReadOnlySpan{nint})" />
public static ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan<T>(this T[]? array, int start, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
=> new ReadOnlyTensorSpan<T>(array, start, lengths, strides);
/// <inheritdoc cref="TensorSpan{T}.TensorSpan(T[])" />
public static TensorSpan<T> AsTensorSpan<T>(this T[]? array)
=> new TensorSpan<T>(array);
/// <inheritdoc cref="TensorSpan{T}.TensorSpan(T[], ReadOnlySpan{nint})" />
public static TensorSpan<T> AsTensorSpan<T>(this T[]? array, scoped ReadOnlySpan<nint> lengths)
=> new TensorSpan<T>(array, lengths);
/// <inheritdoc cref="TensorSpan{T}.TensorSpan(T[], ReadOnlySpan{nint} , ReadOnlySpan{nint})" />
public static TensorSpan<T> AsTensorSpan<T>(this T[]? array, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
=> new TensorSpan<T>(array, lengths, strides);
/// <inheritdoc cref="TensorSpan{T}.TensorSpan(T[], int, ReadOnlySpan{nint}, ReadOnlySpan{nint})" />
public static TensorSpan<T> AsTensorSpan<T>(this T[]? array, int start, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides) => new TensorSpan<T>(array, start, lengths, strides);
#endregion
#region Broadcast
/// <summary>
/// Broadcast the data from <paramref name="source"/> to the smallest broadcastable shape compatible with <paramref name="lengthsSource"/>. Creates a new <see cref="Tensor{T}"/> and allocates new memory.
/// </summary>
/// <param name="source">Input <see cref="Tensor{T}"/>.</param>
/// <param name="lengthsSource">Other <see cref="Tensor{T}"/> to make shapes broadcastable.</param>
/// <remarks>Rank-zero empty spans are treated as empty vectors with shape <c>[0]</c> when checking broadcast compatibility. Explicitly specified dimension lengths are preserved.</remarks>
public static Tensor<T> Broadcast<T>(scoped in ReadOnlyTensorSpan<T> source, scoped in ReadOnlyTensorSpan<T> lengthsSource)
{
return Broadcast(source, lengthsSource.Lengths);
}
/// <summary>
/// Broadcast the data from <paramref name="source"/> to the new shape <paramref name="lengths"/>. Creates a new <see cref="Tensor{T}"/> and allocates new memory.
/// If the shape of the <paramref name="source"/> is not compatible with the new shape, an exception is thrown.
/// </summary>
/// <param name="source">Input <see cref="Tensor{T}"/>.</param>
/// <param name="lengths"><see cref="ReadOnlySpan{T}"/> of the desired new shape.</param>
/// <remarks>A rank-zero empty source or an empty <paramref name="lengths"/> span is treated as the shape <c>[0]</c> when checking broadcast compatibility. Explicitly specified dimension lengths are preserved.</remarks>
/// <exception cref="ArgumentException">The shapes are not broadcast compatible.</exception>
public static Tensor<T> Broadcast<T>(scoped in ReadOnlyTensorSpan<T> source, scoped ReadOnlySpan<nint> lengths)
{
TensorOperation.ValidateCompatibility(source, lengths);
Tensor<T> destination = CreateFromShapeUninitialized<T>(lengths);
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(source, destination);
return destination;
}
#endregion
#region BroadcastTo
/// <summary>
/// Broadcast the data from <paramref name="source"/> to <paramref name="destination"/>.
/// </summary>
/// <param name="source">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static void BroadcastTo<T>(this Tensor<T> source, in TensorSpan<T> destination)
{
BroadcastTo(source.AsReadOnlyTensorSpan(), destination);
}
/// <summary>
/// Broadcast the data from <paramref name="source"/> to <paramref name="destination"/>.
/// </summary>
/// <param name="source">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination">Other <see cref="TensorSpan{T}"/> to make shapes broadcastable.</param>
public static void BroadcastTo<T>(in this TensorSpan<T> source, in TensorSpan<T> destination)
{
BroadcastTo(source.AsReadOnlyTensorSpan(), destination);
}
/// <summary>
/// Broadcast the data from <paramref name="source"/> to <paramref name="destination"/>.
/// </summary>
/// <param name="source">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static void BroadcastTo<T>(in this ReadOnlyTensorSpan<T> source, in TensorSpan<T> destination)
{
TensorOperation.ValidateCompatibility(source, destination);
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(source, destination);
}
#endregion
#region Concatenate
/// <summary>
/// Join a sequence of tensors along an existing axis.
/// </summary>
/// <param name="tensors">The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default).</param>
/// <remarks>Leading singleton padding is ignored when aligning inputs to the first tensor's shape. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static Tensor<T> Concatenate<T>(params scoped ReadOnlySpan<Tensor<T>> tensors)
{
return ConcatenateOnDimension(0, tensors);
}
/// <summary>
/// Join a sequence of tensors along an existing axis.
/// </summary>
/// <param name="tensors">The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default).</param>
/// <param name="dimension">The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0.</param>
/// <remarks>The dimension refers to the first tensor's effective shape. Other inputs are aligned by adding or removing leading singleton dimensions. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static Tensor<T> ConcatenateOnDimension<T>(int dimension, params scoped ReadOnlySpan<Tensor<T>> tensors)
{
if (tensors.Length < 2)
{
ThrowHelper.ThrowArgument_ConcatenateTooFewTensors();
}
ref readonly TensorShape shape = ref TensorShape.Normalize(tensors[0]._shape);
int rank = shape.Rank;
if (dimension < -1 || dimension >= rank)
{
ThrowHelper.ThrowArgument_InvalidDimension();
}
Tensor<T> tensor;
// If axis != -1, make sure all dimensions except the one to concatenate on match.
if (dimension != -1)
{
nint sumOfAxis = GetConcatenatedLength(dimension, tensors, shape);
tensor = CreateConcatenationResult<T>(shape.Lengths, dimension, sumOfAxis);
}
else
{
// Calculate total space needed.
nint totalLength = 0;
for (int i = 0; i < tensors.Length; i++)
{
checked
{
totalLength += tensors[i].FlattenedLength;
}
}
tensor = CreateFromShape<T>([totalLength]);
}
CopyConcatenatedValues(dimension, tensors, tensor, rank);
return tensor;
}
/// <summary>
/// Join a sequence of tensors along an existing axis.
/// </summary>
/// <param name="tensors">The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default).</param>
/// <param name="destination"></param>
/// <remarks>Inputs and the destination are aligned to the first tensor's effective shape by adding or removing leading singleton dimensions. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static ref readonly TensorSpan<T> Concatenate<T>(scoped ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination)
{
return ref ConcatenateOnDimension(0, tensors, destination);
}
/// <summary>
/// Join a sequence of tensors along an existing axis.
/// </summary>
/// <param name="tensors">The tensors must have the same shape, except in the dimension corresponding to axis (the first, by default).</param>
/// <param name="dimension">The axis along which the tensors will be joined. If axis is -1, arrays are flattened before use. Default is 0.</param>
/// <param name="destination"></param>
/// <remarks>The dimension refers to the first tensor's effective shape. Other inputs and the destination are aligned by adding or removing leading singleton dimensions. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static ref readonly TensorSpan<T> ConcatenateOnDimension<T>(int dimension, scoped ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination)
{
if (tensors.Length < 2)
{
ThrowHelper.ThrowArgument_ConcatenateTooFewTensors();
}
ref readonly TensorShape shape = ref TensorShape.Normalize(tensors[0]._shape);
int rank = shape.Rank;
nint destinationLength = destination.FlattenedLength;
if (dimension < -1 || dimension >= rank)
{
ThrowHelper.ThrowArgument_InvalidDimension();
}
// If axis != -1, make sure all dimensions except the one to concatenate on match.
if (dimension != -1)
{
nint sumOfAxis = GetConcatenatedLength(dimension, tensors, shape);
ValidateConcatenationDestination(dimension, shape.Lengths, sumOfAxis, destination);
}
else
{
nint totalLength = 0;
for (int i = 0; i < tensors.Length; i++)
{
totalLength = checked(totalLength + tensors[i].FlattenedLength);
}
if (destinationLength != totalLength)
{
ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination));
}
}
ValidateConcatenationStorage(tensors, destination);
CopyConcatenatedValues(dimension, tensors, destination, rank);
return ref destination;
}
private static void ValidateConcatenationDestination<T>(int dimension, ReadOnlySpan<nint> lengths, nint sumOfAxis,
in TensorSpan<T> destination)
{
ReadOnlySpan<nint> destinationLengths = TensorShape.Normalize(destination._shape).Lengths;
if (!TensorShape.AreLengthsEquivalentExceptDimension(lengths, destinationLengths, dimension, out nint destinationAxisLength)
|| destinationAxisLength != sumOfAxis)
{
ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination));
}
}
private static Tensor<T> CreateConcatenationResult<T>(ReadOnlySpan<nint> sourceLengths, int dimension, nint axisLength)
{
scoped Span<nint> lengths = TensorOperation.RentedBuffer.CreateUninitialized(sourceLengths.Length,
out TensorOperation.RentedBuffer<nint> rentedBuffer);
Tensor<T> result;
try
{
sourceLengths.CopyTo(lengths);
lengths[dimension] = axisLength;
result = CreateFromShape<T>(lengths);
}
finally
{
rentedBuffer.Dispose();
}
return result;
}
private static void ValidateConcatenationStorage<T>(scoped ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination)
{
if (destination._shape.IsSelfOverlapping)
{
ThrowHelper.ThrowArgument_DestinationHasOverlappingElements(nameof(destination));
}
if (destination.FlattenedLength == 0)
{
return;
}
nint destinationLinearLength = destination._shape.LinearLength;
for (int i = 0; i < tensors.Length; i++)
{
ReadOnlyTensorSpan<T> source = tensors[i].AsReadOnlyTensorSpan();
if (source._shape.Overlaps(in source._reference, ref destination._reference, destinationLinearLength))
{
ThrowHelper.ThrowArgument_OverlappingTensorLayoutsNotSupported();
}
}
}
private static void CopyConcatenatedValues<T>(int dimension, scoped ReadOnlySpan<Tensor<T>> tensors,
in TensorSpan<T> destination, int rank)
{
nint destinationLength = destination.FlattenedLength;
if (destinationLength == 0)
{
return;
}
TensorSpan<T> alignedTarget;
scoped ref readonly TensorSpan<T> target = ref destination;
if (dimension != -1 && destination.Rank != rank)
{
alignedTarget = new TensorSpan<T>(ref destination._reference, TensorShape.WithRank(destination._shape, rank));
target = ref alignedTarget;
}
if (!target.IsDense || destinationLength > int.MaxValue)
{
if (dimension == -1)
{
TensorSpan<T>.Enumerator output = target.GetEnumerator();
foreach (Tensor<T> tensor in tensors)
{
Tensor<T>.Enumerator input = tensor.GetEnumerator();
while (input.MoveNext())
{
bool moved = output.MoveNext();
Debug.Assert(moved);
output.Current = input.Current;
}
}
}
else
{
int targetRank = target.Rank;
Span<NRange> ranges = TensorOperation.RentedBuffer.CreateUninitialized(targetRank, out TensorOperation.RentedBuffer<NRange> rentedBuffer);
ranges.Fill(NRange.All);
nint offset = 0;
foreach (Tensor<T> tensor in tensors)
{
ReadOnlyTensorSpan<T> source = tensor.AsReadOnlyTensorSpan();
if (source.Rank != targetRank)
{
source = new ReadOnlyTensorSpan<T>(in source._reference, TensorShape.WithRank(source._shape, targetRank));
}
nint length = source.Lengths[dimension];
ranges[dimension] = new NRange(offset, offset + length);
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(source, target.Slice(ranges));
offset += length;
}
rentedBuffer.Dispose();
}
}
else
{
Span<T> dstSpan = MemoryMarshal.CreateSpan(ref target._reference, (int)destinationLength);
ConcatenateOnDimensionToSpan(dimension, tensors, target, dstSpan);
}
}
private static nint GetConcatenatedLength<T>(int dimension, scoped ReadOnlySpan<Tensor<T>> tensors, in TensorShape shape)
{
ReadOnlySpan<nint> lengths = shape.Lengths;
nint sumOfAxis = lengths[dimension];
for (int i = 1; i < tensors.Length; i++)
{
ReadOnlySpan<nint> sourceLengths = TensorShape.Normalize(tensors[i]._shape).Lengths;
if (!TensorShape.AreLengthsEquivalentExceptDimension(lengths, sourceLengths, dimension, out nint axisLength))
{
ThrowHelper.ThrowArgument_InvalidConcatenateShape();
}
sumOfAxis = checked(sumOfAxis + axisLength);
}
return sumOfAxis;
}
private static void ConcatenateOnDimensionToSpan<T>(int dimension, scoped ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination, Span<T> dstSpan)
{
if (dimension is 0 or -1)
{
for (int i = 0; i < tensors.Length; i++)
{
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(tensors[i], dstSpan);
dstSpan = dstSpan.Slice((int)tensors[i].FlattenedLength);
}
}
else
{
int rank = destination.Rank;
Span<NRange> ranges = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<NRange> rentedBuffer);
for (int i = 0; i < dimension; i++)
{
ranges[i] = 0..1;
}
for (int i = dimension; i < rank; i++)
{
ranges[i] = ..;
}
bool hasMore = !destination.Lengths[..dimension].Contains(0);
while (hasMore)
{
for (int i = 0; i < tensors.Length; i++)
{
ReadOnlyTensorSpan<T> source = tensors[i].AsReadOnlyTensorSpan();
if (source.Rank != rank)
{
source = new ReadOnlyTensorSpan<T>(in source._reference, TensorShape.WithRank(source._shape, rank));
}
ReadOnlyTensorSpan<T> slice = source.Slice(ranges);
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(slice, dstSpan);
dstSpan = dstSpan.Slice((int)slice.FlattenedLength);
}
// We do dimension - 1 because we want to include the dimension we concatenated on.
hasMore = TensorShape.AdjustToNextIndex(ranges, dimension - 1, destination.Lengths);
}
rentedBuffer.Dispose();
}
}
#endregion
#region Create
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[])" />
/// <returns>A new tensor that uses <paramref name="array" /> as its backing buffer.</returns>
public static Tensor<T> Create<T>(T[] array)
=> new Tensor<T>(array);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], ReadOnlySpan{nint})" />
/// <returns>A new tensor that uses <paramref name="array" /> as its backing buffer and with the specified <paramref name="lengths" />.</returns>
public static Tensor<T> Create<T>(T[] array, scoped ReadOnlySpan<nint> lengths)
=> new Tensor<T>(array, lengths, strides: []);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], ReadOnlySpan{nint}, ReadOnlySpan{nint})" />
/// <returns>A new tensor that uses <paramref name="array" /> as its backing buffer and with the specified <paramref name="lengths" /> and <paramref name="strides"/>.</returns>
public static Tensor<T> Create<T>(T[] array, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
=> new Tensor<T>(array, lengths, strides);
/// <inheritdoc cref="ReadOnlyTensorSpan{T}.ReadOnlyTensorSpan(T[], int, ReadOnlySpan{nint}, ReadOnlySpan{nint})" />
/// <returns>A new tensor that uses <paramref name="array" /> as its backing buffer and with the specified <paramref name="lengths" /> and <paramref name="strides" />.</returns>
public static Tensor<T> Create<T>(T[] array, int start, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
=> new Tensor<T>(array, start, lengths, strides);
#endregion
#region CreateFromShape
/// <inheritdoc cref="ITensor{TSelf, T}.CreateFromShape(ReadOnlySpan{nint}, bool)" />
/// <returns>A new tensor with the specified lengths.</returns>
public static Tensor<T> CreateFromShape<T>(scoped ReadOnlySpan<nint> lengths, bool pinned = false)
=> new Tensor<T>(lengths, strides: [], pinned);
/// <inheritdoc cref="ITensor{TSelf, T}.CreateFromShape(ReadOnlySpan{nint}, ReadOnlySpan{nint}, bool)" />
/// <returns>A new tensor with the specified <paramref name="lengths" /> and <paramref name="strides" />.</returns>
public static Tensor<T> CreateFromShape<T>(scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
=> new Tensor<T>(lengths, strides, pinned);
/// <inheritdoc cref="ITensor{TSelf, T}.CreateFromShapeUninitialized(ReadOnlySpan{nint}, bool)" />
public static Tensor<T> CreateFromShapeUninitialized<T>(scoped ReadOnlySpan<nint> lengths, bool pinned = false)
=> CreateFromShapeUninitialized<T>(lengths, strides: [], pinned);
/// <inheritdoc cref="ITensor{TSelf, T}.CreateFromShapeUninitialized(ReadOnlySpan{nint}, ReadOnlySpan{nint}, bool)" />
public static Tensor<T> CreateFromShapeUninitialized<T>(scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
{
TensorShape shape = TensorShape.Create(lengths, strides, pinned);
T[] values = GC.AllocateUninitializedArray<T>(checked((int)(shape.LinearLength)), pinned);
return new Tensor<T>(values, in shape);
}
#endregion
#region Fill
/// <summary>
/// Fills the given <see cref="TensorSpan{T}"/> with random data in a Gaussian normal distribution. <see cref="Random"/>
/// can optionally be provided for seeding.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="destination">The destination <see cref="TensorSpan{T}"/> where the data will be stored.</param>
/// <param name="random"><see cref="Random"/> to provide random seeding. Defaults to <see cref="Random.Shared"/> if not provided.</param>
/// <returns></returns>
public static ref readonly TensorSpan<T> FillGaussianNormalDistribution<T>(in TensorSpan<T> destination, Random? random = null) where T : IFloatingPoint<T>
{
random ??= Random.Shared;
nint flattenedLength = destination.FlattenedLength;
if (destination.IsDense && flattenedLength <= int.MaxValue)
{
Span<T> span = MemoryMarshal.CreateSpan(ref destination._reference, (int)flattenedLength);
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));
}
}
else
{
int dimension = flattenedLength >= TensorOperation.MinSlicedOperationLength
? TensorOperation.GetDenseSliceDimension(destination._shape, destination._shape)
: -1;
if (dimension >= 0)
{
TensorDimensionSpan<T> slices = destination.GetDimensionSpan(dimension);
nint sliceCount = slices.Length;
for (nint i = 0; i < sliceCount; i++)
{
TensorSpan<T> slice = slices[i];
Span<T> span = MemoryMarshal.CreateSpan(ref slice._reference, (int)slice.FlattenedLength);
for (int j = 0; j < span.Length; j++)
{
double u1 = 1.0 - random.NextDouble();
double u2 = 1.0 - random.NextDouble();
span[j] = T.CreateChecked(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2));
}
}
}
else
{
TensorSpan<T>.Enumerator enumerator = destination.GetEnumerator();
while (enumerator.MoveNext())
{
double u1 = 1.0 - random.NextDouble();
double u2 = 1.0 - random.NextDouble();
enumerator.Current = T.CreateChecked(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2));
}
}
}
return ref destination;
}
/// <summary>
/// Fills the given <see cref="TensorSpan{T}"/> with random data in a uniform distribution. <see cref="Random"/>
/// can optionally be provided for seeding.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="destination">The destination <see cref="TensorSpan{T}"/> where the data will be stored.</param>
/// <param name="random"><see cref="Random"/> to provide random seeding. Defaults to <see cref="Random.Shared"/> if not provided.</param>
/// <returns></returns>
public static ref readonly TensorSpan<T> FillUniformDistribution<T>(in TensorSpan<T> destination, Random? random = null) where T : IFloatingPoint<T>
{
random ??= Random.Shared;
nint flattenedLength = destination.FlattenedLength;
if (destination.IsDense && flattenedLength <= int.MaxValue)
{
Span<T> span = MemoryMarshal.CreateSpan(ref destination._reference, (int)flattenedLength);
for (int i = 0; i < span.Length; i++)
{
span[i] = T.CreateChecked(random.NextDouble());
}
}
else
{
int dimension = flattenedLength >= TensorOperation.MinSlicedOperationLength
? TensorOperation.GetDenseSliceDimension(destination._shape, destination._shape)
: -1;
if (dimension >= 0)
{
TensorDimensionSpan<T> slices = destination.GetDimensionSpan(dimension);
nint sliceCount = slices.Length;
for (nint i = 0; i < sliceCount; i++)
{
TensorSpan<T> slice = slices[i];
Span<T> span = MemoryMarshal.CreateSpan(ref slice._reference, (int)slice.FlattenedLength);
for (int j = 0; j < span.Length; j++)
{
span[j] = T.CreateChecked(random.NextDouble());
}
}
}
else
{
TensorSpan<T>.Enumerator enumerator = destination.GetEnumerator();
while (enumerator.MoveNext())
{
enumerator.Current = T.CreateChecked(random.NextDouble());
}
}
}
return ref destination;
}
#endregion
#region Equals
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> 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 <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <returns>A <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not.</returns>
public static Tensor<bool> Equals<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IEqualityOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<bool> destination);
TensorOperation.Invoke<TensorOperation.Equals<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> 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 <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not.</returns>
public static ref readonly TensorSpan<bool> Equals<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IEqualityOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Equals<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> 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 <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare.</param>
/// <returns>A <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not.</returns>
public static Tensor<bool> Equals<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IEqualityOperators<T, T, bool>
{
Tensor<bool> destination = CreateFromShapeUninitialized<bool>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Equals<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> 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 <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="TensorSpan{Boolean}"/> where the value is true if the elements are equal and false if they are not.</returns>
public static ref readonly TensorSpan<bool> Equals<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<bool> destination)
where T : IEqualityOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Equals<T>, T, bool>(x, y, destination);
return ref destination;
}
#endregion
#region EqualsAll
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are eqaul to <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are equal to <paramref name="y"/>.</returns>
public static bool EqualsAll<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IEqualityOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y);
return TensorOperation.Invoke<TensorOperation.Equals<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are eqaul to <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are equal to <paramref name="y"/>.</returns>
public static bool EqualsAll<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IEqualityOperators<T, T, bool> => TensorOperation.Invoke<TensorOperation.Equals<T>, T>(x, y);
#endregion
#region EqualsAny
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are equal to <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are equal to <paramref name="y"/>.</returns>
public static bool EqualsAny<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IEqualityOperators<T, T, bool>
{
// 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.
TensorOperation.ValidateCompatibility(x, y);
return !TensorOperation.Invoke<TensorOperation.EqualsAny<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are equal to <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are equal to <paramref name="y"/>.</returns>
public static bool EqualsAny<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IEqualityOperators<T, T, bool> => !TensorOperation.Invoke<TensorOperation.EqualsAny<T>, T>(x, y);
#endregion
#region FilteredUpdate
/// <summary>
/// Updates the <paramref name="tensor"/> tensor with the <paramref name="value"/> where the <paramref name="filter"/> is true.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="filter">Input filter where if the index is true then it will update the <paramref name="tensor"/>.</param>
/// <param name="value">Value to update in the <paramref name="tensor"/>.</param>
public static ref readonly TensorSpan<T> FilteredUpdate<T>(in this TensorSpan<T> tensor, scoped in ReadOnlyTensorSpan<bool> filter, T value)
{
TensorOperation.ValidateCompatibility(filter, tensor);
TensorOperation.Invoke<TensorOperation.FilteredUpdate<T>, bool, T, T>(filter, value, tensor);
return ref tensor;
}
/// <summary>
/// Updates the <paramref name="tensor"/> tensor with the <paramref name="values"/> where the <paramref name="filter"/> is true.
/// If dimensions are not the same an exception is thrown.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="filter">Input filter where if the index is true then it will update the <paramref name="tensor"/>.</param>
/// <param name="values">Values to update in the <paramref name="tensor"/>.</param>
public static ref readonly TensorSpan<T> FilteredUpdate<T>(in this TensorSpan<T> tensor, scoped in ReadOnlyTensorSpan<bool> filter, scoped in ReadOnlyTensorSpan<T> values)
{
TensorOperation.ValidateCompatibility(filter, values, tensor);
TensorOperation.Invoke<TensorOperation.FilteredUpdate<T>, bool, T, T>(filter, values, tensor);
return ref tensor;
}
#endregion
#region GreaterThan
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/> and
/// false if they are not.</returns>
public static Tensor<bool> GreaterThan<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<bool> destination);
TensorOperation.Invoke<TensorOperation.GreaterThan<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/> and
/// false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThan<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.GreaterThan<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="ReadOnlyTensorSpan{T}"/> to see which elements are greater than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> GreaterThan<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool>
{
Tensor<bool> destination = CreateFromShape<bool>(x.Lengths, false);
GreaterThan(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of a <see cref="ReadOnlyTensorSpan{T}"/> to see which elements are greater than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThan<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.GreaterThan<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares <paramref name="x"/> to see which elements are greater than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> GreaterThan<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThan(y, x);
/// <summary>
/// Compares <paramref name="x"/> to see which elements are greater than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThan<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool> => ref LessThan(y, x, destination);
#endregion
#region GreaterThanAll
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanAll<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y);
return TensorOperation.Invoke<TensorOperation.GreaterThan<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanAll<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => TensorOperation.Invoke<TensorOperation.GreaterThan<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="y"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanAll<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThanAll(y, x);
#endregion
#region GreaterThanAny
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanAny<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
// 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.
TensorOperation.ValidateCompatibility(x, y);
return !TensorOperation.Invoke<TensorOperation.GreaterThanAny<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanAny<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => !TensorOperation.Invoke<TensorOperation.GreaterThanAny<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="y"/> are greater than <paramref name="x"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are greater than <paramref name="x"/>.
/// </summary>
/// <param name="y">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="x">Value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are greater than <paramref name="x"/>.</returns>
public static bool GreaterThanAny<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThanAny(y, x);
#endregion
#region GreaterThanOrEqual
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are greater than or equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/> and
/// false if they are not.</returns>
public static Tensor<bool> GreaterThanOrEqual<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<bool> destination);
TensorOperation.Invoke<TensorOperation.GreaterThanOrEqual<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are greater than or equal to <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/> and
/// false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThanOrEqual<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.GreaterThanOrEqual<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="ReadOnlyTensorSpan{T}"/> to see which elements are greater than or equal to <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> GreaterThanOrEqual<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool>
{
Tensor<bool> destination = CreateFromShape<bool>(x.Lengths, false);
GreaterThanOrEqual(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of a <see cref="ReadOnlyTensorSpan{T}"/> to see which elements are greater than or equal to <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThanOrEqual<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.GreaterThanOrEqual<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares <paramref name="x"/> to see which elements are greater than or equal to <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> GreaterThanOrEqual<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThanOrEqual(y, x);
/// <summary>
/// Compares <paramref name="x"/> to see which elements are greater than or equal to <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are greater than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> GreaterThanOrEqual<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool> => ref LessThanOrEqual(y, x, destination);
#endregion
#region GreaterThanOrEqualAll
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanOrEqualAll<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y);
return TensorOperation.Invoke<TensorOperation.GreaterThanOrEqual<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanOrEqualAll<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => TensorOperation.Invoke<TensorOperation.GreaterThanOrEqual<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="y"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanOrEqualAll<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThanOrEqualAll(y, x);
#endregion
#region GreaterThanOrEqualAny
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanOrEqualAny<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
// 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.
TensorOperation.ValidateCompatibility(x, y);
return !TensorOperation.Invoke<TensorOperation.GreaterThanOrEqualAny<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are greater than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are greater than <paramref name="y"/>.</returns>
public static bool GreaterThanOrEqualAny<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => !TensorOperation.Invoke<TensorOperation.GreaterThanOrEqualAny<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="y"/> are greater than <paramref name="x"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are greater than <paramref name="x"/>.
/// </summary>
/// <param name="y">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="x">Value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are greater than <paramref name="x"/>.</returns>
public static bool GreaterThanOrEqualAny<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => LessThanOrEqualAny(y, x);
#endregion
#region LessThan
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/> and
/// false if they are not.</returns>
public static Tensor<bool> LessThan<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<bool> destination);
TensorOperation.Invoke<TensorOperation.LessThan<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/> and
/// false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThan<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.LessThan<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> LessThan<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool>
{
Tensor<bool> destination = CreateFromShape<bool>(x.Lengths, false);
LessThan(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThan<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.LessThan<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> LessThan<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThan(y, x);
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThan<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool> => ref GreaterThan(y, x, destination);
#endregion
#region LessThanAll
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanAll<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y);
return TensorOperation.Invoke<TensorOperation.LessThan<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanAll<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => TensorOperation.Invoke<TensorOperation.LessThan<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="y"/> are less than <paramref name="x"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are less than <paramref name="x"/>.
/// </summary>
/// <param name="y">First value to compare.</param>
/// <param name="x">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are less than <paramref name="x"/>.</returns>
public static bool LessThanAll<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThanAll(y, x);
#endregion
#region LessThanAny
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanAny<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
// 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.
TensorOperation.ValidateCompatibility(x, y);
return !TensorOperation.Invoke<TensorOperation.LessThanAny<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanAny<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => !TensorOperation.Invoke<TensorOperation.LessThanAny<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="y"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First value to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanAny<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThanAny(y, x);
#endregion
#region LessThanOrEqual
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/> and
/// false if they are not.</returns>
public static Tensor<bool> LessThanOrEqual<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<bool> destination);
TensorOperation.Invoke<TensorOperation.LessThanOrEqual<T>, T, bool>(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see which elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="destination"></param>
/// <returns>A <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/> and
/// false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThanOrEqual<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.LessThanOrEqual<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> LessThanOrEqual<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool>
{
Tensor<bool> destination = CreateFromShape<bool>(x.Lengths, false);
LessThanOrEqual(x, y, destination);
return destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThanOrEqual<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.LessThanOrEqual<T>, T, bool>(x, y, destination);
return ref destination;
}
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static Tensor<bool> LessThanOrEqual<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThanOrEqual(y, x);
/// <summary>
/// Compares the elements of a <see cref="Tensor{T}"/> to see which elements are less than <paramref name="y"/>.
/// It returns a <see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not."/>
/// </summary>
/// <param name="x"><see cref="Tensor{T}"/> to compare.</param>
/// <param name="y"><typeparamref name="T"/> to compare against <paramref name="x"/>.</param>
/// <param name="destination"></param>
/// <returns><see cref="Tensor{Boolean}"/> where the value is true if the elements in <paramref name="x"/> are less than <paramref name="y"/>
/// and false if they are not.</returns>
public static ref readonly TensorSpan<bool> LessThanOrEqual<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<bool> destination)
where T : IComparisonOperators<T, T, bool> => ref GreaterThanOrEqual(y, x, destination);
#endregion
#region LessThanOrEqualAll
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanOrEqualAll<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
TensorOperation.ValidateCompatibility(x, y);
return TensorOperation.Invoke<TensorOperation.LessThanOrEqual<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanOrEqualAll<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => TensorOperation.Invoke<TensorOperation.LessThanOrEqual<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if all elements of <paramref name="y"/> are less than <paramref name="x"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are less than <paramref name="x"/>.
/// </summary>
/// <param name="y">First value to compare.</param>
/// <param name="x">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if all elements in <paramref name="y"/> are less than <paramref name="x"/>.</returns>
public static bool LessThanOrEqualAll<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThanOrEqualAll(y, x);
#endregion
#region LessThanOrEqualAny
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second <see cref="ReadOnlyTensorSpan{T}"/> to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanOrEqualAny<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool>
{
// 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.
TensorOperation.ValidateCompatibility(x, y);
return !TensorOperation.Invoke<TensorOperation.LessThanOrEqualAny<T>, T>(x, y);
}
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="x"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First <see cref="ReadOnlyTensorSpan{T}"/> to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="x"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanOrEqualAny<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IComparisonOperators<T, T, bool> => !TensorOperation.Invoke<TensorOperation.LessThanOrEqualAny<T>, T>(x, y);
/// <summary>
/// Compares the elements of two <see cref="ReadOnlyTensorSpan{T}"/> to see if any elements of <paramref name="y"/> are less than <paramref name="y"/>.
/// If the shapes are not the same, the tensors are broadcasted to the smallest broadcastable size before they are compared.
/// It returns a <see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are less than <paramref name="y"/>.
/// </summary>
/// <param name="x">First value to compare.</param>
/// <param name="y">Second value to compare against.</param>
/// <returns><see cref="bool"/> where the value is true if any elements in <paramref name="y"/> are less than <paramref name="y"/>.</returns>
public static bool LessThanOrEqualAny<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IComparisonOperators<T, T, bool> => GreaterThanOrEqualAny(y, x);
#endregion
#region Permute
/// <summary>
/// Swaps the dimensions of the <paramref name="tensor"/> tensor according to the <paramref name="dimensions"/> parameter.
/// If <paramref name="tensor"/> is a 1D tensor, it will return <paramref name="tensor"/>. Otherwise it creates a new <see cref="Tensor{T}"/>
/// with the new axis ordering by allocating new memory.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/></param>
/// <param name="dimensions"><see cref="ReadOnlySpan{T}"/> with the new axis ordering.</param>
public static Tensor<T> PermuteDimensions<T>(this Tensor<T> tensor, ReadOnlySpan<int> dimensions)
{
int rank = tensor.Rank;
if (rank <= 1 && (dimensions.IsEmpty || (dimensions.Length == 1 && dimensions[0] == 0)))
{
return tensor;
}
else
{
ReadOnlySpan<nint> lengths = tensor.Lengths;
ReadOnlySpan<nint> strides = tensor.Strides;
if (!dimensions.IsEmpty && dimensions.Length != rank)
{
ThrowHelper.ThrowArgument_PermuteAxisOrder();
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
scoped Span<nint> newStrides = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimensions.IsEmpty)
{
for (int i = 0; i < rank; i++)
{
newLengths[i] = lengths[rank - 1 - i];
newStrides[i] = strides[rank - 1 - i];
}
}
else
{
scoped Span<ulong> seen = TensorOperation.RentedBuffer.CreateUninitialized(
(rank - 1) / 64 + 1, out TensorOperation.RentedBuffer<ulong> seenRentedBuffer);
for (int i = 0; i < dimensions.Length; i++)
{
int dimension = dimensions[i];
if ((uint)dimension >= (uint)rank)
{
ThrowHelper.ThrowArgument_InvalidDimension();
}
int word = dimension / 64;
ulong bit = 1UL << (dimension % 64);
if ((seen[word] & bit) != 0)
{
ThrowHelper.ThrowArgument_PermuteAxisOrder();
}
seen[word] |= bit;
newLengths[i] = lengths[dimension];
newStrides[i] = strides[dimension];
}
seenRentedBuffer.Dispose();
}
Tensor<T> result = new Tensor<T>(tensor._values, tensor._start, newLengths, newStrides, tensor.IsPinned);
stridesRentedBuffer.Dispose();
lengthsRentedBuffer.Dispose();
return result;
}
}
#endregion
#region Reshape
private static void GetReshapeLengths(ReadOnlySpan<nint> lengths, nint flattenedLength, Span<nint> result)
{
if (lengths.IsEmpty)
{
if (flattenedLength != 0)
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
return;
}
lengths.CopyTo(result);
int wildcardIndex = -1;
// A zero makes the entire product zero, regardless of preceding dimension sizes.
nint knownProduct = lengths.Contains(0) ? 0 : 1;
for (int i = 0; i < lengths.Length; i++)
{
nint length = lengths[i];
if (length == -1)
{
if (wildcardIndex >= 0)
{
ThrowHelper.ThrowArgument_OnlyOneWildcard();
}
wildcardIndex = i;
}
else
{
if (length < 0 || (length != 0 && knownProduct > nint.MaxValue / length))
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
knownProduct *= length;
}
}
if (wildcardIndex >= 0)
{
if (knownProduct == 0 || flattenedLength % knownProduct != 0)
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
result[wildcardIndex] = flattenedLength / knownProduct;
}
else if (knownProduct != flattenedLength)
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
}
private static void GetReshapeStrides(ReadOnlySpan<nint> sourceLengths, ReadOnlySpan<nint> sourceStrides,
bool isDense, ReadOnlySpan<nint> lengths, Span<nint> strides)
{
if (isDense)
{
return;
}
if (!sourceStrides.ContainsAnyExcept(0) || sourceLengths.Contains(0))
{
return;
}
int sourceIndex = 0;
for (int i = 0; i < lengths.Length; i++)
{
while (sourceIndex < sourceLengths.Length && sourceLengths[sourceIndex] == 1 && lengths[i] != 1)
{
sourceIndex++;
}
if (sourceIndex < sourceLengths.Length && lengths[i] == sourceLengths[sourceIndex])
{
strides[i] = sourceStrides[sourceIndex++];
}
else if (lengths[i] != 1)
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
}
while (sourceIndex < sourceLengths.Length && sourceLengths[sourceIndex] == 1)
{
sourceIndex++;
}
if (sourceIndex != sourceLengths.Length)
{
ThrowHelper.ThrowArgument_InvalidReshapeDimensions();
}
}
/// <summary>
/// Reshapes the <paramref name="tensor"/> tensor to the specified <paramref name="lengths"/>. 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.
/// </summary>
/// <param name="tensor"><see cref="Tensor{T}"/> you want to reshape.</param>
/// <param name="lengths"><see cref="ReadOnlySpan{T}"/> with the new dimensions.</param>
public static Tensor<T> Reshape<T>(this Tensor<T> tensor, ReadOnlySpan<nint> lengths)
{
if (tensor.Lengths.SequenceEqual(lengths))
{
return tensor;
}
if (tensor.Rank == 0)
{
TensorSpan<T> reshaped = tensor.AsTensorSpan().Reshape(lengths);
return new Tensor<T>(tensor._values, tensor._start, reshaped._shape);
}
if (!tensor.IsDense && !tensor.Strides.Contains(0))
{
ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense();
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(lengths.Length, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
GetReshapeLengths(lengths, tensor.FlattenedLength, newLengths);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.IsDense ? 0 : newLengths.Length, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
GetReshapeStrides(tensor.Lengths, tensor.Strides, tensor.IsDense, newLengths, strides);
Tensor<T> result = new Tensor<T>(tensor._values, tensor._start, newLengths, strides, tensor.IsPinned);
stridesRentedBuffer.Dispose();
lengthsRentedBuffer.Dispose();
return result;
}
/// <summary>
/// Reshapes the <paramref name="tensor"/> tensor to the specified <paramref name="lengths"/>. 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.
/// </summary>
/// <param name="tensor"><see cref="TensorSpan{T}"/> you want to reshape.</param>
/// <param name="lengths"><see cref="ReadOnlySpan{T}"/> with the new dimensions.</param>
public static TensorSpan<T> Reshape<T>(this scoped in TensorSpan<T> tensor, scoped ReadOnlySpan<nint> lengths)
{
if (tensor.Lengths.SequenceEqual(lengths))
{
return tensor;
}
ref readonly TensorShape shape = ref TensorShape.Normalize(tensor._shape);
if (!shape.IsDense && !shape.Strides.Contains(0))
{
ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense();
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(lengths.Length, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
GetReshapeLengths(lengths, shape.FlattenedLength, newLengths);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(shape.IsDense ? 0 : newLengths.Length, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
GetReshapeStrides(shape.Lengths, shape.Strides, shape.IsDense, newLengths, strides);
TensorSpan<T> result = new TensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, newLengths, strides, tensor.IsPinned);
stridesRentedBuffer.Dispose();
lengthsRentedBuffer.Dispose();
return result;
}
/// <summary>
/// Reshapes the <paramref name="tensor"/> tensor to the specified <paramref name="lengths"/>. 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.
/// </summary>
/// <param name="tensor"><see cref="TensorSpan{T}"/> you want to reshape.</param>
/// <param name="lengths"><see cref="ReadOnlySpan{T}"/> with the new dimensions.</param>
public static ReadOnlyTensorSpan<T> Reshape<T>(this scoped in ReadOnlyTensorSpan<T> tensor, scoped ReadOnlySpan<nint> lengths)
{
if (tensor.Lengths.SequenceEqual(lengths))
{
return tensor;
}
ref readonly TensorShape shape = ref TensorShape.Normalize(tensor._shape);
if (!shape.IsDense && !shape.Strides.Contains(0))
{
ThrowHelper.ThrowArgument_CannotReshapeNonContiguousOrDense();
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(lengths.Length, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
GetReshapeLengths(lengths, shape.FlattenedLength, newLengths);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(shape.IsDense ? 0 : newLengths.Length, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
GetReshapeStrides(shape.Lengths, shape.Strides, shape.IsDense, newLengths, strides);
ReadOnlyTensorSpan<T> result = new ReadOnlyTensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, newLengths, strides, tensor.IsPinned);
stridesRentedBuffer.Dispose();
lengthsRentedBuffer.Dispose();
return result;
}
#endregion
#region Resize
/// <summary>
/// Creates a new <see cref="Tensor{T}"/>, allocates new memory, and copies the data from <paramref name="tensor"/>. If the final shape is smaller all data after
/// that point is ignored.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="lengths"><see cref="ReadOnlySpan{T}"/> of the desired new shape.</param>
public static Tensor<T> Resize<T>(Tensor<T> tensor, ReadOnlySpan<nint> lengths)
{
Tensor<T> output = CreateFromShape<T>(lengths, tensor.IsPinned);
nint newSize = output.FlattenedLength;
if (tensor.IsDense)
{
ReadOnlySpan<T> span = MemoryMarshal.CreateSpan(ref tensor.AsReadOnlyTensorSpan()._reference, (int)tensor.FlattenedLength);
Span<T> ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength);
if (newSize >= span.Length)
{
span.CopyTo(ospan);
}
else
{
span.Slice(0, ospan.Length).CopyTo(ospan);
}
}
else
{
nint copyLength = Math.Min(tensor.FlattenedLength, newSize);
Span<T> ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength);
ReadOnlyTensorSpan<T> source = tensor.AsReadOnlyTensorSpan();
int dimension = copyLength >= TensorOperation.MinSlicedOperationLength
? TensorOperation.GetDenseSliceDimension(source._shape, source._shape)
: -1;
if (dimension >= 0)
{
ReadOnlyTensorDimensionSpan<T> slices = source.GetDimensionSpan(dimension);
int copied = 0;
for (nint i = 0; copied < copyLength; i++)
{
ReadOnlyTensorSpan<T> slice = slices[i];
int count = (int)Math.Min(slice.FlattenedLength, copyLength - copied);
MemoryMarshal.CreateReadOnlySpan(in slice._reference, count).CopyTo(ospan.Slice(copied, count));
copied += count;
}
}
else
{
ReadOnlyTensorSpan<T>.Enumerator enumerator = source.GetEnumerator();
for (nint i = 0; i < copyLength; i++)
{
bool moved = enumerator.MoveNext();
Debug.Assert(moved);
ospan[(int)i] = enumerator.Current;
}
}
}
return output;
}
/// <summary>
/// Copies the data from <paramref name="tensor"/>. If the final shape is smaller all data after that point is ignored.
/// If the final shape is bigger it is filled with the default value of <typeparamref name="T"/>.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/> with the desired new shape.</param>
public static void ResizeTo<T>(scoped in Tensor<T> tensor, in TensorSpan<T> destination)
{
ResizeTo(tensor.AsReadOnlyTensorSpan(), destination);
}
/// <summary>
/// Copies the data from <paramref name="tensor"/>. If the final shape is smaller all data after that point is ignored.
/// If the final shape is bigger it is filled with the default value of <typeparamref name="T"/>.
/// </summary>
/// <param name="tensor">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/> with the desired new shape.</param>
public static void ResizeTo<T>(scoped in TensorSpan<T> tensor, in TensorSpan<T> destination)
{
ResizeTo(tensor.AsReadOnlyTensorSpan(), destination);
}
/// <summary>
/// Copies the data from <paramref name="tensor"/>. If the final shape is smaller all data after that point is ignored.
/// If the final shape is bigger it is filled with the default value of <typeparamref name="T"/>.
/// </summary>
/// <param name="tensor">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/> with the desired new shape.</param>
public static void ResizeTo<T>(scoped in ReadOnlyTensorSpan<T> tensor, in TensorSpan<T> destination)
{
if (destination._shape.IsSelfOverlapping)
{
ThrowHelper.ThrowArgument_DestinationHasOverlappingElements(nameof(destination));
}
nint destinationLength = destination.FlattenedLength;
nint copyLength = Math.Min(tensor.FlattenedLength, destinationLength);
if (tensor.IsDense && destination.IsDense)
{
if (destinationLength <= int.MaxValue)
{
ReadOnlySpan<T> span = MemoryMarshal.CreateReadOnlySpan(in tensor._reference, (int)copyLength);
Span<T> ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destinationLength);
span.CopyTo(ospan);
ospan[(int)copyLength..].Clear();
}
else
{
TensorOperation.CopyDense(in tensor._reference, ref destination._reference, copyLength);
for (nint offset = copyLength; offset < destinationLength;)
{
int length = (int)Math.Min(destinationLength - offset, int.MaxValue);
MemoryMarshal.CreateSpan(ref Unsafe.Add(ref destination._reference, offset), length).Clear();
offset += length;
}
}
}
else
{
if (tensor._shape.Overlaps(in tensor._reference, ref destination._reference, destination._shape.LinearLength))
{
ThrowHelper.ThrowArgument_OverlappingTensorLayoutsNotSupported();
}
ReadOnlyTensorSpan<T>.Enumerator srcEnumerator = tensor.GetEnumerator();
TensorSpan<T>.Enumerator dstEnumerator = destination.GetEnumerator();
for (nint i = 0; i < copyLength; i++)
{
bool srcMoved = srcEnumerator.MoveNext();
bool dstMoved = dstEnumerator.MoveNext();
Debug.Assert(srcMoved && dstMoved);
dstEnumerator.Current = srcEnumerator.Current;
}
for (nint i = copyLength; i < destinationLength; i++)
{
bool dstMoved = dstEnumerator.MoveNext();
Debug.Assert(dstMoved);
dstEnumerator.Current = default!;
}
}
}
#endregion
#region Reverse
/// <summary>
/// Reverse the order of elements in the <paramref name="tensor"/>. The shape of the tensor is preserved, but the elements are reordered.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
public static Tensor<T> Reverse<T>(in ReadOnlyTensorSpan<T> tensor)
{
Tensor<T> output = CreateFromShape<T>(tensor.Lengths);
ReverseDimension(tensor, output, -1);
return output;
}
/// <summary>
/// Reverse the order of elements in the <paramref name="tensor"/> along the given dimension. The shape of the tensor is preserved, but the elements are reordered.
/// <paramref name="dimension"/> defaults to -1 when not provided, which reverses the entire tensor.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="dimension">dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor.</param>
public static Tensor<T> ReverseDimension<T>(in ReadOnlyTensorSpan<T> tensor, int dimension)
{
Tensor<T> output = CreateFromShape<T>(tensor.Lengths);
ReverseDimension(tensor, output, dimension);
return output;
}
/// <summary>
/// Reverse the order of elements in the <paramref name="tensor"/>. The shape of the tensor is preserved, but the elements are reordered.
/// </summary>
/// <param name="tensor">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Reverse<T>(scoped in ReadOnlyTensorSpan<T> tensor, in TensorSpan<T> destination)
{
return ref ReverseDimension(tensor, destination, -1);
}
/// <summary>
/// Reverse the order of elements in the <paramref name="tensor"/> along the given axis. The shape of the tensor is preserved, but the elements are reordered.
/// <paramref name="dimension"/> defaults to -1 when not provided, which reverses the entire span.
/// </summary>
/// <param name="tensor">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination"></param>
/// <param name="dimension">dimension along which to reverse over. -1 will reverse over all of the dimensions of the left tensor.</param>
public static ref readonly TensorSpan<T> ReverseDimension<T>(scoped in ReadOnlyTensorSpan<T> tensor, in TensorSpan<T> destination, int dimension)
{
if (dimension < -1 || dimension >= TensorShape.Normalize(tensor._shape).Rank)
{
ThrowHelper.ThrowArgument_InvalidDimension();
}
TensorOperation.ValidateCompatibility(tensor, destination);
if (destination.FlattenedLength == 0)
{
return ref destination;
}
if (tensor._shape.Overlaps(in tensor._reference, ref destination._reference, destination._shape.LinearLength))
{
if (Unsafe.AreSame(ref Unsafe.AsRef(in tensor._reference), ref destination._reference)
&& TensorShape.AreLayoutsTheSame(tensor._shape, destination._shape))
{
if ((tensor._shape.LinearLength == 1) || (dimension >= 0 && tensor.Lengths[dimension] == 1))
{
return ref destination;
}
if (tensor.IsDense)
{
int destinationDimension = dimension == -1 ? -1 : dimension + destination.Rank - tensor.Rank;
ReverseDimensionInPlace(destination, destinationDimension);
return ref destination;
}
}
ThrowHelper.ThrowArgument_OverlappingTensorLayoutsNotSupported();
}
ReverseDimensionCore(tensor, destination, dimension);
return ref destination;
}
private static void ReverseDimensionInPlace<T>(in TensorSpan<T> tensor, int dimension)
{
nint axisLength = dimension == -1 ? tensor.FlattenedLength : tensor.Lengths[dimension];
nint innerLength = 1;
if (dimension >= 0)
{
for (int i = dimension + 1; i < tensor.Rank; i++)
{
innerLength *= tensor.Lengths[i];
}
}
nint blockLength = axisLength * innerLength;
for (nint block = 0; block < tensor.FlattenedLength; block += blockLength)
{
for (nint left = 0; left < axisLength / 2; left++)
{
nint leftOffset = block + left * innerLength;
nint rightOffset = block + (axisLength - 1 - left) * innerLength;
for (nint i = 0; i < innerLength; i++)
{
ref T first = ref Unsafe.Add(ref tensor._reference, leftOffset + i);
ref T second = ref Unsafe.Add(ref tensor._reference, rightOffset + i);
(first, second) = (second, first);
}
}
}
}
private static void ReverseDimensionCore<T>(scoped in ReadOnlyTensorSpan<T> tensor, in TensorSpan<T> destination, int dimension)
{
if (dimension == -1)
{
TensorOperation.ReverseInvoke<TensorOperation.CopyTo<T>, T, T>(tensor, destination);
}
else if (tensor.Lengths[dimension] == 1)
{
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(tensor, destination);
}
else
{
int rank = tensor.Rank;
int destinationRank = destination.Rank;
Span<NRange> srcIndexes = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<NRange> srcIndexesRentedBuffer);
Span<NRange> dstIndexes = TensorOperation.RentedBuffer.CreateUninitialized(destinationRank, out TensorOperation.RentedBuffer<NRange> dstIndexesRentedBuffer);
for (int i = 0; i < srcIndexes.Length; i++)
{
srcIndexes[i] = NRange.All;
}
for (int i = 0; i < dstIndexes.Length; i++)
{
dstIndexes[i] = NRange.All;
}
int destinationAxis = dimension + destinationRank - rank;
nint axisLength = destination.Lengths[destinationAxis];
for (nint i = axisLength; i > 0; i--)
{
nint sourceIndex = i - 1;
srcIndexes[dimension] = new NRange(sourceIndex, sourceIndex + 1);
dstIndexes[destinationAxis] = new NRange(axisLength - i, axisLength - i + 1);
TensorOperation.Invoke<TensorOperation.CopyTo<T>, T, T>(tensor.Slice(srcIndexes), destination.Slice(dstIndexes));
}
srcIndexesRentedBuffer.Dispose();
dstIndexesRentedBuffer.Dispose();
}
}
#endregion
#region SequenceEqual
/// <summary>
/// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
/// </summary>
/// <remarks>Shapes must be equivalent after ignoring leading singleton padding and treating rank-zero empty tensors as shape <c>[0]</c>. Other dimensions are not broadcast.</remarks>
public static bool SequenceEqual<T>(this scoped in TensorSpan<T> tensor, scoped in ReadOnlyTensorSpan<T> other)
where T : IEquatable<T>?
{
return ((ReadOnlyTensorSpan<T>)tensor).SequenceEqual(other);
}
/// <summary>
/// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
/// </summary>
/// <remarks>Shapes must be equivalent after ignoring leading singleton padding and treating rank-zero empty tensors as shape <c>[0]</c>. Other dimensions are not broadcast.</remarks>
public static bool SequenceEqual<T>(this scoped in ReadOnlyTensorSpan<T> tensor, scoped in ReadOnlyTensorSpan<T> other)
where T : IEquatable<T>?
{
nint flattenedLength = tensor.FlattenedLength;
if (flattenedLength != other.FlattenedLength
|| !TensorShape.AreLengthsEquivalent(tensor.Lengths, other.Lengths))
{
return false;
}
if (flattenedLength == 0)
{
return true;
}
if (tensor.IsDense && other.IsDense && flattenedLength <= int.MaxValue)
{
return MemoryMarshal.CreateReadOnlySpan(in tensor.GetPinnableReference(), (int)flattenedLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)flattenedLength));
}
ReadOnlyTensorSpan<T> alignedOther = other;
int rank = tensor.Rank;
if (rank != other.Rank && flattenedLength >= TensorOperation.MinSlicedOperationLength)
{
alignedOther = new ReadOnlyTensorSpan<T>(in other._reference, TensorShape.WithRank(other._shape, rank));
}
int dimension = flattenedLength >= TensorOperation.MinSlicedOperationLength
? TensorOperation.GetDenseSliceDimension(tensor._shape, alignedOther._shape)
: -1;
if (dimension >= 0)
{
ReadOnlyTensorDimensionSpan<T> tensorSlices = tensor.GetDimensionSpan(dimension);
ReadOnlyTensorDimensionSpan<T> otherSlices = alignedOther.GetDimensionSpan(dimension);
nint sliceCount = tensorSlices.Length;
for (nint i = 0; i < sliceCount; i++)
{
ReadOnlyTensorSpan<T> tensorSlice = tensorSlices[i];
ReadOnlyTensorSpan<T> otherSlice = otherSlices[i];
if (!MemoryMarshal.CreateReadOnlySpan(in tensorSlice._reference, (int)tensorSlice.FlattenedLength)
.SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in otherSlice._reference, (int)otherSlice.FlattenedLength)))
{
return false;
}
}
return true;
}
ReadOnlyTensorSpan<T>.Enumerator enumerator1 = tensor.GetEnumerator();
ReadOnlyTensorSpan<T>.Enumerator enumerator2 = other.GetEnumerator();
while (enumerator1.MoveNext())
{
bool moved = enumerator2.MoveNext();
Debug.Assert(moved);
if (!(enumerator1.Current?.Equals(enumerator2.Current) ?? (object?)enumerator2.Current is null))
{
return false;
}
}
return true;
}
#endregion
#region SetSlice
/// <summary>
/// Sets a slice of the given <paramref name="tensor"/> with the provided <paramref name="values"/> for the given <paramref name="ranges"/>
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="values">The values you want to set in the <paramref name="tensor"/>.</param>
/// <param name="ranges">The ranges you want to set.</param>
public static Tensor<T> SetSlice<T>(this Tensor<T> tensor, in ReadOnlyTensorSpan<T> values, params ReadOnlySpan<NRange> ranges)
{
tensor.AsTensorSpan().SetSlice(values, ranges);
return tensor;
}
/// <summary>
/// Sets a slice of the given <paramref name="tensor"/> with the provided <paramref name="values"/> for the given <paramref name="ranges"/>
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="values">The values you want to set in the <paramref name="tensor"/>.</param>
/// <param name="ranges">The ranges you want to set.</param>
public static ref readonly TensorSpan<T> SetSlice<T>(this in TensorSpan<T> tensor, scoped in ReadOnlyTensorSpan<T> values, params scoped ReadOnlySpan<NRange> ranges)
{
if (ranges.IsEmpty)
{
values.CopyTo(tensor);
}
else
{
values.CopyTo(tensor.Slice(ranges));
}
return ref tensor;
}
#endregion
#region Split
/// <summary>
/// Split a <see cref="Tensor{T}"/> into <paramref name="splitCount"/> along the given <paramref name="dimension"/>. If the tensor cannot be split
/// evenly on the given <paramref name="dimension"/> an exception is thrown.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="splitCount">How many times to split the <paramref name="tensor"/></param>
/// <param name="dimension">The axis to split on.</param>
/// <remarks>Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="splitCount"/> is not positive.</exception>
public static Tensor<T>[] Split<T>(scoped in ReadOnlyTensorSpan<T> tensor, int splitCount, nint dimension)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(splitCount);
ref readonly TensorShape shape = ref TensorShape.Normalize(tensor._shape);
int rank = shape.Rank;
ReadOnlySpan<nint> lengths = shape.Lengths;
if (dimension < 0 || dimension >= rank)
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
if (lengths[(int)dimension] % splitCount != 0)
{
ThrowHelper.ThrowArgument_SplitNotSplitEvenly();
}
Tensor<T>[] outputs = new Tensor<T>[splitCount];
nint totalToCopy = tensor.FlattenedLength / splitCount;
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
lengths.CopyTo(newLengths);
nint splitLength = newLengths[(int)dimension] / splitCount;
newLengths[(int)dimension] = splitLength;
scoped Span<NRange> sliceDims = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<NRange> sliceDimsRentedBuffer);
for (int i = 0; i < sliceDims.Length; i++)
{
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] = new Tensor<T>(values, newLengths, strides: []);
tensor.Slice(sliceDims).CopyTo(outputs[i]);
start += splitLength;
}
sliceDimsRentedBuffer.Dispose();
lengthsRentedBuffer.Dispose();
return outputs;
}
#endregion
#region Squeeze
/// <summary>
/// Removes all dimensions of length one from the <paramref name="tensor"/>.
/// </summary>
/// <param name="tensor">The <see cref="Tensor{T}"/> to remove all dimensions of length 1.</param>
public static Tensor<T> Squeeze<T>(this Tensor<T> tensor)
{
return SqueezeDimension(tensor, -1);
}
/// <summary>
/// Removes axis of length one from the <paramref name="tensor"/> for the given <paramref name="dimension"/>.
/// If the dimension is not of length one it will throw an exception.
/// </summary>
/// <param name="tensor">The <see cref="Tensor{T}"/> to remove dimension of length 1.</param>
/// <param name="dimension">The dimension to remove.</param>
public static Tensor<T> SqueezeDimension<T>(this Tensor<T> tensor, int dimension)
{
int rank = tensor.Rank;
if (rank == 0 && dimension == -1)
{
return tensor;
}
if ((dimension >= rank) || (dimension < -1))
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
scoped Span<nint> lengths = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == -1)
{
for (int i = 0, index = 0; i < tensor.Lengths.Length; i++)
{
if (tensor.Lengths[i] != 1)
{
lengths[index] = tensor.Lengths[i];
strides[index] = tensor.Strides[i];
index++;
}
else
{
rank--;
}
}
}
else
{
if (tensor.Lengths[dimension] != 1)
{
ThrowHelper.ThrowArgument_InvalidSqueezeAxis();
}
rank--;
tensor.Lengths[..dimension].CopyTo(lengths);
tensor.Lengths[(dimension + 1)..].CopyTo(lengths[dimension..]);
tensor.Strides[..dimension].CopyTo(strides);
tensor.Strides[(dimension + 1)..].CopyTo(strides[dimension..]);
}
if (rank == 0)
{
lengths[0] = 1;
strides[0] = 0;
rank = 1;
}
Tensor<T> output = new Tensor<T>(tensor._values, tensor._start, lengths[..rank], strides[..rank], tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
/// <summary>
/// Removes all dimensions of length one from the <paramref name="tensor"/>.
/// </summary>
/// <param name="tensor">The <see cref="TensorSpan{T}"/> to remove all dimensions of length 1.</param>
public static TensorSpan<T> Squeeze<T>(this scoped in TensorSpan<T> tensor)
{
return SqueezeDimension(tensor, -1);
}
/// <summary>
/// Removes axis of length one from the <paramref name="tensor"/> for the given <paramref name="dimension"/>.
/// If the dimension is not of length one it will throw an exception.
/// </summary>
/// <param name="tensor">The <see cref="TensorSpan{T}"/> to remove dimension of length 1.</param>
/// <param name="dimension">The dimension to remove.</param>
public static TensorSpan<T> SqueezeDimension<T>(this scoped in TensorSpan<T> tensor, int dimension)
{
int rank = tensor.Rank;
if (rank == 0 && dimension == -1)
{
return tensor;
}
if ((dimension >= rank) || (dimension < -1))
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
scoped Span<nint> lengths = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == -1)
{
for (int i = 0, index = 0; i < tensor.Lengths.Length; i++)
{
if (tensor.Lengths[i] != 1)
{
lengths[index] = tensor.Lengths[i];
strides[index] = tensor.Strides[i];
index++;
}
else
{
rank--;
}
}
}
else
{
if (tensor.Lengths[dimension] != 1)
{
ThrowHelper.ThrowArgument_InvalidSqueezeAxis();
}
rank--;
tensor.Lengths[..dimension].CopyTo(lengths);
tensor.Lengths[(dimension + 1)..].CopyTo(lengths[dimension..]);
tensor.Strides[..dimension].CopyTo(strides);
tensor.Strides[(dimension + 1)..].CopyTo(strides[dimension..]);
}
if (rank == 0)
{
lengths[0] = 1;
strides[0] = 0;
rank = 1;
}
TensorSpan<T> output = new TensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, lengths[..rank], strides[..rank], tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
/// <summary>
/// Removes all dimensions of length one from the <paramref name="tensor"/>.
/// </summary>
/// <param name="tensor">The <see cref="ReadOnlyTensorSpan{T}"/> to remove all dimensions of length 1.</param>
public static ReadOnlyTensorSpan<T> Squeeze<T>(this scoped in ReadOnlyTensorSpan<T> tensor)
{
return SqueezeDimension(tensor, -1);
}
/// <summary>
/// Removes axis of length one from the <paramref name="tensor"/> for the given <paramref name="dimension"/>.
/// If the dimension is not of length one it will throw an exception.
/// </summary>
/// <param name="tensor">The <see cref="ReadOnlyTensorSpan{T}"/> to remove dimension of length 1.</param>
/// <param name="dimension">The dimension to remove.</param>
public static ReadOnlyTensorSpan<T> SqueezeDimension<T>(this scoped in ReadOnlyTensorSpan<T> tensor, int dimension)
{
int rank = tensor.Rank;
if (rank == 0 && dimension == -1)
{
return tensor;
}
if ((dimension >= rank) || (dimension < -1))
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
scoped Span<nint> lengths = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(rank, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == -1)
{
for (int i = 0, index = 0; i < tensor.Lengths.Length; i++)
{
if (tensor.Lengths[i] != 1)
{
lengths[index] = tensor.Lengths[i];
strides[index] = tensor.Strides[i];
index++;
}
else
{
rank--;
}
}
}
else
{
if (tensor.Lengths[dimension] != 1)
{
ThrowHelper.ThrowArgument_InvalidSqueezeAxis();
}
rank--;
tensor.Lengths[..dimension].CopyTo(lengths);
tensor.Lengths[(dimension + 1)..].CopyTo(lengths[dimension..]);
tensor.Strides[..dimension].CopyTo(strides);
tensor.Strides[(dimension + 1)..].CopyTo(strides[dimension..]);
}
if (rank == 0)
{
lengths[0] = 1;
strides[0] = 0;
rank = 1;
}
ReadOnlyTensorSpan<T> output = new ReadOnlyTensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, lengths[..rank], strides[..rank], tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
#endregion
#region Stack
/// <summary>
/// Join multiple <see cref="Tensor{T}"/> along a new dimension that is added at position 0. All tensors must have equivalent shapes.
/// </summary>
/// <param name="tensors">Input <see cref="Tensor{T}"/>.</param>
/// <remarks>Inputs are aligned to the first tensor's shape by adding or removing leading singleton dimensions. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static Tensor<T> Stack<T>(params ReadOnlySpan<Tensor<T>> tensors)
{
return StackAlongDimension(0, tensors);
}
/// <summary>
/// Join multiple <see cref="Tensor{T}"/> along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have equivalent shapes.
/// </summary>
/// <param name="tensors">Input <see cref="Tensor{T}"/>.</param>
/// <param name="dimension">Index of where the new dimension will be.</param>
/// <remarks>The dimension refers to the first tensor's effective shape. Other inputs are aligned by adding or removing leading singleton dimensions. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static Tensor<T> StackAlongDimension<T>(int dimension, params ReadOnlySpan<Tensor<T>> tensors)
{
Tensor<T>[] outputs = UnsqueezeForStack(dimension, tensors);
ReadOnlySpan<nint> lengths = outputs[0].Lengths;
Tensor<T> result = CreateConcatenationResult<T>(lengths, dimension, outputs.Length);
CopyConcatenatedValues(dimension, outputs, result, lengths.Length);
return result;
}
/// <summary>
/// Join multiple <see cref="Tensor{T}"/> along a new dimension that is added at position 0. All tensors must have equivalent shapes.
/// </summary>
/// <param name="tensors">Input <see cref="Tensor{T}"/>.</param>
/// <param name="destination"></param>
/// <remarks>Inputs are aligned to the first tensor's shape by adding or removing leading singleton dimensions. The destination may differ from the result by leading singleton padding. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static ref readonly TensorSpan<T> Stack<T>(scoped in ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination)
{
return ref StackAlongDimension(tensors, destination, 0);
}
/// <summary>
/// Join multiple <see cref="Tensor{T}"/> along a new dimension. The axis parameter specifies the index of the new dimension. All tensors must have equivalent shapes.
/// </summary>
/// <param name="tensors">Input <see cref="Tensor{T}"/>.</param>
/// <param name="destination"></param>
/// <param name="dimension">Index of where the new dimension will be.</param>
/// <remarks>The dimension refers to the first tensor's effective shape. Other inputs are aligned by adding or removing leading singleton dimensions. The destination may differ from the result by leading singleton padding. Rank-zero empty tensors have effective shape <c>[0]</c>.</remarks>
public static ref readonly TensorSpan<T> StackAlongDimension<T>(scoped ReadOnlySpan<Tensor<T>> tensors, in TensorSpan<T> destination, int dimension)
{
Tensor<T>[] outputs = UnsqueezeForStack(dimension, tensors);
ReadOnlySpan<nint> lengths = outputs[0].Lengths;
ValidateConcatenationDestination(dimension, lengths, outputs.Length, destination);
ValidateConcatenationStorage<T>(outputs, destination);
CopyConcatenatedValues<T>(dimension, outputs, destination, lengths.Length);
return ref destination;
}
private static Tensor<T>[] UnsqueezeForStack<T>(int dimension, scoped ReadOnlySpan<Tensor<T>> tensors)
{
if (tensors.Length < 2)
{
ThrowHelper.ThrowArgument_StackTooFewTensors();
}
ref readonly TensorShape shape = ref TensorShape.Normalize(tensors[0]._shape);
int rank = shape.Rank;
ReadOnlySpan<nint> lengths = shape.Lengths;
for (int i = 1; i < tensors.Length; i++)
{
if (!TensorShape.AreLengthsEquivalent(lengths, tensors[i].Lengths))
{
ThrowHelper.ThrowArgument_StackShapesNotSame();
}
}
// Insertion also accepts the position immediately after the last axis.
if (dimension < 0 || dimension > rank)
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
Tensor<T>[] outputs = new Tensor<T>[tensors.Length];
for (int i = 0; i < tensors.Length; i++)
{
Tensor<T> tensor = tensors[i];
if (tensor.Rank != rank)
{
tensor = new Tensor<T>(tensor._values, tensor._start, TensorShape.WithRank(tensor._shape, rank));
}
outputs[i] = Unsqueeze(tensor, dimension);
}
return outputs;
}
#endregion
#region ToString
internal static string ToString<T>(in ReadOnlyTensorSpan<T> tensor, ReadOnlySpan<nint> maximumLengths, string typeName)
{
if (!maximumLengths.IsEmpty)
{
ArgumentOutOfRangeException.ThrowIfNotEqual(maximumLengths.Length, tensor.Rank);
foreach (nint maximumLength in maximumLengths)
{
ArgumentOutOfRangeException.ThrowIfNegative(maximumLength, nameof(maximumLengths));
}
}
var sb = new StringBuilder(typeName);
sb.Append('<');
sb.Append(typeof(T).Name);
sb.Append('>');
sb.Append('[');
sb.AppendJoin(", ", tensor.Lengths);
sb.Append(']');
if (!maximumLengths.IsEmpty)
{
sb.AppendLine(" {");
if (tensor.Rank == 1)
{
nint length = nint.Min(tensor.Lengths[0], maximumLengths[0]);
ToString(tensor, length, sb, indentLevel: 1);
sb.AppendLine();
}
else
{
ToString(tensor, maximumLengths, sb);
}
sb.Append('}');
}
return sb.ToString();
}
private static void ToString<T>(in ReadOnlyTensorSpan<T> tensor, ReadOnlySpan<nint> maximumLengths, StringBuilder sb, int indentLevel = 0)
{
int rank = tensor.Rank;
ReadOnlySpan<nint> lengths = tensor.Lengths;
nint firstDimensionLength = lengths[0];
nint length = nint.Min(firstDimensionLength, maximumLengths[0]);
if (indentLevel != 0)
{
if (rank != 1)
{
sb.Append(' ', indentLevel * 2);
sb.AppendLine("[");
}
else
{
ToString(tensor, length, sb, indentLevel);
return;
}
}
if (length != 0)
{
TensorShape tmpShape = TensorShape.Create(lengths[1..], tensor.Strides[1..], tensor.IsPinned);
ReadOnlyTensorSpan<T> tmpTensor = new ReadOnlyTensorSpan<T>(ref tensor._reference, tmpShape);
ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1);
for (nint i = 1; i < length; i++)
{
sb.AppendLine(",");
nint linearOffset = tensor._shape.GetLinearOffsetForDimension(i, 1);
tmpTensor = new ReadOnlyTensorSpan<T>(ref Unsafe.Add(ref tensor._reference, linearOffset), tmpShape);
ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1);
}
if (length != firstDimensionLength)
{
sb.AppendLine(",");
sb.Append(' ', (indentLevel + 1) * 2);
sb.Append("..");
}
sb.AppendLine();
}
else
{
sb.Append(' ', (indentLevel + 1) * 2);
sb.AppendLine("..");
}
sb.Append(' ', indentLevel * 2);
if (indentLevel != 0)
{
sb.Append(']');
}
}
private static void ToString<T>(in ReadOnlyTensorSpan<T> tensor, nint length, StringBuilder sb, int indentLevel)
{
sb.Append(' ', indentLevel * 2);
sb.Append('[');
if (length != 0)
{
sb.Append(tensor._reference);
for (nint i = 1; i < length; i++)
{
sb.Append(", ");
sb.Append(Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]));
}
if (length != tensor.Lengths[0])
{
sb.Append(", ..");
}
}
else
{
sb.Append("..");
}
sb.Append(']');
}
private static StringBuilder AppendJoin<T>(this StringBuilder sb, string separator, ReadOnlySpan<T> values)
{
if (values.IsEmpty)
{
return sb;
}
if (values[0] is not null)
{
sb.Append(values[0]);
}
for (int i = 1; i < values.Length; i++)
{
sb.Append(separator);
if (values[i] is not null)
{
sb.Append(values[i]);
}
}
return sb;
}
#endregion
#region Transpose
/// <summary>
/// Swaps the last two dimensions of the <paramref name="tensor"/> tensor.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
public static Tensor<T> Transpose<T>(Tensor<T> tensor)
{
if (tensor.Lengths.Length < 2)
ThrowHelper.ThrowArgument_TransposeTooFewDimensions();
scoped Span<nint> lengths = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
scoped Span<nint> strides = TensorOperation.RentedBuffer.CreateUninitialized(tensor.Rank, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
tensor.Lengths.CopyTo(lengths);
tensor.Strides.CopyTo(strides);
nint temp = lengths[^1];
lengths[^1] = lengths[^2];
lengths[^2] = temp;
temp = strides[^1];
strides[^1] = strides[^2];
strides[^2] = temp;
Tensor<T> output = new Tensor<T>(tensor._values, tensor._start, lengths, strides, tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
#endregion
#region TryBroadcastTo
/// <summary>
/// Broadcast the data from <paramref name="tensor"/> to the smallest broadcastable shape compatible with <paramref name="destination"/> and stores it in <paramref name="destination"/>
/// If the shapes are not compatible, false is returned.
/// </summary>
/// <param name="tensor">Input <see cref="Tensor{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/>.</param>
public static bool TryBroadcastTo<T>(this Tensor<T> tensor, in TensorSpan<T> destination)
{
return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination);
}
/// <summary>
/// Broadcast the data from <paramref name="tensor"/> to the smallest broadcastable shape compatible with <paramref name="destination"/> and stores it in <paramref name="destination"/>
/// If the shapes are not compatible, false is returned.
/// </summary>
/// <param name="tensor">Input <see cref="TensorSpan{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/>.</param>
public static bool TryBroadcastTo<T>(in this TensorSpan<T> tensor, in TensorSpan<T> destination)
{
return tensor.AsReadOnlyTensorSpan().TryBroadcastTo(destination);
}
/// <summary>
/// Broadcast the data from <paramref name="tensor"/> to the smallest broadcastable shape compatible with <paramref name="destination"/> and stores it in <paramref name="destination"/>
/// If the shapes are not compatible, false is returned.
/// </summary>
/// <param name="tensor">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination">Destination <see cref="TensorSpan{T}"/>.</param>
public static bool TryBroadcastTo<T>(in this ReadOnlyTensorSpan<T> tensor, in TensorSpan<T> destination)
{
if (!TensorShape.AreCompatible(destination._shape, tensor._shape, false))
{
return false;
}
BroadcastTo(tensor, destination);
return true;
}
#endregion
#region Unsqueeze
/// <summary>
/// Insert a new dimension of length 1 that will appear at the dimension position.
/// </summary>
/// <param name="tensor">The <see cref="Tensor{T}"/> to add a dimension of length 1.</param>
/// <param name="dimension">The index of the dimension to add.</param>
public static Tensor<T> Unsqueeze<T>(this Tensor<T> tensor, int dimension)
{
if (tensor.Rank == 0)
{
TensorSpan<T> unsqueezed = tensor.AsTensorSpan().Unsqueeze(dimension);
return new Tensor<T>(tensor._values, tensor._start, unsqueezed._shape);
}
int rank = tensor.Rank;
ReadOnlySpan<nint> lengths = tensor.Lengths;
ReadOnlySpan<nint> strides = tensor.Strides;
if (dimension > rank)
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
if (dimension < 0)
{
dimension = rank - dimension;
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
lengths.Slice(0, dimension).CopyTo(newLengths);
lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1));
newLengths[dimension] = 1;
Span<nint> newStrides = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == rank)
{
strides.CopyTo(newStrides);
newStrides[dimension] = 0;
}
else
{
strides.Slice(0, dimension).CopyTo(newStrides);
strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1));
newStrides[dimension] = 0;
}
Tensor<T> output = new Tensor<T>(tensor._values, tensor._start, newLengths, newStrides, tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
/// <summary>
/// Insert a new dimension of length 1 that will appear at the dimension position.
/// </summary>
/// <param name="tensor">The <see cref="TensorSpan{T}"/> to add a dimension of length 1.</param>
/// <param name="dimension">The index of the dimension to add.</param>
public static TensorSpan<T> Unsqueeze<T>(this scoped in TensorSpan<T> tensor, int dimension)
{
ref readonly TensorShape shape = ref TensorShape.Normalize(tensor._shape);
int rank = shape.Rank;
ReadOnlySpan<nint> lengths = shape.Lengths;
ReadOnlySpan<nint> strides = shape.Strides;
if (dimension > rank)
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
if (dimension < 0)
{
dimension = rank - dimension;
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
lengths.Slice(0, dimension).CopyTo(newLengths);
lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1));
newLengths[dimension] = 1;
Span<nint> newStrides = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == rank)
{
strides.CopyTo(newStrides);
newStrides[dimension] = 0;
}
else
{
strides.Slice(0, dimension).CopyTo(newStrides);
strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1));
newStrides[dimension] = 0;
}
TensorSpan<T> output = new TensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, newLengths, newStrides, tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
/// <summary>
/// Insert a new dimension of length 1 that will appear at the dimension position.
/// </summary>
/// <param name="tensor">The <see cref="ReadOnlyTensorSpan{T}"/> to add a dimension of length 1.</param>
/// <param name="dimension">The index of the dimension to add.</param>
public static ReadOnlyTensorSpan<T> Unsqueeze<T>(this scoped in ReadOnlyTensorSpan<T> tensor, int dimension)
{
ref readonly TensorShape shape = ref TensorShape.Normalize(tensor._shape);
int rank = shape.Rank;
ReadOnlySpan<nint> lengths = shape.Lengths;
ReadOnlySpan<nint> strides = shape.Strides;
if (dimension > rank)
{
ThrowHelper.ThrowArgument_AxisLargerThanRank();
}
if (dimension < 0)
{
dimension = rank - dimension;
}
scoped Span<nint> newLengths = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> lengthsRentedBuffer);
lengths.Slice(0, dimension).CopyTo(newLengths);
lengths.Slice(dimension).CopyTo(newLengths.Slice(dimension + 1));
newLengths[dimension] = 1;
Span<nint> newStrides = TensorOperation.RentedBuffer.CreateUninitialized(rank + 1, out TensorOperation.RentedBuffer<nint> stridesRentedBuffer);
if (dimension == rank)
{
strides.CopyTo(newStrides);
newStrides[dimension] = 0;
}
else
{
strides.Slice(0, dimension).CopyTo(newStrides);
strides.Slice(dimension).CopyTo(newStrides.Slice(dimension + 1));
newStrides[dimension] = 0;
}
ReadOnlyTensorSpan<T> output = new ReadOnlyTensorSpan<T>(ref tensor._reference, tensor._shape.LinearLength, newLengths, newStrides, tensor.IsPinned);
lengthsRentedBuffer.Dispose();
stridesRentedBuffer.Dispose();
return output;
}
#endregion
#region TensorPrimitives
#region Abs
/// <summary>
/// Takes the absolute value of each element of the <see cref="Tensor{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the abs of.</param>
public static Tensor<T> Abs<T>(in ReadOnlyTensorSpan<T> x)
where T : INumberBase<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Abs<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the absolute value of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="TensorSpan{T}"/> to take the abs of.</param>
/// <param name="destination">The <see cref="TensorSpan{T}"/> destination.</param>
public static ref readonly TensorSpan<T> Abs<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : INumberBase<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Abs<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Acos
/// <summary>
/// Takes the inverse cosine of each element of the <see cref="Tensor{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Acos<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Acos<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse cosine of each element of the <see cref="Tensor{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="TensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Acos<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Acos<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Acosh
/// <summary>
/// Takes the inverse hyperbolic cosine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Acosh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Acosh<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic cosine of each element of the <see cref="TensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="TensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Acosh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Acosh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region AcosPi
/// <summary>
/// Takes the inverse hyperbolic cosine divided by pi of each element of the <see cref="Tensor{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> AcosPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.AcosPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic cosine divided by pi of each element of the <see cref="TensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> AcosPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.AcosPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Asin
/// <summary>
/// Takes the inverse sin of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Asin<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Asin<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse sin of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Asin<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Asin<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Asinh
/// <summary>
/// Takes the inverse hyperbolic sine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Asinh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Asinh<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic sine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Asinh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Asinh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region AsinPi
/// <summary>
/// Takes the inverse hyperbolic sine divided by pi of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> AsinPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.AsinPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic sine divided by pi of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> AsinPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.AsinPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Atan
/// <summary>
/// Takes the arc tangent of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/></param>
public static Tensor<T> Atan<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Atan<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Atan<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Atan2
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(y.Lengths);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(y, destination);
TensorOperation.Invoke<TensorOperation.Atan2<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Atan2Pi
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2Pi<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2Pi<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2Pi<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2Pi<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atan2Pi<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(y.Lengths);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// Takes the arc tangent of the two input <see cref="ReadOnlyTensorSpan{T}"/>, divides each element by pi, and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atan2Pi<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(y, destination);
TensorOperation.Invoke<TensorOperation.Atan2Pi<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Atanh
/// <summary>
/// Takes the inverse hyperbolic tangent of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Atanh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Atanh<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic tangent of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Atanh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Atanh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region AtanPi
/// <summary>
/// Takes the inverse hyperbolic tangent divided by pi of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The input<see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> AtanPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.AtanPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the inverse hyperbolic tangent divided by pi of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The input<see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> AtanPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.AtanPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Average
/// <summary>
/// Returns the average of the elements in the <paramref name="x"/> tensor.
/// </summary>
/// <param name="x">The <see cref="TensorSpan{T}"/> to take the mean of.</param>
/// <returns><typeparamref name="T"/> representing the mean.</returns>
public static T Average<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumberBase<T>
{
// 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
#region CubeRoot
/// <summary>
/// Computes the element-wise cube root of the input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Cbrt<T>(in ReadOnlyTensorSpan<T> x)
where T : IRootFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Cbrt<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise cube root of the input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Cbrt<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Cbrt<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Ceiling
/// <summary>
/// Computes the element-wise ceiling of the input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Ceiling<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Ceiling<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise ceiling of the input <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Ceiling<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Ceiling<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region ConvertChecked
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="ReadOnlyTensorSpan{TTO}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<TTo> ConvertChecked<TFrom, TTo>(in ReadOnlyTensorSpan<TFrom> source)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
Tensor<TTo> destination = CreateFromShapeUninitialized<TTo>(source.Lengths);
TensorOperation.Invoke<TensorOperation.ConvertChecked<TFrom, TTo>, TFrom, TTo>(source, destination);
return destination;
}
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="TensorSpan{TTo}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="TensorSpan{TFrom}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<TTo> ConvertChecked<TFrom, TTo>(scoped in ReadOnlyTensorSpan<TFrom> source, in TensorSpan<TTo> destination)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
TensorOperation.ValidateCompatibility(source, destination);
TensorOperation.Invoke<TensorOperation.ConvertChecked<TFrom, TTo>, TFrom, TTo>(source, destination);
return ref destination;
}
#endregion
#region ConvertSaturating
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="ReadOnlyTensorSpan{TTO}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<TTo> ConvertSaturating<TFrom, TTo>(in ReadOnlyTensorSpan<TFrom> source)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
Tensor<TTo> destination = CreateFromShapeUninitialized<TTo>(source.Lengths);
TensorOperation.Invoke<TensorOperation.ConvertSaturating<TFrom, TTo>, TFrom, TTo>(source, destination);
return destination;
}
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="TensorSpan{TTo}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="TensorSpan{TFrom}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<TTo> ConvertSaturating<TFrom, TTo>(scoped in ReadOnlyTensorSpan<TFrom> source, in TensorSpan<TTo> destination)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
TensorOperation.ValidateCompatibility(source, destination);
TensorOperation.Invoke<TensorOperation.ConvertSaturating<TFrom, TTo>, TFrom, TTo>(source, destination);
return ref destination;
}
#endregion
#region ConvertTruncating
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="ReadOnlyTensorSpan{TTO}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<TTo> ConvertTruncating<TFrom, TTo>(in ReadOnlyTensorSpan<TFrom> source)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
Tensor<TTo> destination = CreateFromShapeUninitialized<TTo>(source.Lengths);
TensorOperation.Invoke<TensorOperation.ConvertTruncating<TFrom, TTo>, TFrom, TTo>(source, destination);
return destination;
}
/// <summary>
/// Copies <paramref name="source"/> to a new <see cref="TensorSpan{TTo}"/> converting each <typeparamref name="TFrom"/>
/// value to a <typeparamref name="TTo"/> value.
/// </summary>
/// <param name="source">The input <see cref="TensorSpan{TFrom}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<TTo> ConvertTruncating<TFrom, TTo>(scoped in ReadOnlyTensorSpan<TFrom> source, in TensorSpan<TTo> destination)
where TFrom : IEquatable<TFrom>, IEqualityOperators<TFrom, TFrom, bool>, INumberBase<TFrom>
where TTo : INumberBase<TTo>
{
TensorOperation.ValidateCompatibility(source, destination);
TensorOperation.Invoke<TensorOperation.ConvertTruncating<TFrom, TTo>, TFrom, TTo>(source, destination);
return ref destination;
}
#endregion
#region CopySign
/// <summary>
/// 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.
/// </summary>
/// <param name="x">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="sign">The number with the associated sign.</param>
public static Tensor<T> CopySign<T>(in ReadOnlyTensorSpan<T> x, T sign)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.CopySign<T>, T, T>(x, sign, destination);
return destination;
}
/// <summary>
/// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="sign">The <see cref="ReadOnlyTensorSpan{T}"/> with the associated signs.</param>
public static Tensor<T> CopySign<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> sign)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(x, sign, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.CopySign<T>, T, T>(x, sign, destination);
return destination;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="sign">The number with the associated sign.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> CopySign<T>(scoped in ReadOnlyTensorSpan<T> x, T sign, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.CopySign<T>, T, T>(x, sign, destination);
return ref destination;
}
/// <summary>
/// Computes the element-wise result of copying the sign from one number to another number in the specified tensors and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">Input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="sign">The <see cref="ReadOnlyTensorSpan{T}"/> with the associated signs.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> CopySign<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> sign, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(x, sign, destination);
TensorOperation.Invoke<TensorOperation.CopySign<T>, T, T>(x, sign, destination);
return ref destination;
}
#endregion
#region Cos
/// <summary>
/// Takes the cosine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the cosine of.</param>
public static Tensor<T> Cos<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Cos<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the cosine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the cosine of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Cos<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Cos<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Cosh
/// <summary>
/// Takes the hyperbolic cosine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the cosine of.</param>
public static Tensor<T> Cosh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Cosh<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the hyperbolic cosine of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the cosine of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Cosh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Cosh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region CosineSimilarity
/// <summary>
/// Compute cosine similarity between <paramref name="x"/> and <paramref name="y"/>.
/// </summary>
/// <param name="x">The first <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="y">The second <see cref="ReadOnlyTensorSpan{T}"/></param>
public static T CosineSimilarity<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y);
ValueTuple<T, T, T> result = (T.AdditiveIdentity, T.AdditiveIdentity, T.AdditiveIdentity);
TensorOperation.Invoke<TensorOperation.CosineSimilarity<T>, T, ValueTuple<T, T, T>>(x, y, ref result);
return result.Item1 / (T.Sqrt(result.Item2) * T.Sqrt(result.Item3));
}
#endregion
#region CosPi
/// <summary>Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new <see cref="Tensor{T}"/> with the results.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <remarks>
/// <para>
/// This method effectively computes <c><typeparamref name="T"/>.CosPi(<paramref name="x" />[i])</c>.
/// </para>
/// <para>
/// The angles in x must be in radians. Use <see cref="M:System.Single.DegreesToRadians(System.Single)"/> or multiply by <typeparamref name="T"/>.Pi/180 to convert degrees to radians.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
public static Tensor<T> CosPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.CosPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi and returns a new <see cref="TensorSpan{T}"/> with the results.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
/// <remarks>
/// <para>
/// This method effectively computes <c><typeparamref name="T"/>.CosPi(<paramref name="x" />[i])</c>.
/// </para>
/// <para>
/// The angles in x must be in radians. Use <see cref="M:System.Single.DegreesToRadians(System.Single)"/> or multiply by <typeparamref name="T"/>.Pi/180 to convert degrees to radians.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
public static ref readonly TensorSpan<T> CosPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.CosPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region DegreesToRadians
/// <summary>
/// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> DegreesToRadians<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.DegreesToRadians<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise conversion of each number of degrees in the specified tensor to radians and returns a new tensor with the results.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> DegreesToRadians<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.DegreesToRadians<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Distance
/// <summary>
/// Computes the distance between two points, specified as non-empty, equal-length tensors of numbers, in Euclidean space.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T Distance<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y);
T result = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.SumOfSquaredDifferences<T>, T, T>(x, y, ref result);
return T.Sqrt(result);
}
#endregion
#region Dot
/// <summary>
/// Computes the dot product of two tensors containing numbers.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T Dot<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>, IMultiplicativeIdentity<T, T>, IMultiplyOperators<T, T, T>
{
TensorOperation.ValidateCompatibility(x, y);
T result = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.Dot<T>, T, T>(x, y, ref result);
return result;
}
#endregion
#region Exp
/// <summary>
/// Computes the element-wise result of raising <c>e</c> to the single-precision floating-point number powers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Exp<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Exp<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise result of raising <c>e</c> to the single-precision floating-point number powers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Exp<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Exp<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Exp10
/// <summary>
/// Computes the element-wise result of raising 10 to the number powers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Exp10<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Exp10<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise result of raising 10 to the number powers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Exp10<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Exp10<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Exp10M1
/// <summary>Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Exp10M1<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Exp10M1<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Exp10M1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Exp10M1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Exp2
/// <summary>Computes the element-wise result of raising 2 to the number powers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Exp2<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Exp2<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise result of raising 2 to the number powers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Exp2<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Exp2<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Exp2M1
/// <summary>Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Exp2M1<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Exp2M1<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Exp2M1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Exp2M1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region ExpM1
/// <summary>Computes the element-wise result of raising <c>e</c> to the number powers in the specified tensor, minus 1.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> ExpM1<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.ExpM1<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise result of raising <c>e</c> to the number powers in the specified tensor, minus 1.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> ExpM1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.ExpM1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Floor
/// <summary>Computes the element-wise floor of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Floor<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Floor<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise floor of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Floor<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Floor<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Hypotenuse
/// <summary>
/// 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.
/// </summary>
/// <param name="x">Left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">Right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Hypot<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Hypot<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">Left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">Right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Hypot<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Hypot<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Ieee754Remainder
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// If the shapes are not the same they are broadcast to the smallest compatible shape.
/// <param name="x">Left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">Right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Ieee754Remainder<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// If the shapes are not the same they are broadcast to the smallest compatible shape.
/// <param name="x">Left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">Right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Ieee754Remainder<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Ieee754Remainder<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Ieee754Remainder<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Ieee754Remainder<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IFloatingPointIeee754<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(y.Lengths);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise remainder of the numbers in the specified tensors.</summary>
/// <param name="x">The left <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The right <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Ieee754Remainder<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(y, destination);
TensorOperation.Invoke<TensorOperation.Ieee754Remainder<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region ILogB
/// <summary>Computes the element-wise integer logarithm of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<int> ILogB<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPointIeee754<T>
{
Tensor<int> destination = CreateFromShapeUninitialized<int>(x.Lengths);
TensorOperation.Invoke<TensorOperation.ILogB<T>, T, int>(x, destination);
return destination;
}
/// <summary>Computes the element-wise integer logarithm of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<int> ILogB<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<int> destination)
where T : IFloatingPointIeee754<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.ILogB<T>, T, int>(x, destination);
return ref destination;
}
#endregion
#region IndexOfMax
/// <summary>Searches for the index of the largest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static nint IndexOfMax<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
return TensorOperation.IndexOfMinMax<T, TensorPrimitives.IndexOfMaxOperator<T>>(x);
}
#endregion
#region IndexOfMaxMagnitude
/// <summary>Searches for the index of the number with the largest magnitude in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static nint IndexOfMaxMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
return TensorOperation.IndexOfMinMax<T, TensorPrimitives.IndexOfMaxMagnitudeOperator<T>>(x);
}
#endregion
#region IndexOfMin
/// <summary>Searches for the index of the smallest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static nint IndexOfMin<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
return TensorOperation.IndexOfMinMax<T, TensorPrimitives.IndexOfMinOperator<T>>(x);
}
#endregion
#region IndexOfMinMagnitude
/// <summary>
/// Searches for the index of the number with the smallest magnitude in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static nint IndexOfMinMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
return TensorOperation.IndexOfMinMax<T, TensorPrimitives.IndexOfMinMagnitudeOperator<T>>(x);
}
#endregion
#region LeadingZeroCount
/// <summary>
/// Computes the element-wise leading zero count of numbers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> LeadingZeroCount<T>(in ReadOnlyTensorSpan<T> x)
where T : IBinaryInteger<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.LeadingZeroCount<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Computes the element-wise leading zero count of numbers in the specified tensor.
/// </summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> LeadingZeroCount<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IBinaryInteger<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.LeadingZeroCount<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Log
/// <summary>
/// Takes the natural logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the natural logarithm of.</param>
public static Tensor<T> Log<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the natural logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the natural logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Log<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, destination);
return ref destination;
}
/// <summary>Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor.</summary>
/// <param name="x">The first tensor</param>
/// <param name="y">The second tensor</param>
public static Tensor<T> Log<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor.</summary>
/// <param name="x">The first tensor</param>
/// <param name="y">The second tensor</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Log<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor.</summary>
/// <param name="x">The first tensor</param>
/// <param name="y">The second tensor</param>
public static Tensor<T> Log<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor.</summary>
/// <param name="x">The first tensor</param>
/// <param name="y">The second tensor</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Log<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Log10
/// <summary>
/// Takes the base 10 logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 10 logarithm of.</param>
public static Tensor<T> Log10<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log10<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the base 10 logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 10 logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Log10<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log10<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Log10P1
/// <summary>
/// Takes the base 10 logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 10 logarithm of.</param>
public static Tensor<T> Log10P1<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log10P1<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the base 10 logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 10 logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Log10P1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log10P1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Log2
/// <summary>
/// Takes the base 2 logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 2 logarithm of.</param>
public static Tensor<T> Log2<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log2<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the base 2 logarithm of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 2 logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Log2<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log2<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Log2P1
/// <summary>
/// Takes the base 2 logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 2 logarithm of.</param>
public static Tensor<T> Log2P1<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Log2P1<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the base 2 logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the base 2 logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Log2P1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Log2P1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region LogP1
/// <summary>
/// Takes the natural logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the natural logarithm of.</param>
public static Tensor<T> LogP1<T>(in ReadOnlyTensorSpan<T> x)
where T : ILogarithmicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.LogP1<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the natural logarithm plus 1 of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the natural logarithm of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> LogP1<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ILogarithmicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.LogP1<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Max
/// <summary>Searches for the largest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T Max<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.Max<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> Max<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> output);
TensorOperation.Invoke<TensorOperation.Max<T>, T, T>(x, y, output);
return output;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Max<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.Max<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> Max<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Max<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Max<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.Max<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MaxMagnitude
/// <summary>Searches for the number with the largest magnitude in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MaxMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MaxMagnitude<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxMagnitude<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitude<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitude<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxMagnitude<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MaxMagnitude<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitude<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MaxMagnitudeNumber
/// <summary>Searches for the number with the largest magnitude in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MaxMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumberBase<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MaxMagnitudeNumber<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxMagnitudeNumber<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitudeNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitudeNumber<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxMagnitudeNumber<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MaxMagnitudeNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the largest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MaxMagnitudeNumber<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MaxNumber
/// <summary>Searches for the largest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MaxNumber<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MaxNumber<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxNumber<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MaxNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxNumber<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MaxNumber<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MaxNumber<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MaxNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise maximum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MaxNumber<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MaxNumber<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Min
/// <summary>Searches for the smallest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T Min<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.Min<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> Min<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> output);
TensorOperation.Invoke<TensorOperation.Min<T>, T, T>(x, y, output);
return output;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Min<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.Min<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> Min<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Min<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> Min<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.Min<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MinMagnitude
/// <summary>Searches for the number with the smallest magnitude in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MinMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MinMagnitude<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinMagnitude<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MinMagnitude<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MinMagnitude<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinMagnitude<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MinMagnitude<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinMagnitude<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MinMagnitude<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MinMagnitudeNumber
/// <summary>Searches for the number with the smallest magnitude in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MinMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumberBase<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MinMagnitudeNumber<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinMagnitudeNumber<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MinMagnitudeNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MinMagnitudeNumber<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinMagnitudeNumber<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MinMagnitudeNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise number with the smallest magnitude in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinMagnitudeNumber<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MinMagnitudeNumber<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region MinNumber
/// <summary>Searches for the smallest number in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T MinNumber<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : INumber<T>
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
T result = x._reference;
TensorOperation.Invoke<TensorOperation.MinNumber<T>, T, T>(x, ref result);
return result;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinNumber<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.MinNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinNumber<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in y, in destination);
TensorOperation.Invoke<TensorOperation.MinNumber<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
public static Tensor<T> MinNumber<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : INumber<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.MinNumber<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise minimum of the numbers in the specified tensors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
public static ref readonly TensorSpan<T> MinNumber<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : INumber<T>
{
TensorOperation.ValidateCompatibility(in x, in destination);
TensorOperation.Invoke<TensorOperation.MinNumber<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Norm
/// <summary>
/// Takes the norm of the <see cref="ReadOnlyTensorSpan{T}"/> and returns the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the norm of.</param>
public static T Norm<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : IRootFunctions<T>
{
T result = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.SumOfSquares<T>, T, T>(x, ref result);
return T.Sqrt(result);
}
#endregion
#region PopCount
/// <summary>Computes the element-wise population count of numbers in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/></param>
public static Tensor<T> PopCount<T>(in ReadOnlyTensorSpan<T> x)
where T : IBinaryInteger<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.PopCount<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise population count of numbers in the specified tensor.</summary>
/// <param name="y">The <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> PopCount<T>(scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IBinaryInteger<T>
{
TensorOperation.ValidateCompatibility(y, destination);
TensorOperation.Invoke<TensorOperation.PopCount<T>, T, T>(y, destination);
return ref destination;
}
#endregion
#region Pow
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input <see cref="ReadOnlyTensorSpan{T}"/></param>
public static Tensor<T> Pow<T>(in ReadOnlyTensorSpan<T> x, in ReadOnlyTensorSpan<T> y)
where T : IPowerFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, out Tensor<T> destination);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Pow<T>(scoped in ReadOnlyTensorSpan<T> x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IPowerFunctions<T>
{
TensorOperation.ValidateCompatibility(x, y, destination);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input</param>
public static Tensor<T> Pow<T>(in ReadOnlyTensorSpan<T> x, T y)
where T : IPowerFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Pow<T>(scoped in ReadOnlyTensorSpan<T> x, T y, in TensorSpan<T> destination)
where T : IPowerFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return ref destination;
}
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input</param>
public static Tensor<T> Pow<T>(T x, in ReadOnlyTensorSpan<T> y)
where T : IPowerFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(y.Lengths);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return destination;
}
/// <summary>Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="y">The second input <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Pow<T>(T x, scoped in ReadOnlyTensorSpan<T> y, in TensorSpan<T> destination)
where T : IPowerFunctions<T>
{
TensorOperation.ValidateCompatibility(y, destination);
TensorOperation.Invoke<TensorOperation.Pow<T>, T, T>(x, y, destination);
return ref destination;
}
#endregion
#region Product
/// <summary>Computes the product of all elements in the specified non-empty tensor of numbers.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static T Product<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : IMultiplicativeIdentity<T, T>, IMultiplyOperators<T, T, T>
{
T destination = T.MultiplicativeIdentity;
TensorOperation.Invoke<TensorOperation.Product<T>, T, T>(x, ref destination);
return destination;
}
#endregion
#region RadiansToDegrees
/// <summary>Computes the element-wise conversion of each number of radians in the specified tensor to degrees.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> RadiansToDegrees<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.RadiansToDegrees<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise conversion of each number of radians in the specified tensor to degrees.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> RadiansToDegrees<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.RadiansToDegrees<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Reciprocal
/// <summary>Computes the element-wise reciprocal of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Reciprocal<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Reciprocal<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise reciprocal of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Reciprocal<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Reciprocal<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region RootN
/// <summary>Computes the element-wise n-th root of the values in the specified tensor.</summary>
/// <param name="x">The tensor, represented as a span.</param>
/// <param name="n">The degree of the root to be computed, represented as a scalar.</param>
public static Tensor<T> RootN<T>(in ReadOnlyTensorSpan<T> x, int n)
where T : IRootFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.RootN<T>, T, T>(x, n, destination);
return destination;
}
/// <summary>Computes the element-wise n-th root of the values in the specified tensor.</summary>
/// <param name="x">The tensor, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <param name="n">The degree of the root to be computed, represented as a scalar.</param>
public static ref readonly TensorSpan<T> RootN<T>(scoped in ReadOnlyTensorSpan<T> x, int n, in TensorSpan<T> destination)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.RootN<T>, T, T>(x, n, destination);
return ref destination;
}
#endregion
#region RotateLeft
/// <summary>Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount.</summary>
/// <param name="x">The tensor</param>
/// <param name="rotateAmount">The number of bits to rotate, represented as a scalar.</param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
public static Tensor<T> RotateLeft<T>(in ReadOnlyTensorSpan<T> x, int rotateAmount)
where T : IBinaryInteger<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.RotateLeft<T>, T, T>(x, rotateAmount, destination);
return destination;
}
/// <summary>Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount.</summary>
/// <param name="x">The tensor</param>
/// <param name="rotateAmount">The number of bits to rotate, represented as a scalar.</param>
/// <param name="destination"></param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
public static ref readonly TensorSpan<T> RotateLeft<T>(scoped in ReadOnlyTensorSpan<T> x, int rotateAmount, in TensorSpan<T> destination)
where T : IBinaryInteger<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.RotateLeft<T>, T, T>(x, rotateAmount, destination);
return ref destination;
}
#endregion
#region RotateRight
/// <summary>Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount.</summary>
/// <param name="x">The tensor</param>
/// <param name="rotateAmount">The number of bits to rotate, represented as a scalar.</param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
public static Tensor<T> RotateRight<T>(in ReadOnlyTensorSpan<T> x, int rotateAmount)
where T : IBinaryInteger<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.RotateRight<T>, T, T>(x, rotateAmount, destination);
return destination;
}
/// <summary>Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount.</summary>
/// <param name="x">The tensor</param>
/// <param name="rotateAmount">The number of bits to rotate, represented as a scalar.</param>
/// <param name="destination"></param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
public static ref readonly TensorSpan<T> RotateRight<T>(scoped in ReadOnlyTensorSpan<T> x, int rotateAmount, in TensorSpan<T> destination)
where T : IBinaryInteger<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.RotateRight<T>, T, T>(x, rotateAmount, destination);
return ref destination;
}
#endregion
#region Round
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Round<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Round<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Round<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Round<T>, T, T>(x, destination);
return ref destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="digits"></param>
/// <param name="mode"></param>
public static Tensor<T> Round<T>(in ReadOnlyTensorSpan<T> x, int digits, MidpointRounding mode)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(digits, mode), destination);
return destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="digits"></param>
/// <param name="mode"></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Round<T>(scoped in ReadOnlyTensorSpan<T> x, int digits, MidpointRounding mode, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(digits, mode), in destination);
return ref destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="digits"></param>
public static Tensor<T> Round<T>(in ReadOnlyTensorSpan<T> x, int digits)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(digits, MidpointRounding.ToEven), destination);
return destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="digits"></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Round<T>(scoped in ReadOnlyTensorSpan<T> x, int digits, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(digits, MidpointRounding.ToEven), in destination);
return ref destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="mode"></param>
public static Tensor<T> Round<T>(in ReadOnlyTensorSpan<T> x, MidpointRounding mode)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(0, mode), destination);
return destination;
}
/// <summary>Computes the element-wise rounding of the numbers in the specified tensor</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="mode"></param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Round<T>(scoped in ReadOnlyTensorSpan<T> x, MidpointRounding mode, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Round<T>, T, Tuple<int, MidpointRounding>, T>(x, Tuple.Create(0, mode), in destination);
return ref destination;
}
#endregion
#region Sigmoid
/// <summary>Computes the element-wise sigmoid function on the specified non-empty tensor of numbers.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Sigmoid<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Sigmoid<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise sigmoid function on the specified non-empty tensor of numbers.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Sigmoid<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Sigmoid<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Sin
/// <summary>
/// Takes the sin of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Sin<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Sin<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the sin of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Sin<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Sin<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Sinh
/// <summary>Computes the element-wise hyperbolic sine of each radian angle in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Sinh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Sinh<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise hyperbolic sine of each radian angle in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Sinh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Sinh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region SinPi
/// <summary>Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> SinPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.SinPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> SinPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.SinPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region SoftMax
/// <summary>Computes the softmax function over the specified non-empty tensor of numbers.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> SoftMax<T>(in ReadOnlyTensorSpan<T> x)
where T : IExponentialFunctions<T>
{
(T Shift, T Sum, bool HasValue) sumExp = default;
TensorOperation.Invoke<TensorOperation.SumExp<T>, T, (T Shift, T Sum, bool HasValue)>(x, ref sumExp);
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.SoftMax<T>, T, (T Shift, T Sum), T>(x, (sumExp.Shift, sumExp.Sum), destination);
return destination;
}
/// <summary>Computes the softmax function over the specified non-empty tensor of numbers.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> SoftMax<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IExponentialFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
(T Shift, T Sum, bool HasValue) sumExp = default;
TensorOperation.Invoke<TensorOperation.SumExp<T>, T, (T Shift, T Sum, bool HasValue)>(x, ref sumExp);
TensorOperation.Invoke<TensorOperation.SoftMax<T>, T, (T Shift, T Sum), T>(x, (sumExp.Shift, sumExp.Sum), destination);
return ref destination;
}
#endregion
#region Sqrt
/// <summary>
/// Takes the square root of each element of the <see cref="ReadOnlyTensorSpan{T}"/> and returns a new <see cref="Tensor{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the square root of.</param>
public static Tensor<T> Sqrt<T>(in ReadOnlyTensorSpan<T> x)
where T : IRootFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Sqrt<T>, T, T>(x, destination);
return destination;
}
/// <summary>
/// Takes the square root of each element of the <paramref name="x"/> and returns a new <see cref="TensorSpan{T}"/> with the result.
/// </summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the square root of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Sqrt<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IRootFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Sqrt<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region StdDev
/// <summary>
/// Returns the standard deviation of the elements in the <paramref name="x"/> tensor.
/// </summary>
/// <param name="x">The <see cref="TensorSpan{T}"/> to take the standard deviation of.</param>
/// <returns><typeparamref name="T"/> representing the standard deviation.</returns>
public static T StdDev<T>(in ReadOnlyTensorSpan<T> x)
where T : IRootFunctions<T>
{
T mean = Average(x);
T result = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.SumOfSquaredAbsoluteDifferences<T>, T, T>(x, mean, ref result);
T variance = result / T.CreateChecked(x.FlattenedLength);
return T.Sqrt(variance);
}
#endregion
#region Sum
/// <summary>
/// Sums the elements of the specified tensor.
/// </summary>
/// <param name="x">Tensor to sum</param>
/// <returns></returns>
public static T Sum<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
{
T destination = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.Sum<T>, T, T>(x, ref destination);
return destination;
}
#endregion
#region SumOfSquares
/// <summary>
/// Sums the squared elements of the specified tensor.
/// </summary>
/// <param name="x">Tensor to sum squares of</param>
/// <returns></returns>
internal static T SumOfSquares<T>(scoped in ReadOnlyTensorSpan<T> x)
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>, IMultiplyOperators<T, T, T>
{
T result = T.AdditiveIdentity;
TensorOperation.Invoke<TensorOperation.SumOfSquares<T>, T, T>(x, ref result);
return result;
}
#endregion
#region Tan
/// <summary>Computes the element-wise tangent of the value in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Tan<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Tan<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise tangent of the value in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Tan<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Tan<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Tanh
/// <summary>Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> Tanh<T>(in ReadOnlyTensorSpan<T> x)
where T : IHyperbolicFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Tanh<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Tanh<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IHyperbolicFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Tanh<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region TanPi
/// <summary>Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
public static Tensor<T> TanPi<T>(in ReadOnlyTensorSpan<T> x)
where T : ITrigonometricFunctions<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.TanPi<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi.</summary>
/// <param name="x">The <see cref="ReadOnlyTensorSpan{T}"/> to take the sin of.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> TanPi<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : ITrigonometricFunctions<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.TanPi<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region TrailingZeroCount
/// <summary>Computes the element-wise trailing zero count of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> TrailingZeroCount<T>(in ReadOnlyTensorSpan<T> x)
where T : IBinaryInteger<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.TrailingZeroCount<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise trailing zero count of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> TrailingZeroCount<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IBinaryInteger<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.TrailingZeroCount<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#region Truncate
/// <summary>Computes the element-wise truncation of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
public static Tensor<T> Truncate<T>(in ReadOnlyTensorSpan<T> x)
where T : IFloatingPoint<T>
{
Tensor<T> destination = CreateFromShapeUninitialized<T>(x.Lengths);
TensorOperation.Invoke<TensorOperation.Truncate<T>, T, T>(x, destination);
return destination;
}
/// <summary>Computes the element-wise truncation of numbers in the specified tensor.</summary>
/// <param name="x">The input <see cref="ReadOnlyTensorSpan{T}"/>.</param>
/// <param name="destination"></param>
public static ref readonly TensorSpan<T> Truncate<T>(scoped in ReadOnlyTensorSpan<T> x, in TensorSpan<T> destination)
where T : IFloatingPoint<T>
{
TensorOperation.ValidateCompatibility(x, destination);
TensorOperation.Invoke<TensorOperation.Truncate<T>, T, T>(x, destination);
return ref destination;
}
#endregion
#endregion
}
}