// Ceres Solver - A fast non-linear least squares minimizer
// Copyright 2012 Google Inc. All rights reserved.
// http://code.google.com/p/ceres-solver/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. 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 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.
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
//
// Interface for and implementation of various Line search algorithms.

#ifndef CERES_INTERNAL_LINE_SEARCH_H_
#define CERES_INTERNAL_LINE_SEARCH_H_

#ifndef CERES_NO_LINE_SEARCH_MINIMIZER

#include <glog/logging.h>
#include <vector>
#include "ceres/internal/eigen.h"
#include "ceres/internal/port.h"

namespace ceres {
namespace internal {

class Evaluator;

// Line search is another name for a one dimensional optimization
// algorithm. The name "line search" comes from the fact one
// dimensional optimization problems that arise as subproblems of
// general multidimensional optimization problems.
//
// While finding the exact minimum of a one dimensionl function is
// hard, instances of LineSearch find a point that satisfies a
// sufficient decrease condition. Depending on the particular
// condition used, we get a variety of different line search
// algorithms, e.g., Armijo, Wolfe etc.
class LineSearch {
 public:
  class Function;

  struct Options {
    Options()
        : interpolation_degree(1),
          use_higher_degree_interpolation_when_possible(false),
          sufficient_decrease(1e-4),
          min_relative_step_size_change(1e-3),
          max_relative_step_size_change(0.6),
          step_size_threshold(1e-9),
          function(NULL) {}

    // TODO(sameeragarwal): Replace this with enums which are common
    // across various line searches.
    //
    // Degree of the polynomial used to approximate the objective
    // function. Valid values are {0, 1, 2}.
    //
    // For Armijo line search
    //
    // 0: Bisection based backtracking search.
    // 1: Quadratic interpolation.
    // 2: Cubic interpolation.
    int interpolation_degree;

    // Usually its possible to increase the degree of the
    // interpolation polynomial by storing and using an extra point.
    bool use_higher_degree_interpolation_when_possible;

    // Armijo line search parameters.

    // Solving the line search problem exactly is computationally
    // prohibitive. Fortunately, line search based optimization
    // algorithms can still guarantee convergence if instead of an
    // exact solution, the line search algorithm returns a solution
    // which decreases the value of the objective function
    // sufficiently. More precisely, we are looking for a step_size
    // s.t.
    //
    //  f(step_size) <= f(0) + sufficient_decrease * f'(0) * step_size
    double sufficient_decrease;

    // In each iteration of the Armijo line search,
    //
    // new_step_size >= min_relative_step_size_change * step_size
    double min_relative_step_size_change;

    // In each iteration of the Armijo line search,
    //
    // new_step_size <= max_relative_step_size_change * step_size
    double max_relative_step_size_change;

    // If during the line search, the step_size falls below this
    // value, it is truncated to zero.
    double step_size_threshold;

    // The one dimensional function that the line search algorithm
    // minimizes.
    Function* function;
  };

  // An object used by the line search to access the function values
  // and gradient of the one dimensional function being optimized.
  //
  // In practice, this object will provide access to the objective
  // function value and the directional derivative of the underlying
  // optimization problem along a specific search direction.
  //
  // See LineSearchFunction for an example implementation.
  class Function {
   public:
    virtual ~Function() {}
    // Evaluate the line search objective
    //
    //   f(x) = p(position + x * direction)
    //
    // Where, p is the objective function of the general optimization
    // problem.
    //
    // g is the gradient f'(x) at x.
    //
    // f must not be null. The gradient is computed only if g is not null.
    virtual bool Evaluate(double x, double* f, double* g) = 0;
  };

  // Result of the line search.
  struct Summary {
    Summary()
        : success(false),
          optimal_step_size(0.0),
          num_evaluations(0) {}

    bool success;
    double optimal_step_size;
    int num_evaluations;
  };

  virtual ~LineSearch() {}

  // Perform the line search.
  //
  // initial_step_size must be a positive number.
  //
  // initial_cost and initial_gradient are the values and gradient of
  // the function at zero.
  // summary must not be null and will contain the result of the line
  // search.
  //
  // Summary::success is true if a non-zero step size is found.
  virtual void Search(const LineSearch::Options& options,
                      double initial_step_size,
                      double initial_cost,
                      double initial_gradient,
                      Summary* summary) = 0;
};

class LineSearchFunction : public LineSearch::Function {
 public:
  explicit LineSearchFunction(Evaluator* evaluator);
  virtual ~LineSearchFunction() {}
  void Init(const Vector& position, const Vector& direction);
  virtual bool Evaluate(const double x, double* f, double* g);

 private:
  Evaluator* evaluator_;
  Vector position_;
  Vector direction_;

  // evaluation_point = Evaluator::Plus(position_,  x * direction_);
  Vector evaluation_point_;

  // scaled_direction = x * direction_;
  Vector scaled_direction_;
  Vector gradient_;
};

// Backtracking and interpolation based Armijo line search. This
// implementation is based on the Armijo line search that ships in the
// minFunc package by Mark Schmidt.
//
// For more details: http://www.di.ens.fr/~mschmidt/Software/minFunc.html
class ArmijoLineSearch : public LineSearch {
 public:
  virtual ~ArmijoLineSearch() {}
  virtual void Search(const LineSearch::Options& options,
                      double initial_step_size,
                      double initial_cost,
                      double initial_gradient,
                      Summary* summary);
};

}  // namespace internal
}  // namespace ceres

#endif  // CERES_NO_LINE_SEARCH_MINIMIZER
#endif  // CERES_INTERNAL_LINE_SEARCH_H_
