Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths,
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
{
return new Tensor<T>(values, lengths, strides, pinned);
return new Tensor<T>(values, lengths, strides, memoryOffset: 0, pinned);
}

/// <summary>
Expand All @@ -77,7 +77,7 @@ public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths,
public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nint> lengths, bool pinned = false)
{
T[] data = values.ToArray();
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, pinned);
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, memoryOffset: 0, pinned);
}

/// <summary>
Expand All @@ -90,7 +90,7 @@ public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nin
public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
{
T[] data = values.ToArray();
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, strides, pinned);
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, strides, memoryOffset: 0, pinned);
}

#region Normal
Expand All @@ -115,7 +115,7 @@ public static Tensor<T> CreateAndFillGaussianNormalDistribution<T>(Random random
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = new T[linearLength];
GaussianDistribution<T>(values, linearLength, random);
return new Tensor<T>(values, lengths, false);
return new Tensor<T>(values, lengths, memoryOffset: 0, isPinned: false);
}

private static void GaussianDistribution<T>(in Span<T> values, nint linearLength, Random random)
Expand Down Expand Up @@ -154,7 +154,7 @@ public static Tensor<T> CreateAndFillUniformDistribution<T>(Random random, param
for (int i = 0; i < values.Length; i++)
values[i] = T.CreateChecked(random.NextDouble());

return new Tensor<T>(values, lengths, false);
return new Tensor<T>(values, lengths, memoryOffset: 0, isPinned: false);
}

/// <summary>
Expand All @@ -175,7 +175,7 @@ public static Tensor<T> CreateUninitialized<T>(scoped ReadOnlySpan<nint> lengths
{
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
return new Tensor<T>(values, lengths, strides, pinned);
return new Tensor<T>(values, lengths, strides, memoryOffset: 0, pinned);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ internal Tensor()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, bool isPinned = false, int memoryOffset = 0) : this(values, lengths, Array.Empty<nint>(), isPinned, memoryOffset) { }
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, int memoryOffset, bool isPinned = false) : this(values, lengths, Array.Empty<nint>(), memoryOffset, isPinned) { }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> strides, bool isPinned = false, int memoryOffset = 0)
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> strides, int memoryOffset, bool isPinned = false)
{
if (values == null)
{
Expand All @@ -69,6 +69,9 @@ internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> stri
_lengths = lengths.IsEmpty ? [values.Length] : lengths.ToArray();
_memoryOffset = memoryOffset;

if (_memoryOffset < 0 || (_memoryOffset >= values.Length && values.Length != 0 ))
ThrowHelper.ThrowIndexOutOfRangeException();

_flattenedLength = TensorSpanHelpers.CalculateTotalLength(_lengths);
_strides = strides.IsEmpty ? TensorSpanHelpers.CalculateStrides(_lengths, _flattenedLength) : strides.ToArray();
TensorSpanHelpers.ValidateStrides(_strides, _lengths);
Expand All @@ -77,12 +80,12 @@ internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> stri
if (Environment.Is64BitProcess)
{
// See comment in Span<T>.Slice for how this works.
if ((ulong)(uint)maxElements >= (ulong)(uint)values.Length && values.Length != 0)
if ((ulong)(uint)maxElements >= (ulong)(uint)(values.Length - memoryOffset) && values.Length != 0)
ThrowHelper.ThrowArgument_InvalidStridesAndLengths();
}
else
{
if (((uint)maxElements >= (uint)(values.Length)) && values.Length != 0)
if (((uint)maxElements >= (uint)(values.Length - memoryOffset)) && values.Length != 0)
ThrowHelper.ThrowArgument_InvalidStridesAndLengths();
}

Expand All @@ -99,7 +102,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.Create(scoped ReadOnlySpan<nint> lengths,
{
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = pinned ? GC.AllocateArray<T>((int)linearLength, pinned) : (new T[linearLength]);
return new Tensor<T>(values, lengths.ToArray(), pinned);
return new Tensor<T>(values, lengths.ToArray(), memoryOffset: 0, pinned);
}

/// <summary>
Expand All @@ -112,7 +115,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.Create(scoped ReadOnlySpan<nint> lengths,
{
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = pinned ? GC.AllocateArray<T>((int)linearLength, pinned) : (new T[linearLength]);
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), pinned);
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned);
}

/// <summary>
Expand All @@ -124,7 +127,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.CreateUninitialized(scoped ReadOnlySpan<n
{
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
return new Tensor<T>(values, lengths.ToArray(), pinned);
return new Tensor<T>(values, lengths.ToArray(), memoryOffset: 0, pinned);
}

/// <summary>
Expand All @@ -137,7 +140,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.CreateUninitialized(scoped ReadOnlySpan<n
{
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), pinned);
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned);
}

// ITensor
Expand Down Expand Up @@ -374,24 +377,24 @@ public Tensor<T> this[Tensor<bool> filter]
}
}

return new Tensor<T>(values, [linearLength], _isPinned);
return new Tensor<T>(values, [linearLength], _memoryOffset, _isPinned);
}
}

