[mlpack-svn] r11660 - in mlpack/trunk/src/mlpack: core/kernels tests

fastlab-svn at coffeetalk-1.cc.gatech.edu fastlab-svn at coffeetalk-1.cc.gatech.edu
Wed Feb 29 16:06:31 EST 2012


Author: nslagle
Date: 2012-02-29 16:06:30 -0500 (Wed, 29 Feb 2012)
New Revision: 11660

Added:
   mlpack/trunk/src/mlpack/core/kernels/epanechnikov_kernel.hpp
   mlpack/trunk/src/mlpack/core/kernels/spherical_kernel.hpp
Modified:
   mlpack/trunk/src/mlpack/core/kernels/CMakeLists.txt
   mlpack/trunk/src/mlpack/core/kernels/example_kernel.hpp
   mlpack/trunk/src/mlpack/core/kernels/gaussian_kernel.hpp
   mlpack/trunk/src/mlpack/tests/kernel_test.cpp
Log:
mlpack/core/kernels: add a couple of kernels appropriate for the forthcoming KCDE/KDE combo, per #200

Modified: mlpack/trunk/src/mlpack/core/kernels/CMakeLists.txt
===================================================================
--- mlpack/trunk/src/mlpack/core/kernels/CMakeLists.txt	2012-02-29 21:05:13 UTC (rev 11659)
+++ mlpack/trunk/src/mlpack/core/kernels/CMakeLists.txt	2012-02-29 21:06:30 UTC (rev 11660)
@@ -3,13 +3,14 @@
 # Define the files we need to compile.
 # Anything not in this list will not be compiled into MLPACK.
 set(SOURCES
-  gaussian_kernel.hpp
-  linear_kernel.hpp
   cosine_distance.hpp
   cosine_distance_impl.hpp
+  gaussian_kernel.hpp
+  hyperbolic_tangent_kernel.hpp
   laplacian_kernel.hpp
+  linear_kernel.hpp
   polynomial_kernel.hpp
-  hyperbolic_tangent_kernel.hpp
+  spherical_kernel.hpp
 )
 
 # add directory name to sources

Added: mlpack/trunk/src/mlpack/core/kernels/epanechnikov_kernel.hpp
===================================================================
--- mlpack/trunk/src/mlpack/core/kernels/epanechnikov_kernel.hpp	                        (rev 0)
+++ mlpack/trunk/src/mlpack/core/kernels/epanechnikov_kernel.hpp	2012-02-29 21:06:30 UTC (rev 11660)
@@ -0,0 +1,100 @@
+/**
+ * @file epanechnikov_kernel.hpp
+ * @author Neil Slagle
+ *
+ * This is an example kernel.  If you are making your own kernel, follow the
+ * outline specified in this file.
+ */
+#ifndef __MLPACK_CORE_KERNELS_EPANECHNIKOV_KERNEL_H
+#define __MLPACK_CORE_KERNELS_EPANECHNIKOV_KERNEL_H
+
+#include <boost/math/special_functions/gamma.hpp>
+
+#include <mlpack/core.hpp>
+#include <mlpack/core/metrics/lmetric.hpp>
+
+namespace mlpack {
+namespace kernel {
+
+class EpanechnikovKernel
+{
+ public:
+  EpanechnikovKernel() :
+    bandwidth(1.0),
+    inverseBandwidthSquared(1.0) {}
+  EpanechnikovKernel(double b) :
+    bandwidth(b),
+    inverseBandwidthSquared(1.0/(b*b)) {}
+
+  template<typename VecType>
+  double Evaluate(const VecType& a, const VecType& b)
+  {
+    double evaluatee =
+      1.0 - metric::SquaredEuclideanDistance::Evaluate(a, b) * inverseBandwidthSquared;
+    return (evaluatee > 0.0) ? evaluatee : 0.0;
+  }
+  /**
+   * Obtains the convolution integral [integral K(||x-a||)K(||b-x||)dx]
+   * for the two vectors.  In this case, because
+   * our simple example kernel has no internal parameters, we can declare the
+   * function static.  For a more complex example which cannot be declared
+   * static, see the GaussianKernel, which stores an internal parameter.
+   *
+   * @tparam VecType Type of vector (arma::vec, arma::spvec should be expected).
+   * @param a First vector.
+   * @param b Second vector.
+   * @return the convolution integral value.
+   */
+  template<typename VecType>
+  double ConvolutionIntegral(const VecType& a, const VecType& b)
+  {
+    double distance = sqrt(metric::SquaredEuclideanDistance::Evaluate(a, b));
+    if (distance >= 2.0 * bandwidth)
+    {
+      return 0.0;
+    }
+    double volumeSquared = pow(Normalizer(a.n_rows), 2.0);
+
+    switch(a.n_rows)
+    {
+      case 1:
+        return 1.0 / volumeSquared *
+          (16.0/15.0*bandwidth-4.0*distance*distance /
+          (3.0*bandwidth)+2.0*distance*distance*distance/
+          (3.0*bandwidth*bandwidth) -
+          pow(distance,5.0)/(30.0*pow(bandwidth,4.0)));
+        break;
+      case 2:
+        return 1.0 / volumeSquared *
+          ((2.0/3.0*bandwidth*bandwidth-distance*distance)*
+          asin(sqrt(1.0-pow(distance/(2.0*bandwidth),2.0))) +
+          sqrt(4.0*bandwidth*bandwidth-distance*distance)*
+          (distance/6.0+2.0/9.0*distance*pow(distance/bandwidth,2.0)-
+          distance/72.0*pow(distance/bandwidth,4.0)));
+        break;
+      default:
+        Log::Fatal << "Epanechnikov doesn't support your dimension (yet).";
+        return -1.0;
+        break;
+    }
+  }
+
+  double Normalizer(size_t dimension)
+  {
+    return 2.0 * pow(bandwidth, dimension) * pow(M_PI, dimension / 2.0) /
+             (tgamma(dimension / 2.0 + 1.0) * (dimension + 2.0));
+  }
+  double Evaluate(double t)
+  {
+    double evaluatee = 1.0 - t * t * inverseBandwidthSquared;
+    return (evaluatee > 0.0) ? evaluatee : 0.0;
+  }
+ private:
+  double bandwidth;
+  double inverseBandwidthSquared;
+};
+
+}; // namespace kernel
+}; // namespace mlpack
+
+#endif

