Skip to content

Commit cfaae8c

Browse files
authored
Add implementation of dpnp.float_power (#1957)
* Add implementation of dpnp.float_power() * Updated third party tests * Added more tests to cover different scenarios * Mute back test for dpnp.floor_divide * Update docstring with another way to state NaN value * Address comment to update tests
1 parent d353299 commit cfaae8c

File tree

12 files changed

+341
-8
lines changed

12 files changed

+341
-8
lines changed

doc/reference/ufunc.rst

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,27 @@ Math operations
2020
dpnp.add
2121
dpnp.subtract
2222
dpnp.multiply
23+
dpnp.matmul
2324
dpnp.divide
2425
dpnp.logaddexp
2526
dpnp.logaddexp2
2627
dpnp.true_divide
2728
dpnp.floor_divide
2829
dpnp.negative
30+
dpnp.positive
2931
dpnp.power
32+
dpnp.float_power
3033
dpnp.remainder
3134
dpnp.mod
3235
dpnp.fmod
33-
dpnp.abs
36+
dpnp.divmod
3437
dpnp.absolute
3538
dpnp.fabs
3639
dpnp.rint
3740
dpnp.sign
41+
dpnp.heaviside
42+
dpnp.conj
43+
dpnp.conjugate
3844
dpnp.exp
3945
dpnp.exp2
4046
dpnp.log
@@ -44,13 +50,24 @@ Math operations
4450
dpnp.log1p
4551
dpnp.proj
4652
dpnp.sqrt
47-
dpnp.cbrt
4853
dpnp.square
54+
dpnp.cbrt
4955
dpnp.reciprocal
5056
dpnp.rsqrt
5157
dpnp.gcd
5258
dpnp.lcm
5359

60+
.. tip::
61+
62+
The optional output arguments can be used to help you save memory
63+
for large calculations. If your arrays are large, complicated
64+
expressions can take longer than absolutely necessary due to the
65+
creation and (later) destruction of temporary calculation
66+
spaces. For example, the expression ``G = A * B + C`` is equivalent to
67+
``T1 = A * B; G = T1 + C; del T1``. It will be more quickly executed
68+
as ``G = A * B; add(G, C, G)`` which is the same as
69+
``G = A * B; G += C``.
70+
5471

5572
Trigonometric functions
5673
~~~~~~~~~~~~~~~~~~~~~~~

dpnp/backend/extensions/ufunc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ set(_elementwise_sources
2727
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/common.cpp
2828
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/degrees.cpp
2929
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/fabs.cpp
30+
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/float_power.cpp
3031
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/fmax.cpp
3132
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/fmin.cpp
3233
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/fmod.cpp

dpnp/backend/extensions/ufunc/elementwise_functions/common.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "degrees.hpp"
2929
#include "fabs.hpp"
30+
#include "float_power.hpp"
3031
#include "fmax.hpp"
3132
#include "fmin.hpp"
3233
#include "fmod.hpp"
@@ -44,6 +45,7 @@ void init_elementwise_functions(py::module_ m)
4445
{
4546
init_degrees(m);
4647
init_fabs(m);
48+
init_float_power(m);
4749
init_fmax(m);
4850
init_fmin(m);
4951
init_fmod(m);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2024, Intel Corporation
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// maxification, are permitted provided that the following conditions are met:
7+
// - Redistributions of source code must retain the above copyright notice,
8+
// this list of conditions and the following disclaimer.
9+
// - Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
// THE POSSIBILITY OF SUCH DAMAGE.
24+
//*****************************************************************************
25+
26+
#include <sycl/sycl.hpp>
27+
28+
#include "dpctl4pybind11.hpp"
29+
30+
#include "float_power.hpp"
31+
32+
// include a local copy of elementwise common header from dpctl tensor:
33+
// dpctl/tensor/libtensor/source/elementwise_functions/elementwise_functions.hpp
34+
// TODO: replace by including dpctl header once available
35+
#include "../../elementwise_functions/elementwise_functions.hpp"
36+
37+
// dpctl tensor headers
38+
#include "utils/type_dispatch.hpp"
39+
40+
namespace dpnp::extensions::ufunc
41+
{
42+
namespace py = pybind11;
43+
namespace py_int = dpnp::extensions::py_internal;
44+
45+
namespace impl
46+
{
47+
namespace td_ns = dpctl::tensor::type_dispatch;
48+
49+
// Supports only float and complex types
50+
template <typename T1, typename T2>
51+
struct OutputType
52+
{
53+
using value_type = typename std::disjunction<
54+
td_ns::BinaryTypeMapResultEntry<T1, float, T2, float, float>,
55+
td_ns::BinaryTypeMapResultEntry<T1, double, T2, double, double>,
56+
td_ns::BinaryTypeMapResultEntry<T1,
57+
std::complex<float>,
58+
T2,
59+
std::complex<float>,
60+
std::complex<float>>,
61+
td_ns::BinaryTypeMapResultEntry<T1,
62+
std::complex<double>,
63+
T2,
64+
std::complex<double>,
65+
std::complex<double>>,
66+
td_ns::DefaultResultEntry<void>>::result_type;
67+
};
68+
69+
static int float_power_output_typeid_table[td_ns::num_types][td_ns::num_types];
70+
71+
template <typename fnT, typename T1, typename T2>
72+
struct TypeMapFactory
73+
{
74+
std::enable_if_t<std::is_same<fnT, int>::value, int> get()
75+
{
76+
using rT = typename OutputType<T1, T2>::value_type;
77+
return td_ns::GetTypeid<rT>{}.get();
78+
}
79+
};
80+
81+
void populate_float_power_dispatch_tables(void)
82+
{
83+
td_ns::DispatchTableBuilder<int, TypeMapFactory, td_ns::num_types> dvb;
84+
dvb.populate_dispatch_table(float_power_output_typeid_table);
85+
}
86+
} // namespace impl
87+
88+
void init_float_power(py::module_ m)
89+
{
90+
impl::populate_float_power_dispatch_tables();
91+
using impl::float_power_output_typeid_table;
92+
93+
auto float_power_result_type_pyapi = [&](const py::dtype &dtype1,
94+
const py::dtype &dtype2) {
95+
return py_int::py_binary_ufunc_result_type(
96+
dtype1, dtype2, float_power_output_typeid_table);
97+
};
98+
m.def("_float_power_result_type", float_power_result_type_pyapi);
99+
}
100+
} // namespace dpnp::extensions::ufunc
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2024, Intel Corporation
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are met:
7+
// - Redistributions of source code must retain the above copyright notice,
8+
// this list of conditions and the following disclaimer.
9+
// - Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
// THE POSSIBILITY OF SUCH DAMAGE.
24+
//*****************************************************************************
25+
26+
#pragma once
27+
28+
#include <pybind11/pybind11.h>
29+
30+
namespace py = pybind11;
31+
32+
namespace dpnp::extensions::ufunc
33+
{
34+
void init_float_power(py::module_ m);
35+
} // namespace dpnp::extensions::ufunc

dpnp/dpnp_iface_mathematical.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"divide",
9999
"ediff1d",
100100
"fabs",
101+
"float_power",
101102
"floor",
102103
"floor_divide",
103104
"fmax",
@@ -1364,6 +1365,105 @@ def ediff1d(x1, to_end=None, to_begin=None):
13641365
)
13651366

13661367

1368+
_FLOAT_POWER_DOCSTRING = """
1369+
Calculates `x1_i` raised to `x2_i` for each element `x1_i` of the input array
1370+
`x1` with the respective element `x2_i` of the input array `x2`.
1371+
1372+
This differs from the power function in that boolean, integers, and float16 are
1373+
promoted to floats with a minimum precision of float32 so that the result is
1374+
always inexact. The intent is that the function will return a usable result for
1375+
negative powers and seldom overflow for positive powers.
1376+
1377+
Negative values raised to a non-integral value will return ``NaN``. To get
1378+
complex results, cast the input to complex, or specify the ``dtype`` to be one
1379+
of complex dtype.
1380+
1381+
For full documentation refer to :obj:`numpy.float_power`.
1382+
1383+
Parameters
1384+
----------
1385+
x1 : {dpnp.ndarray, usm_ndarray, scalar}
1386+
First input array, expected to have floating-point data types.
1387+
Both inputs `x1` and `x2` can not be scalars at the same time.
1388+
x2 : {dpnp.ndarray, usm_ndarray, scalar}
1389+
Second input array, also expected to floating-point data types.
1390+
Both inputs `x1` and `x2` can not be scalars at the same time.
1391+
out : {None, dpnp.ndarray, usm_ndarray}, optional
1392+
Output array to populate. Array must have the correct shape and
1393+
the expected data type.
1394+
Default: ``None``.
1395+
order : {"C", "F", "A", "K"}, optional
1396+
Memory layout of the newly output array, if parameter `out` is ``None``.
1397+
Default: ``"K"``.
1398+
1399+
Returns
1400+
-------
1401+
out : dpnp.ndarray
1402+
An array containing the bases in `x1` raised to the exponents in `x2`
1403+
element-wise.
1404+
1405+
Limitations
1406+
-----------
1407+
Parameters `where` and `subok` are supported with their default values.
1408+
Keyword argument `kwargs` is currently unsupported.
1409+
Otherwise ``NotImplementedError`` exception will be raised.
1410+
1411+
See Also
1412+
--------
1413+
:obj:`dpnp.power` : Power function that preserves type.
1414+
1415+
Examples
1416+
--------
1417+
>>> import dpnp as np
1418+
1419+
Cube each element in an array:
1420+
1421+
>>> x1 = np.arange(6)
1422+
>>> x1
1423+
array([0, 1, 2, 3, 4, 5])
1424+
>>> np.float_power(x1, 3)
1425+
array([ 0., 1., 8., 27., 64., 125.])
1426+
1427+
Raise the bases to different exponents:
1428+
1429+
>>> x2 = np.array([1.0, 2.0, 3.0, 3.0, 2.0, 1.0])
1430+
>>> np.float_power(x1, x2)
1431+
array([ 0., 1., 8., 27., 16., 5.])
1432+
1433+
The effect of broadcasting:
1434+
1435+
>>> x2 = np.array([[1, 2, 3, 3, 2, 1], [1, 2, 3, 3, 2, 1]])
1436+
>>> x2
1437+
array([[1, 2, 3, 3, 2, 1],
1438+
[1, 2, 3, 3, 2, 1]])
1439+
>>> np.float_power(x1, x2)
1440+
array([[ 0., 1., 8., 27., 16., 5.],
1441+
[ 0., 1., 8., 27., 16., 5.]])
1442+
1443+
Negative values raised to a non-integral value will result in ``NaN``:
1444+
1445+
>>> x3 = np.array([-1, -4])
1446+
>>> np.float_power(x3, 1.5)
1447+
array([nan, nan])
1448+
1449+
To get complex results, give the argument one of complex dtype, i.e.
1450+
``dtype=np.complex64``:
1451+
1452+
>>> np.float_power(x3, 1.5, dtype=np.complex64)
1453+
array([1.1924881e-08-1.j, 9.5399045e-08-8.j], dtype=complex64)
1454+
"""
1455+
1456+
float_power = DPNPBinaryFunc(
1457+
"float_power",
1458+
ufi._float_power_result_type,
1459+
ti._pow,
1460+
_FLOAT_POWER_DOCSTRING,
1461+
mkl_fn_to_call=vmi._mkl_pow_to_call,
1462+
mkl_impl_fn=vmi._pow,
1463+
binary_inplace_fn=ti._pow_inplace,
1464+
)
1465+
1466+
13671467
_FLOOR_DOCSTRING = """
13681468
Returns the floor for each element `x_i` for input array `x`.
13691469
@@ -2468,6 +2568,7 @@ def modf(x1, **kwargs):
24682568
:obj:`dpnp.fmax` : Element-wise maximum of array elements.
24692569
:obj:`dpnp.fmin` : Element-wise minimum of array elements.
24702570
:obj:`dpnp.fmod` : Calculate the element-wise remainder of division.
2571+
:obj:`dpnp.float_power` : Power function that promotes integers to floats.
24712572
24722573
Examples
24732574
--------

tests/skipped_tests.tbl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ tests/test_umath.py::test_umaths[('divmod', 'ii')]
3939
tests/test_umath.py::test_umaths[('divmod', 'll')]
4040
tests/test_umath.py::test_umaths[('divmod', 'ff')]
4141
tests/test_umath.py::test_umaths[('divmod', 'dd')]
42-
tests/test_umath.py::test_umaths[('float_power', 'dd')]
4342
tests/test_umath.py::test_umaths[('frexp', 'f')]
4443
tests/test_umath.py::test_umaths[('frexp', 'd')]
4544
tests/test_umath.py::test_umaths[('gcd', 'ii')]

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ tests/test_umath.py::test_umaths[('divmod', 'ii')]
2121
tests/test_umath.py::test_umaths[('divmod', 'll')]
2222
tests/test_umath.py::test_umaths[('divmod', 'ff')]
2323
tests/test_umath.py::test_umaths[('divmod', 'dd')]
24-
tests/test_umath.py::test_umaths[('float_power', 'dd')]
2524
tests/test_umath.py::test_umaths[('floor_divide', 'ff')]
2625
tests/test_umath.py::test_umaths[('frexp', 'f')]
2726
tests/test_umath.py::test_umaths[('frexp', 'd')]

tests/test_sycl_queue.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,11 @@ def test_reduce_hypot(device):
655655
pytest.param("dot", [3, 4, 5], [1, 2, 3]),
656656
pytest.param("dot", [3 + 2j, 4 + 1j, 5], [1, 2 + 3j, 3]),
657657
pytest.param("extract", [False, True, True, False], [0, 1, 2, 3]),
658+
pytest.param(
659+
"float_power",
660+
[0, 1, 2, 3, 4, 5],
661+
[1.0, 2.0, 3.0, 3.0, 2.0, 1.0],
662+
),
658663
pytest.param(
659664
"floor_divide", [1.0, 2.0, 3.0, 4.0], [2.5, 2.5, 2.5, 2.5]
660665
),

0 commit comments

Comments
 (0)