Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tensor wave 1 API's. #101196

Merged
merged 25 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f8f3e7d
Native Index/Range and ref.
michaelgsharp Mar 9, 2024
edf1d1f
SpanND without slice
michaelgsharp Mar 13, 2024
576321b
index/range testing and implicit conversions
michaelgsharp Mar 13, 2024
0ed6cf9
SpanND Tests
michaelgsharp Mar 22, 2024
6d95237
tensor working, still need more statics
michaelgsharp Mar 25, 2024
0ae1719
more tensors updates
michaelgsharp Mar 29, 2024
00cbca9
ref files updated
michaelgsharp Apr 3, 2024
015063a
final ref update
michaelgsharp Apr 3, 2024
79eaabb
span updates
michaelgsharp Apr 11, 2024
ffaa308
all but broadcast and some TensorPrimitives
michaelgsharp Apr 15, 2024
ed3cc90
broadcast in
michaelgsharp Apr 17, 2024
3aad0ae
organizational changes
michaelgsharp Apr 19, 2024
c31c376
ref and implicit broadcast
michaelgsharp Apr 22, 2024
f68ea45
build failures
michaelgsharp Apr 22, 2024
b68df66
updates from PR comments
michaelgsharp Apr 26, 2024
58cfa63
error text moved to strings.resx
michaelgsharp Apr 26, 2024
b004517
exception strings moved to strings.resc
michaelgsharp Apr 29, 2024
4645fe6
comments from PR
michaelgsharp Apr 29, 2024
53f21a8
more fixes from PR and API review
michaelgsharp May 8, 2024
c093979
rebase on main. XML comments. API updates
michaelgsharp May 9, 2024
ff72361
NIndex,NRange,RO/TensorSpan API updates
michaelgsharp May 10, 2024
94674f1
IROTensor,ITensor,Tensor API updates
michaelgsharp May 13, 2024
7872a18
changes from pr comments
michaelgsharp May 14, 2024
f43fb19
changes from PR comments
michaelgsharp May 15, 2024
fcf93a3
fixed test failure
michaelgsharp May 15, 2024
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 @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down

Large diffs are not rendered by default.

