[mlpack] 97/324: Initial commit of simulated annealing optimizer from Zhihao Lou.

Barak A. Pearlmutter barak+git at cs.nuim.ie
Sun Aug 17 08:22:00 UTC 2014


This is an automated email from the git hooks/post-receive script.

bap pushed a commit to branch svn-trunk
in repository mlpack.

commit 656b1bc0a65ae466aa2955e7a12eac4a8439d9ce
Author: rcurtin <rcurtin at 9d5b8971-822b-0410-80eb-d18c1038ef23>
Date:   Wed Jul 2 12:36:20 2014 +0000

    Initial commit of simulated annealing optimizer from Zhihao Lou.
    
    
    git-svn-id: http://svn.cc.gatech.edu/fastlab/mlpack/trunk@16735 9d5b8971-822b-0410-80eb-d18c1038ef23
---
 src/mlpack/core/optimizers/CMakeLists.txt          |   1 +
 src/mlpack/core/optimizers/sa/CMakeLists.txt       |  14 ++
 .../core/optimizers/sa/exponential_schedule.hpp    |  52 +++++
 .../core/optimizers/sa/laplace_distribution.cpp    |  22 ++
 .../core/optimizers/sa/laplace_distribution.hpp    |  34 ++++
 src/mlpack/core/optimizers/sa/sa.hpp               | 175 ++++++++++++++++
 src/mlpack/core/optimizers/sa/sa_impl.hpp          | 225 +++++++++++++++++++++
 src/mlpack/tests/CMakeLists.txt                    |   1 +
 src/mlpack/tests/sa_test.cpp                       |  46 +++++
 9 files changed, 570 insertions(+)

diff --git a/src/mlpack/core/optimizers/CMakeLists.txt b/src/mlpack/core/optimizers/CMakeLists.txt
index 34032b0..1243586 100644
--- a/src/mlpack/core/optimizers/CMakeLists.txt
+++ b/src/mlpack/core/optimizers/CMakeLists.txt
@@ -2,6 +2,7 @@ set(DIRS
   aug_lagrangian
   lbfgs
   lrsdp
+  sa
   sgd
 )
 
