diff --git a/src/shogun/kernel/ShiftInvariantKernel.cpp b/src/shogun/kernel/ShiftInvariantKernel.cpp new file mode 100644 index 00000000000..955b62dbc15 --- /dev/null +++ b/src/shogun/kernel/ShiftInvariantKernel.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) The Shogun Machine Learning Toolbox + * Written (w) 2016 Soumyajit De + * 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. + * + * 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 OWNER 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. + * + * The views and conclusions contained in the software and documentation are those + * of the authors and should not be interpreted as representing official policies, + * either expressed or implied, of the Shogun Development Team. + */ + +#include +#include +#include + +using namespace shogun; + +CShiftInvariantKernel::CShiftInvariantKernel() : CKernel(0) +{ + register_params(); +} + +CShiftInvariantKernel::CShiftInvariantKernel(CFeatures *l, CFeatures *r) : CKernel(l, r, 0) +{ + register_params(); + init(l, r); +} + +CShiftInvariantKernel::~CShiftInvariantKernel() +{ + cleanup(); + SG_UNREF(m_distance); +} + +bool CShiftInvariantKernel::init(CFeatures* l, CFeatures* r) +{ + REQUIRE(m_distance, "The distance instance cannot be NULL!\n"); + CKernel::init(l,r); + m_distance->init(l, r); + return init_normalizer(); +} + +void CShiftInvariantKernel::precompute_distance() +{ + REQUIRE(m_distance, "The distance instance cannot be NULL!\n"); + REQUIRE(m_distance->init(lhs, rhs), "Could not initialize the distance instance!\n"); + + SGMatrix dist_mat=m_distance->get_distance_matrix(); + if (m_precomputed_distance==NULL) + { + m_precomputed_distance=new CCustomDistance(); + SG_REF(m_precomputed_distance); + } + + if (lhs==rhs) + m_precomputed_distance->set_triangle_distance_matrix_from_full(dist_mat.data(), dist_mat.num_rows, dist_mat.num_cols); + else + m_precomputed_distance->set_full_distance_matrix_from_full(dist_mat.data(), dist_mat.num_rows, dist_mat.num_cols); +} + +void CShiftInvariantKernel::cleanup() +{ + SG_UNREF(m_precomputed_distance); + m_precomputed_distance=NULL; + CKernel::cleanup(); +} + +EDistanceType CShiftInvariantKernel::get_distance_type() const +{ + REQUIRE(m_distance, "The distance instance cannot be NULL!\n"); + return m_distance->get_distance_type(); +} + +float64_t CShiftInvariantKernel::distance(int32_t a, int32_t b) const +{ + REQUIRE(m_distance, "The distance instance cannot be NULL!\n"); + if (m_precomputed_distance!=NULL) + return m_precomputed_distance->distance(a, b); + else + return m_distance->distance(a, b); +} + +void CShiftInvariantKernel::register_params() +{ + SG_ADD((CSGObject**) &m_distance, "m_distance", "Distance to be used.", MS_NOT_AVAILABLE); + SG_ADD((CSGObject**) &m_precomputed_distance, "m_precomputed_distance", "Precomputed istance to be used.", MS_NOT_AVAILABLE); + + m_distance=NULL; + m_precomputed_distance=NULL; +} + +void CShiftInvariantKernel::set_precomputed_distance(CCustomDistance* precomputed_distance) +{ + REQUIRE(precomputed_distance, "The precomputed distance instance cannot be NULL!\n"); + SG_REF(precomputed_distance); + SG_UNREF(m_precomputed_distance); + m_precomputed_distance=precomputed_distance; +} + +CCustomDistance* CShiftInvariantKernel::get_precomputed_distance() const +{ + REQUIRE(m_precomputed_distance, "The precomputed distance instance cannot be NULL!\n"); + SG_REF(m_precomputed_distance); + return m_precomputed_distance; +} diff --git a/src/shogun/kernel/ShiftInvariantKernel.h b/src/shogun/kernel/ShiftInvariantKernel.h new file mode 100644 index 00000000000..0de57f1a318 --- /dev/null +++ b/src/shogun/kernel/ShiftInvariantKernel.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) The Shogun Machine Learning Toolbox + * Written (w) 2016 Soumyajit De + * 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. + * + * 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 OWNER 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. + * + * The views and conclusions contained in the software and documentation are those + * of the authors and should not be interpreted as representing official policies, + * either expressed or implied, of the Shogun Development Team. + */ + +#include + +#ifndef SHIFT_INVARIANT_KERNEL_H_ +#define SHIFT_INVARIANT_KERNEL_H_ + +#include +#include + +namespace shogun +{ + +/** @brief Base class for the family of kernel functions that only depend on + * the difference of the inputs, i.e. whose values does not change if the + * inputs are shifted by the same amount. More precisely, + * \f[ + * k(\mathbf{x}, \mathbf{x'}) = k(\mathbf{x-x'}) + * \f] + * For example, Gaussian (RBF) kernel is a shfit invariant kernel. + */ +class CShiftInvariantKernel: public CKernel +{ +public: + /** Default constructor. */ + CShiftInvariantKernel(); + + /** + * Constructor that initializes the kernel with two feature instances. + * + * @param l features of left-hand side + * @param r features of right-hand side + */ + CShiftInvariantKernel(CFeatures *l, CFeatures *r); + + /** Destructor. */ + virtual ~CShiftInvariantKernel(); + + /** + * Initialize kernel. + * + * @param l features of left-hand side + * @param r features of right-hand side + * @return if initializing was successful + */ + virtual bool init(CFeatures* l, CFeatures* r); + + /** Method that precomputes the distance */ + virtual void precompute_distance(); + + /** + * Method that releases any precomputed distance instance in addition to + * clean up the base class methods. + */ + virtual void cleanup(); + + /** @return kernel type */ + virtual EKernelType get_kernel_type()=0; + + /** @return feature type of distance used */ + virtual EFeatureType get_feature_type()=0; + + /** @return feature class of distance used */ + virtual EFeatureClass get_feature_class()=0; + + /** @return the distance type */ + virtual EDistanceType get_distance_type() const; + + /** @return name Distance */ + virtual const char* get_name() const + { + return "ShiftInvariantKernel"; + } + +protected: + /** + * Computes distance between features a and b, where idx_{a,b} denote the indices + * of the feature vectors in the corresponding feature object. + * + * @param idx_a index a + * @param idx_b index b + * @return distance between features a and b + */ + virtual float64_t distance(int32_t idx_a, int32_t idx_b) const; + + /** Distance instance for the kernel. MUST be initialized by the subclasses */ + CDistance* m_distance; + +private: + /** Registers the parameters (serialization support). */ + virtual void register_params(); + + /** Precomputed distance instance */ + CCustomDistance* m_precomputed_distance; + + /** + * Method that sets a precomputed distance. + * + * @param precomputed_distance The precomputed distance object. + */ + void set_precomputed_distance(CCustomDistance* precomputed_distance); + + /** @return the precomputed distance. */ + CCustomDistance* get_precomputed_distance() const; + +}; + +} + +#endif // SHIFT_INVARIANT_KERNEL_H__ diff --git a/tests/unit/kernel/ShiftInvariantKernel_unittest.cc b/tests/unit/kernel/ShiftInvariantKernel_unittest.cc new file mode 100644 index 00000000000..a49d5ed2eb6 --- /dev/null +++ b/tests/unit/kernel/ShiftInvariantKernel_unittest.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) The Shogun Machine Learning Toolbox + * Written (w) 2016 Soumyajit De + * 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. + * + * 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 OWNER 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. + * + * The views and conclusions contained in the software and documentation are those + * of the authors and should not be interpreted as representing official policies, + * either expressed or implied, of the Shogun Development Team. + */ + +#include + +#ifdef HAVE_CXX11 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace shogun; +using std::iota; +using std::for_each; + +class CShiftInvariantKernelMock : public CShiftInvariantKernel +{ +public: + CShiftInvariantKernelMock() : CShiftInvariantKernel() + { + m_distance=new CEuclideanDistance(); + SG_REF(m_distance); + } + + float64_t get_distance(int32_t a, int32_t b) const + { + return CShiftInvariantKernel::distance(a, b); + } + + MOCK_METHOD2(compute, float64_t(int32_t, int32_t)); + MOCK_METHOD0(get_kernel_type, EKernelType()); + MOCK_METHOD0(get_feature_type, EFeatureType()); + MOCK_METHOD0(get_feature_class, EFeatureClass()); +}; + +TEST(ShiftInvariantKernel, precompute_distance_asymmetric) +{ + const index_t dim=1; + const index_t N=10; + const index_t M=15; + + SGMatrix data_1(dim, N); + SGMatrix data_2(dim, M); + + iota(data_1.data(), data_1.data()+data_1.size(), 1); + iota(data_2.data(), data_2.data()+data_2.size(), data_1.size()+1); + + for_each(data_1.data(), data_1.data()+data_1.size(), [&data_1](float64_t& val) { val=val/data_1.size(); }); + for_each(data_2.data(), data_2.data()+data_2.size(), [&data_2](float64_t& val) { val=val/data_2.size(); }); + + auto feats_1=some >(data_1); + auto feats_2=some >(data_2); + + auto kernel_1=some(); + auto kernel_2=some(); + + kernel_1->init(feats_1, feats_2); + kernel_2->init(feats_1, feats_2); + + kernel_1->precompute_distance(); + + for (auto i=0; iget_distance(i, j), kernel_1->get_distance(i, j), 1E-6); + } +} + +TEST(ShiftInvariantKernel, precompute_distance_symmetric) +{ + const index_t dim=1; + const index_t N=10; + + SGMatrix data(dim, N); + iota(data.data(), data.data()+data.size(), 1); + for_each(data.data(), data.data()+data.size(), [&data](float64_t& val) { val=val/data.size(); }); + auto feats=some >(data); + + auto kernel_1=some(); + auto kernel_2=some(); + + kernel_1->init(feats, feats); + kernel_2->init(feats, feats); + + kernel_1->precompute_distance(); + + for (auto i=0; iget_distance(i, j), kernel_1->get_distance(i, j), 1E-6); + } +} +#endif // HAVE_CXX11