// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // This file is copied and adapted from the following git repository - // https://github.com/dotnet/corefx // Commit ID: bdd0814360d4c3a58860919f292a306242f27da1 // Path: /src/System.Numerics.Tensors/src/System/Numerics/Tensors/DenseTensor.cs // Original license statement below - // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; using System; namespace Microsoft.ML.OnnxRuntime.Tensors { /// /// Represents a multi-dimensional collection of objects of type T that can be accessed by indices. /// DenseTensor stores values in a contiguous sequential block of memory where all values are represented. /// /// /// Type contained within the Tensor. Typically a value type such as int, double, float, etc. /// public class DenseTensor : Tensor { private readonly Memory memory; internal DenseTensor(Array fromArray, bool reverseStride = false) : base(fromArray, reverseStride) { // copy initial array var backingArray = new T[fromArray.Length]; int index = 0; if (reverseStride) { // Array is always row-major var sourceStrides = ArrayUtilities.GetStrides(dimensions); foreach (var item in fromArray) { var destIndex = ArrayUtilities.TransformIndexByStrides(index++, sourceStrides, false, strides); backingArray[destIndex] = (T)item; } } else { foreach (var item in fromArray) { backingArray[index++] = (T)item; } } memory = backingArray; } /// /// Initializes a rank-1 Tensor using the specified . /// /// Size of the 1-dimensional tensor public DenseTensor(int length) : base(length) { memory = new T[length]; } /// /// Initializes a rank-n Tensor using the dimensions specified in . /// /// /// An span of integers that represent the size of each dimension of the DenseTensor to create. /// /// /// False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension /// is most minor (closest together): akin to row-major in a rank-2 tensor. /// True to indicate that the last dimension is most major (farthest apart) and the first dimension is most /// minor (closest together): akin to column-major in a rank-2 tensor. /// public DenseTensor(ReadOnlySpan dimensions, bool reverseStride = false) : base(dimensions, reverseStride) { memory = new T[Length]; } /// /// Constructs a new DenseTensor of the specified dimensions, wrapping existing backing memory for the contents. /// /// /// /// An span of integers that represent the size of each dimension of the DenseTensor to create. /// /// False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension /// is most minor (closest together): akin to row-major in a rank-2 tensor. /// True to indicate that the last dimension is most major (farthest apart) and the first dimension is most /// minor (closest together): akin to column-major in a rank-2 tensor. /// public DenseTensor(Memory memory, ReadOnlySpan dimensions, bool reverseStride = false) : base(dimensions, reverseStride) { this.memory = memory; if (Length != memory.Length) { throw new ArgumentException( $"Length of {nameof(memory)} ({memory.Length}) must match product of " + $"{nameof(dimensions)} ({Length})."); } } /// /// Memory storing backing values of this tensor. /// public Memory Buffer => memory; /// /// Gets the value at the specified index, where index is a linearized version of n-dimension indices /// using strides. For a scalar, use index = 0 /// /// An integer index computed as a dot-product of indices. /// The value at the specified position in this Tensor. public override T GetValue(int index) { return Buffer.Span[index]; } /// /// Sets the value at the specified index, where index is a linearized version of n-dimension indices /// using strides. For a scalar, use index = 0 /// /// An integer index computed as a dot-product of indices. /// The new value to set at the specified position in this Tensor. public override void SetValue(int index, T value) { Buffer.Span[index] = value; } /// /// Overrides Tensor.CopyTo(). Copies the content of the Tensor /// to the specified array starting with arrayIndex /// /// destination array /// start index protected override void CopyTo(T[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (array.Length < arrayIndex + Length) { throw new ArgumentException( "The number of elements in the Tensor is greater than the available space from index to " + "the end of the destination array.", nameof(array)); } Buffer.Span.CopyTo(array.AsSpan(arrayIndex)); } /// /// Determines the index of a specific item in the Tensor<T>. /// /// Object to locate /// The index of item if found in the tensor; otherwise, -1 protected override int IndexOf(T item) { // TODO: use Span.IndexOf when/if it removes the IEquatable type constraint if (MemoryMarshal.TryGetArray(Buffer, out var arraySegment)) { var result = Array.IndexOf(arraySegment.Array, item, arraySegment.Offset, arraySegment.Count); if (result != -1) { result -= arraySegment.Offset; } return result; } else { return base.IndexOf(item); } } /// /// Creates a shallow copy of this tensor, with new backing storage. /// /// A shallow copy of this tensor. public override Tensor Clone() { // create copy return new DenseTensor(new Memory(memory.ToArray()), dimensions, IsReversedStride); } /// /// Creates a new Tensor of a different type with the specified dimensions and the same layout as this tensor /// with elements initialized to their default value. /// /// Type contained in the returned Tensor. /// /// An span of integers that represent the size of each dimension of the DenseTensor to create. /// A new tensor with the same layout as this tensor but different type and dimensions. public override Tensor CloneEmpty(ReadOnlySpan dimensions) { return new DenseTensor(dimensions, IsReversedStride); } /// /// Reshapes the current tensor to new dimensions, using the same backing storage. /// /// /// An span of integers that represent the size of each dimension of the DenseTensor to create. /// A new tensor that reinterprets backing Buffer of this tensor with different dimensions. public override Tensor Reshape(ReadOnlySpan dimensions) { var newSize = ArrayUtilities.GetProduct(dimensions); if (newSize != Length) { throw new ArgumentException($"Cannot reshape array due to mismatch in lengths, " + "currently {Length} would become {newSize}.", nameof(dimensions)); } return new DenseTensor(Buffer, dimensions, IsReversedStride); } } }