Modified: mlpack/trunk/src/mlpack/core/kernels/example_kernel.hpp
===================================================================
--- mlpack/trunk/src/mlpack/core/kernels/example_kernel.hpp	2012-02-29 21:05:13 UTC (rev 11659)
+++ mlpack/trunk/src/mlpack/core/kernels/example_kernel.hpp	2012-02-29 21:06:30 UTC (rev 11660)
@@ -100,6 +100,32 @@
    */
   template<typename VecType>
   static double Evaluate(const VecType& a, const VecType& b) { return 0; }
+  /**
+   * Obtains the convolution integral [integral K(||x-a||)K(||b-x||)dx]
+   * for the two vectors.  In this case, because
+   * our simple example kernel has no internal parameters, we can declare the
+   * function static.  For a more complex example which cannot be declared
+   * static, see the GaussianKernel, which stores an internal parameter.
+   *
+   * @tparam VecType Type of vector (arma::vec, arma::spvec should be expected).
+   * @param a First vector.
+   * @param b Second vector.
+   * @return the convolution integral value.
+   */
+  template<typename VecType>
+  static double ConvolutionIntegral(const VecType& a, const VecType& b) { return 0; }
+
+  /**
+   * Obtains the normalizing volume for the kernel with dimension $dimension$.
+   * In this case, because our simple example kernel has no internal parameters,
+   * we can declare the function static.  For a more complex example which
+   * cannot be declared static, see the GaussianKernel, which stores an internal
+   * parameter.
+   *
+   * @param dimension the dimension of the space.
+   * @return the normalization constant.
+   */
+  static double Normalizer(size_t dimension) { return 0; }
 };
 
 }; // namespace kernel

Modified: mlpack/trunk/src/mlpack/core/kernels/gaussian_kernel.hpp
===================================================================
--- mlpack/trunk/src/mlpack/core/kernels/gaussian_kernel.hpp	2012-02-29 21:05:13 UTC (rev 11659)
+++ mlpack/trunk/src/mlpack/core/kernels/gaussian_kernel.hpp	2012-02-29 21:06:30 UTC (rev 11660)
@@ -74,7 +74,31 @@
     // The precalculation of gamma saves us a little computation time.
     return exp(gamma * t * t);
   }
