Skip to content

Commit 275b7be

Browse files
ENH: impl random.triangular (#573)
1 parent cec823a commit 275b7be

8 files changed

Lines changed: 181 additions & 5 deletions

File tree

dpnp/backend/include/dpnp_iface_fptr.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ enum class DPNPFuncName : size_t
152152
DPNP_FN_RNG_STANDARD_GAMMA, /**< Used in numpy.random.standard_gamma() implementation */
153153
DPNP_FN_RNG_STANDARD_NORMAL, /**< Used in numpy.random.standard_normal() implementation */
154154
DPNP_FN_RNG_STANDARD_T, /**< Used in numpy.random.standard_t() implementation */
155+
DPNP_FN_RNG_TRIANGULAR, /**< Used in numpy.random.triangular() implementation */
155156
DPNP_FN_RNG_UNIFORM, /**< Used in numpy.random.uniform() implementation */
156157
DPNP_FN_RNG_WEIBULL, /**< Used in numpy.random.weibull() implementation */
157158
DPNP_FN_SIGN, /**< Used in numpy.sign() implementation */

dpnp/backend/include/dpnp_iface_random.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,20 @@ INP_DLLEXPORT void dpnp_rng_standard_normal_c(void* result, const size_t size);
370370
template <typename _DataType>
371371
INP_DLLEXPORT void dpnp_rng_standard_t_c(void* result, const _DataType df, const size_t size);
372372

373+
/**
374+
* @ingroup BACKEND_RANDOM_API
375+
* @brief math library implementation of random number generator (Triangular distribution)
376+
*
377+
* @param [in] size Number of elements in `result` arrays.
378+
* @param [in] x_min Lower limit.
379+
* @param [in] x_mode The value where the peak of the distribution occurs.
380+
* @param [in] x_max Upper limit.
381+
* @param [out] result Output array.
382+
*/
383+
template <typename _DataType>
384+
INP_DLLEXPORT void dpnp_rng_triangular_c(
385+
void* result, const _DataType x_min, const _DataType x_mode, const _DataType x_max, const size_t size);
386+
373387
/**
374388
* @ingroup BACKEND_RANDOM_API
375389
* @brief math library implementation of random number generator (uniform distribution)

dpnp/backend/kernels/dpnp_krnl_random.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,76 @@ void dpnp_rng_standard_t_c(void* result, const _DataType df, const size_t size)
763763
}
764764
}
765765

766+
template <typename _KernelNameSpecialization>
767+
class dpnp_rng_triangular_ration_acceptance_c_kernel;
768+
769+
template <typename _DataType>
770+
void dpnp_rng_triangular_c(
771+
void* result, const _DataType x_min, const _DataType x_mode, const _DataType x_max, const size_t size)
772+
{
773+
if (!size)
774+
{
775+
return;
776+
}
777+
_DataType* result1 = reinterpret_cast<_DataType*>(result);
778+
const _DataType d_zero = (_DataType(0));
779+
const _DataType d_one = (_DataType(1));
780+
781+
_DataType ratio, lpr, rpr;
782+
783+
mkl_rng::uniform<_DataType> uniform_distribution(d_zero, d_one);
784+
auto event_out = mkl_rng::generate(uniform_distribution, DPNP_RNG_ENGINE, size, result1);
785+
event_out.wait();
786+
787+
{
788+
_DataType wtot, wl, wr;
789+
790+
wtot = x_max - x_min;
791+
wl = x_mode - x_min;
792+
wr = x_max - x_mode;
793+
794+
ratio = wl / wtot;
795+
lpr = wl * wtot;
796+
rpr = wr * wtot;
797+
}
798+
799+
if (!(0 <= ratio && ratio <= 1))
800+
{
801+
throw std::runtime_error("DPNP RNG Error: dpnp_rng_triangular_c() failed.");
802+
}
803+
804+
cl::sycl::range<1> gws(size);
805+
auto kernel_parallel_for_func = [=](cl::sycl::id<1> global_id) {
806+
size_t i = global_id[0];
807+
if (ratio <= 0)
808+
{
809+
result1[i] = x_max - cl::sycl::sqrt(result1[i] * rpr);
810+
}
811+
else if (ratio >= 1)
812+
{
813+
result1[i] = x_min + cl::sycl::sqrt(result1[i] * lpr);
814+
}
815+
else
816+
{
817+
_DataType ui = result1[i];
818+
if (ui > ratio)
819+
{
820+
result1[i] = x_max - cl::sycl::sqrt((1.0 - ui) * rpr);
821+
}
822+
else
823+
{
824+
result1[i] = x_min + cl::sycl::sqrt(ui * lpr);
825+
}
826+
}
827+
};
828+
auto kernel_func = [&](cl::sycl::handler& cgh) {
829+
cgh.parallel_for<class dpnp_rng_triangular_ration_acceptance_c_kernel<_DataType>>(gws,
830+
kernel_parallel_for_func);
831+
};
832+
event_out = DPNP_QUEUE.submit(kernel_func);
833+
event_out.wait();
834+
}
835+
766836
template <typename _DataType>
767837
void dpnp_rng_uniform_c(void* result, const long low, const long high, const size_t size)
768838
{
@@ -867,6 +937,8 @@ void func_map_init_random(func_map_t& fmap)
867937
(void*)dpnp_rng_standard_normal_c<double>};
868938
fmap[DPNPFuncName::DPNP_FN_RNG_STANDARD_T][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_standard_t_c<double>};
869939

940+
fmap[DPNPFuncName::DPNP_FN_RNG_TRIANGULAR][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_triangular_c<double>};
941+
870942
fmap[DPNPFuncName::DPNP_FN_RNG_UNIFORM][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_uniform_c<double>};
871943
fmap[DPNPFuncName::DPNP_FN_RNG_UNIFORM][eft_FLT][eft_FLT] = {eft_FLT, (void*)dpnp_rng_uniform_c<float>};
872944
fmap[DPNPFuncName::DPNP_FN_RNG_UNIFORM][eft_INT][eft_INT] = {eft_INT, (void*)dpnp_rng_uniform_c<int>};

dpnp/dpnp_algo/dpnp_algo.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na
125125
DPNP_FN_RNG_STANDARD_GAMMA
126126
DPNP_FN_RNG_STANDARD_NORMAL
127127
DPNP_FN_RNG_STANDARD_T
128+
DPNP_FN_RNG_TRIANGULAR
128129
DPNP_FN_RNG_UNIFORM
129130
DPNP_FN_RNG_WEIBULL
130131
DPNP_FN_SIGN

dpnp/random/dpnp_algo_random.pyx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ __all__ = [
7171
"dpnp_rng_standard_gamma",
7272
"dpnp_rng_standard_normal",
7373
"dpnp_rng_standard_t",
74+
"dpnp_rng_triangular",
7475
"dpnp_rng_uniform",
7576
"dpnp_rng_weibull"
7677
]
@@ -113,6 +114,11 @@ ctypedef void(*fptr_dpnp_rng_standard_exponential_c_1out_t)(void * , const size_
113114
ctypedef void(*fptr_dpnp_rng_standard_gamma_c_1out_t)(void * , const double, const size_t) except +
114115
ctypedef void(*fptr_dpnp_rng_standard_normal_c_1out_t)(void * , const size_t) except +
115116
ctypedef void(*fptr_dpnp_rng_standard_t_c_1out_t)(void * , const double, const size_t) except +
117+
ctypedef void(*fptr_dpnp_rng_triangular_c_1out_t)(void * ,
118+
const double,
119+
const double,
120+
const double,
121+
const size_t) except +
116122
ctypedef void(*fptr_dpnp_rng_uniform_c_1out_t)(void * , const long, const long, const size_t) except +
117123
ctypedef void(*fptr_dpnp_rng_weibull_c_1out_t)(void * , const double, const size_t) except +
118124

@@ -988,6 +994,32 @@ cpdef dparray dpnp_rng_standard_t(double df, size):
988994
return result
989995

990996

997+
cpdef dparray dpnp_rng_triangular(double left, double mode, double right, size):
998+
"""
999+
Returns an array populated with samples from triangular distribution.
1000+
`dpnp_rng_triangular` generates a matrix filled with random floats sampled from a
1001+
univariate triangular distribution.
1002+
1003+
"""
1004+
1005+
dtype = numpy.float64
1006+
# convert string type names (dparray.dtype) to C enum DPNPFuncType
1007+
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(dtype)
1008+
1009+
# get the FPTR data structure
1010+
cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_RNG_TRIANGULAR, param1_type, param1_type)
1011+
1012+
result_type = dpnp_DPNPFuncType_to_dtype(< size_t > kernel_data.return_type)
1013+
# ceate result array with type given by FPTR data
1014+
cdef dparray result = dparray(size, dtype=dtype)
1015+
1016+
cdef fptr_dpnp_rng_triangular_c_1out_t func = <fptr_dpnp_rng_triangular_c_1out_t > kernel_data.ptr
1017+
# call FPTR function
1018+
func(result.get_data(), left, mode, right, result.size)
1019+
1020+
return result
1021+
1022+
9911023
cpdef dparray dpnp_rng_uniform(long low, long high, size, dtype):
9921024
"""
9931025
Returns an array populated with samples from standard uniform distribution.

dpnp/random/dpnp_iface_random.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,6 @@ def standard_t(df, size=None):
14461446
pass
14471447
else:
14481448
return dpnp_rng_standard_t(df, size)
1449-
print("here")
14501449
return call_origin(numpy.random.standard_t, df, size)
14511450

14521451

@@ -1458,13 +1457,39 @@ def triangular(left, mode, right, size=None):
14581457
14591458
For full documentation refer to :obj:`numpy.random.triangular`.
14601459
1461-
Notes
1462-
-----
1463-
The function uses `numpy.random.triangular` on the backend and
1464-
will be executed on fallback backend.
1460+
Limitations
1461+
-----------
1462+
Parameter ``left``, ``mode`` and ``right`` are supported as scalar.
1463+
Otherwise, :obj:`numpy.random.triangular(left, mode, right, size)`
1464+
samples are drawn.
1465+
Output array data type is :obj:`dpnp.float64`.
1466+
1467+
Examples
1468+
--------
1469+
Draw samples from the distribution:
1470+
>>> df = 2.
1471+
>>> s = dpnp.random.triangular(-3, 0, 8, 1000000)
14651472
14661473
"""
14671474

1475+
if not use_origin_backend(left):
1476+
# TODO:
1477+
# array_like of floats for `left`, `mode`, `right`.
1478+
if not dpnp.isscalar(left):
1479+
pass
1480+
elif not dpnp.isscalar(mode):
1481+
pass
1482+
elif not dpnp.isscalar(right):
1483+
pass
1484+
elif left > mode:
1485+
pass
1486+
elif mode > right:
1487+
pass
1488+
elif left == right:
1489+
pass
1490+
else:
1491+
return dpnp_rng_triangular(left, mode, right, size)
1492+
14681493
return call_origin(numpy.random.triangular, left, mode, right, size)
14691494

14701495

tests/test_random.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,35 @@ def test_seed(self):
757757
self.check_seed('standard_t', {'df': 10.0})
758758

759759

760+
class TestDistributionsTriangular(TestDistribution):
761+
762+
def test_moments(self):
763+
left = 1.0
764+
mode = 2.0
765+
right = 3.0
766+
expected_mean = (left + mode + right) / 3
767+
expected_var = (left ** 2 + mode ** 2 + right ** 2 - left * mode -left * right -mode * right) / 18
768+
self.check_moments('triangular', expected_mean,
769+
expected_var, {'left': left, 'mode': mode, 'right': right})
770+
771+
def test_invalid_args(self):
772+
left = 2.0 # `left` is expected <= `mode`
773+
mode = 1.0 # `mode` is expected > `left`
774+
right = 3.0 # OK
775+
self.check_invalid_args('triangular', {'left': left, 'mode': mode, 'right': right})
776+
777+
left = 1.0 # OK
778+
mode = 3.0 # `mode` is expected <= `right`
779+
right = 2.0 # `right` is expected > `mode`
780+
self.check_invalid_args('triangular', {'left': left, 'mode': mode, 'right': right})
781+
782+
def test_seed(self):
783+
left = 1.0
784+
mode = 2.0
785+
right = 3.0
786+
self.check_seed('triangular', {'left': left, 'mode': mode, 'right': right})
787+
788+
760789
class TestDistributionsUniform(TestDistribution):
761790

762791
def test_extreme_value(self):

tests_external/skipped_tests_numpy.tbl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,7 @@ tests/test_random.py::TestRandomDist::test_standard_gamma
15061506
tests/test_random.py::TestRandomDist::test_standard_gamma_0
15071507
tests/test_random.py::TestRandomDist::test_standard_normal
15081508
tests/test_random.py::TestRandomDist::test_standard_t
1509+
tests/test_random.py::TestRandomDist::test_triangular
15091510
tests/test_random.py::TestRandomDist::test_uniform
15101511
tests/test_random.py::TestRandomDist::test_uniform_range_bounds
15111512
tests/test_random.py::TestRandomDist::test_weibull
@@ -1605,6 +1606,7 @@ tests/test_randomstate.py::TestRandomDist::test_standard_gamma_0
16051606
tests/test_randomstate.py::TestRandomDist::test_standard_normal
16061607
tests/test_randomstate.py::TestRandomDist::test_standard_t
16071608
tests/test_randomstate.py::TestRandomDist::test_tomaxint
1609+
tests/test_randomstate.py::TestRandomDist::test_triangular
16081610
tests/test_randomstate.py::TestRandomDist::test_uniform
16091611
tests/test_randomstate.py::TestRandomDist::test_uniform_range_bounds
16101612
tests/test_randomstate.py::TestRandomDist::test_weibull

0 commit comments

Comments
 (0)