/// <summary>
/// Defines an implicit conversion of an array to a <see cref="Tensor{T}"/>.
/// </summary>
public static implicit operator Tensor<T>(T[] array) => new Tensor<T>(array, [array.Length]);
public static implicit operator Tensor<T>(T[] array) => new Tensor<T>(array, [array.Length], memoryOffset: 0);

/// <summary>
/// Defines an implicit conversion of a <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/>.
/// </summary>
public static implicit operator TensorSpan<T>(Tensor<T> value) => new TensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(value._values), value._lengths, value._strides, value._flattenedLength);
public static implicit operator TensorSpan<T>(Tensor<T> value) => value.AsTensorSpan();

/// <summary>
/// Defines an implicit conversion of a <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/>.
/// </summary>
public static implicit operator ReadOnlyTensorSpan<T>(Tensor<T> value) => new ReadOnlyTensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(value._values), value._lengths, value._strides, value.FlattenedLength);
public static implicit operator ReadOnlyTensorSpan<T>(Tensor<T> value) => value.AsReadOnlyTensorSpan();

/// <summary>
/// Converts this <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/> pointing to the same backing memory."/>
Expand All @@ -411,7 +414,7 @@ public Tensor<T> this[Tensor<bool> filter]
/// </summary>
/// <param name="start">The start location you want in the <see cref="TensorSpan{T}"/>.</param>
/// <returns><see cref="TensorSpan{T}"/> based on the provided ranges.</returns>
public TensorSpan<T> AsTensorSpan(params scoped ReadOnlySpan<nint> start) => Slice(start);
public TensorSpan<T> AsTensorSpan(params scoped ReadOnlySpan<nint> start) => AsTensorSpan().Slice(start);

/// <summary>
/// Converts this <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/> pointing to the same backing memory based on the provided start indexes."/>
Expand All @@ -424,7 +427,7 @@ public Tensor<T> this[Tensor<bool> filter]
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory."/>
/// </summary>
/// <returns><see cref="ReadOnlyTensorSpan{T}"/></returns>
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(_values), _lengths, _strides, _flattenedLength);
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan<T>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _memoryOffset), _lengths, _strides, _flattenedLength);

/// <summary>
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory based on the provided ranges."/>
Expand All @@ -438,7 +441,7 @@ public Tensor<T> this[Tensor<bool> filter]
/// </summary>
/// <param name="start">The start locations you want in the <see cref="ReadOnlyTensorSpan{T}"/></param>
/// <returns></returns>
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<nint> start) => Slice(start);
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<nint> start) => AsTensorSpan().Slice(start);

/// <summary>
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory based on the provided start indexes."/>
Expand Down Expand Up @@ -515,7 +518,7 @@ public Tensor<T> Slice(params ReadOnlySpan<NRange> start)
if ((memoryOffset >= _values.Length || memoryOffset < 0) && flattenedLength != 0)
ThrowHelper.ThrowIndexOutOfRangeException();

Tensor<T> toReturn = new Tensor<T>(_values, lengths, Strides, _isPinned, memoryOffset);
Tensor<T> toReturn = new Tensor<T>(_values, lengths, Strides, memoryOffset, _isPinned);

if (offsetsArray != null)
ArrayPool<nint>.Shared.Return(offsetsArray);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ internal static ReadOnlyTensorSpan<T> LazyBroadcast<T>(in ReadOnlyTensorSpan<T>
internal static Tensor<T> LazyBroadcast<T>(Tensor<T> input, ReadOnlySpan<nint> lengths)
{
if (input.Lengths.SequenceEqual(lengths))
return new Tensor<T>(input._values, lengths, false);
return new Tensor<T>(input._values, lengths, input._memoryOffset, isPinned: false);

if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths))
ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible();
Expand All @@ -244,7 +244,7 @@ internal static Tensor<T> LazyBroadcast<T>(Tensor<T> input, ReadOnlySpan<nint> l
}
}

Tensor<T> output = new Tensor<T>(input._values, lengths, strides);
Tensor<T> output = new Tensor<T>(input._values, lengths, strides, input._memoryOffset);

return output;
}
Expand Down Expand Up @@ -2660,7 +2660,7 @@ public static Tensor<T> PermuteDimensions<T>(this Tensor<T> tensor, params ReadO
lengths[i] = tensor.Lengths[dimensions[i]];
permutation = dimensions.ToArray();
}
outTensor = new Tensor<T>(values, lengths, Array.Empty<nint>(), tensor._isPinned);
outTensor = new Tensor<T>(values, lengths, Array.Empty<nint>(), tensor._memoryOffset, tensor._isPinned);

ospan = outTensor.AsTensorSpan();
ispan = tensor.AsTensorSpan();
Expand Down Expand Up @@ -2771,7 +2771,7 @@ public static Tensor<T> Reshape<T>(this Tensor<T> tensor, params ReadOnlySpan<ni
else
strides = TensorSpanHelpers.CalculateStrides(arrLengths);

return new Tensor<T>(tensor._values, arrLengths, strides);
return new Tensor<T>(tensor._values, arrLengths, strides, tensor._memoryOffset);
}