+  /**
+   * Obtain the normalization constant of the Gaussian kernel.
+   *
+   * @param dimension
+   * @return the normalization constant
+   */
+  double Normalizer(size_t dimension)
+  {
+    return pow(sqrt(2.0 * M_PI) * bandwidth, dimension);
+  }
+  /**
+   * Obtain a convolution integral of the Gaussian kernel.
+   *
+   * @param a, first vector
+   * @param b, second vector
+   * @return the convolution integral
+   */
+  template<typename VecType>
+  double ConvolutionIntegral(const VecType& a, const VecType& b)
+  {
+    return Evaluate(sqrt(metric::SquaredEuclideanDistance::Evaluate(a, b) / 2.0)) /
+      (Normalizer(a.n_rows) * pow(2.0, (double) a.n_rows / 2.0));
+  }
 
+
   //! Get the bandwidth.
   double Bandwidth() const { return bandwidth; }
   //! Modify the bandwidth.  This takes an argument because we must update the

Added: mlpack/trunk/src/mlpack/core/kernels/spherical_kernel.hpp
===================================================================
--- mlpack/trunk/src/mlpack/core/kernels/spherical_kernel.hpp	                        (rev 0)
+++ mlpack/trunk/src/mlpack/core/kernels/spherical_kernel.hpp	2012-02-29 21:06:30 UTC (rev 11660)
@@ -0,0 +1,90 @@
+/**
+ * @file spherical_kernel.hpp
+ * @author Neil Slagle
+ *
+ * This is an example kernel.  If you are making your own kernel, follow the
+ * outline specified in this file.
+ */
+#ifndef __MLPACK_CORE_KERNELS_SPHERICAL_KERNEL_H
+#define __MLPACK_CORE_KERNELS_SPHERICAL_KERNEL_H
+
+#include <boost/math/special_functions/gamma.hpp>
+#include <mlpack/core.hpp>
+
+namespace mlpack {
+namespace kernel {
+
+class SphericalKernel
+{
+ public:
+  SphericalKernel() :
+    bandwidth(1.0),
+    bandwidthSquared(1.0) {}
+  SphericalKernel(double b) :
+    bandwidth(b),
+    bandwidthSquared(b*b) {}
+
+  template<typename VecType>
+  double Evaluate(const VecType& a, const VecType& b)
+  {
+    return
+      (metric::SquaredEuclideanDistance::Evaluate(a, b) <= bandwidthSquared) ?
+        1.0 : 0.0;
+  }
+  /**
+   * Obtains the convolution integral [integral K(||x-a||)K(||b-x||)dx]
+   * for the two vectors.  In this case, because
+   * our simple example kernel has no internal parameters, we can declare the
+   * function static.  For a more complex example which cannot be declared
+   * static, see the GaussianKernel, which stores an internal parameter.
+   *
+   * @tparam VecType Type of vector (arma::vec, arma::spvec should be expected).
+   * @param a First vector.
+   * @param b Second vector.
+   * @return the convolution integral value.
+   */
+  template<typename VecType>
+  double ConvolutionIntegral(const VecType& a, const VecType& b)
+  {
+    double distance = sqrt(metric::SquaredEuclideanDistance::Evaluate(a, b));
+    if (distance >= 2.0 * bandwidth)
+    {
+      return 0.0;
+    }
+    double volumeSquared = pow(Normalizer(a.n_rows), 2.0);
+
+    switch(a.n_rows)
+    {
+      case 1:
+        return 1.0 / volumeSquared * (2.0 * bandwidth - distance);
+        break;
+      case 2:
+        return 1.0 / volumeSquared *
+          (2.0 * bandwidth * bandwidth * acos(distance/(2.0 * bandwidth)) -
+          distance / 4.0 * sqrt(4.0*bandwidth*bandwidth-distance*distance));
+        break;
+      default:
+        Log::Fatal << "The spherical kernel does not support convolution\
+          integrals above dimension two, yet..." << std::endl;
+        return -1.0;
+        break;
+    }
+  }
+  double Normalizer(size_t dimension)
+  {
+    return pow(bandwidth, dimension) * pow(M_PI, dimension / 2.0) /
+             tgamma(dimension / 2.0 + 1.0);
+  }
+  double Evaluate(double t)
+  {
+    return (t <= bandwidth) ? 1.0 : 0.0;
+  }
+ private:
+  double bandwidth;
+  double bandwidthSquared;
+};
+
+}; // namespace kernel
+}; // namespace mlpack
+
+#endif

Modified: mlpack/trunk/src/mlpack/tests/kernel_test.cpp
===================================================================
--- mlpack/trunk/src/mlpack/tests/kernel_test.cpp	2012-02-29 21:05:13 UTC (rev 11659)
+++ mlpack/trunk/src/mlpack/tests/kernel_test.cpp	2012-02-29 21:06:30 UTC (rev 11660)
@@ -5,14 +5,17 @@
  *
  * Tests for the various kernel classes.
  */
