// 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);
}
}
}