/// <summary>
Expand Down Expand Up @@ -2926,7 +2926,7 @@ public static Tensor<T> Resize<T>(Tensor<T> tensor, ReadOnlySpan<nint> lengths)
{
nint newSize = TensorSpanHelpers.CalculateTotalLength(lengths);
T[] values = tensor.IsPinned ? GC.AllocateArray<T>((int)newSize) : (new T[newSize]);
Tensor<T> output = new Tensor<T>(values, lengths, false);
Tensor<T> output = new Tensor<T>(values, lengths, tensor._memoryOffset, isPinned: false);
ReadOnlySpan<T> span = MemoryMarshal.CreateSpan(ref tensor.AsTensorSpan()._reference, (int)tensor._values.Length);
Span<T> ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength);
if (newSize > tensor._values.Length)
Expand Down Expand Up @@ -3217,7 +3217,7 @@ public static Tensor<T>[] Split<T>(scoped in ReadOnlyTensorSpan<T> tensor, int s
for (int i = 0; i < outputs.Length; i++)
{
T[] values = new T[(int)totalToCopy];
outputs[i] = new Tensor<T>(values, newShape);
outputs[i] = new Tensor<T>(values, newShape, memoryOffset: 0);
oIndices.Clear();
iIndices.Clear();

Expand Down Expand Up @@ -3299,7 +3299,7 @@ public static Tensor<T> SqueezeDimension<T>(this Tensor<T> tensor, int dimension
strides = TensorSpanHelpers.CalculateStrides(lengths);
}

return new Tensor<T>(tensor._values, lengths, strides);
return new Tensor<T>(tensor._values, lengths, strides, tensor._memoryOffset);
}

/// <summary>
Expand Down Expand Up @@ -3661,7 +3661,7 @@ public static Tensor<T> Unsqueeze<T>(this Tensor<T> tensor, int dimension)
strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension];
}

return new Tensor<T>(tensor._values, lengths, strides);
return new Tensor<T>(tensor._values, lengths, strides, tensor._memoryOffset);
}

/// <summary>
Expand Down
46 changes: 43 additions & 3 deletions src/libraries/System.Numerics.Tensors/tests/TensorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,46 @@ public static void TensorStdDevTests()
Assert.Equal(0f, Tensor.StdDev(upperLeft));
}

[Fact]
public static void TensorSumTests()
{
float[] values = new float[] { 1, 2, 3, 4, 5, 6 };
Tensor<float> t0 = Tensor.Create<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3]);
float sum = Tensor.Sum<float>(t0);
Assert.Equal(21, sum);

// Slice first row of 2 x 2 for sum
Tensor<float> t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(1)), new NRange(new NIndex(0), new NIndex(0, true)));
sum = Tensor.Sum<float>(t1);
Assert.Equal(6, sum);

// Slice second row of 2 x 2 for sum.
t1 = t0.Slice(new NRange(new NIndex(1), new NIndex(2)), new NRange(new NIndex(0), new NIndex(0, true)));
sum = Tensor.Sum<float>(t1);
Assert.Equal(15, sum);

// Slice first column of 2 x 2 for sum.
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(0), new NIndex(1)));
sum = Tensor.Sum<float>(t1);
Assert.Equal(5, sum);

// Slice second column of 2 x 2 for sum.
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(1), new NIndex(2)));
sum = Tensor.Sum<float>(t1);
Assert.Equal(7, sum);

// Slice Third column of 2 x 2 for sum.
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(2), new NIndex(3)));
sum = Tensor.Sum<float>(t1);
Assert.Equal(9, sum);

Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], -1));
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 100));
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MinValue));
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MaxValue));
Assert.Throws<ArgumentException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 2));
}

public static float StdDev(float[] values)
{
float mean = Mean(values);
Expand Down Expand Up @@ -2649,7 +2689,7 @@ public void TensorFilteredUpdateTest()
[Fact]
public void TensorObjectFillTests()
{
ITensor tensor = (ITensor)new Tensor<int>(new int[4], new nint[] { 2, 2 });
ITensor tensor = (ITensor)new Tensor<int>(new int[4], new nint[] { 2, 2 }, 0);
tensor.Fill(5);

Assert.Equal(5, tensor[0, 0]);
Expand All @@ -2670,7 +2710,7 @@ public void TensorObjectFillTests()
[Fact]
public void TensorObjectIndexerTests()
{
ITensor tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
ITensor tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0);

Assert.Equal(1, tensor[new nint[] { 0, 0 }]);
Assert.Equal(2, tensor[new nint[] { 0, 1 }]);
Expand Down Expand Up @@ -2701,7 +2741,7 @@ public void TensorObjectIndexerTests()
[Fact]
public void TensorGetPinnedHandleTests()
{
Tensor<int> tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
Tensor<int> tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0);

using MemoryHandle handle = tensor.GetPinnedHandle();
unsafe
Expand Down
Loading