Skip to content

[API Proposal]: Non-generic Tensor base type #111968

@michaelgsharp

Description

@michaelgsharp

Background and motivation

We currently do not have a non-generic base type for Tensor. This is fine, but there are many times you need info about the Tensor (its rank, lengths of dimenions, etc) and you can get this info without needing to know or care about the generic type itself. We need to add a base type that will better facilitate this.

I've added some basic access via boxing for the most general applications.

API Proposal

namespace System.Numerics.Tensors;

// All of these existed in higher types. Just creating a new base type and moving these down.
+ public interface IReadOnlyTensor
+ {
+    /// <summary>
+   /// Gets a value that indicates whether the collection is currently empty.
+    /// </summary>
+    bool IsEmpty { get; }
    
+     /// <summary>
+     /// Gets a value that indicates whether the underlying buffer is pinned.
+     /// </summary>
+     bool IsPinned { get; }
    
+     /// <summary>
+     /// Gets the number of elements in the tensor.
+     /// </summary>
+     nint FlattenedLength { get; }
    
+     /// <summary>
+     /// Gets the number of dimensions in the tensor.
+     /// </summary>
+     int Rank { get; }
    
+     /// <summary>
+     /// Gets the length of each dimension in the tensor.
+     /// </summary>
+     [UnscopedRef]
+     ReadOnlySpan<nint> Lengths { get; }
    
+     /// <summary>
+     /// Gets the stride of each dimension in the tensor.
+     /// </summary>
+     [UnscopedRef]
+     ReadOnlySpan<nint> Strides { get; }
    
+     /// <summary>
+     /// Gets the value at the specified indexes.
+     /// </summary>
+     /// <param name="indexes">The indexes to be used.</param>
+     object this[params scoped ReadOnlySpan<nint> indexes] { get; }
    
+     /// <summary>
+     /// Gets the value at the specified indexes.
+     /// </summary>
+     /// <param name="indexes">The indexes to be used.</param>
+     object this[params scoped ReadOnlySpan<NIndex> indexes] { get; }
+ }

+ public interface ITensor : IReadOnlyTensor
+ {
+     /// <summary>
+     /// Gets the value at the specified indexes.
+     /// </summary>
+     /// <param name="indexes">The indexes to be used.</param>
+     object this[params scoped ReadOnlySpan<nint> indexes] { get; set; }
    
+     /// <summary>
+     /// Gets the value at the specified indexes.
+     /// </summary>
+     /// <param name="indexes">The indexes to be used.</param>
+     object this[params scoped ReadOnlySpan<NIndex> indexes] { get; set; }

+     /// <summary>
+     /// Gets a value that indicates whether the collection is read-only.
+     /// </summary>
+     bool IsReadOnly { get; }

+     /// <summary>
+     /// Clears the tensor.
+     /// </summary>
+     void Clear();

+     /// <summary>
+     /// Fills the contents of this tensor with the given value.
+     /// </summary>
+     void Fill(object value);
+ }

// And we are removing these methods from the typed base interfaces
public interface IReadOnlyTensor<TSelf, T> : IReadOnlyTensor, IEnumerable<T>
        where TSelf : IReadOnlyTensor<TSelf, T>
{
-    /// <summary>
-    /// Gets a value that indicates whether the collection is currently empty.
-    /// </summary>
-    bool IsEmpty { get; }
    
-     /// <summary>
-     /// Gets a value that indicates whether the underlying buffer is pinned.
-     /// </summary>
-     bool IsPinned { get; }
    
-     /// <summary>
-     /// Gets the number of elements in the tensor.
-     /// </summary>
-     nint FlattenedLength { get; }
    
-     /// <summary>
-     /// Gets the number of dimensions in the tensor.
-     /// </summary>
-     int Rank { get; }
    
-     /// <summary>
-     /// Gets the length of each dimension in the tensor.
-     /// </summary>
-     [UnscopedRef]
-     ReadOnlySpan<nint> Lengths { get; }
    
-     /// <summary>
-     /// Gets the stride of each dimension in the tensor.
-     /// </summary>
-     [UnscopedRef]
-     ReadOnlySpan<nint> Strides { get; }
}

public interface ITensor<TSelf, T>  : ITensor, IReadOnlyTensor<TSelf, T>
        where TSelf : ITensor<TSelf, T>
{
-     /// <summary>
-     /// Gets a value that indicates whether the collection is read-only.
-     /// </summary>
-    bool IsReadOnly { get; }

-     /// <summary>
-     /// Clears the tensor.
-     /// </summary>
-     void Clear();
}

API Usage

public void DoStuffWithNonGenericTensor(ITensor tensor);

Tensor<int> tensor = Tensor.Create<int>([1, 2, 3, 4], [2, 2]);

DoStuffWithNonGenericTensor(tensor);

Alternative Designs

We may decide we don't want access to the data via boxing, in which case we can remove those methods.

Risks

Low risk because it's a preview type, but because we are changing the class hierarchy there is more than none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.Numerics.Tensorsin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions