Skip to content

Commit b26d624

Browse files
authored
EIG combine existing MKL and non-existing SYCL to one kernel (#112)
1 parent 68815e3 commit b26d624

File tree

9 files changed

+112
-184
lines changed

9 files changed

+112
-184
lines changed

dpnp/backend/backend_iface.hpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,19 @@ INP_DLLEXPORT void custom_prod_c(void* array, void* result, size_t size);
173173

174174
/**
175175
* @ingroup BACKEND_API
176-
* @brief math library implementation of eig function
176+
* @brief Compute the eigenvalues and right eigenvectors of a square array.
177177
*
178-
* @param [in] array1 Input array.
178+
* @param [in] array_in Input array[size][size]
179179
*
180-
* @param [out] result1 Output array.
180+
* @param [out] result1 The eigenvalues, each repeated according to its multiplicity
181181
*
182-
* @param [in] size Number of elements in input arrays.
182+
* @param [out] result2 The normalized (unit “length”) eigenvectors
183+
*
184+
* @param [in] size One dimension of square [size][size] array
183185
*
184186
*/
185-
template <typename _DataType>
186-
INP_DLLEXPORT void mkl_lapack_syevd_c(void* array1, void* result1, size_t size);
187+
template <typename _DataType, typename _ResultType>
188+
INP_DLLEXPORT void custom_lapack_eig_c(const void* array_in, void* result1, void* result2, size_t size);
187189

188190
/**
189191
* @ingroup BACKEND_API

dpnp/backend/backend_iface_fptr.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,10 @@ static func_map_t func_map_init()
309309
fmap[DPNPFuncName::DPNP_FN_DOT][eft_FLT][eft_FLT] = {eft_FLT, (void*)custom_blas_dot_c<float>};
310310
fmap[DPNPFuncName::DPNP_FN_DOT][eft_DBL][eft_DBL] = {eft_DBL, (void*)custom_blas_dot_c<double>};
311311

312-
fmap[DPNPFuncName::DPNP_FN_EIG][eft_INT][eft_INT] = {eft_DBL, (void*)mkl_lapack_syevd_c<double>};
313-
fmap[DPNPFuncName::DPNP_FN_EIG][eft_LNG][eft_LNG] = {eft_DBL, (void*)mkl_lapack_syevd_c<double>};
314-
fmap[DPNPFuncName::DPNP_FN_EIG][eft_FLT][eft_FLT] = {eft_FLT, (void*)mkl_lapack_syevd_c<float>};
315-
fmap[DPNPFuncName::DPNP_FN_EIG][eft_DBL][eft_DBL] = {eft_DBL, (void*)mkl_lapack_syevd_c<double>};
312+
fmap[DPNPFuncName::DPNP_FN_EIG][eft_INT][eft_INT] = {eft_DBL, (void*)custom_lapack_eig_c<int, double>};
313+
fmap[DPNPFuncName::DPNP_FN_EIG][eft_LNG][eft_LNG] = {eft_DBL, (void*)custom_lapack_eig_c<long, double>};
314+
fmap[DPNPFuncName::DPNP_FN_EIG][eft_FLT][eft_FLT] = {eft_FLT, (void*)custom_lapack_eig_c<float, float>};
315+
fmap[DPNPFuncName::DPNP_FN_EIG][eft_DBL][eft_DBL] = {eft_DBL, (void*)custom_lapack_eig_c<double, double>};
316316

317317
fmap[DPNPFuncName::DPNP_FN_EXP][eft_INT][eft_INT] = {eft_DBL, (void*)custom_elemwise_exp_c<int, double>};
318318
fmap[DPNPFuncName::DPNP_FN_EXP][eft_LNG][eft_LNG] = {eft_DBL, (void*)custom_elemwise_exp_c<long, double>};

dpnp/backend/custom_kernels.cpp

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "queue_sycl.hpp"
3333

3434
namespace mkl_blas = oneapi::mkl::blas;
35+
namespace mkl_lapack = oneapi::mkl::lapack;
3536

3637
template <typename _KernelNameSpecialization>
3738
class custom_blas_gemm_c_kernel;
@@ -182,60 +183,69 @@ template void custom_blas_dot_c<long>(void* array1_in, void* array2_in, void* re
182183
template void custom_blas_dot_c<float>(void* array1_in, void* array2_in, void* result1, size_t size);
183184
template void custom_blas_dot_c<double>(void* array1_in, void* array2_in, void* result1, size_t size);
184185

185-
#if 0 // Example for OpenCL kernel
186-
#include <map>
187-
#include <typeindex>
188-
189-
static std::map<std::type_index, std::string> types_map = {{typeid(long), "long"}, {typeid(int), "int"}};
190-
191-
static const char* blas_gemm_naive =
192-
"//#define __KERNEL_TYPE__ long \n"
193-
"#define __KERNEL_TYPE_ZERO__ 0 \n"
194-
"__kernel void blas_gemm_naive(__global __KERNEL_TYPE__* array_1, \n"
195-
" __global __KERNEL_TYPE__* array_2, \n"
196-
" __global __KERNEL_TYPE__* result, \n"
197-
" unsigned long size) \n"
198-
"{ \n"
199-
" size_t i = get_global_id(0); //for (size_t i = 0; i < size; ++i) \n"
200-
" { \n"
201-
" size_t j = get_global_id(1); //for (size_t j = 0; j < size; ++j) \n"
202-
" { \n"
203-
" __KERNEL_TYPE__ temp = __KERNEL_TYPE_ZERO__; \n"
204-
" for (size_t k = 0; k < size; ++k) \n"
205-
" { \n"
206-
" const size_t index_1 = i * size + k; \n"
207-
" const size_t index_2 = k * size + j; \n"
208-
" temp += array_1[index_1] * array_2[index_2]; \n"
209-
" } \n"
210-
" \n"
211-
" const size_t index_result = i * size + j; \n"
212-
" result[index_result] = temp; \n"
213-
" } \n"
214-
" } \n"
215-
"} \n";
216-
217-
template <typename _DataType>
218-
void custom_dgemm_c_opencl(void* array_1_in, void* array_2_in, void* result_1, size_t size)
186+
template <typename _DataType, typename _ResultType>
187+
void custom_lapack_eig_c(const void* array_in, void* result1, void* result2, size_t size)
219188
{
220-
_DataType* array_1 = reinterpret_cast<_DataType*>(array_1_in);
221-
_DataType* array_2 = reinterpret_cast<_DataType*>(array_2_in);
222-
_DataType* result = reinterpret_cast<_DataType*>(result_1);
189+
// TODO this kernel works with square 2-D array only
223190

224-
std::string compile_time_options("-cl-std=CL1.2");
225-
compile_time_options += " -D__KERNEL_TYPE__=" + types_map.at(typeid(_DataType));
191+
// Kernel Type for calculation is double type
192+
// because interface requires float type but calculations are expected in double type
226193

227-
cl::sycl::program program_src(DPNP_QUEUE.get_context());
228-
program_src.build_with_source(blas_gemm_naive, compile_time_options);
194+
if (!size)
195+
{
196+
return;
197+
}
229198

230-
cl::sycl::range<2> kernel_work_ids(size, size); // dimensions are: "i" and "j"
231-
DPNP_QUEUE.submit([&](cl::sycl::handler& cgh) {
232-
cgh.set_args(array_1, array_2, result, size);
233-
cgh.parallel_for(kernel_work_ids, program_src.get_kernel("blas_gemm_naive"));
234-
});
199+
cl::sycl::event event;
235200

236-
DPNP_QUEUE.wait();
237-
}
201+
const _DataType* array = reinterpret_cast<const _DataType*>(array_in);
202+
_ResultType* result_val = reinterpret_cast<_ResultType*>(result1);
203+
_ResultType* result_vec = reinterpret_cast<_ResultType*>(result2);
204+
205+
double* result_val_kern = reinterpret_cast<double*>(dpnp_memory_alloc_c(size * sizeof(double)));
206+
double* result_vec_kern = reinterpret_cast<double*>(dpnp_memory_alloc_c(size * size * sizeof(double)));
207+
208+
// type conversion. Also, math library requires copy memory because override
209+
for (size_t it = 0; it < (size * size); ++it)
210+
{
211+
result_vec_kern[it] = array[it];
212+
}
213+
214+
const std::int64_t lda = std::max<size_t>(1UL, size);
215+
216+
const std::int64_t scratchpad_size = mkl_lapack::syevd_scratchpad_size<double>(
217+
DPNP_QUEUE, oneapi::mkl::job::vec, oneapi::mkl::uplo::upper, size, lda);
218+
219+
double* scratchpad = reinterpret_cast<double*>(dpnp_memory_alloc_c(scratchpad_size * sizeof(double)));
220+
221+
event = mkl_lapack::syevd(DPNP_QUEUE, // queue
222+
oneapi::mkl::job::vec, // jobz
223+
oneapi::mkl::uplo::upper, // uplo
224+
size, // The order of the matrix A (0 <= n)
225+
result_vec_kern, // will be overwritten with eigenvectors
226+
lda,
227+
result_val_kern,
228+
scratchpad,
229+
scratchpad_size);
230+
event.wait();
238231

239-
template void custom_dgemm_c_opencl<long>(void* array_1_in, void* array_2_in, void* result_1, size_t size);
232+
dpnp_memory_free_c(scratchpad);
233+
234+
for (size_t it1 = 0; it1 < size; ++it1)
235+
{
236+
result_val[it1] = result_val_kern[it1];
237+
for (size_t it2 = 0; it2 < size; ++it2)
238+
{
239+
// copy + transpose
240+
result_vec[it2 * size + it1] = result_vec_kern[it1 * size + it2];
241+
}
242+
}
243+
244+
dpnp_memory_free_c(result_val_kern);
245+
dpnp_memory_free_c(result_vec_kern);
246+
}
240247

241-
#endif
248+
template void custom_lapack_eig_c<int, double>(const void* array_in, void* result1, void* result2, size_t size);
249+
template void custom_lapack_eig_c<long, double>(const void* array_in, void* result1, void* result2, size_t size);
250+
template void custom_lapack_eig_c<float, float>(const void* array_in, void* result1, void* result2, size_t size);
251+
template void custom_lapack_eig_c<double, double>(const void* array_in, void* result1, void* result2, size_t size);

dpnp/backend/mkl_wrap_lapack.cpp

Lines changed: 0 additions & 77 deletions
This file was deleted.

dpnp/linalg/linalg.pyx

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,21 @@ __all__ = [
4444
]
4545

4646

47-
cpdef tuple dpnp_eig(dparray in_array1):
48-
cdef dparray_shape_type shape1 = in_array1.shape
47+
cpdef tuple dpnp_eig(dparray x1):
48+
cdef dparray_shape_type x1_shape = x1.shape
4949

50-
cdef size_t size1 = 0
51-
if not shape1.empty():
52-
size1 = shape1.front()
50+
cdef size_t size = 0 if x1_shape.empty() else x1_shape.front()
5351

54-
# convert string type names (dparray.dtype) to C enum DPNPFuncType
55-
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(in_array1.dtype)
56-
57-
# get the FPTR data structure
52+
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(x1.dtype)
5853
cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_EIG, param1_type, param1_type)
5954

6055
result_type = dpnp_DPNPFuncType_to_dtype( < size_t > kernel_data.return_type)
61-
# this array is used as input for math library and will be overwritten with eigen vectors
62-
res_vec = in_array1.astype(result_type)
63-
# ceate result array with type given by FPTR data
64-
cdef dparray res_val = dparray((size1,), dtype=result_type)
6556

66-
cdef fptr_1in_1out_t func = <fptr_1in_1out_t > kernel_data.ptr
57+
cdef dparray res_val = dparray((size,), dtype=result_type)
58+
cdef dparray res_vec = dparray(x1_shape, dtype=result_type)
59+
60+
cdef fptr_2in_1out_t func = <fptr_2in_1out_t > kernel_data.ptr
6761
# call FPTR function
68-
func(res_vec.get_data(), res_val.get_data(), size1)
62+
func(x1.get_data(), res_val.get_data(), res_vec.get_data(), size)
6963

70-
return res_val, res_vec
64+
return (res_val, res_vec)

dpnp/linalg/linalg_iface.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,21 @@
5555
]
5656

5757

58-
def eig(in_array1):
58+
def eig(x1):
5959
"""
6060
Compute the eigenvalues and right eigenvectors of a square array.
61-
Parameters
62-
----------
63-
a : (..., M, M) array
64-
Matrices for which the eigenvalues and right eigenvectors will
65-
be computed
66-
Returns
67-
-------
68-
w : (..., M) array
69-
The eigenvalues, each repeated according to its multiplicity.
70-
The eigenvalues are not necessarily ordered. The resulting
71-
array will be of complex type, unless the imaginary part is
72-
zero in which case it will be cast to a real type. When `a`
73-
is real the resulting eigenvalues will be real (0 imaginary
74-
part) or occur in conjugate pairs
75-
v : (..., M, M) array
76-
The normalized (unit "length") eigenvectors, such that the
77-
column ``v[:,i]`` is the eigenvector corresponding to the
78-
eigenvalue ``w[i]``.
61+
62+
.. seealso:: :func:`numpy.linalg.eig`
63+
7964
"""
8065

81-
if (use_origin_backend()):
82-
return numpy.linalg.eig(in_array1)
66+
is_x1_dparray = isinstance(x1, dparray)
67+
68+
if (not use_origin_backend(x1) and is_x1_dparray):
69+
if (x1.size > 0):
70+
return dpnp_eig(x1)
8371

84-
return dpnp_eig(in_array1)
72+
return call_origin(numpy.linalg.eig, x1)
8573

8674

8775
def matrix_power(input, count):

examples/example7.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ int main(int, char**)
4545

4646
dpnp_queue_initialize_c(QueueOptions::CPU_SELECTOR);
4747

48-
int* array = (int*)dpnp_memory_alloc_c(len * sizeof(int));
49-
double* result = (double*)dpnp_memory_alloc_c(size * sizeof(double));
48+
float* array = (float*)dpnp_memory_alloc_c(len * sizeof(float));
49+
float* result1 = (float*)dpnp_memory_alloc_c(size * sizeof(float));
50+
float* result2 = (float*)dpnp_memory_alloc_c(len * sizeof(float));
5051

5152
/* init input diagonal array like:
5253
1, 0, 0,
@@ -62,16 +63,17 @@ int main(int, char**)
6263
array[size * i + i] = i + 1;
6364
}
6465

65-
mkl_lapack_syevd_c<double>(array, result, size);
66+
custom_lapack_eig_c<float, float>(array, result1, result2, size);
6667

6768
std::cout << "eigen values" << std::endl;
6869
for (size_t i = 0; i < size; ++i)
6970
{
70-
std::cout << result[i] << ", ";
71+
std::cout << result1[i] << ", ";
7172
}
7273
std::cout << std::endl;
7374

74-
dpnp_memory_free_c(result);
75+
dpnp_memory_free_c(result2);
76+
dpnp_memory_free_c(result1);
7577
dpnp_memory_free_c(array);
7678

7779
return 0;

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,6 @@
312312
"dpnp/backend/custom_kernels_sorting.cpp",
313313
"dpnp/backend/custom_kernels_statistics.cpp",
314314
"dpnp/backend/memory_sycl.cpp",
315-
"dpnp/backend/mkl_wrap_lapack.cpp",
316315
"dpnp/backend/mkl_wrap_rng.cpp",
317316
"dpnp/backend/queue_sycl.cpp"
318317
],

tests/test_linalg.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ def vvsort(val, vec, size):
2929
[2, 4, 8, 16, 300])
3030
def test_eig_arange(type, size):
3131
a = numpy.arange(size * size, dtype=type).reshape((size, size))
32-
symm = numpy.tril(a) + numpy.tril(a, -1).T + numpy.diag(numpy.full((size,), size * size, dtype=type))
33-
isymm = inp.array(symm)
32+
symm_orig = numpy.tril(a) + numpy.tril(a, -1).T + numpy.diag(numpy.full((size,), size * size, dtype=type))
33+
symm = symm_orig
34+
dpnp_symm_orig = inp.array(symm)
35+
dpnp_symm = dpnp_symm_orig
3436

35-
dpnp_val, dpnp_vec = inp.linalg.eig(isymm)
37+
dpnp_val, dpnp_vec = inp.linalg.eig(dpnp_symm)
3638
np_val, np_vec = numpy.linalg.eig(symm)
3739

3840
# DPNP sort val/vec by abs value
@@ -46,5 +48,13 @@ def test_eig_arange(type, size):
4648
if np_vec[0, i] * dpnp_vec[0, i] < 0:
4749
np_vec[:, i] = -np_vec[:, i]
4850

51+
numpy.testing.assert_array_equal(symm_orig, symm)
52+
numpy.testing.assert_array_equal(dpnp_symm_orig, dpnp_symm)
53+
54+
assert (dpnp_val.dtype == np_val.dtype)
55+
assert (dpnp_vec.dtype == np_vec.dtype)
56+
assert (dpnp_val.shape == np_val.shape)
57+
assert (dpnp_vec.shape == np_vec.shape)
58+
4959
numpy.testing.assert_allclose(dpnp_val, np_val, rtol=1e-05, atol=1e-05)
5060
numpy.testing.assert_allclose(dpnp_vec, np_vec, rtol=1e-05, atol=1e-05)

0 commit comments

Comments
 (0)