Skip to content

Tensor<T> and supporting types, part 3 #104117

Open
@tannergooding

Description

@tannergooding

This is a continuation of #103611 and covers the various operation like APIs that do meaningful work with Tensor<T> and supporting types.

API Proposal -- New APIs for Tensor, TensorSpan, and TensorPrimitives

namespace System.Numerics.Tensors;

public static partial class Tensor
{
    public static T Mean<T>(in ROTensorSpan<T> x) where T : IFloatingPoint<T>;

    // The following APIs could have Tensor<bool> and bool versions,
    // following the `*()`, `*All()` and `*Any()` pattern:
    //
    // IBinaryNumber
    // * IsPow2
    // INumberBase
    // * IsCanonical
    // * IsComplexNumber
    // * IsEvenInteger
    // * IsFinite
    // * IsImaginaryNumber
    // * IsInfinity
    // * IsInteger
    // * IsNaN
    // * IsNegative
    // * IsNegativeInfinity
    // * IsNormal
    // * IsOddInteger
    // * IsPositive
    // * IsPositiveInfinity
    // * IsRealNumber
    // * IsSubnormal
    // * IsZero
}

public static partial class TensorPrimitives
{
    public static T Mean<T>(ROSpan<T> x) where T : IFloatingPoint<T>;

    // The following APIs could have Tensor<bool> and bool versions,
    // following the `*()`, `*All()` and `*Any()` pattern:
    //
    // IBinaryNumber
    // * IsPow2
    // INumberBase
    // * IsCanonical
    // * IsComplexNumber
    // * IsEvenInteger
    // * IsFinite
    // * IsImaginaryNumber
    // * IsInfinity
    // * IsInteger
    // * IsNaN
    // * IsNegative
    // * IsNegativeInfinity
    // * IsNormal
    // * IsOddInteger
    // * IsPositive
    // * IsPositiveInfinity
    // * IsRealNumber
    // * IsSubnormal
    // * IsZero
}

API Proposal -- APIs unique to Tensor and TensorSpan

These cover APIs that do not have an equivalent in TensorPrimitives, typically being functionality that is unique to multi-dimensional types:

namespace System.Numerics.Tensors;

public static partial class Tensor
{
    public static ROTensorSpan<T> AsROTensorSpan<T>(this T[]? array, params scoped ROSpan<nint> lengths);
    public static TensorSpan<T> AsTensorSpan<T>(this T[]? array, params scoped ROSpan<nint> lengths);

    public static Tensor<T> Broadcast<T>(in ROTensorSpan<T> x, ROSpan<nint> lengths);

    // This gets the lengths from lengthsSource
    public static Tensor<T> Broadcast<T>(in ROTensorSpan<T> x, in ROTensorSpan<T> lengthsSource);

    public static void BroadcastTo<T>(this Tensor<T> x, in TensorSpan<T> destination);
    public static void BroadcastTo<T>(in this TensorSpan<T> x, in TensorSpan<T> destination);
    public static void BroadcastTo<T>(in this ROTensorSpan<T> x, in TensorSpan<T> destination);

    // The parameter order here feels off. Ideally tensors could be params
    // and we'd have overloads taking 2-3x ROTensorSpan<T>. Maybe we can
    // explode the overloads a bit to make this nicer?
    public static Tensor<T> Concatenate<T>(ROSpan<Tensor<T>> tensors, int axis = 0);
    public static TensorSpan<T> Concatenate<T>(scoped ROSpan<Tensor<T>> tensors, in TensorSpan<T> destination, int axis = 0);

    public static Tensor<T> Create<T>(ROSpan<nint> lengths, bool pinned = false);
    public static Tensor<T> Create<T>(ROSpan<nint> lengths, ROSpan<nint> strides, bool pinned = false);
    public static Tensor<T> Create<T>(T[] values, ROSpan<nint> lengths);
    public static Tensor<T> Create<T>(T[] values, ROSpan<nint> lengths, ROSpan<nint> strides, bool isPinned = false);
    public static Tensor<T> Create<T>(IEnumerable<T> values, ROSpan<nint> lengths);
    public static Tensor<T> Create<T>(IEnumerable<T> values, ROSpan<nint> lengths, ROSpan<nint> strides, bool isPinned = false);

    public static Tensor<T> CreateAndFillGaussianNormalDistribution<T>(params ROSpan<nint> lengths) where T : IFloatingPoint<T>;
    public static Tensor<T> CreateAndFillUniformDistribution<T>(params ROSpan<nint> lengths) where T : IFloatingPoint<T>;

    public static Tensor<T> CreateUninitialized<T>(ROSpan<nint> lengths, bool pinned = false);
    public static Tensor<T> CreateUninitialized<T>(ROSpan<nint> lengths, ROSpan<nint> strides, bool pinned = false);

