diff --git a/src/shogun/lib/GPUMatrix.cpp b/src/shogun/lib/GPUMatrix.cpp index 1fd06d48b54..503d8433ce9 100644 --- a/src/shogun/lib/GPUMatrix.cpp +++ b/src/shogun/lib/GPUMatrix.cpp @@ -70,7 +70,7 @@ CGPUMatrix::CGPUMatrix(VCLMemoryArray mem, index_t nrows, index_t ncols, } template -CGPUMatrix::CGPUMatrix(SGMatrix< T >& cpu_mat) +CGPUMatrix::CGPUMatrix(const SGMatrix< T >& cpu_mat) { init(); diff --git a/src/shogun/lib/GPUMatrix.h b/src/shogun/lib/GPUMatrix.h index c98a706484c..18e9539ac78 100644 --- a/src/shogun/lib/GPUMatrix.h +++ b/src/shogun/lib/GPUMatrix.h @@ -55,7 +55,7 @@ namespace shogun * as backend for managing GPU memory. * * It supports conversion to/from SGMatrix objects and Eigen3 matrices. Native - * ViennaCL methods can also be using on this class through vcl_matrix() + * ViennaCL methods can also be used on the data of the matrix through vcl_matrix() * * Supported scalar types: char, uint8_t, int16_t, uint16_t, int32_t, * uint32_t, int64_t, uint64_t, float32_t, float64_t. @@ -87,12 +87,12 @@ template class CGPUMatrix CGPUMatrix(VCLMemoryArray mem, index_t nrows, index_t ncols, index_t mem_offset=0); /** Creates a gpu matrix using data from an SGMatrix */ - CGPUMatrix(SGMatrix& cpu_mat); + CGPUMatrix(const SGMatrix& cpu_mat); #ifdef HAVE_EIGEN3 /** Creates a gpu matrix using data from an Eigen3 matrix */ template - CGPUMatrix(Eigen::PlainObjectBase& cpu_mat) + CGPUMatrix(const Eigen::PlainObjectBase& cpu_mat) { init(); @@ -126,6 +126,16 @@ template class CGPUMatrix vcl_matrix().clear(); } + /** Sets all the elements of the matrix to a constant value + * + * @param value New value for all the elements in the matrix + */ + void set_const(T value) + { + VCLMatrixBase m = vcl_matrix(); + viennacl::linalg::matrix_assign(m, value); + } + /** Displays the matrix */ void display_matrix(const char* name="matrix") const { diff --git a/src/shogun/lib/GPUVector.cpp b/src/shogun/lib/GPUVector.cpp new file mode 100644 index 00000000000..047463f71ad --- /dev/null +++ b/src/shogun/lib/GPUVector.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014, Shogun Toolbox Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Written (W) 2014 Khaled Nasr + */ + +#include + +#ifdef HAVE_VIENNACL + +#include + +namespace shogun +{ + +template +CGPUVector::CGPUVector() +{ + init(); +} + +template +CGPUVector::CGPUVector(index_t length) +{ + init(); + + vlen = length; + + viennacl::backend::memory_create(vector, sizeof(T)*vlen, + viennacl::context()); +} + +template +CGPUVector::CGPUVector(VCLMemoryArray mem, index_t length, index_t mem_offset) +{ + init(); + + vector = mem; + vlen = length; + offset = mem_offset; +} + +template +CGPUVector::CGPUVector(const SGVector& cpu_vec) +{ + init(); + vlen = cpu_vec.vlen; + + viennacl::backend::memory_create(vector, sizeof(T)*vlen, + viennacl::context()); + + viennacl::backend::memory_write(vector, 0, vlen*sizeof(T), + cpu_vec.vector); +} + +#ifdef HAVE_EIGEN3 +template +CGPUVector::operator Eigen::Matrix() const +{ + Eigen::Matrix cpu_vec(vlen); + + viennacl::backend::memory_read(vector, offset*sizeof(T), vlen*sizeof(T), + cpu_vec.data()); + + return cpu_vec; +} + +template +CGPUVector::operator Eigen::Matrix() const +{ + Eigen::Matrix cpu_vec(vlen); + + viennacl::backend::memory_read(vector, offset*sizeof(T), vlen*sizeof(T), + cpu_vec.data()); + + return cpu_vec; +} +#endif + +template +CGPUVector::operator SGVector() const +{ + SGVector cpu_vec(vlen); + + viennacl::backend::memory_read(vector, offset*sizeof(T), vlen*sizeof(T), + cpu_vec.vector); + + return cpu_vec; +} + +template +void CGPUVector::init() +{ + vlen = 0; + offset = 0; +} + +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +template class CGPUVector; +} + +#endif diff --git a/src/shogun/lib/GPUVector.h b/src/shogun/lib/GPUVector.h new file mode 100644 index 00000000000..1dafee07e28 --- /dev/null +++ b/src/shogun/lib/GPUVector.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2014, Shogun Toolbox Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Written (W) 2014 Khaled Nasr + */ + +#ifndef __GPUVECTOR_H__ +#define __GPUVECTOR_H__ + +#include + +#ifdef HAVE_VIENNACL + +#include + +#include + +#ifdef HAVE_EIGEN3 +#include +#endif + +namespace shogun +{ + +/** @brief Represents a vector on the GPU + * + * This class handles vectors on the GPU using [ViennaCL](http://viennacl.sourceforge.net/) + * as backend for managing GPU memory. + * + * It supports conversion to/from SGVector objects and Eigen3 vectors. Native + * ViennaCL methods can also be used on the vector's data through vcl_vector() + * + * Supported scalar types: char, uint8_t, int16_t, uint16_t, int32_t, + * uint32_t, int64_t, uint64_t, float32_t, float64_t. + */ +template class CGPUVector +{ + typedef viennacl::vector_base VCLVectorBase; + typedef viennacl::backend::mem_handle VCLMemoryArray; + +public: + /** Default Constructor */ + CGPUVector(); + + /** Creates a new vector + * + * @param length Number of elements + */ + CGPUVector(index_t length); + + /** Wraps a vector around an existing memery segment + * + * @param mem A memory segment + * @param length Number of elements + * @param mem_offset Offset for the memory segment, i.e the data of the vector + * starts at mem+mem_offset + */ + CGPUVector(VCLMemoryArray mem, index_t length, index_t mem_offset=0); + + /** Creates a gpu vector using data from an SGVector */ + CGPUVector(const SGVector& cpu_vec); + +#ifdef HAVE_EIGEN3 + /** Creates a gpu vector using data from an Eigen3 vector */ + template + CGPUVector(const Eigen::PlainObjectBase& cpu_vec) + { + init(); + vlen = cpu_vec.size(); + + viennacl::backend::memory_create(vector, sizeof(T)*vlen, + viennacl::context()); + + viennacl::backend::memory_write(vector, 0, vlen*sizeof(T), + cpu_vec.data()); + } + + /** Converts the vector into an Eigen3 column vector */ + operator Eigen::Matrix() const; + + /** Converts the vector into an Eigen3 row vector */ + operator Eigen::Matrix() const; +#endif + + /** Converts the vector into an SGVector */ + operator SGVector() const; + + /** Returns a ViennaCL vector wrapped around the data of this vector. Can be + * used to call native ViennaCL methods on this vector + */ + VCLVectorBase vcl_vector() + { + return VCLVectorBase(vector,vlen, offset, 1); + } + + /** Sets all the elements of the vector to zero */ + void zero() + { + vcl_vector().clear(); + } + + /** Sets all the elements of the vector to a constant value + * + * @param value New value for all the elements in the vector + */ + void set_const(T value) + { + VCLVectorBase v = vcl_vector(); + viennacl::linalg::vector_assign(v, value); + } + + /** Displays the vector */ + void display_vector(const char* name="vector") const + { + ((SGVector)*this).display_vector(name); + } + + /** Read only memory access. Note that this is very slow as it copies the + * element from the GPU to the CPU + * + * @param index Element index + */ + inline viennacl::const_entry_proxy operator[](index_t index) const + { + return viennacl::const_entry_proxy(offset+index, vector); + } + + /** Read/write memory access. Note that this is very slow as it copies the + * element between the GPU and the CPU + * + * @param index Element index + */ + inline viennacl::entry_proxy operator[](index_t index) + { + return viennacl::entry_proxy(offset+index, vector); + } + +private: + void init(); + +public: + /** Memory segment holding the data for the vector */ + VCLMemoryArray vector; + + /** Offset for the memory segment, i.e the data of the vector + * starts at vector+offset + */ + index_t offset; + + /** Vector length */ + index_t vlen; +}; + +} + +#endif +#endif diff --git a/src/shogun/lib/SGMatrix.h b/src/shogun/lib/SGMatrix.h index 060a8020940..5acddff83df 100644 --- a/src/shogun/lib/SGMatrix.h +++ b/src/shogun/lib/SGMatrix.h @@ -57,10 +57,10 @@ template class SGMatrix : public SGReferencedData template SGMatrix(Eigen::PlainObjectBase& mat) : SGReferencedData(false), matrix(mat.data()), - num_rows(mat.rows()), num_cols(mat.cols()) { } + num_rows(mat.rows()), num_cols(mat.cols()) { } /** Wraps an Eigen3 matrix around the data of this matrix */ - operator Eigen::Matrix() const + operator Eigen::Map >() const { return Eigen::Map >( matrix, num_rows, num_cols); diff --git a/src/shogun/lib/SGVector.h b/src/shogun/lib/SGVector.h index af5e1168917..55cadfaecd3 100644 --- a/src/shogun/lib/SGVector.h +++ b/src/shogun/lib/SGVector.h @@ -19,6 +19,10 @@ #include #include +#ifdef HAVE_EIGEN3 +#include +#endif + namespace shogun { template class SGSparseVector; @@ -35,13 +39,38 @@ template class SGVector : public SGReferencedData /** constructor for setting params */ SGVector(T* v, index_t len, bool ref_counting=true); - + + /** Wraps a vector around an existing memory segment with an offset */ + SGVector(T* m, index_t len, index_t offset) + : SGReferencedData(false), vector(m+offset), vlen(len) { } + /** constructor to create new vector in memory */ SGVector(index_t len, bool ref_counting=true); /** copy constructor */ SGVector(const SGVector &orig); - + +#ifndef SWIG // SWIG should skip this part +#ifdef HAVE_EIGEN3 + /** Wraps a vector around the data of an Eigen3 vector */ + template + SGVector(Eigen::PlainObjectBase& vec) + : SGReferencedData(false), vector(vec.data()), vlen(vec.size()) { } + + /** Wraps an Eigen3 column vector around the data of this vector */ + operator Eigen::Map >() const + { + return Eigen::Map >(vector, vlen); + } + + /** Wraps an Eigen3 row vector around the data of this vector */ + operator Eigen::Map >() const + { + return Eigen::Map >(vector, vlen); + } +#endif +#endif + /** wrapper for the copy constructor useful for SWIG interfaces * * @param orig vector to set diff --git a/tests/unit/lib/GPUMatrix_unittest.cc b/tests/unit/lib/GPUMatrix_unittest.cc index 592d3a5a932..7942a36a23d 100644 --- a/tests/unit/lib/GPUMatrix_unittest.cc +++ b/tests/unit/lib/GPUMatrix_unittest.cc @@ -91,6 +91,18 @@ TEST(GPUMatrix, zero) EXPECT_EQ(0, mat[i]); } +TEST(GPUMatrix, set_const) +{ + const int nrows = 3; + const int ncols = 4; + + CGPUMatrix mat(nrows,ncols); + mat.set_const(3); + + for (int32_t i=0; i + +#ifdef HAVE_VIENNACL + +#include +#include +#include + +#ifdef HAVE_EIGEN3 +#include +#endif + +using namespace shogun; + +TEST(GPUVector, element_read_write) +{ + const int n = 9; + + CGPUVector vec(n); + + for (int32_t i=0; i vec(n); + vec.zero(); + + for (int32_t i=0; i vec(n); + vec.set_const(3); + + for (int32_t i=0; i data(25); + for (int32_t i=0; i<25; i++) + data[i] = i; + + CGPUVector vec(data.vector, 9, 7); + + for (int32_t i=0; i<9; i++) + EXPECT_EQ(data[i+7], vec[i]); +} + +/** Tests dot product performed using ViennaCL on two CGPUVector objects + * that were created with offsets + */ +TEST(GPUVector, dot_product_with_offset) +{ + CGPUVector data(24); + for (int32_t i=0; i<24; i++) + data[i] = i; + + CGPUVector A(data.vector, 12, 0); + CGPUVector B(data.vector, 12, 12); + + float c = viennacl::linalg::inner_prod(A.vcl_vector(), B.vcl_vector()); + + float c_sg = SGVector::dot( + ((SGVector)A).vector, ((SGVector)B).vector, 12); + + EXPECT_NEAR(c_sg, c, 1e-15); +} + +TEST(GPUVector, to_sgvector) +{ + const int n = 9; + + CGPUVector gpu_vec(9); + for (int32_t i=0; i sg_vec = gpu_vec; + + for (int32_t i=0; i sg_vec(9); + for (int32_t i=0; i gpu_vec = sg_vec; + + for (int32_t i=0; i gpu_vec(9); + for (int32_t i=0; i gpu_vec = eigen_vec; + + for (int32_t i=0; i gpu_vec(9); + for (int32_t i=0; i gpu_vec = eigen_vec; + + for (int32_t i=0; i eigen_mat = sg_mat; for (int32_t i=0; i #include +#ifdef HAVE_EIGEN3 +#include +#endif + using namespace shogun; TEST(SGVectorTest,ctor) @@ -387,3 +391,63 @@ TEST(SGVectorTest,is_sorted_2) EXPECT_EQ(v.is_sorted(), true); } + +#ifdef HAVE_EIGEN3 + +TEST(SGVectorTest, to_eigen3_column_vector) +{ + const int n = 9; + + SGVector sg_vec(9); + for (int32_t i=0; i eigen_vec = sg_vec; + + for (int32_t i=0; i sg_vec = eigen_vec; + + for (int32_t i=0; i sg_vec(9); + for (int32_t i=0; i eigen_vec = sg_vec; + + for (int32_t i=0; i sg_vec = eigen_vec; + + for (int32_t i=0; i