86 changes: 85 additions & 1 deletion src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,88 @@
<data name="Argument_InvalidEnumValue" xml:space="preserve">
<value>The value '{0}' is not valid for this usage of the type {1}.</value>
</data>
</root>
<data name="Argument_InvalidTypeWithPointersNotSupported" xml:space="preserve">
<value>Cannot use type '{0}'. Only value types without pointers or references are supported.</value>
</data>
<data name="DestinationTooShort" xml:space="preserve">
<value>Destination is too short.</value>
</data>
<data name="NotSupported_CannotCallEqualsOnSpan" xml:space="preserve">
<value>Equals() on TensorSpan and ReadOnlyTensorSpan is not supported. Use operator== instead.</value>
</data>
<data name="NotSupported_CannotCallGetHashCodeOnSpan" xml:space="preserve">
<value>GetHashCode() on TensorSpan and ReadOnlyTensorSpan is not supported.</value>
</data>
<data name="ThrowArgument_IndicesLengthMustEqualRank" xml:space="preserve">
<value>Number of Indices must equal the rank of the TensorSpan.</value>
</data>
<data name="ThrowArgument_LengthsMustEqualArrayLength" xml:space="preserve">
<value>The total length specified by the lengths must equal the length of the array.</value>
</data>
<data name="ThrowArgument_1DTensorRequired" xml:space="preserve">
<value>Must be a 1d tensor</value>
</data>
<data name="ThrowArgument_AxisLargerThanRank" xml:space="preserve">
<value>Cannot select an axis greater than the current Rank</value>
</data>
<data name="ThrowArgument_ConcatenateTooFewTensors" xml:space="preserve">
<value>Must provide at least 2 tensors to Concatenate</value>
</data>
<data name="ThrowArgument_DimensionsNotSame" xml:space="preserve">
<value>Number of dimensions to slice does not equal the number of dimensions in the span</value>
</data>
<data name="ThrowArgument_FilterTensorMustEqualTensorLength" xml:space="preserve">
<value>The total length of the filter tensor must equal the length of the tensor to be filtered.</value>
</data>
<data name="ThrowArgument_IncorrectNumberOfFilterItems" xml:space="preserve">
<value>Number of elements provided does not match the number of filters.</value>
</data>
<data name="ThrowArgument_InPlaceInvalidShape" xml:space="preserve">
<value>In place operations require the same shape for both tensors</value>
</data>
<data name="ThrowArgument_InvalidAxis" xml:space="preserve">
<value>Invalid axis provided. Must be greater then or equal to 0 and less than the tensor rank.</value>
</data>
<data name="ThrowArgument_InvalidConcatenateShape" xml:space="preserve">
<value>The tensors must have the same shape, except in the dimension corresponding to axis.</value>
</data>
<data name="ThrowArgument_InvalidReshapeDimensions" xml:space="preserve">
<value>Provided dimensions are not valid for reshaping</value>
</data>
<data name="ThrowArgument_InvalidSqueezeAxis" xml:space="preserve">
<value>Cannot select an axis to squeeze which has size not equal to one</value>
</data>
<data name="ThrowArgument_OnlyOneWildcard" xml:space="preserve">
<value>Provided dimensions can only include 1 wildcard.</value>
</data>
<data name="ThrowArgument_PermuteAxisOrder" xml:space="preserve">
<value>Must provide an axis order for each axis</value>
</data>
<data name="ThrowArgument_SetSliceInvalidShapes" xml:space="preserve">
<value>Provided values must have the same shape as the input tensor.</value>
</data>
<data name="ThrowArgument_SetSliceNoRange" xml:space="preserve">
<value>When no ranges are specified the values tensor must be equal in size as the input tensor.</value>
</data>
<data name="ThrowArgument_ShapesNotBroadcastCompatible" xml:space="preserve">
<value>Shapes are not broadcast compatible.</value>
</data>
<data name="ThrowArgument_SplitNotSplitEvenly" xml:space="preserve">
<value>The number of splits must perfectly divide the dimension.</value>
</data>
<data name="ThrowArgument_StackTooFewTensors" xml:space="preserve">
<value>Must provide at least 2 tensors to Stack.</value>
</data>
<data name="ThrowArgument_TransposeTooFewDimensions" xml:space="preserve">
<value>Must provide a tensor with at least 2 dimensions to transpose it.</value>
</data>
<data name="ThrowArgument_ValueNonNegative" xml:space="preserve">
<value>The provided value must be non-negative.</value>
</data>
<data name="ThrowArgument_InvalidStridesAndLengths" xml:space="preserve">
<value>The provided lengths and strides would allow you to access elements outside the provided memory.</value>
</data>
<data name="ThrowArgument_StrideLessThan0" xml:space="preserve">
<value>Strides cannot be less than 0.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<IsPackable>true</IsPackable>
<PackageDescription>Provides support for operating over tensors.</PackageDescription>
<GenAPIExcludeApiList>ReferenceAssemblyExclusions.txt</GenAPIExcludeApiList>
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
</PropertyGroup>