-#include <mlpack/core/metrics/lmetric.hpp>
-#include <mlpack/core/metrics/mahalanobis_distance.hpp>
 #include <mlpack/core/kernels/cosine_distance.hpp>
+#include <mlpack/core/kernels/epanechnikov_kernel.hpp>
 #include <mlpack/core/kernels/gaussian_kernel.hpp>
+#include <mlpack/core/kernels/hyperbolic_tangent_kernel.hpp>
+#include <mlpack/core/kernels/laplacian_kernel.hpp>
 #include <mlpack/core/kernels/linear_kernel.hpp>
+#include <mlpack/core/kernels/linear_kernel.hpp>
 #include <mlpack/core/kernels/polynomial_kernel.hpp>
-#include <mlpack/core/kernels/laplacian_kernel.hpp>
-#include <mlpack/core/kernels/hyperbolic_tangent_kernel.hpp>
+#include <mlpack/core/kernels/spherical_kernel.hpp>
+#include <mlpack/core/metrics/lmetric.hpp>
+#include <mlpack/core/metrics/mahalanobis_distance.hpp>
 
 #include <boost/test/unit_test.hpp>
 
@@ -242,8 +245,73 @@
   BOOST_REQUIRE_CLOSE(gk.Evaluate(c,a), .018315638888734, 1e-5);
   BOOST_REQUIRE_CLOSE(gk.Evaluate(b,c), .018315638888734, 1e-5);
   BOOST_REQUIRE_CLOSE(gk.Evaluate(c,b), .018315638888734, 1e-5);
+  /* check the single dimension evaluate function */
+  BOOST_REQUIRE_CLOSE(gk.Evaluate(1.0), 0.1353352832366127, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.Evaluate(2.0), 0.00033546262790251185, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.Evaluate(3.0), 1.5229979744712629e-08, 1e-5);
+  /* check the normalization constant */
+  BOOST_REQUIRE_CLOSE(gk.Normalizer(1), 1.2533141373155001, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.Normalizer(2), 1.5707963267948963, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.Normalizer(3), 1.9687012432153019, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.Normalizer(4), 2.4674011002723386, 1e-5);
+  /* check the convolution integral */
+  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(a,b), 0.024304474038457577, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(a,c), 0.024304474038457577, 1e-5);
+  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(b,c), 0.024304474038457577, 1e-5);
+
 }
 
+BOOST_AUTO_TEST_CASE(spherical_kernel)
+{
+  arma::vec a = "1.0 0.0";
+  arma::vec b = "0.0 1.0";
+  arma::vec c = "0.2 0.9";
+
+  SphericalKernel sk(.5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(a,b), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(a,c), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(b,c), 1.0, 1e-5);
+  /* check the single dimension evaluate function */
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.10), 1.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.25), 1.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.50), 1.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Evaluate(1.00), 0.0, 1e-5);
+  /* check the normalization constant */
+  BOOST_REQUIRE_CLOSE(sk.Normalizer(1), 1.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Normalizer(2), 0.78539816339744828, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Normalizer(3), 0.52359877559829893, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.Normalizer(4), 0.30842513753404244, 1e-5);
+  /* check the convolution integral */
+  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(a,b), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(a,c), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(b,c), 1.0021155029652784, 1e-5);
+}
+
+BOOST_AUTO_TEST_CASE(epanechnikov_kernel)
+{
+  arma::vec a = "1.0 0.0";
+  arma::vec b = "0.0 1.0";
+  arma::vec c = "0.1 0.9";
+
+  EpanechnikovKernel ek(.5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(a,b), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(b,c), 0.92, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(a,c), 0.0, 1e-5);
+  /* check the single dimension evaluate function */
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.10), 0.96, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.25), 0.75, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.50), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Evaluate(1.00), 0.0, 1e-5);
+  /* check the normalization constant */
+  BOOST_REQUIRE_CLOSE(ek.Normalizer(1), 0.666666666666666, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Normalizer(2), 0.39269908169872414, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Normalizer(3), 0.20943951023931956, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.Normalizer(4), 0.10280837917801415, 1e-5);
+  /* check the convolution integral */
+  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(a,b), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(a,c), 0.0, 1e-5);
+  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(b,c), 1.5263455690698258, 1e-5);
+}
 BOOST_AUTO_TEST_CASE(polynomial_kernel)
 {
   arma::vec a = "0 0 1";




More information about the mlpack-svn mailing list