The main tagline: The motivation behind introducing Tensor is to make it easy for Machine Learning library vendors like CNTK, Tensorflow, Caffe, Scikit-Learn to port their libraries over to .NET with minimal dependencies in place.
namespace System.Numerics.Tensors
{
// All interface members will be implemented explicitly unless exposed below
public abstract class Tensor<T> : ICollection, ICollection<T>, IEnumerable, IEnumerable<T>, IList, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, IStructuralComparable, IStructuralEquatable
{
protected Tensor(Array fromArray, bool reverseStride);
protected Tensor(int length);
protected Tensor(ReadOnlySpan<int> dimensions, bool reverseStride);
public ReadOnlySpan<int> Dimensions { get; }
public bool IsFixedSize { get; }
public bool IsReadOnly { get; }
public bool IsReversedStride { get; }
public long Length { get; }
public int Rank { get; }
public ReadOnlySpan<int> Strides { get; }
public virtual T this[params int[] indices] { get; set; }
public virtual T this[ReadOnlySpan<int> indices] { get; set; }
public abstract Tensor<T> Clone();
public virtual Tensor<T> CloneEmpty();
public virtual Tensor<T> CloneEmpty(ReadOnlySpan<int> dimensions);
public virtual Tensor<TResult> CloneEmpty<TResult>();
public abstract Tensor<TResult> CloneEmpty<TResult>(ReadOnlySpan<int> dimensions);
protected virtual bool Contains(T item);
protected virtual void CopyTo(T[] array, int arrayIndex);
protected virtual int IndexOf(T item);
public virtual void Fill(T value);
public string GetArrayString(bool includeWhitespace=true);
public Tensor<T> GetDiagonal();
public Tensor<T> GetDiagonal(int offset);
public Tensor<T> GetTriangle();
public Tensor<T> GetTriangle(int offset);
public Tensor<T> GetUpperTriangle();
public Tensor<T> GetUpperTriangle(int offset);
public abstract T GetValue(int index);
public abstract void SetValue(int index, T value);
public Tensor<T> MatrixMultiply(Tensor<T> right);
public abstract Tensor<T> Reshape(ReadOnlySpan<int> dimensions);
public Tensor<T> Slice(params Range[] ranges);
public virtual Tensor<T> Slice(ReadOnlySpan<Range> ranges);
public virtual CompressedSparseTensor<T> ToCompressedSparseTensor();
public virtual DenseTensor<T> ToDenseTensor();
public virtual SparseTensor<T> ToSparseTensor();
public static int Compare(Tensor<T> left, Tensor<T> right);
public static bool Equals(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator +(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator +(Tensor<T> tensor, T scalar);
public static Tensor<T> operator &(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator &(Tensor<T> tensor, T scalar);
public static Tensor<T> operator |(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator |(Tensor<T> tensor, T scalar);
public static Tensor<T> operator --(Tensor<T> tensor);
public static Tensor<T> operator /(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator /(Tensor<T> tensor, T scalar);
public static Tensor<T> operator ^(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator ^(Tensor<T> tensor, T scalar);
public static Tensor<T> operator ++(Tensor<T> tensor);
public static Tensor<T> operator <<(Tensor<T> tensor, int value);
public static Tensor<T> operator %(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator %(Tensor<T> tensor, T scalar);
public static Tensor<T> operator *(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator *(Tensor<T> tensor, T scalar);
public static Tensor<T> operator >>(Tensor<T> tensor, int value);
public static Tensor<T> operator -(Tensor<T> left, Tensor<T> right);
public static Tensor<T> operator -(Tensor<T> tensor, T scalar);
public static Tensor<T> operator -(Tensor<T> tensor);
public static Tensor<T> operator +(Tensor<T> tensor);
}
public class DenseTensor<T> : Tensor<T>
{
public DenseTensor(int length);
public DenseTensor(Memory<T> memory, ReadOnlySpan<int> dimensions, bool reverseStride=false);
public DenseTensor(ReadOnlySpan<int> dimensions, bool reverseStride=false);
public Memory<T> Buffer { get; }
public override Tensor<T> Clone();
public override Tensor<TResult> CloneEmpty<TResult>(ReadOnlySpan<int> dimensions);
protected override void CopyTo(T[] array, int arrayIndex);
protected override int IndexOf(T item);
public override T GetValue(int index);
public override void SetValue(int index, T value);
public override Tensor<T> Reshape(ReadOnlySpan<int> dimensions);
}
public class CompressedSparseTensor<T> : Tensor<T>
{
public CompressedSparseTensor(Memory<T> values, Memory<int> compressedCounts, Memory<int> indices, int nonZeroCount, ReadOnlySpan<int> dimensions, bool reverseStride=false);
public CompressedSparseTensor(ReadOnlySpan<int> dimensions, bool reverseStride=false);
public CompressedSparseTensor(ReadOnlySpan<int> dimensions, int capacity, bool reverseStride=false);
public int Capacity { get; }
public Memory<int> CompressedCounts { get; }
public Memory<int> Indices { get; }
public int NonZeroCount { get; }
public Memory<T> Values { get; }
public override T this[ReadOnlySpan<int> indices] { get; set; }
public override Tensor<T> Clone();
public override Tensor<TResult> CloneEmpty<TResult>(ReadOnlySpan<int> dimensions);
public override T GetValue(int index);
public override void SetValue(int index, T value);
public override Tensor<T> Reshape(ReadOnlySpan<int> dimensions);
public override CompressedSparseTensor<T> ToCompressedSparseTensor();
public override DenseTensor<T> ToDenseTensor();
public override SparseTensor<T> ToSparseTensor();
}
public class SparseTensor<T> : Tensor<T>
{
public SparseTensor(ReadOnlySpan<int> dimensions, bool reverseStride=false, int capacity=0);
public int NonZeroCount { get; }
public override Tensor<T> Clone();
public override Tensor<TResult> CloneEmpty<TResult>(ReadOnlySpan<int> dimensions);
public override T GetValue(int index);
public override void SetValue(int index, T value);
public override Tensor<T> Reshape(ReadOnlySpan<int> dimensions);
public override CompressedSparseTensor<T> ToCompressedSparseTensor();
public override DenseTensor<T> ToDenseTensor();
public override SparseTensor<T> ToSparseTensor();
}
public static class ArrayTensorExtensions
{
public static CompressedSparseTensor<T> ToCompressedSparseTensor<T>(this Array array, bool reverseStride=false);
public static CompressedSparseTensor<T> ToCompressedSparseTensor<T>(this T[,,] array, bool reverseStride=false);
public static CompressedSparseTensor<T> ToCompressedSparseTensor<T>(this T[,] array, bool reverseStride=false);
public static CompressedSparseTensor<T> ToCompressedSparseTensor<T>(this T[] array);
public static SparseTensor<T> ToSparseTensor<T>(this Array array, bool reverseStride=false);
public static SparseTensor<T> ToSparseTensor<T>(this T[,,] array, bool reverseStride=false);
public static SparseTensor<T> ToSparseTensor<T>(this T[,] array, bool reverseStride=false);
public static SparseTensor<T> ToSparseTensor<T>(this T[] array);
public static DenseTensor<T> ToTensor<T>(this Array array, bool reverseStride=false);
public static DenseTensor<T> ToTensor<T>(this T[,,] array, bool reverseStride=false);
public static DenseTensor<T> ToTensor<T>(this T[,] array, bool reverseStride=false);
public static DenseTensor<T> ToTensor<T>(this T[] array);
}
public static class Tensor
{
public static Tensor<T> Add<T>(Tensor<T> left, Tensor<T> right);
public static void Add<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Add<T>(Tensor<T> tensor, T scalar);
public static void Add<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<T> And<T>(Tensor<T> left, Tensor<T> right);
public static void And<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> And<T>(Tensor<T> tensor, T scalar);
public static void And<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<T> Contract<T>(Tensor<T> left, Tensor<T> right, int[] leftAxes, int[] rightAxes);
public static void Contract<T>(Tensor<T> left, Tensor<T> right, int[] leftAxes, int[] rightAxes, Tensor<T> result);
public static Tensor<T> CreateFromDiagonal<T>(Tensor<T> diagonal);
public static Tensor<T> CreateFromDiagonal<T>(Tensor<T> diagonal, int offset);
public static Tensor<T> CreateIdentity<T>(int size);
public static Tensor<T> CreateIdentity<T>(int size, bool columMajor);
public static Tensor<T> CreateIdentity<T>(int size, bool columMajor, T oneValue);
public static Tensor<T> Decrement<T>(Tensor<T> tensor);
public static void Decrement<T>(Tensor<T> tensor, Tensor<T> result);
public static Tensor<T> Divide<T>(Tensor<T> left, Tensor<T> right);
public static void Divide<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Divide<T>(Tensor<T> tensor, T scalar);
public static void Divide<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<Boolean> Equals<T>(Tensor<T> left, Tensor<T> right);
public static void Equals<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<Boolean> GreaterThan<T>(Tensor<T> left, Tensor<T> right);
public static void GreaterThan<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<Boolean> GreaterThanOrEqual<T>(Tensor<T> left, Tensor<T> right);
public static void GreaterThanOrEqual<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<T> Increment<T>(Tensor<T> tensor);
public static void Increment<T>(Tensor<T> tensor, Tensor<T> result);
public static Tensor<T> LeftShift<T>(Tensor<T> tensor, int value);
public static void LeftShift<T>(Tensor<T> tensor, int value, Tensor<T> result);
public static Tensor<Boolean> LessThan<T>(Tensor<T> left, Tensor<T> right);
public static void LessThan<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<Boolean> LessThanOrEqual<T>(Tensor<T> left, Tensor<T> right);
public static void LessThanOrEqual<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<T> Modulo<T>(Tensor<T> left, Tensor<T> right);
public static void Modulo<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Modulo<T>(Tensor<T> tensor, T scalar);
public static void Modulo<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<T> Multiply<T>(Tensor<T> left, Tensor<T> right);
public static void Multiply<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Multiply<T>(Tensor<T> tensor, T scalar);
public static void Multiply<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<Boolean> NotEquals<T>(Tensor<T> left, Tensor<T> right);
public static void NotEquals<T>(Tensor<T> left, Tensor<T> right, Tensor<Boolean> result);
public static Tensor<T> Or<T>(Tensor<T> left, Tensor<T> right);
public static void Or<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Or<T>(Tensor<T> tensor, T scalar);
public static void Or<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<T> RightShift<T>(Tensor<T> tensor, int value);
public static void RightShift<T>(Tensor<T> tensor, int value, Tensor<T> result);
public static Tensor<T> Subtract<T>(Tensor<T> left, Tensor<T> right);
public static void Subtract<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Subtract<T>(Tensor<T> tensor, T scalar);
public static void Subtract<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
public static Tensor<T> UnaryMinus<T>(Tensor<T> tensor);
public static void UnaryMinus<T>(Tensor<T> tensor, Tensor<T> result);
public static Tensor<T> UnaryPlus<T>(Tensor<T> tensor);
public static void UnaryPlus<T>(Tensor<T> tensor, Tensor<T> result);
public static Tensor<T> Xor<T>(Tensor<T> left, Tensor<T> right);
public static void Xor<T>(Tensor<T> left, Tensor<T> right, Tensor<T> result);
public static Tensor<T> Xor<T>(Tensor<T> tensor, T scalar);
public static void Xor<T>(Tensor<T> tensor, T scalar, Tensor<T> result);
}
}
namespace System
{
struct Range
{
public Range(int start, int end);
public int Start { get; }
public int End { get; }
}
}
Rationale
See https://blogs.msdn.microsoft.com/dotnet/2017/11/15/introducing-tensor-for-multi-dimensional-machine-learning-and-ai-data/ for rationale.
The main tagline: The motivation behind introducing Tensor is to make it easy for Machine Learning library vendors like CNTK, Tensorflow, Caffe, Scikit-Learn to port their libraries over to .NET with minimal dependencies in place.
Usage:
Scenario 1 - convert an Bitmap to Tensor to prepare the data to pass into a machine learning algorithm. Taken from here
Scenario 2 - Pass Tensor data into a native library that does math operations. Taken from here
Proposed API
Details
Rangetype is needed in order to support theTensor.Slicemethod. The proposal is to introduce aSystem.Range structto theSystem.Memorypackage. Note that this Range is also necessary for the new range syntax proposed for C#.longRange type, because all the dimensions are represented byint.