diff --git a/src/shogun/mathematics/linalg/internal/implementation/ElementwiseProduct.h b/src/shogun/mathematics/linalg/internal/implementation/ElementwiseProduct.h index 482849fe2b0..dd193a29853 100644 --- a/src/shogun/mathematics/linalg/internal/implementation/ElementwiseProduct.h +++ b/src/shogun/mathematics/linalg/internal/implementation/ElementwiseProduct.h @@ -74,13 +74,49 @@ struct elementwise_product /** Scalar type */ typedef typename Matrix::Scalar T; + /** Return type */ + typedef SGMatrix ReturnType; + /** Eigen3 matrix type */ typedef Eigen::Matrix MatrixXt; - /** Eigen3 vector type */ - typedef Eigen::Matrix VectorXt; + /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version returns the result in a newly created matrix. If elementwise-product + * is desired that will work irrespective of the backend and the matrix type used, + * then this method should be used. + * + * @param A First matrix + * @param B Second matrix + * @return The result of the operation + */ + static ReturnType compute(SGMatrix A, SGMatrix B) + { + REQUIRE(A.matrix, "Matrix A is not initialized!\n"); + REQUIRE(B.matrix, "Matrix A is not initialized!\n"); + + REQUIRE(A.num_rows == B.num_rows && A.num_cols == B.num_cols, + "Dimension mismatch! A(%d x %d) vs B(%d x %d)\n", + A.num_rows, A.num_cols, B.num_rows, B.num_cols); - /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication */ + ReturnType retMatrix(A.num_rows, A.num_cols); + compute(A, B, retMatrix); + + return retMatrix; + } + + /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version should be used for backend specific code requirements. For example, + * use this with CGPUMatrix and explicitly set ViennaCL backend, or SGMatrix and + * explicitly set Eigen3 backend. If matrix-type/backend-type independent code is + * desired, use the version that does not support preallocated result matrix but + * returns the result in a newly created matrix instead. + * + * @param A First matrix + * @param B Second matrix + * @param C Result of the operation + */ static void compute(SGMatrix A, SGMatrix B, SGMatrix C) { Eigen::Map A_eig = A; @@ -101,7 +137,46 @@ struct elementwise_product /** Scalar type */ typedef typename Matrix::Scalar T; - /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication */ + /** Return type */ + typedef CGPUMatrix ReturnType; + + /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version returns the result in a newly created matrix. If elementwise-product + * is desired that will work irrespective of the backend and the matrix type used, + * then this method should be used. + * + * @param A First matrix + * @param B Second matrix + * @return The result of the operation + */ + static ReturnType compute(CGPUMatrix A, CGPUMatrix B) + { + REQUIRE(A.matrix, "Matrix A is not initialized!\n"); + REQUIRE(B.matrix, "Matrix A is not initialized!\n"); + + REQUIRE(A.num_rows == B.num_rows && A.num_cols == B.num_cols, + "Dimension mismatch! A(%d x %d) vs B(%d x %d)\n", + A.num_rows, A.num_cols, B.num_rows, B.num_cols); + + ReturnType retMatrix(A.num_rows, A.num_cols); + compute(A, B, retMatrix); + + return retMatrix; + } + + /** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version should be used for backend specific code requirements. For example, + * use this with CGPUMatrix and explicitly set ViennaCL backend, or SGMatrix and + * explicitly set Eigen3 backend. If matrix-type/backend-type independent code is + * desired, use the version that does not support preallocated result matrix but + * returns the result in a newly created matrix instead. + * + * @param A First matrix + * @param B Second matrix + * @param C Result of the operation + */ static void compute(CGPUMatrix A, CGPUMatrix B, CGPUMatrix C) { C.vcl_matrix() = viennacl::linalg::element_prod(A.vcl_matrix(), B.vcl_matrix()); diff --git a/src/shogun/mathematics/linalg/internal/modules/Core.h b/src/shogun/mathematics/linalg/internal/modules/Core.h index e1973b85fc5..ab31df89371 100644 --- a/src/shogun/mathematics/linalg/internal/modules/Core.h +++ b/src/shogun/mathematics/linalg/internal/modules/Core.h @@ -122,13 +122,40 @@ void subtract(Matrix A, Matrix B, Matrix C, implementation::add::compute(A, B, C, alpha, -1*beta); } -/** Performs the operation C = A .* B where ".*" denotes elementwise multiplication */ +/** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version should be used for backend specific code requirements. For example, + * use this with CGPUMatrix and explicitly set ViennaCL backend, or SGMatrix and + * explicitly set Eigen3 backend. If matrix-type/backend-type independent code is + * desired, use the version that does not support preallocated result matrix but + * returns the result in a newly created matrix instead. + * + * @param A First matrix + * @param B Second matrix + * @param C Result of the operation + */ template ::backend,class Matrix> void elementwise_product(Matrix A, Matrix B, Matrix C) { implementation::elementwise_product::compute(A, B, C); } +/** Performs the operation C = A .* B where ".*" denotes elementwise multiplication. + * + * This version returns the result in a newly created matrix. If elementwise-product + * is desired that will work irrespective of the backend and the matrix type used, + * then this method should be used. + * + * @param A First matrix + * @param B Second matrix + * @return The result of the operation + */ +template ::backend,class Matrix> +typename implementation::elementwise_product::ReturnType elementwise_product(Matrix A, Matrix B) +{ + return implementation::elementwise_product::compute(A, B); +} + /** * Wrapper method for internal implementation of square of co-efficients that works * with generic dense matrices. diff --git a/tests/unit/mathematics/linalg/ElementwiseProduct_unittest.cc b/tests/unit/mathematics/linalg/ElementwiseProduct_unittest.cc index b1c621db6ac..d0a70976d1c 100644 --- a/tests/unit/mathematics/linalg/ElementwiseProduct_unittest.cc +++ b/tests/unit/mathematics/linalg/ElementwiseProduct_unittest.cc @@ -46,43 +46,81 @@ using namespace shogun; #ifdef HAVE_EIGEN3 -TEST(ElementwiseProduct, eigen3_backend) +TEST(ElementwiseProduct, SGMatrix_eigen3_backend) { SGMatrix A(3,3); SGMatrix B(3,3); SGMatrix C(3,3); - + for (int32_t i=0; i<9; i++) { A[i] = i; B[i] = 0.5*i; } - + linalg::elementwise_product(A, B, C); - + + for (int32_t i=0; i<9; i++) + EXPECT_NEAR(A[i]*B[i], C[i], 1e-15); +} + +#ifdef HAVE_VIENNACL +TEST(ElementwiseProduct, CGPUMatrix_eigen3_backend) +{ + CGPUMatrix A(3,3); + CGPUMatrix B(3,3); + + for (int32_t i=0; i<9; i++) + { + A[i] = i; + B[i] = 0.5*i; + } + + CGPUMatrix C = linalg::elementwise_product(A, B); + for (int32_t i=0; i<9; i++) EXPECT_NEAR(A[i]*B[i], C[i], 1e-15); } +#endif // HAVE_VIENNACL #endif // HAVE_EIGEN3 #ifdef HAVE_VIENNACL -TEST(ElementwiseProduct, viennacl_backend) +TEST(ElementwiseProduct, CGPUMatrix_viennacl_backend) { CGPUMatrix A(3,3); CGPUMatrix B(3,3); CGPUMatrix C(3,3); - + for (int32_t i=0; i<9; i++) { A[i] = i; B[i] = 0.5*i; } - + linalg::elementwise_product(A, B, C); - + + for (int32_t i=0; i<9; i++) + EXPECT_NEAR(A[i]*B[i], C[i], 1e-15); +} + +#ifdef HAVE_EIGEN3 +TEST(ElementwiseProduct, SGMatrix_viennacl_backend) +{ + SGMatrix A(3,3); + SGMatrix B(3,3); + + for (int32_t i=0; i<9; i++) + { + A[i] = i; + B[i] = 0.5*i; + } + + SGMatrix C = linalg::elementwise_product(A, B); + for (int32_t i=0; i<9; i++) EXPECT_NEAR(A[i]*B[i], C[i], 1e-15); } +#endif // HAVE_EIGEN3 #endif // HAVE_VIENNACL #endif // HAVE_LINALG_LIB