Skip to content

Commit 59f0ce9

Browse files
ENH: impl random.f (#528)
1 parent 399d74d commit 59f0ce9

File tree

9 files changed

+189
-4
lines changed

9 files changed

+189
-4
lines changed

dpnp/backend/include/dpnp_iface_fptr.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ enum class DPNPFuncName : size_t
126126
DPNP_FN_RNG_BINOMIAL, /**< Used in numpy.random.binomial() implementation */
127127
DPNP_FN_RNG_CHISQUARE, /**< Used in numpy.random.chisquare() implementation */
128128
DPNP_FN_RNG_EXPONENTIAL, /**< Used in numpy.random.exponential() implementation */
129+
DPNP_FN_RNG_F, /**< Used in numpy.random.f() implementation */
129130
DPNP_FN_RNG_GAMMA, /**< Used in numpy.random.gamma() implementation */
130131
DPNP_FN_RNG_GAUSSIAN, /**< Used in numpy.random.randn() implementation */
131132
DPNP_FN_RNG_GEOMETRIC, /**< Used in numpy.random.geometric() implementation */

dpnp/backend/include/dpnp_iface_random.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ INP_DLLEXPORT void dpnp_rng_chi_square_c(void* result, int df, size_t size);
9898
template <typename _DataType>
9999
INP_DLLEXPORT void dpnp_rng_exponential_c(void* result, _DataType beta, size_t size);
100100

101+
/**
102+
* @ingroup BACKEND_RANDOM_API
103+
* @brief math library implementation of random number generator (F distribution)
104+
*
105+
* @param [in] size Number of elements in `result` arrays.
106+
* @param [in] df_num Degrees of freedom in numerator.
107+
* @param [in] df_den Degrees of freedom in denominator.
108+
* @param [out] result Output array.
109+
*/
110+
template <typename _DataType>
111+
INP_DLLEXPORT void dpnp_rng_f_c(void* result, _DataType df_num, _DataType df_den, size_t size);
112+
101113
/**
102114
* @ingroup BACKEND_RANDOM_API
103115
* @brief math library implementation of random number generator (gamma distribution)

dpnp/backend/kernels/dpnp_krnl_random.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,72 @@ void dpnp_rng_exponential_c(void* result, _DataType beta, size_t size)
176176
event_out.wait();
177177
}
178178

179+
template <typename _DataType>
180+
void dpnp_rng_f_c(void* result, const _DataType df_num, const _DataType df_den, size_t size)
181+
{
182+
if (!size)
183+
{
184+
return;
185+
}
186+
cl::sycl::vector_class<cl::sycl::event> no_deps;
187+
188+
const _DataType d_zero = (_DataType(0.0));
189+
190+
_DataType shape = 0.5*df_num;
191+
_DataType scale = 2.0/df_num;
192+
_DataType *den = nullptr;
193+
194+
_DataType* result1 = reinterpret_cast<_DataType*>(result);
195+
196+
if (dpnp_queue_is_cpu_c())
197+
{
198+
mkl_rng::gamma<_DataType> gamma_distribution1(shape, d_zero, scale);
199+
auto event_out = mkl_rng::generate(gamma_distribution1, DPNP_RNG_ENGINE, size, result1);
200+
event_out.wait();
201+
202+
den = reinterpret_cast<_DataType*>(dpnp_memory_alloc_c(size * sizeof(_DataType)));
203+
if (den == nullptr)
204+
{
205+
throw std::runtime_error("DPNP RNG Error: dpnp_rng_f_c() failed.");
206+
}
207+
shape = 0.5*df_den;
208+
scale = 2.0/df_den;
209+
mkl_rng::gamma<_DataType> gamma_distribution2(shape, d_zero, scale);
210+
event_out = mkl_rng::generate(gamma_distribution2, DPNP_RNG_ENGINE, size, den);
211+
event_out.wait();
212+
213+
event_out = mkl_vm::div(DPNP_QUEUE, size, result1, den, result1, no_deps,
214+
mkl_vm::mode::ha);
215+
event_out.wait();
216+
217+
dpnp_memory_free_c(den);
218+
}
219+
else
220+
{
221+
int errcode = vdRngGamma(VSL_RNG_METHOD_GAMMA_GNORM_ACCURATE, get_rng_stream(),
222+
size, result1, shape, d_zero, scale);
223+
if (errcode != VSL_STATUS_OK)
224+
{
225+
throw std::runtime_error("DPNP RNG Error: dpnp_rng_f_c() failed.");
226+
}
227+
den = (_DataType *) mkl_malloc(size * sizeof(_DataType), 64);
228+
if (den == nullptr)
229+
{
230+
throw std::runtime_error("DPNP RNG Error: dpnp_rng_f_c() failed.");
231+
}
232+
shape = 0.5*df_den;
233+
scale = 2.0/df_den;
234+
errcode = vdRngGamma(VSL_RNG_METHOD_GAMMA_GNORM_ACCURATE, get_rng_stream(),
235+
size, den, shape, d_zero, scale);
236+
if (errcode != VSL_STATUS_OK)
237+
{
238+
throw std::runtime_error("DPNP RNG Error: dpnp_rng_f_c() failed.");
239+
}
240+
vmdDiv(size, result1, den, result1, VML_HA);
241+
mkl_free(den);
242+
}
243+
}
244+
179245
template <typename _DataType>
180246
void dpnp_rng_gamma_c(void* result, _DataType shape, _DataType scale, size_t size)
181247
{
@@ -732,6 +798,8 @@ void func_map_init_random(func_map_t& fmap)
732798
fmap[DPNPFuncName::DPNP_FN_RNG_EXPONENTIAL][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_exponential_c<double>};
733799
fmap[DPNPFuncName::DPNP_FN_RNG_EXPONENTIAL][eft_FLT][eft_FLT] = {eft_FLT, (void*)dpnp_rng_exponential_c<float>};
734800

801+
fmap[DPNPFuncName::DPNP_FN_RNG_F][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_f_c<double>};
802+
735803
fmap[DPNPFuncName::DPNP_FN_RNG_GAMMA][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_gamma_c<double>};
736804

737805
fmap[DPNPFuncName::DPNP_FN_RNG_GAUSSIAN][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_rng_gaussian_c<double>};

dpnp/backend/tests/test_random.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,33 @@ TEST (TestBackendRandomBeta, test_seed) {
3232
}
3333
}
3434

35+
TEST (TestBackendRandomF, test_seed) {
36+
const size_t size = 256;
37+
size_t seed = 10;
38+
double dfnum = 10.4;
39+
double dfden = 12.5;
40+
41+
auto QueueOptionsDevices = std::vector<QueueOptions>{ QueueOptions::CPU_SELECTOR,
42+
QueueOptions::GPU_SELECTOR };
43+
44+
for (auto device_selector : QueueOptionsDevices) {
45+
dpnp_queue_initialize_c(device_selector);
46+
double* result1 = (double*)dpnp_memory_alloc_c(size * sizeof(double));
47+
double* result2 = (double*)dpnp_memory_alloc_c(size * sizeof(double));
48+
49+
dpnp_rng_srand_c(seed);
50+
dpnp_rng_f_c<double>(result1, dfnum, dfden, size);
51+
52+
dpnp_rng_srand_c(seed);
53+
dpnp_rng_f_c<double>(result2, dfnum, dfden, size);
54+
55+
for (size_t i = 0; i < size; ++i)
56+
{
57+
EXPECT_NEAR (result1[i], result2[i], 0.004);
58+
}
59+
}
60+
}
61+
3562
TEST (TestBackendRandomNormal, test_seed) {
3663
const size_t size = 256;
3764
size_t seed = 10;

dpnp/dpnp_algo/dpnp_algo.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na
9999
DPNP_FN_RNG_BINOMIAL
100100
DPNP_FN_RNG_CHISQUARE
101101
DPNP_FN_RNG_EXPONENTIAL
102+
DPNP_FN_RNG_F
102103
DPNP_FN_RNG_GAMMA
103104
DPNP_FN_RNG_GAUSSIAN
104105
DPNP_FN_RNG_GEOMETRIC

dpnp/random/dpnp_algo_random.pyx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ __all__ = [
4747
"dpnp_rng_binomial",
4848
"dpnp_rng_chisquare",
4949
"dpnp_rng_exponential",
50+
"dpnp_rng_f",
5051
"dpnp_rng_gamma",
5152
"dpnp_rng_geometric",
5253
"dpnp_rng_gumbel",
@@ -79,6 +80,7 @@ ctypedef void(*fptr_dpnp_rng_beta_c_1out_t)(void *, double, double, size_t) exce
7980
ctypedef void(*fptr_dpnp_rng_binomial_c_1out_t)(void *, int, double, size_t) except +
8081
ctypedef void(*fptr_dpnp_rng_chi_square_c_1out_t)(void *, int, size_t) except +
8182
ctypedef void(*fptr_dpnp_rng_exponential_c_1out_t)(void *, double, size_t) except +
83+
ctypedef void(*fptr_dpnp_rng_f_c_1out_t)(void *, const double, const double, size_t) except +
8284
ctypedef void(*fptr_dpnp_rng_gamma_c_1out_t)(void *, double, double, size_t) except +
8385
ctypedef void(*fptr_dpnp_rng_geometric_c_1out_t)(void *, float, size_t) except +
8486
ctypedef void(*fptr_dpnp_rng_gaussian_c_1out_t)(void *, double, double, size_t) except +
@@ -226,6 +228,31 @@ cpdef dparray dpnp_rng_exponential(double beta, size):
226228
return result
227229

228230

231+
cpdef dparray dpnp_rng_f(double df_num, double df_den, size):
232+
"""
233+
Returns an array populated with samples from F distribution.
234+
`dpnp_rng_f` generates a matrix filled with random floats sampled from a
235+
univariate F distribution.
236+
"""
237+
238+
dtype = numpy.float64
239+
# convert string type names (dparray.dtype) to C enum DPNPFuncType
240+
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(dtype)
241+
242+
# get the FPTR data structure
243+
cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_RNG_F, param1_type, param1_type)
244+
245+
result_type = dpnp_DPNPFuncType_to_dtype( < size_t > kernel_data.return_type)
246+
# ceate result array with type given by FPTR data
247+
cdef dparray result = dparray(size, dtype=dtype)
248+
249+
cdef fptr_dpnp_rng_f_c_1out_t func = <fptr_dpnp_rng_f_c_1out_t > kernel_data.ptr
250+
# call FPTR function
251+
func(result.get_data(), df_num, df_den, result.size)
252+
253+
return result
254+
255+
229256
cpdef dparray dpnp_rng_gamma(double shape, double scale, size):
230257
"""
231258
Returns an array populated with samples from gamma distribution.

dpnp/random/dpnp_iface_random.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,32 @@ def f(dfnum, dfden, size=None):
315315
316316
For full documentation refer to :obj:`numpy.random.f`.
317317
318-
Notes
319-
-----
320-
The function uses `numpy.random.f` on the backend and will be
321-
executed on fallback backend.
318+
Limitations
319+
-----------
320+
Parameters ``dfnum`` and ``dfden`` are supported as scalar.
321+
Otherwise, :obj:`numpy.random.f(dfnum, dfden, size)` samples are drawn.
322+
Output array data type is :obj:`dpnp.float64`.
323+
Examples
324+
--------
325+
>>> dfnum, dfden = 3., 2.
326+
>>> s = dpnp.random.f(dfnum, dfden, size)
322327
323328
"""
324329

330+
if not use_origin_backend(dfnum):
331+
# TODO:
332+
# array_like of floats for `dfnum` and `dfden`
333+
if not dpnp.isscalar(dfnum):
334+
pass
335+
elif not dpnp.isscalar(dfden):
336+
pass
337+
elif dfnum <= 0:
338+
pass
339+
elif dfden <= 0:
340+
pass
341+
else:
342+
return dpnp_rng_f(dfnum, dfden, size)
343+
325344
return call_origin(numpy.random.f, dfnum, dfden, size)
326345

327346

tests/test_random.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,34 @@ def test_seed(self):
225225
self.check_seed('exponential', {'scale': scale})
226226

227227

228+
class TestDistributionsF(TestDistribution):
229+
230+
def test_moments(self):
231+
dfnum = 12.56
232+
dfden = 13.0
233+
# for dfden > 2
234+
expected_mean = dfden / (dfden - 2)
235+
# for dfden > 4
236+
expected_var = 2 * (dfden ** 2) * (dfnum + dfden - 2) / \
237+
(dfnum * ((dfden - 2) ** 2) * ((dfden - 4)))
238+
self.check_moments('f', expected_mean, expected_var,
239+
{'dfnum': dfnum, 'dfden': dfden})
240+
241+
def test_invalid_args(self):
242+
size = 10
243+
dfnum = -1.0 # positive `dfnum` is expected
244+
dfden = 1.0 # OK
245+
self.check_invalid_args('f', {'dfnum': dfnum, 'dfden': dfden})
246+
dfnum = 1.0 # OK
247+
dfden = -1.0 # positive `dfden` is expected
248+
self.check_invalid_args('f', {'dfnum': dfnum, 'dfden': dfden})
249+
250+
def test_seed(self):
251+
dfnum = 3.56 # `dfden` param for Wald distr
252+
dfden = 2.8 # `dfden` param for Wald distr
253+
self.check_seed('f', {'dfnum': dfnum, 'dfden': dfden})
254+
255+
228256
class TestDistributionsGamma(TestDistribution):
229257

230258
def test_moments(self):

tests_external/skipped_tests_numpy.tbl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,7 @@ tests/test_random.py::TestRandomDist::test_dirichlet_bad_alpha
14661466
tests/test_random.py::TestRandomDist::test_dirichlet_size
14671467
tests/test_random.py::TestRandomDist::test_exponential
14681468
tests/test_random.py::TestRandomDist::test_exponential_0
1469+
tests/test_random.py::TestRandomDist::test_f
14691470
tests/test_random.py::TestRandomDist::test_gamma
14701471
tests/test_random.py::TestRandomDist::test_gamma_0
14711472
tests/test_random.py::TestRandomDist::test_geometric
@@ -1559,6 +1560,7 @@ tests/test_randomstate.py::TestRandomDist::test_dirichlet
15591560
tests/test_randomstate.py::TestRandomDist::test_dirichlet_size
15601561
tests/test_randomstate.py::TestRandomDist::test_exponential
15611562
tests/test_randomstate.py::TestRandomDist::test_exponential_0
1563+
tests/test_randomstate.py::TestRandomDist::test_f
15621564
tests/test_randomstate.py::TestRandomDist::test_gamma
15631565
tests/test_randomstate.py::TestRandomDist::test_gamma_0
15641566
tests/test_randomstate.py::TestRandomDist::test_geometric

0 commit comments

Comments
 (0)