diff --git a/src/mlpack/core/optimizers/sa/CMakeLists.txt b/src/mlpack/core/optimizers/sa/CMakeLists.txt
new file mode 100644
index 0000000..088abec
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(SOURCES
+  sa.hpp
+  sa_impl.hpp
+  laplace_distribution.hpp
+  laplace_distribution.cpp
+  exponential_schedule.hpp
+)
+
+set(DIR_SRCS)
+foreach(file ${SOURCES})
+  set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
+endforeach()
+
+set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE)
diff --git a/src/mlpack/core/optimizers/sa/exponential_schedule.hpp b/src/mlpack/core/optimizers/sa/exponential_schedule.hpp
new file mode 100644
index 0000000..c3d6019
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/exponential_schedule.hpp
@@ -0,0 +1,52 @@
+/*
+ * @file exponential_schedule.hpp
+ * @author Zhihao Lou
+ *
+ * Exponential (geometric) cooling schedule used in SA
+ */
+
+#ifndef __MLPACK_CORE_OPTIMIZERS_SA_EXPONENTIAL_SCHEDULE_HPP
+#define __MLPACK_CORE_OPTIMIZERS_SA_EXPONENTIAL_SCHEDULE_HPP
+
+namespace mlpack {
+namespace optimization {
+
+/* 
+ * The exponential cooling schedule cools the temperature T at every step
+ * \f[
+ * T_{n+1}=(1-\lambda)T_{n}
+ * \f]
+ * where \f$ 0<\lambda<1 \f$ is the cooling speed. The smaller \f$ \lambda \f$
+ * is, the slower the cooling speed, and better the final result will be. Some
+ * literature uses \f$ \alpha=(-1\lambda) \f$ instead. In practice, \f$ \alpha \f$
+ * is very close to 1 and will be awkward to input (e.g. alpha=0.999999 vs
+ * lambda=1e-6).
+ */
+class ExponentialSchedule
+{
+ public:
+  /* 
+   * Construct the ExponentialSchedule with the given parameter
+   *
+   * @param lambda Cooling speed
+   */
+  ExponentialSchedule(const double lambda = 0.001) : lambda(lambda){};
+
+  //! returns the next temperature given current status
+  double nextTemperature(const double currentTemperate, const double)
+  {return (1-lambda) * currentTemperate;}
+
+  //! Get the cooling speed lambda
+  double Lambda() const {return lambda;}
+  //! Modify the cooling speed lambda
+  double& Lambda() {return lambda;}
+ private:
+  double lambda;
+
+
+};
+
+}; // namespace optimization
+}; // namespace mlpack
+
+#endif
diff --git a/src/mlpack/core/optimizers/sa/laplace_distribution.cpp b/src/mlpack/core/optimizers/sa/laplace_distribution.cpp
new file mode 100644
index 0000000..69d96cb
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/laplace_distribution.cpp
@@ -0,0 +1,22 @@
+/*
+ * @file laplace_distribution.cpp
+ * @author Zhihao Lou
+ *
+ * Implementation of Laplace distribution
+ */
+
+#include <mlpack/core.hpp>
+#include "laplace_distribution.hpp"
+using namespace mlpack;
+using namespace mlpack::optimization;
+double LaplaceDistribution::operator () (const double param)
+{
+  // uniform [-1, 1]
+  double unif = 2.0 * math::Random() - 1.0;
+  // Laplace Distribution with mean 0
+  // x = - param * sign(unif) * log(1 - |unif|)
+  if (unif < 0) // why oh why we don't have a sign function in c++?
+      return (param * std::log(1 + unif));
+  else
+      return (-1.0 * param * std::log(1 - unif));
+}
diff --git a/src/mlpack/core/optimizers/sa/laplace_distribution.hpp b/src/mlpack/core/optimizers/sa/laplace_distribution.hpp
new file mode 100644
index 0000000..15a52a3
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/laplace_distribution.hpp
@@ -0,0 +1,34 @@
+/*
+ * @file laplace.hpp
+ * @author Zhihao Lou
+ *
+ * Laplace (double exponential) distribution used in SA
+ */
+
+#ifndef __MLPACK_CORE_OPTIMIZER_SA_LAPLACE_DISTRIBUTION_HPP
+#define __MLPACK_CORE_OPTIMIZER_SA_LAPLACE_DISTRIBUTION_HPP
+
+namespace mlpack {
+namespace optimization {
+
+/* 
+ * The Laplace distribution centered at 0 has pdf
+ * \f[
+ * f(x|\theta) = \frac{1}{2\theta}\exp\left(-\frac{|x|}{\theta}\right)
+ * \f]
+ * given scale parameter \f$\theta\f$.
+ */
+class LaplaceDistribution
+{
+ public:
+  //! Nothing to do for the constructor
+  LaplaceDistribution(){}
+  //! Return random value from Laplace distribution with parameter param
+  double operator () (const double param);
+
+};
+
+}; // namespace optimization
+}; // namespace mlpack
+
+#endif
diff --git a/src/mlpack/core/optimizers/sa/sa.hpp b/src/mlpack/core/optimizers/sa/sa.hpp
new file mode 100644
index 0000000..902e380
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/sa.hpp
@@ -0,0 +1,175 @@
+/*
+ * @file sa.hpp
+ * @author Zhihao Lou
+ *
+ * Simulated Annealing (SA).
+ */
+#ifndef __MLPACK_CORE_OPTIMIZERS_SA_SA_HPP
+#define __MLPACK_CORE_OPTIMIZERS_SA_SA_HPP
+
+namespace mlpack {
+namespace optimization {
+
+/**
+ * Simulated Annealing is an stochastic optimization algorithm which is able to
+ * deliver near-optimal results quickly without knowing the gradient of the
+ * function being optimized. It has unique hill climbing capability that makes
+ * it less vulnerable to local minima.  This implementation uses exponential
+ * cooling schedule and feedback move control by default, but the cooling
+ * schedule can be changed via a template parameter.
+ *
+ * The algorithm keeps the temperature at initial temperature for initMove
+ * steps to get rid of the dependency of initial condition. After that, it
+ * cools every step until the system is considered frozen or maxIterations is
+ * reached.
+ *
+ * At each step, SA only perturbs one parameter at a time. The process that SA
+ * perturbed all parameters in a problem is called a sweep. Every moveCtrlSweep
+ * the algorithm does feedback move control to change the average move size
+ * depending on the responsiveness of each parameter. Parameter gain controls
+ * the proportion of the feedback control.
+ *
+ * The system is considered "frozen" when its score failed to change more then
+ * tolerance for consecutive maxToleranceSweep sweeps.
+ *
+ * For SA to work, a function must implement the following methods:
+ *   double Evaluate(const arma::mat& coordinates);
+ *   arma::mat& GetInitialPoint();
+ *
+ * In additional, a move generation distribution with overloaded operator():
+ *   double operator () (const double param);
+ * which returns a random value from the distribution given parameter param,
+ * and a cooling schedule with method:
+ *   doulbe nextTemperature(const double currentTemperature, const double currentValue);
+ * which returns the next temperature given current temperature and the value
+ * of the function being optimized.
+ *
+ * @tparam FunctionType objective function type to be minimized.
+ * @tparam MoveDistributionType distribution type for move generation
+ * @tparam CoolingScheduleType type for cooling schedule
+ */
+template<typename FunctionType, typename MoveDistributionType, typename CoolingScheduleType>
+class SA
+{
+ public:
+  /*
+   * Construct the SA optimizer with the given function and paramters.
+   *
+   * @param function Function to be minimized.
+   * @param moveDistribution Distribution for move generation
+   * @param coolingSchedule Cooling schedule
+   * @param initT Initial temperature.
+   * @param initMoves Iterations without changing temperature.
+   * @param moveCtrlSweep Sweeps per move control.
+   * @param tolerance Tolerance to consider system frozen.
+   * @param maxToleranceSweep Maximum sweeps below tolerance to consider system frozen.
+   * @param maxMoveCoef Maximum move size.
+   * @param initMoveCoef Initial move size.
+   * @param gain Proportional control in feedback move control.
+   * @param maxIterations Maximum number of iterations allowed (0 indicates no limit).
+   */
+  SA(FunctionType& function,
+     MoveDistributionType& moveDistribution,
+     CoolingScheduleType& coolingSchedule,
+     const double initT = 10000.,
+     const size_t initMoves = 1000,
+     const size_t moveCtrlSweep = 100,
+     const double tolerance = 1e-5,
+     const size_t maxToleranceSweep = 3,
+     const double maxMoveCoef = 20,
+     const double initMoveCoef = 0.3,
+     const double gain = 0.3,
+     const size_t maxIterations = 1000000);
+  /*
+   * Optimize the given function using simulated annealing. The given starting
+   * point will be modified to store the finishing point of the algorithm, and
+   * the final objective value is returned.
+   *
+   * @param iterate Starting point (will be modified).
+   * @return Objective value of the final point.
+   */
+  double Optimize(arma::mat& iterate);
+
+  //! Get the instantiated function to be optimized.
+  const FunctionType& Function() const {return function;}
+  //! Modify the instantiated function.
+  FunctionType& Function() {return function;}
+
+  //! Get the temperature.
+  double Temperature() const {return T;}
+  //! Modify the temperature.
+  double& Temperature() {return T;}
+
+  //! Get the initial moves.
+  size_t InitMoves() const {return initMoves;}
+  //! Modify the initial moves.
+  size_t& InitMoves() {return initMoves;}
+
+  //! Get sweeps per move control.
+  size_t MoveCtrlSweep() const {return moveCtrlSweep;}
+  //! Modify sweeps per move control.
+  size_t& MoveCtrlSweep() {return moveCtrlSweep;}
+
+  //! Get the tolerance.
+  double Tolerance() const {return tolerance;}
+  //! Modify the tolerance.
+  double& Tolerance() {return tolerance;}
+
+  //! Get the maxToleranceSweep.
+  size_t MaxToleranceSweep() const {return maxToleranceSweep;}
+  //! Modify the maxToleranceSweep.
+  size_t& MaxToleranceSweep() {return maxToleranceSweep;}
+
+  //! Get the gain.
+  double Gain() const {return gain;}
+  //! Modify the gain.
+  double& Gain() {return gain;}
+
+  //! Get the maxIterations.
+  size_t MaxIterations() const {return maxIterations;}
+  //! Modify the maxIterations.
+  size_t& MaxIterations() {return maxIterations;}
+
+  //! Get Maximum move size of each parameter
+  arma::mat MaxMove() const {return maxMove;}
+  //! Modify maximum move size of each parameter
+  arma::mat& MaxMove() {return maxMove;}
+
+  //! Get move size of each parameter
+  arma::mat MoveSize() const {return moveSize;}
+  //! Modify  move size of each parameter
+  arma::mat& MoveSize() {return moveSize;}
+
+  std::string ToString() const;
+ private:
+  FunctionType &function;
+  MoveDistributionType &moveDistribution;
+  CoolingScheduleType &coolingSchedule;
+  double T;
+  size_t initMoves;
+  size_t moveCtrlSweep;
+  double tolerance;
+  size_t maxToleranceSweep;
+  double gain;
+  size_t maxIterations;
+  arma::mat maxMove;
+  arma::mat moveSize;
+
+
+  // following variables are initialized inside Optimize
+  arma::mat accept;
+  double energy;
+  size_t idx;
+  size_t nVars;
+  size_t sweepCounter;
+
+  void GenerateMove(arma::mat& iterate);
+  void MoveControl(size_t nMoves);
+};
+
+}; // namespace optimization
+}; // namespace mlpack
+
+#include "sa_impl.hpp"
+
+#endif
diff --git a/src/mlpack/core/optimizers/sa/sa_impl.hpp b/src/mlpack/core/optimizers/sa/sa_impl.hpp
new file mode 100644
index 0000000..d72fcc9
--- /dev/null
+++ b/src/mlpack/core/optimizers/sa/sa_impl.hpp
@@ -0,0 +1,225 @@
+/*
+ * @file sa_impl.hpp
+ * @auther Zhihao Lou
+ *
+ * The implementation of the SA optimizer.
+ */
+#ifndef __MLPACK_CORE_OPTIMIZERS_SA_SA_IMPL_HPP
+#define __MLPACK_CORE_OPTIMIZERS_SA_SA_IMPL_HPP
+
+namespace mlpack {
+namespace optimization {
+
+template<
+    typename FunctionType,
+    typename MoveDistributionType,
+    typename CoolingScheduleType
+>
+SA<FunctionType, MoveDistributionType, CoolingScheduleType>::SA(
+    FunctionType& function,
+    MoveDistributionType& moveDistribution,
+    CoolingScheduleType& coolingSchedule,
+    const double initT,
+    const size_t initMoves,
+    const size_t moveCtrlSweep,
+    const double tolerance,
+    const size_t maxToleranceSweep,
+    const double maxMoveCoef,
+    const double initMoveCoef,
+    const double gain,
+    const size_t maxIterations) :
+    function(function),
+    moveDistribution(moveDistribution),
+    coolingSchedule(coolingSchedule),
+    T(initT),
+    initMoves(initMoves),
+    moveCtrlSweep(moveCtrlSweep),
+    tolerance(tolerance),
+    maxToleranceSweep(maxToleranceSweep),
+    gain(gain),
+    maxIterations(maxIterations)
+{
+  const size_t rows = function.GetInitialPoint().n_rows;
+  const size_t cols = function.GetInitialPoint().n_cols;
+
+  maxMove.set_size(rows, cols);
+  maxMove.fill(maxMoveCoef);
+  moveSize.set_size(rows, cols);
+  moveSize.fill(initMoveCoef);
+  accept.zeros(rows, cols);
+}
+
+//! Optimize the function (minimize).
+template<
+    typename FunctionType,
+    typename MoveDistributionType,
+    typename CoolingScheduleType
+>
+double SA<FunctionType, MoveDistributionType, CoolingScheduleType>::Optimize(
+    arma::mat &iterate)
+{
+  const size_t rows = function.GetInitialPoint().n_rows;
+  const size_t cols = function.GetInitialPoint().n_cols;
+
+  size_t i;
+  size_t frozenCount = 0;
+  energy = function.Evaluate(iterate);
+  size_t oldEnergy = energy;
+  math::RandomSeed(std::time(NULL));
+
+  nVars = rows * cols;
+  idx = 0;
+  sweepCounter = 0;
+  accept.zeros();
+
+  // Initial Moves to get rid of dependency of initial states.
+  for (i = 0; i < initMoves; ++i)
+    GenerateMove(iterate);
+
+  // Iterating and cooling.
+  for (i = 0; i != maxIterations; ++i)
+  {
+    oldEnergy = energy;
+    GenerateMove(iterate);
+    T = coolingSchedule.nextTemperature(T, energy);
+
+    // Determine if the optimization has entered (or continues to be in) a
+    // frozen state.
+    if (std::abs(energy - oldEnergy) < tolerance)
+      ++frozenCount;
+    else
+      frozenCount = 0;
+
+    // Terminate, if possible.
+    if (frozenCount >= maxToleranceSweep * nVars)
+    {
+      Log::Debug << "SA: minimized within tolerance " << tolerance << " for "
+          << maxToleranceSweep << " sweeps after " << i << " iterations; "
+          << "terminating optimization." << std::endl;
+      return energy;
+    }
+  }
+
+  Log::Debug << "SA: maximum iterations (" << maxIterations << ") reached; "
+      << "terminating optimization." << std::endl;
+  return energy;
+}
+
+/**
+ * GenerateMove proposes a move on element iterate(idx), and determines
+ * it that move is acceptable or not according to the Metropolis criterion.
+ * After that it increments idx so next call will make a move on next
+ * parameters. When all elements of the state has been moved (a sweep), it
+ * resets idx and increments sweepCounter. When sweepCounter reaches
+ * moveCtrlSweep, it performs moveControl and resets sweepCounter.
+ */
+template<
+    typename FunctionType,
+    typename MoveDistributionType,
+    typename CoolingScheduleType
+>
+void SA<FunctionType, MoveDistributionType, CoolingScheduleType>::GenerateMove(
+    arma::mat& iterate)
+{
+  double prevEnergy = energy;
+  double prevValue = iterate(idx);
+  double move = moveDistribution(moveSize(idx));
+  iterate(idx) += move;
+  energy = function.Evaluate(iterate);
+  // According to Metropolis criterion, accept the move with probability
+  // min{1, exp(-(E_new - E_old) / T)}.
+  double xi = math::Random();
+  double delta = energy - prevEnergy;
+  double criterion = std::exp(-delta / T);
+  if (delta <= 0. || criterion > xi)
+  {
+    accept(idx) += 1.;
+  }
+  else // Reject the move; restore previous state.
+  {
+    iterate(idx) = prevValue;
+    energy = prevEnergy;
+  }
+
+  ++idx;
+  if (idx == nVars) // Finished with a sweep.
+  {
+    idx = 0;
+    ++sweepCounter;
+  }
+
+  if (sweepCounter == moveCtrlSweep) // Do MoveControl().
+  {
+    MoveControl(moveCtrlSweep);
+    sweepCounter = 0;
+  }
+}
+
+/*
+ * MoveControl() uses a proportional feedback control to determine the size
+ * parameter to pass to the move generation distribution. The target of such
+ * move control is to make the acceptance ratio, accept/nMoves, be as close to
+ * 0.44 as possible. Generally speaking, the larger the move size is, the larger
+ * the function value change of the move will be, and less likely such move will
+ * be accepted by the Metropolis criterion. Thus, the move size is controlled by
+ *
+ * log(moveSize) = log(moveSize) + gain * (accept/nMoves - target)
+ *
+ * For more theory and the mysterious 0.44 value, see Jimmy K.-C. Lam and
+ * Jean-Marc Delosme. `An efficient simulated annealing schedule: derivation'.
+ * Technical Report 8816, Yale University, 1988
+ */
+template<
+    typename FunctionType,
+    typename MoveDistributionType,
+    typename CoolingScheduleType
+>
+void SA<FunctionType, MoveDistributionType, CoolingScheduleType>::MoveControl(
+    size_t nMoves)
+{
+  arma::mat target;
+  target.copy_size(accept);
+  target.fill(0.44);
+  moveSize = arma::log(moveSize);
+  moveSize += gain * (accept / (double) nMoves - target);
+  moveSize = arma::exp(moveSize);
+
+  // To avoid the use of element-wise arma::min(), which is only available in
+  // Armadillo after v3.930, we use a for loop here instead.
+  for (size_t i = 0; i < nVars; ++i)
+    moveSize(i) = (moveSize(i) > maxMove(i)) ? maxMove(i) : moveSize(i);
+
+  accept.zeros();
+}
+
+template<
+    typename FunctionType,
+    typename MoveDistributionType,
+    typename CoolingScheduleType
+>
+std::string SA<FunctionType, MoveDistributionType, CoolingScheduleType>::
+ToString() const
+{
+  std::ostringstream convert;
+  convert << "SA [" << this << "]" << std::endl;
+  convert << "  Function:" << std::endl;
+  convert << util::Indent(function.ToString(), 2);
+  convert << "  Move Distribution:" << std::endl;
+  convert << util::Indent(moveDistribution.ToString(), 2);
+  convert << "  Cooling Schedule:" << std::endl;
+  convert << util::Indent(coolingSchedule.ToString(), 2);
+  convert << "  Temperature: " << T << std::endl;
+  convert << "  Initial moves: " << initMoves << std::endl;
+  convert << "  Sweeps per move control: " << moveCtrlSweep << std::endl;
+  convert << "  Tolerance: " << tolerance << std::endl;
+  convert << "  Maximum sweeps below tolerance: " << maxToleranceSweep
+      << std::endl;
+  convert << "  Move control gain: " << gain << std::endl;
+  convert << "  Maximum iterations: " << maxIterations << std::endl;
+  return convert.str();
+}
+
+}; // namespace optimization
+}; // namespace mlpack
+
+#endif
diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt
index c49b70f..3175331 100644
--- a/src/mlpack/tests/CMakeLists.txt
+++ b/src/mlpack/tests/CMakeLists.txt
@@ -39,6 +39,7 @@ add_executable(mlpack_test
   radical_test.cpp
   range_search_test.cpp
   rectangle_tree_test.cpp
+  sa_test.cpp
   save_restore_utility_test.cpp
   sgd_test.cpp
   sort_policy_test.cpp
diff --git a/src/mlpack/tests/sa_test.cpp b/src/mlpack/tests/sa_test.cpp
new file mode 100644
index 0000000..97304b9
--- /dev/null
+++ b/src/mlpack/tests/sa_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * @file sa_test.cpp
+ * @auther Zhihao Lou
+ *
+ * Test file for SA (simulated annealing).
+ */
+#include <mlpack/core.hpp>
+#include <mlpack/core/optimizers/sa/sa.hpp>
+#include <mlpack/core/optimizers/sa/exponential_schedule.hpp>
+#include <mlpack/core/optimizers/sa/laplace_distribution.hpp>
+#include <mlpack/core/optimizers/lbfgs/test_functions.hpp>
+
+#include <mlpack/core/metrics/ip_metric.hpp>
+#include <mlpack/core/metrics/lmetric.hpp>
+#include <mlpack/core/metrics/mahalanobis_distance.hpp>
+
+#include <boost/test/unit_test.hpp>
+#include "old_boost_test_definitions.hpp"
+
+using namespace std;
+using namespace arma;
+using namespace mlpack;
+using namespace mlpack::optimization;
+using namespace mlpack::optimization::test;
+using namespace mlpack::metric;
+
+BOOST_AUTO_TEST_SUITE(SATest);
+
+BOOST_AUTO_TEST_CASE(GeneralizedRosenbrockTest)
+{
+  size_t dim = 50;
+  GeneralizedRosenbrockFunction f(dim);
+
+  LaplaceDistribution moveDist;
+  ExponentialSchedule schedule(1e-5);
+  SA<GeneralizedRosenbrockFunction, LaplaceDistribution, ExponentialSchedule> 
+      sa(f, moveDist, schedule, 1000.,1000, 100, 1e-9, 3, 20, 0.3, 0.3, 10000000);
+  arma::mat coordinates = f.GetInitialPoint();
+  double result = sa.Optimize(coordinates);
+
+  BOOST_REQUIRE_SMALL(result, 1e-6);
+  for (size_t j = 0; j < dim; ++j)
+      BOOST_REQUIRE_CLOSE(coordinates[j], (double) 1.0, 1e-2);
+}
+
+BOOST_AUTO_TEST_SUITE_END();

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mlpack.git



More information about the debian-science-commits mailing list