Skip to content

Commit d76d44e

Browse files
authored
implement dpnp.logaddexp (#1561)
* implement dpnp.logaddexp * address comments * add nan test
1 parent 946ff08 commit d76d44e

File tree

8 files changed

+150
-7
lines changed

8 files changed

+150
-7
lines changed

dpnp/dpnp_algo/dpnp_elementwise_common.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"dpnp_less",
7474
"dpnp_less_equal",
7575
"dpnp_log",
76+
"dpnp_logaddexp",
7677
"dpnp_logical_and",
7778
"dpnp_logical_not",
7879
"dpnp_logical_or",
@@ -1705,6 +1706,58 @@ def dpnp_log(x, out=None, order="K"):
17051706
return dpnp_array._create_from_usm_ndarray(res_usm)
17061707

17071708

1709+
_logaddexp_docstring_ = """
1710+
logaddexp(x1, x2, out=None, order="K")
1711+
1712+
Calculates the natural logarithm of the sum of exponentiations for each element
1713+
`x1_i` of the input array `x1` with the respective element `x2_i` of the input
1714+
array `x2`.
1715+
1716+
This function calculates `log(exp(x1) + exp(x2))` more accurately for small
1717+
values of `x`.
1718+
1719+
Args:
1720+
x1 (dpnp.ndarray):
1721+
First input array, expected to have a real-valued floating-point
1722+
data type.
1723+
x2 (dpnp.ndarray):
1724+
Second input array, also expected to have a real-valued
1725+
floating-point data type.
1726+
out ({None, dpnp.ndarray}, optional):
1727+
Output array to populate.
1728+
Array have the correct shape and the expected data type.
1729+
order ("C","F","A","K", None, optional):
1730+
Memory layout of the newly output array, if parameter `out` is `None`.
1731+
Default: "K".
1732+
Returns:
1733+
dpnp.ndarray:
1734+
An array containing the result of element-wise result. The data type
1735+
of the returned array is determined by the Type Promotion Rules.
1736+
"""
1737+
1738+
1739+
logaddexp_func = BinaryElementwiseFunc(
1740+
"logaddexp",
1741+
ti._logaddexp_result_type,
1742+
ti._logaddexp,
1743+
_logaddexp_docstring_,
1744+
)
1745+
1746+
1747+
def dpnp_logaddexp(x1, x2, out=None, order="K"):
1748+
"""Invokes logaddexp() from dpctl.tensor implementation for logaddexp() function."""
1749+
1750+
# dpctl.tensor only works with usm_ndarray or scalar
1751+
x1_usm_or_scalar = dpnp.get_usm_ndarray_or_scalar(x1)
1752+
x2_usm_or_scalar = dpnp.get_usm_ndarray_or_scalar(x2)
1753+
out_usm = None if out is None else dpnp.get_usm_ndarray(out)
1754+
1755+
res_usm = logaddexp_func(
1756+
x1_usm_or_scalar, x2_usm_or_scalar, out=out_usm, order=order
1757+
)
1758+
return dpnp_array._create_from_usm_ndarray(res_usm)
1759+
1760+
17081761
_logical_and_docstring_ = """
17091762
logical_and(x1, x2, out=None, order='K')
17101763

dpnp/dpnp_iface_trigonometric.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
dpnp_cosh,
6060
dpnp_hypot,
6161
dpnp_log,
62+
dpnp_logaddexp,
6263
dpnp_sin,
6364
dpnp_sinh,
6465
dpnp_sqrt,
@@ -88,6 +89,7 @@
8889
"log10",
8990
"log1p",
9091
"log2",
92+
"logaddexp",
9193
"rad2deg",
9294
"radians",
9395
"reciprocal",
@@ -1058,6 +1060,69 @@ def log2(x1):
10581060
return call_origin(numpy.log2, x1)
10591061

10601062

1063+
def logaddexp(
1064+
x1,
1065+
x2,
1066+
/,
1067+
out=None,
1068+
*,
1069+
where=True,
1070+
order="K",
1071+
dtype=None,
1072+
subok=True,
1073+
**kwargs,
1074+
):
1075+
"""
1076+
Calculates ``log(exp(x1) + exp(x2))``, element-wise.
1077+
1078+
For full documentation refer to :obj:`numpy.logaddexp`.
1079+
1080+
Returns
1081+
-------
1082+
out : dpnp.ndarray
1083+
Logarithm of ``exp(x1) + exp(x2)``, element-wise.
1084+
1085+
Limitations
1086+
-----------
1087+
Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray`
1088+
or :class:`dpctl.tensor.usm_ndarray`, but both `x1` and `x2` can not be scalars at the same time.
1089+
Parameters `where`, `dtype` and `subok` are supported with their default values.
1090+
Keyword arguments `kwargs` are currently unsupported.
1091+
Otherwise the function will be executed sequentially on CPU.
1092+
Input array data types are limited by supported DPNP :ref:`Data types`.
1093+
1094+
See Also
1095+
--------
1096+
:obj:`dpnp.log` : Natural logarithm, element-wise.
1097+
:obj:`dpnp.exp` : Exponential, element-wise.
1098+
1099+
Examples
1100+
--------
1101+
>>> import dpnp as np
1102+
>>> prob1 = np.log(np.array(1e-50))
1103+
>>> prob2 = np.log(np.array(2.5e-50))
1104+
>>> prob12 = np.logaddexp(prob1, prob2)
1105+
>>> prob12
1106+
array(-113.87649168)
1107+
>>> np.exp(prob12)
1108+
array(3.5e-50)
1109+
1110+
"""
1111+
1112+
return check_nd_call_func(
1113+
numpy.logaddexp,
1114+
dpnp_logaddexp,
1115+
x1,
1116+
x2,
1117+
out=out,
1118+
where=where,
1119+
order=order,
1120+
dtype=dtype,
1121+
subok=subok,
1122+
**kwargs,
1123+
)
1124+
1125+
10611126
def reciprocal(x1, **kwargs):
10621127
"""
10631128
Return the reciprocal of the argument, element-wise.

tests/skipped_tests.tbl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ tests/test_umath.py::test_umaths[('ldexp', 'fi')]
107107
tests/test_umath.py::test_umaths[('ldexp', 'fl')]
108108
tests/test_umath.py::test_umaths[('ldexp', 'di')]
109109
tests/test_umath.py::test_umaths[('ldexp', 'dl')]
110-
tests/test_umath.py::test_umaths[('logaddexp', 'ff')]
111-
tests/test_umath.py::test_umaths[('logaddexp', 'dd')]
112110
tests/test_umath.py::test_umaths[('logaddexp2', 'ff')]
113111
tests/test_umath.py::test_umaths[('logaddexp2', 'dd')]
114112
tests/test_umath.py::test_umaths[('nextafter', 'ff')]
@@ -490,7 +488,6 @@ tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticModf::test_m
490488

491489
tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_3_{name='angle', nargs=1}::test_raises_with_numpy_input
492490

493-
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp
494491
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp2
495492
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp2_infinities
496493
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_copysign_float

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ tests/test_umath.py::test_umaths[('ldexp', 'fi')]
5353
tests/test_umath.py::test_umaths[('ldexp', 'fl')]
5454
tests/test_umath.py::test_umaths[('ldexp', 'di')]
5555
tests/test_umath.py::test_umaths[('ldexp', 'dl')]
56-
tests/test_umath.py::test_umaths[('logaddexp', 'ff')]
57-
tests/test_umath.py::test_umaths[('logaddexp', 'dd')]
5856
tests/test_umath.py::test_umaths[('logaddexp2', 'ff')]
5957
tests/test_umath.py::test_umaths[('logaddexp2', 'dd')]
6058
tests/test_umath.py::test_umaths[('nextafter', 'ff')]
@@ -625,7 +623,6 @@ tests/third_party/cupy/manipulation_tests/test_tiling.py::TestTile_param_5_{reps
625623

626624
tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_3_{name='angle', nargs=1}::test_raises_with_numpy_input
627625

628-
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp
629626
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp2
630627
tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp2_infinities
631628
tests/third_party/cupy/math_tests/test_floating.py::TestFloating::test_copysign_float

tests/test_strides.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def test_strides_reciprocal(dtype, shape):
167167
"fmax",
168168
"fmin",
169169
"hypot",
170+
"logaddexp",
170171
"maximum",
171172
"minimum",
172173
"multiply",

tests/test_sycl_queue.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ def test_proj(device):
369369
[[1.0, 2.0, 3.0, 4.0]],
370370
[[-1.0, -2.0, -4.0, -5.0]],
371371
),
372+
pytest.param(
373+
"logaddexp",
374+
[[-1, 2, 5, 9]],
375+
[[4, -3, 2, -8]],
376+
),
372377
pytest.param(
373378
"matmul", [[1.0, 0.0], [0.0, 1.0]], [[4.0, 1.0], [1.0, 2.0]]
374379
),

tests/test_usm_type.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,11 @@ def test_1in_1out(func, data, usm_type):
371371
[[1.0, 2.0, 3.0, 4.0]],
372372
[[-1.0, -2.0, -4.0, -5.0]],
373373
),
374+
pytest.param(
375+
"logaddexp",
376+
[[-1, 2, 5, 9]],
377+
[[4, -3, 2, -8]],
378+
),
374379
pytest.param(
375380
"maximum",
376381
[[0.0, 1.0, 2.0]],

tests/third_party/cupy/math_tests/test_explog.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import unittest
2+
import warnings
23

34
import numpy
45
import pytest
56

7+
from tests.helper import has_support_aspect64
68
from tests.third_party.cupy import testing
79

810

@@ -17,7 +19,7 @@ def check_unary(self, name, xp, dtype, no_complex=False):
1719
return getattr(xp, name)(a)
1820

1921
@testing.for_all_dtypes()
20-
@testing.numpy_cupy_allclose(atol=1e-5)
22+
@testing.numpy_cupy_allclose(atol=1e-5, type_check=has_support_aspect64())
2123
def check_binary(self, name, xp, dtype, no_complex=False):
2224
if no_complex:
2325
if numpy.dtype(dtype).kind == "c":
@@ -62,3 +64,21 @@ def test_logaddexp2(self):
6264
def test_logaddexp2_infinities(self, xp, dtype, val):
6365
a = xp.full((2, 3), val, dtype=dtype)
6466
return xp.logaddexp2(a, a)
67+
68+
69+
@pytest.mark.parametrize("val", [numpy.inf, -numpy.inf])
70+
@testing.for_float_dtypes()
71+
@testing.numpy_cupy_allclose()
72+
def test_logaddexp_infinities(xp, dtype, val):
73+
a = xp.full((2, 3), val, dtype=dtype)
74+
return xp.logaddexp(a, a)
75+
76+
77+
@testing.for_float_dtypes()
78+
@testing.numpy_cupy_allclose()
79+
def test_logaddexp_nan(xp, dtype):
80+
a = xp.full((2, 3), xp.nan, dtype=dtype)
81+
with warnings.catch_warnings():
82+
warnings.filterwarnings("ignore", category=RuntimeWarning)
83+
result = xp.logaddexp(a, a)
84+
return result

0 commit comments

Comments
 (0)