<ItemGroup>
Expand All @@ -15,6 +16,20 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="System\Numerics\Tensors\netcore\Common\IReadOnlyTensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorHelpers.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorExtensions.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor.Factory.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Tensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ITensor.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanDebugView.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanExtensions.cs" />
<Compile Include="System\Numerics\Tensors\netcore\ReadOnlyTensorSpan.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanHelpers.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpanHelpers.T.cs" />
<Compile Include="System\Numerics\Tensors\netcore\TensorSpan.cs" />
<Compile Include="System\NIndex.cs" />
<Compile Include="System\NRange.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IAggregationOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IBinaryOperator.cs" />
<Compile Include="System\Numerics\Tensors\netcore\Common\TensorPrimitives.IIndexOfOperator.cs" />
Expand Down
175 changes: 175 additions & 0 deletions src/libraries/System.Numerics.Tensors/src/System/NIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace System.Buffers
{
/// <summary>Represent a type can be used to index a collection either from the start or the end.</summary>
/// <remarks>
/// <code>
/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
/// int lastElement = someArray[^1]; // lastElement = 5
/// </code>
/// </remarks>
public readonly struct NIndex : IEquatable<NIndex>
{
private readonly nint _value;

/// <summary>Construct an NIndex using a value and indicating if the NIndex is from the start or from the end.</summary>
/// <param name="value">The index value. it has to be zero or positive number.</param>
/// <param name="fromEnd">Indicating if the index is from the start or from the end.</param>
/// <remarks>
/// If the NIndex constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NIndex(nint value, bool fromEnd = false)
{
if (value < 0)
{
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
}

if (fromEnd)
_value = ~value;
else
_value = value;
}

/// <summary>Construct a <see cref="NIndex"/> from a <see cref="Index"/></summary>
/// <param name="index">The <see cref="Index"/> to create the <see cref="NIndex"/> from.</param>
/// <remarks>
/// If the NIndex constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NIndex(Index index)
{
if (index.IsFromEnd)
_value = ~index.Value;
else
_value = index.Value;
}

// The following private constructor exists to skip the checks in the public ctor
private NIndex(nint value)
{
_value = value;
}

/// <summary>Create an NIndex pointing at first element.</summary>
public static NIndex Start => new NIndex((nint)0);

/// <summary>Create an NIndex pointing at beyond last element.</summary>
public static NIndex End => new NIndex((nint)~0);

/// <summary>Create an NIndex from the start at the position indicated by the value.</summary>
/// <param name="value">The index value from the start.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NIndex FromStart(nint value)
{
if (value < 0)
{
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
}

return new NIndex(value);
}

/// <summary>Create an NIndex from the end at the position indicated by the value.</summary>
/// <param name="value">The index value from the end.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NIndex FromEnd(nint value)
{
if (value < 0)
{
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
}

return new NIndex(~value);
}

public Index ToIndex() => checked((Index)this);
public Index ToIndexUnchecked() => (Index)this;

/// <summary>Returns the NIndex value.</summary>
public nint Value
{
get
{
if (_value < 0)
return ~_value;
else
return _value;
}
}

/// <summary>Indicates whether the NIndex is from the start or the end.</summary>
public bool IsFromEnd => _value < 0;

/// <summary>Calculate the offset from the start using the giving collection length.</summary>
/// <param name="length">The length of the collection that the NIndex will be used with. length has to be a positive value</param>
/// <remarks>
/// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
/// we don't validate either the returned offset is greater than the input length.
/// It is expected NIndex will be used with collections which always have non negative length/count. If the returned offset is negative and
/// then used to NIndex a collection will get out of range exception which will be same affect as the validation.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public nint GetOffset(nint length)
{
nint offset = _value;
if (IsFromEnd)
{
// offset = length - (~value)
// offset = length + (~(~value) + 1)
// offset = length + value + 1

offset += length + 1;
}
return offset;
}

/// <summary>Indicates whether the current NIndex object is equal to another object of the same type.</summary>
/// <param name="value">An object to compare with this object</param>
public override bool Equals([NotNullWhen(true)] object? value) => value is NIndex other && _value == other._value;

/// <summary>Indicates whether the current NIndex object is equal to another NIndex object.</summary>
/// <param name="other">An object to compare with this object</param>
public bool Equals(NIndex other) => _value == other._value;

/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode() => _value.GetHashCode();

/// <summary>Converts integer number to an NIndex.</summary>
public static implicit operator NIndex(nint value) => FromStart(value);

/// <summary>Converts native integer number to an NIndex.</summary>
public static implicit operator NIndex(Index value) => new NIndex(value);

/// <summary>Converts a <see cref="NIndex"/> to an <see cref="Index"/>."/></summary>
public static explicit operator Index(NIndex value) => new Index((int)value.Value, value.IsFromEnd);

/// <summary>Converts a <see cref="NIndex"/> to an <see cref="Index"/>."/></summary>
public static explicit operator checked Index(NIndex value) => new Index(checked((int)value.Value), value.IsFromEnd);

/// <summary>Converts the value of the current NIndex object to its equivalent string representation.</summary>
public override string ToString()
{
if (IsFromEnd)
return ToStringFromEnd();

return Value.ToString();
}

private string ToStringFromEnd()
{
Span<char> span = stackalloc char[21]; // 1 for ^ and 20 for longest possible nuint value
bool formatted = ((uint)Value).TryFormat(span.Slice(1), out int charsWritten);
Debug.Assert(formatted);
span[0] = '^';
return new string(span.Slice(0, charsWritten + 1));
}
}
}
Loading
Loading