    public static TensorSpan<T> FillGaussianNormalDistribution<T>(in TensorSpan<T> destination) where T : IFloatingPoint<T>;
    public static TensorSpan<T> FillUniformDistribution<T>(in TensorSpan<T> destination) where T : IFloatingPoint<T>;

    // Should this be some overload of SetSlice or similar?
    public static TensorSpan<T> FilteredUpdate<T>(in this TensorSpan<T> x, scoped in ROTensorSpan<bool> filter, scoped in ROTensorSpan<T> values);
    public static TensorSpan<T> FilteredUpdate<T>(in this TensorSpan<T> x, scoped in ROTensorSpan<bool> filter, T value);

    // Change order of the stride parameters
    public static Tensor<T> Permute<T>(this Tensor<T> x, params ROSpan<int> axis);
    public static TensorSpan<T> Permute<T>(in this TensorSpan<T> x, params scoped ROSpan<int> axis);
    public static ROTensorSpan<T> Permute<T>(in this ROTensorSpan<T> x, params scoped ROSpan<int> axis);

    // Change the lengths
    public static Tensor<T> Reshape<T>(this Tensor<T> x, params ROSpan<nint> lengths);
    public static TensorSpan<T> Reshape<T>(in this TensorSpan<T> x, params scoped ROSpan<nint> lengths);
    public static ROTensorSpan<T> Reshape<T>(in this ROTensorSpan<T> x, params scoped ROSpan<nint> lengths);

    public static Tensor<T> Resize<T>(Tensor<T> x, ROSpan<nint> lengths);

    public static void ResizeTo<T>(this Tensor<T> x, in TensorSpan<nint> destination);
    public static void ResizeTo<T>(in this TensorSpan<T> x, in TensorSpan<nint> destination);
    public static void ResizeTo<T>(in this ROTensorSpan<T> x, in TensorSpan<nint> destination);

    // Should this be expanded to avoid the optional axis parameter?
    public static Tensor<T> Reverse<T>(in ROTensorSpan<T> x, nint axis = -1);
    public static TensorSpan<T> Reverse<T>(scoped in ROTensorSpan<T> x, in TensorSpan<T> destination, nint axis = -1);

    // These are distinct from EqualsAll in that they use IEquatable
    public static bool SequenceEqual<T>(this in ROTensorSpan<T> span, in ROTensorSpan<T> other) where T : IEquatable<T>?;
    public static bool SequenceEqual<T>(this in TensorSpan<T> span, in ROTensorSpan<T> other) where T : IEquatable<T>?;

    // The ordering of these parameters is slightly backwards so params can work for the ranges
    public static Tensor<T> SetSlice<T>(this Tensor<T> tensor, in ROTensorSpan<T> values, params ROSpan<NRange> ranges);
    public static TensorSpan<T> SetSlice<T>(this TensorSpan<T> tensor, scoped in ROTensorSpan<T> values, params scoped ROSpan<NRange> ranges);

    public static Tensor<T>[] Split<T>(in ROTensorSpan<T> x, nint numSplits, nint axis);

    // Remove dimensions of length 1
    public static Tensor<T> Squeeze<T>(this Tensor<T> x, int axis = -1);
    public static TensorSpan<T> Squeeze<T>(in this TensorSpan<T> x, int axis = -1);
    public static ROTensorSpan<T> Squeeze<T>(in this ROTensorSpan<T> x, int axis = -1);

    public static Tensor<T> Stack<T>(ROSpan<Tensor<T>> tensors, int axis = 0);
    public static TensorSpan<T> Stack<T>(scoped ROSpan<Tensor<T>> tensors, in TensorSpan<T> destination, int axis = 0);

    public static string ToString<T>(in this ROTensorSpan<T> span, params ROSpan<nint> maximumLengths);
    public static string ToString<T>(in this TensorSpan<T> span, params ROSpan<nint> maximumLengths);

    public static Tensor<T> Transpose<T>(in ROTensorSpan<T> x);
    public static TensorSpan<T> Transpose<T>(scoped in ROTensorSpan<T> x, in TensorSpan<T> destination);

    public static bool TryBroadcastTo<T>(this Tensor<T> x, in TensorSpan<T> destination);
    public static bool TryBroadcastTo<T>(in this TensorSpan<T> x, in TensorSpan<T> destination);
    public static bool TryBroadcastTo<T>(in this ROTensorSpan<T> x, in TensorSpan<T> destination);

    // Adds dimension of length 1
    public static Tensor<T> Unsqueeze<T>(this Tensor<T> x, int axis);
    public static TensorSpan<T> Unsqueeze<T>(in this TensorSpan<T> x, int axis);
    public static ROTensorSpan<T> Unsqueeze<T>(in this ROTensorSpan<T> x, int axis);
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions