Skip to content
Open
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
@@ -1,6 +1,7 @@
// 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.Runtime.Intrinsics;

namespace System.Numerics.Tensors
Expand All @@ -26,14 +27,68 @@ public static void Acosh<T>(ReadOnlySpan<T> x, Span<T> destination)
InvokeSpanIntoSpan<T, AcoshOperator<T>>(x, destination);

/// <summary>T.Acosh(x)</summary>
private readonly struct AcoshOperator<T> : IUnaryOperator<T, T>
internal readonly struct AcoshOperator<T> : IUnaryOperator<T, T>
where T : IHyperbolicFunctions<T>
{
public static bool Vectorizable => false; // TODO: Vectorize
public static bool Vectorizable =>
#if NET11_0_OR_GREATER
typeof(T) == typeof(float) || typeof(T) == typeof(double);
#else
false;
#endif

public static T Invoke(T x) => T.Acosh(x);
public static Vector128<T> Invoke(Vector128<T> x) => throw new NotSupportedException();
public static Vector256<T> Invoke(Vector256<T> x) => throw new NotSupportedException();
public static Vector512<T> Invoke(Vector512<T> x) => throw new NotSupportedException();

public static Vector128<T> Invoke(Vector128<T> x)
{
#if NET11_0_OR_GREATER
if (typeof(T) == typeof(double))
{
return Vector128.Acosh(x.AsDouble()).As<double, T>();
}
else
{
Debug.Assert(typeof(T) == typeof(float));
return Vector128.Acosh(x.AsSingle()).As<float, T>();
}
#else
throw new NotSupportedException();
#endif
}

public static Vector256<T> Invoke(Vector256<T> x)
{
#if NET11_0_OR_GREATER
if (typeof(T) == typeof(double))
{
return Vector256.Acosh(x.AsDouble()).As<double, T>();
}
else
{
Debug.Assert(typeof(T) == typeof(float));
return Vector256.Acosh(x.AsSingle()).As<float, T>();
}
#else
throw new NotSupportedException();
#endif
}

public static Vector512<T> Invoke(Vector512<T> x)
{
#if NET11_0_OR_GREATER
if (typeof(T) == typeof(double))
{
return Vector512.Acosh(x.AsDouble()).As<double, T>();
}
else
{
Debug.Assert(typeof(T) == typeof(float));
return Vector512.Acosh(x.AsSingle()).As<float, T>();
}
#else
throw new NotSupportedException();
#endif
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ public static IEnumerable<object[]> SpanDestinationFunctionsToTest()
// The current trigonometric algorithm depends on hardware FMA support for best precision.
T? trigTolerance = IsFmaSupported ? null : Helpers.DetermineTolerance<T>(doubleTolerance: 1e-10, floatTolerance: 1e-4f);

yield return Create(TensorPrimitives.Acosh, T.Acosh);
yield return Create(TensorPrimitives.Acosh, T.Acosh, Helpers.DetermineTolerance<T>(doubleTolerance: 1e-14, floatTolerance: 1e-6f));
yield return Create(TensorPrimitives.AcosPi, T.AcosPi);
yield return Create(TensorPrimitives.Acos, T.Acos);
yield return Create(TensorPrimitives.Asinh, T.Asinh);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,53 @@ public static Vector128<float> Asin(Vector128<float> vector)
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> Acosh(Vector128<double> vector)
{
if (IsHardwareAccelerated)
{
return VectorMath.AcoshDouble<Vector128<double>, Vector128<long>, Vector128<ulong>>(vector);
}
else
{
return Create(
Vector64.Acosh(vector._lower),
Vector64.Acosh(vector._upper)
);
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> Acosh(Vector128<float> vector)
{
if (IsHardwareAccelerated)
{
if (Vector256.IsHardwareAccelerated)
{
return VectorMath.AcoshSingle<Vector128<float>, Vector128<int>, Vector128<uint>, Vector256<double>, Vector256<long>, Vector256<ulong>>(vector);
}
else
{
return VectorMath.AcoshSingle<Vector128<float>, Vector128<int>, Vector128<uint>, Vector128<double>, Vector128<long>, Vector128<ulong>>(vector);
}
}
else
{
return Create(
Vector64.Acosh(vector._lower),
Vector64.Acosh(vector._upper)
);
}
}

/// <inheritdoc cref="Vector64.Cos(Vector64{double})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> Cos(Vector128<double> vector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,53 @@ public static Vector256<float> Asin(Vector256<float> vector)
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<double> Acosh(Vector256<double> vector)
{
if (IsHardwareAccelerated)
{
return VectorMath.AcoshDouble<Vector256<double>, Vector256<long>, Vector256<ulong>>(vector);
}
else
{
return Create(
Vector128.Acosh(vector._lower),
Vector128.Acosh(vector._upper)
);
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Acosh(Vector256<float> vector)
{
if (IsHardwareAccelerated)
{
if (Vector512.IsHardwareAccelerated)
{
return VectorMath.AcoshSingle<Vector256<float>, Vector256<int>, Vector256<uint>, Vector512<double>, Vector512<long>, Vector512<ulong>>(vector);
}
else
{
return VectorMath.AcoshSingle<Vector256<float>, Vector256<int>, Vector256<uint>, Vector256<double>, Vector256<long>, Vector256<ulong>>(vector);
}
}
else
{
return Create(
Vector128.Acosh(vector._lower),
Vector128.Acosh(vector._upper)
);
}
}

/// <inheritdoc cref="Vector128.Cos(Vector128{double})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<double> Cos(Vector256<double> vector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,46 @@ public static Vector512<float> Asin(Vector512<float> vector)
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<double> Acosh(Vector512<double> vector)
{
if (IsHardwareAccelerated)
{
return VectorMath.AcoshDouble<Vector512<double>, Vector512<long>, Vector512<ulong>>(vector);
}
else
{
return Create(
Vector256.Acosh(vector._lower),
Vector256.Acosh(vector._upper)
);
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Acosh(Vector512<float> vector)
{
if (IsHardwareAccelerated)
{
return VectorMath.AcoshSingle<Vector512<float>, Vector512<int>, Vector512<uint>, Vector512<double>, Vector512<long>, Vector512<ulong>>(vector);
}
else
{
return Create(
Vector256.Acosh(vector._lower),
Vector256.Acosh(vector._upper)
);
}
}

/// <inheritdoc cref="Vector256.Cos(Vector256{double})" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<double> Cos(Vector512<double> vector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,47 @@ public static Vector64<float> Asin(Vector64<float> vector)
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector64<double> Acosh(Vector64<double> vector)
{
if (IsHardwareAccelerated)
{
return VectorMath.AcoshDouble<Vector64<double>, Vector64<long>, Vector64<ulong>>(vector);
}
else
{
return Acosh<double>(vector);
}
}

/// <summary>Computes the inverse hyperbolic cosine of each element in a vector.</summary>
/// <param name="vector">The vector whose inverse hyperbolic cosine is to be computed.</param>
/// <returns>A vector whose elements are the inverse hyperbolic cosine of the corresponding elements in <paramref name="vector" />.</returns>
/// <remarks>The input should be greater than or equal to 1.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector64<float> Acosh(Vector64<float> vector)
{
if (IsHardwareAccelerated)
{
if (Vector128.IsHardwareAccelerated)
{
return VectorMath.AcoshSingle<Vector64<float>, Vector64<int>, Vector64<uint>, Vector128<double>, Vector128<long>, Vector128<ulong>>(vector);
}
else
{
return VectorMath.AcoshSingle<Vector64<float>, Vector64<int>, Vector64<uint>, Vector64<double>, Vector64<long>, Vector64<ulong>>(vector);
}
}
else
{
return Acosh<float>(vector);
}
}

/// <summary>Computes the cos of each element in a vector.</summary>
/// <param name="vector">The vector that will have its Cos computed.</param>
/// <returns>A vector whose elements are the cos of the elements in <paramref name="vector" />.</returns>
Expand Down Expand Up @@ -3713,6 +3754,20 @@ internal static Vector64<T> Asin<T>(Vector64<T> vector)
return result;
}

internal static Vector64<T> Acosh<T>(Vector64<T> vector)
where T : IHyperbolicFunctions<T>
{
Unsafe.SkipInit(out Vector64<T> result);

for (int index = 0; index < Vector64<T>.Count; index++)
{
T value = T.Acosh(vector.GetElementUnsafe(index));
result.SetElementUnsafe(index, value);
}

return result;
}

internal static Vector64<T> Sin<T>(Vector64<T> vector)
where T : ITrigonometricFunctions<T>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3221,5 +3221,77 @@ private static TVectorDouble AsinSingleCoreDouble<TVectorDouble>(TVectorDouble a

return ax + ax * g * poly + (TVectorDouble.Create(PIBY2) & gtHalf);
}

public static TVectorDouble AcoshDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(TVectorDouble x)
where TVectorDouble : unmanaged, ISimdVector<TVectorDouble, double>
where TVectorInt64 : unmanaged, ISimdVector<TVectorInt64, long>
where TVectorUInt64 : unmanaged, ISimdVector<TVectorUInt64, ulong>
{
// The AMD AOCL-LibM scalar acosh implementation (acosh.c) uses range-based
// polynomial lookup tables which cannot be trivially vectorized due to the cost
// of gather instructions. Instead, this uses the mathematical identity:
// acosh(x) = log(x + sqrt((x - 1) * (x + 1)))
// using (x-1)*(x+1) instead of x^2-1 to avoid catastrophic cancellation near x=1,
// with special handling for large x for improved accuracy.

const double LN2 = 0.693147180559945309417;
const double LARGE_THRESHOLD = 268435456.0; // 2^28

// Return NaN for x < 1
TVectorDouble nanMask = TVectorDouble.LessThan(x, TVectorDouble.One);

// For large values (x > 2^28), use log(2) + log(x)
TVectorDouble largeMask = TVectorDouble.GreaterThan(x, TVectorDouble.Create(LARGE_THRESHOLD));

// Normal case: log(x + sqrt((x - 1) * (x + 1)))
// Using (x-1)*(x+1) avoids catastrophic cancellation when x is near 1
TVectorDouble xm1 = x - TVectorDouble.One;
TVectorDouble xp1 = x + TVectorDouble.One;
TVectorDouble normal = LogDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(x + TVectorDouble.Sqrt(xm1 * xp1));

// Large value case: log(2) + log(x)
TVectorDouble large = TVectorDouble.Create(LN2) + LogDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(x);

// Select appropriate result based on magnitude
TVectorDouble result = TVectorDouble.ConditionalSelect(largeMask, large, normal);
result = TVectorDouble.ConditionalSelect(nanMask, TVectorDouble.Create(double.NaN), result);

return result;
}

public static TVectorSingle AcoshSingle<TVectorSingle, TVectorInt32, TVectorUInt32, TVectorDouble, TVectorInt64, TVectorUInt64>(TVectorSingle x)
where TVectorSingle : unmanaged, ISimdVector<TVectorSingle, float>
where TVectorInt32 : unmanaged, ISimdVector<TVectorInt32, int>
where TVectorUInt32 : unmanaged, ISimdVector<TVectorUInt32, uint>
where TVectorDouble : unmanaged, ISimdVector<TVectorDouble, double>
where TVectorInt64 : unmanaged, ISimdVector<TVectorInt64, long>
where TVectorUInt64 : unmanaged, ISimdVector<TVectorUInt64, ulong>
{
// This code is based on `acoshf` from amd/aocl-libm-ose
// Copyright (C) 2008-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the BSD 3-Clause "New" or "Revised" License
// See THIRD-PARTY-NOTICES.TXT for the full license text

// This implementation computes single-precision acosh by widening the
// input to double precision, calling AcoshDouble, and then narrowing
// the result back to single precision. AcoshDouble uses mathematical
// identities (no polynomial approximation) for improved accuracy.

if (TVectorSingle.ElementCount == TVectorDouble.ElementCount)
{
TVectorDouble dx = Widen<TVectorSingle, TVectorDouble>(x);
return Narrow<TVectorDouble, TVectorSingle>(AcoshDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(dx));
}
else
{
TVectorDouble dxLo = WidenLower<TVectorSingle, TVectorDouble>(x);
TVectorDouble dxHi = WidenUpper<TVectorSingle, TVectorDouble>(x);
return Narrow<TVectorDouble, TVectorSingle>(
AcoshDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(dxLo),
AcoshDouble<TVectorDouble, TVectorInt64, TVectorUInt64>(dxHi)
);
}
}
}
}
Loading
Loading