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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This release is compatible with NumPy 2.4.5.
* Fixed incorrect `dpnp.tensor.acosh` result for `complex(±0, NaN)` special case to match the Python Array API specification [#2914](https://github.com/IntelPython/dpnp/pull/2914)
* Fixed fork PR documentation workflow failures by implementing conditional publishing strategy: upstream PRs publish to GitHub Pages with comment, fork PRs upload artifacts [#2910](https://github.com/IntelPython/dpnp/pull/2910)
* Fixed missing `libtensor` headers in the installed `dpnp` package [#2915](https://github.com/IntelPython/dpnp/pull/2915)
* Fixed `dpnp.tensor.acosh` and `dpnp.tensor.acos` returning infinity for complex numbers with large negative real parts [#2928](https://github.com/IntelPython/dpnp/pull/2928)

### Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,17 @@
//===---------------------------------------------------------------------===//

#pragma once
#include <cmath>
#include <complex>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <vector>

#include "sycl_complex.hpp"
#include "common.hpp"
#include "complex_math.hpp"
#include "vec_size_util.hpp"

#include "kernels/dpnp_tensor_types.hpp"
#include "kernels/elementwise_functions/common.hpp"

#include "utils/type_dispatch_building.hpp"
#include "utils/type_utils.hpp"
Expand Down Expand Up @@ -75,58 +73,7 @@ struct AcosFunctor
resT operator()(const argT &in) const
{
if constexpr (is_complex<argT>::value) {
using realT = typename argT::value_type;

static constexpr realT q_nan =
std::numeric_limits<realT>::quiet_NaN();

const realT x = std::real(in);
const realT y = std::imag(in);

if (std::isnan(x)) {
/* acos(NaN + I*+-Inf) = NaN + I*-+Inf */
if (std::isinf(y)) {
return resT{q_nan, -y};
}

/* all other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}
if (std::isnan(y)) {
/* acos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */
if (std::isinf(x)) {
return resT{q_nan, -std::numeric_limits<realT>::infinity()};
}
/* acos(0 + I*NaN) = PI/2 + I*NaN with inexact */
if (x == realT(0)) {
const realT res_re = sycl::atan(realT(1)) * 2; // PI/2
return resT{res_re, q_nan};
}

/* all other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}

/*
* For large x or y including acos(+-Inf + I*+-Inf)
*/
static constexpr realT r_eps =
realT(1) / std::numeric_limits<realT>::epsilon();
if (sycl::fabs(x) > r_eps || sycl::fabs(y) > r_eps) {
using sycl_complexT = exprm_ns::complex<realT>;
sycl_complexT log_in =
exprm_ns::log(exprm_ns::complex<realT>(in));

const realT wx = log_in.real();
const realT wy = log_in.imag();
const realT rx = sycl::fabs(wy);

realT ry = wx + sycl::log(realT(2));
return resT{rx, (sycl::signbit(y)) ? ry : -ry};
}

/* ordinary cases */
return exprm_ns::acos(exprm_ns::complex<realT>(in)); // acos(in);
return dpnp::tensor::kernels::complex_math::cacos<argT>(in);
}
else {
static_assert(std::is_floating_point_v<argT> ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@
#include <complex>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <vector>

#include <sycl/sycl.hpp>

#include "sycl_complex.hpp"
#include "common.hpp"
#include "complex_math.hpp"
#include "vec_size_util.hpp"

#include "kernels/dpnp_tensor_types.hpp"
#include "kernels/elementwise_functions/common.hpp"

#include "utils/type_dispatch_building.hpp"
#include "utils/type_utils.hpp"
Expand Down Expand Up @@ -79,63 +78,8 @@ struct AcoshFunctor
if constexpr (is_complex<argT>::value) {
using realT = typename argT::value_type;

static constexpr realT q_nan =
std::numeric_limits<realT>::quiet_NaN();
/*
* acosh(in) = I*acos(in) or -I*acos(in)
* where the sign is chosen so Re(acosh(in)) >= 0.
* So, we first calculate acos(in) and then acosh(in).
*/
const realT x = std::real(in);
const realT y = std::imag(in);

resT acos_in;
if (std::isnan(x)) {
/* acos(NaN + I*+-Inf) = NaN + I*-+Inf */
if (std::isinf(y)) {
acos_in = resT{q_nan, -y};
}
else {
acos_in = resT{q_nan, q_nan};
}
}
else if (std::isnan(y)) {
/* acos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */
static constexpr realT inf =
std::numeric_limits<realT>::infinity();

if (std::isinf(x)) {
acos_in = resT{q_nan, -inf};
}
/* acos(0 + I*NaN) = Pi/2 + I*NaN with inexact */
else if (x == realT(0)) {
const realT pi_half = sycl::atan(realT(1)) * 2;
acos_in = resT{pi_half, q_nan};
}
else {
acos_in = resT{q_nan, q_nan};
}
}

static constexpr realT r_eps =
realT(1) / std::numeric_limits<realT>::epsilon();
/*
* For large x or y including acos(+-Inf + I*+-Inf)
*/
if (sycl::fabs(x) > r_eps || sycl::fabs(y) > r_eps) {
using sycl_complexT = typename exprm_ns::complex<realT>;
const sycl_complexT log_in = exprm_ns::log(sycl_complexT(in));
const realT wx = log_in.real();
const realT wy = log_in.imag();
const realT rx = sycl::fabs(wy);
realT ry = wx + sycl::log(realT(2));
acos_in = resT{rx, (sycl::signbit(y)) ? ry : -ry};
}
else {
/* ordinary cases */
acos_in =
exprm_ns::acos(exprm_ns::complex<realT>(in)); // acos(in);
}
/* calculate acos value */
resT acos_in = dpnp::tensor::kernels::complex_math::cacos<argT>(in);

/* Now we calculate acosh(z) */
const realT rx = std::real(acos_in);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,18 @@
//===---------------------------------------------------------------------===//

#pragma once
#include <cmath>
#include <complex>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <vector>

#include <sycl/sycl.hpp>

#include "sycl_complex.hpp"
#include "common.hpp"
#include "complex_math.hpp"
#include "vec_size_util.hpp"

#include "kernels/dpnp_tensor_types.hpp"
#include "kernels/elementwise_functions/common.hpp"

#include "utils/type_dispatch_building.hpp"
#include "utils/type_utils.hpp"
Expand Down Expand Up @@ -77,78 +74,7 @@ struct AsinFunctor
resT operator()(const argT &in) const
{
if constexpr (is_complex<argT>::value) {
using realT = typename argT::value_type;

static constexpr realT q_nan =
std::numeric_limits<realT>::quiet_NaN();

/*
* asin(in) = I * conj( asinh(I * conj(in)) )
* so we first calculate w = asinh(I * conj(in)) with
* x = real(I * conj(in)) = imag(in)
* y = imag(I * conj(in)) = real(in)
* and then return {imag(w), real(w)} which is asin(in)
*/
const realT x = std::imag(in);
const realT y = std::real(in);

if (std::isnan(x)) {
/* asinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */
if (std::isinf(y)) {
const realT asinh_re = y;
const realT asinh_im = q_nan;
return resT{asinh_im, asinh_re};
}
/* asinh(NaN + I*0) = NaN + I*0 */
if (y == realT(0)) {
const realT asinh_re = q_nan;
const realT asinh_im = y;
return resT{asinh_im, asinh_re};
}
/* All other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}
else if (std::isnan(y)) {
/* asinh(+-Inf + I*NaN) = +-Inf + I*NaN */
if (std::isinf(x)) {
const realT asinh_re = x;
const realT asinh_im = q_nan;
return resT{asinh_im, asinh_re};
}
/* All other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}

/*
* For large x or y including asinh(+-Inf + I*+-Inf)
* asinh(in) = sign(x)*log(sign(x)*in) + O(1/in^2) as in ->
* infinity The above formula works for the imaginary part as well,
* because Im(asinh(in)) = sign(x)*atan2(sign(x)*y, fabs(x)) +
* O(y/in^3) as in -> infinity, uniformly in y
*/
static constexpr realT r_eps =
realT(1) / std::numeric_limits<realT>::epsilon();
if (sycl::fabs(x) > r_eps || sycl::fabs(y) > r_eps) {
using sycl_complexT = exprm_ns::complex<realT>;
const sycl_complexT z{x, y};
realT wx, wy;
if (!sycl::signbit(x)) {
const auto log_z = exprm_ns::log(z);
wx = log_z.real() + sycl::log(realT(2));
wy = log_z.imag();
}
else {
const auto log_mz = exprm_ns::log(-z);
wx = log_mz.real() + sycl::log(realT(2));
wy = log_mz.imag();
}
const realT asinh_re = sycl::copysign(wx, x);
const realT asinh_im = sycl::copysign(wy, y);
return resT{asinh_im, asinh_re};
}
/* ordinary cases */
return exprm_ns::asin(
exprm_ns::complex<realT>(in)); // sycl::asin(in);
return complex_math::casin(in);
}
else {
static_assert(std::is_floating_point_v<argT> ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,18 @@
//===---------------------------------------------------------------------===//

#pragma once
#include <cmath>
#include <complex>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <vector>

#include <sycl/sycl.hpp>

#include "sycl_complex.hpp"
#include "common.hpp"
#include "complex_math.hpp"
#include "vec_size_util.hpp"

#include "kernels/dpnp_tensor_types.hpp"
#include "kernels/elementwise_functions/common.hpp"

#include "utils/type_dispatch_building.hpp"
#include "utils/type_utils.hpp"
Expand Down Expand Up @@ -77,61 +74,7 @@ struct AsinhFunctor
resT operator()(const argT &in) const
{
if constexpr (is_complex<argT>::value) {
using realT = typename argT::value_type;

static constexpr realT q_nan =
std::numeric_limits<realT>::quiet_NaN();

const realT x = std::real(in);
const realT y = std::imag(in);

if (std::isnan(x)) {
/* asinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */
if (std::isinf(y)) {
return resT{y, q_nan};
}
/* asinh(NaN + I*0) = NaN + I*0 */
if (y == realT(0)) {
return resT{q_nan, y};
}
/* All other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}

if (std::isnan(y)) {
/* asinh(+-Inf + I*NaN) = +-Inf + I*NaN */
if (std::isinf(x)) {
return resT{x, q_nan};
}
/* All other cases involving NaN return NaN + I*NaN. */
return resT{q_nan, q_nan};
}

/*
* For large x or y including asinh(+-Inf + I*+-Inf)
* asinh(in) = sign(x)*log(sign(x)*in) + O(1/in^2) as in ->
* infinity The above formula works for the imaginary part as well,
* because Im(asinh(in)) = sign(x)*atan2(sign(x)*y, fabs(x)) +
* O(y/in^3) as in -> infinity, uniformly in y
*/
static constexpr realT r_eps =
realT(1) / std::numeric_limits<realT>::epsilon();

if (sycl::fabs(x) > r_eps || sycl::fabs(y) > r_eps) {
using sycl_complexT = exprm_ns::complex<realT>;
sycl_complexT log_in = (sycl::signbit(x))
? exprm_ns::log(sycl_complexT(-in))
: exprm_ns::log(sycl_complexT(in));
realT wx = log_in.real() + sycl::log(realT(2));
realT wy = log_in.imag();

const realT res_re = sycl::copysign(wx, x);
const realT res_im = sycl::copysign(wy, y);
return resT{res_re, res_im};
}

/* ordinary cases */
return exprm_ns::asinh(exprm_ns::complex<realT>(in)); // asinh(in);
return complex_math::casinh(in);
}
else {
static_assert(std::is_floating_point_v<argT> ||
Expand Down
Loading
Loading