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 @@ -234,9 +234,6 @@
<data name="Argument_CannotReshapeNonContiguousOrDense" xml:space="preserve">
<value>The Tensor provided is either non-contiguous or non-dense. Reshape only works with contigous and dense memory. You may need to Broadcast or Copy the data to be contigous.</value>
</data>
<data name="Argument_MinGreaterThanMax" xml:space="preserve">
<value>A minimum value may not be greater than a maximum value.</value>
</data>
<data name="Arithmetic_NaN" xml:space="preserve">
<value>Function does not accept floating point Not-a-Number values.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public static partial class TensorPrimitives
/// <param name="min">The tensor of inclusive lower bounds, represented as a span.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Length of <paramref name="x" /> must be same as length of <paramref name="min" /> and the length of <paramref name="max" />.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="x"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="min"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="max"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />[i], <paramref name="min" />[i], <paramref name="max" />[i])</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />[i], <paramref name="min" />[i]), <paramref name="max" />[i])</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> min, ReadOnlySpan<T> max, Span<T> destination)
Expand All @@ -46,14 +46,14 @@ public static void Clamp<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> min, ReadOnlySpan
/// <param name="min">The tensor of inclusive lower bounds, represented as a span.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a scalar.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Length of <paramref name="x" /> must be same as length of <paramref name="min" />.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="x"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="min"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />[i], <paramref name="min" />[i], <paramref name="max" />)</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />[i], <paramref name="min" />[i]), <paramref name="max" />)</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> min, T max, Span<T> destination)
Expand All @@ -75,14 +75,14 @@ public static void Clamp<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> min, T max, Span<
/// <param name="min">The tensor of inclusive lower bounds, represented as a scalar.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Length of <paramref name="x" /> must be same as length of <paramref name="max" />.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="x"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="max"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />[i], <paramref name="min" />, <paramref name="max" />[i])</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />[i], <paramref name="min" />), <paramref name="max" />[i])</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(ReadOnlySpan<T> x, T min, ReadOnlySpan<T> max, Span<T> destination)
Expand All @@ -104,14 +104,14 @@ public static void Clamp<T>(ReadOnlySpan<T> x, T min, ReadOnlySpan<T> max, Span<
/// <param name="min">The tensor of inclusive lower bounds, represented as a span.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Length of <paramref name="min" /> must be same as length of <paramref name="max" />.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="min"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="max"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />, <paramref name="min" />[i], <paramref name="max" />[i])</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />, <paramref name="min" />[i]), <paramref name="max" />[i])</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(T x, ReadOnlySpan<T> min, ReadOnlySpan<T> max, Span<T> destination)
Expand All @@ -133,22 +133,17 @@ public static void Clamp<T>(T x, ReadOnlySpan<T> min, ReadOnlySpan<T> max, Span<
/// <param name="min">The tensor of inclusive lower bounds, represented as a scalar.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a scalar.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException"><paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="x"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException"><paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />[i], <paramref name="min" />, <paramref name="max" />)</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />[i], <paramref name="min" />), <paramref name="max" />)</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(ReadOnlySpan<T> x, T min, T max, Span<T> destination)
where T : INumber<T>
{
if (min > max)
{
ThrowHelper.ThrowArgument_MinGreaterThanMax();
}

if (typeof(T) == typeof(Half) && TryTernaryInvokeHalfAsInt16<T, ClampOperatorXMinMax<float>>(x, min, max, destination))
{
return;
Expand All @@ -165,12 +160,12 @@ public static void Clamp<T>(ReadOnlySpan<T> x, T min, T max, Span<T> destination
/// <param name="min">The tensor of inclusive lower bounds, represented as a span.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a scalar.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="min"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />, <paramref name="min" />[i], <paramref name="max" />)</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />, <paramref name="min" />[i]), <paramref name="max" />)</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(T x, ReadOnlySpan<T> min, T max, Span<T> destination)
Expand All @@ -192,12 +187,12 @@ public static void Clamp<T>(T x, ReadOnlySpan<T> min, T max, Span<T> destination
/// <param name="min">The tensor of inclusive lower bounds, represented as a scalar.</param>
/// <param name="max">The tensor of inclusive upper bounds, represented as a span.</param>
/// <param name="destination">The destination tensor, represented as a span.</param>
/// <exception cref="ArgumentException">An element-wise <paramref name="min"/> is greater than <paramref name="max"/>.</exception>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException"><paramref name="max"/> and <paramref name="destination"/> reference overlapping memory locations and do not begin at the same location.</exception>
/// <exception cref="ArgumentException">An element-wise <paramref name="min" /> is greater than <paramref name="max" /> and <typeparamref name="T" /> is not a vectorizable type.</exception>
/// <remarks>
/// <para>
/// This method effectively computes <c><paramref name="destination" />[i] = T.Clamp(<paramref name="x" />, <paramref name="min" />, <paramref name="max" />[i])</c>.
/// This method effectively computes <c><paramref name="destination" />[i] = <typeparamref name="T" />.Min(<typeparamref name="T" />.Max(<paramref name="x" />, <paramref name="min" />), <paramref name="max" />[i])</c>.
/// </para>
/// </remarks>
public static void Clamp<T>(T x, T min, ReadOnlySpan<T> max, Span<T> destination)
Expand All @@ -211,40 +206,44 @@ public static void Clamp<T>(T x, T min, ReadOnlySpan<T> max, Span<T> destination
InvokeSpanScalarScalarIntoSpan<T, ClampOperatorMaxXMin<T>>(max, x, min, destination);
}

/// <summary>T.Clamp(x, min, max)</summary>
/// <summary>Min(Max(x, min), max)</summary>
internal readonly struct ClampOperatorXMinMax<T> : ITernaryOperator<T>
where T : INumber<T>
{
public static bool Vectorizable => true;

public static T Invoke(T x, T min, T max) => T.Clamp(x, min, max);
// When T is vectorizable, the scalar remainder must match the vectorized Vector128/256/512.Clamp path,
// which follows HLSL and computes Min(Max(x, min), max) without validating that min <= max. Half is also
// vectorizable here via the Half-as-Int16 path (see TryTernaryInvokeHalfAsInt16), so it uses the same
// formula to stay consistent regardless of length. Every other T flows entirely through this scalar path,
// so we defer to T.Clamp, which throws when min > max.
public static T Invoke(T x, T min, T max) => Vector128<T>.IsSupported || typeof(T) == typeof(Half) ? T.Min(T.Max(x, min), max) : T.Clamp(x, min, max);

Comment thread
tannergooding marked this conversation as resolved.
public static Vector128<T> Invoke(Vector128<T> x, Vector128<T> min, Vector128<T> max) => Vector128.Clamp(x, min, max);
public static Vector256<T> Invoke(Vector256<T> x, Vector256<T> min, Vector256<T> max) => Vector256.Clamp(x, min, max);
public static Vector512<T> Invoke(Vector512<T> x, Vector512<T> min, Vector512<T> max) => Vector512.Clamp(x, min, max);
}

/// <summary>T.Clamp(min, max, x)</summary>
/// <summary>Min(Max(x, min), max)</summary>
internal readonly struct ClampOperatorMinMaxX<T> : ITernaryOperator<T>
where T : INumber<T>
{
public static bool Vectorizable => true;

public static T Invoke(T min, T max, T x) => T.Clamp(x, min, max);
public static T Invoke(T min, T max, T x) => Vector128<T>.IsSupported || typeof(T) == typeof(Half) ? T.Min(T.Max(x, min), max) : T.Clamp(x, min, max);

public static Vector128<T> Invoke(Vector128<T> min, Vector128<T> max, Vector128<T> x) => Vector128.Clamp(x, min, max);
public static Vector256<T> Invoke(Vector256<T> min, Vector256<T> max, Vector256<T> x) => Vector256.Clamp(x, min, max);
public static Vector512<T> Invoke(Vector512<T> min, Vector512<T> max, Vector512<T> x) => Vector512.Clamp(x, min, max);
}

/// <summary>T.Clamp(max, x, min)</summary>
/// <summary>Min(Max(x, min), max)</summary>
internal readonly struct ClampOperatorMaxXMin<T> : ITernaryOperator<T>
where T : INumber<T>
{
public static bool Vectorizable => true;

public static T Invoke(T max, T x, T min) => T.Clamp(x, min, max);

public static T Invoke(T max, T x, T min) => Vector128<T>.IsSupported || typeof(T) == typeof(Half) ? T.Min(T.Max(x, min), max) : T.Clamp(x, min, max);

public static Vector128<T> Invoke(Vector128<T> max, Vector128<T> x, Vector128<T> min) => Vector128.Clamp(x, min, max);
public static Vector256<T> Invoke(Vector256<T> max, Vector256<T> x, Vector256<T> min) => Vector256.Clamp(x, min, max);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,6 @@ internal static void ThrowArgument_CannotReshapeNonContiguousOrDense()
throw new ArgumentException(SR.Argument_CannotReshapeNonContiguousOrDense);
}

[DoesNotReturn]
internal static void ThrowArgument_MinGreaterThanMax()
{
throw new ArgumentException(SR.Argument_MinGreaterThanMax);
}

[DoesNotReturn]
internal static void ThrowArithmetic_NaN()
{
Expand Down
Loading