288 lines
8.1 KiB
C++

// METSlib source file - termination-criteria.hh -*- C++ -*-
/*
* Software License Agreement (BSD License)
*
* Copyright (c) 2006-2012, Mirko Maischberger <mirko.maischberger@gmail.com>
* All rights reserved.
*
* 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.
*
* 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.
*
*/
#ifndef METS_TERMINATION_CRITERIA_HH_
#define METS_TERMINATION_CRITERIA_HH_
namespace mets {
/// @defgroup common Termination criteria
/// @{
/// @brief Function object expressing a termination criteria
///
/// The search loop ends when the termination criteria is met.
class termination_criteria_chain
{
public:
/// @brief Constructor.
///
/// @param next Optional next criterium in the chain.
explicit
termination_criteria_chain(termination_criteria_chain* next = 0)
: next_m(next)
{ }
/// purposely not implemented (see Effective C++)
termination_criteria_chain(const termination_criteria_chain&);
termination_criteria_chain& operator=(const termination_criteria_chain&);
/// @brief Virtual destructor.
virtual
~termination_criteria_chain()
{ }
/// @brief Alternate function that decides if we shoud terminate the
/// search process
///
/// (chain of responsibility)
///
/// @param fs The current working solution.
/// @return True if we shoud terminate
virtual bool
operator()(const feasible_solution& fs);
/// @brief Reset the criterion to its initial state.
///
/// (chain of responsibility)
///
virtual void reset();
protected:
termination_criteria_chain* next_m;
};
/// @brief Termination criteria based on the number of iterations.
///
/// This termination criteria terminates the tabu-search
/// after a fixed number of itarations.
class iteration_termination_criteria
: public termination_criteria_chain
{
public:
/// @brief Ctor. Max is the number of iterations to do.
iteration_termination_criteria(int max)
: termination_criteria_chain(),
max_m(max),
iterations_m(max) {}
explicit
iteration_termination_criteria
(termination_criteria_chain* next, int max)
: termination_criteria_chain(next),
max_m(max),
iterations_m(max) {}
bool
operator()(const feasible_solution& fs)
{
if (iterations_m <= 0)
return true;
--iterations_m;
return termination_criteria_chain::operator()(fs);
}
void
reset()
{ iterations_m = max_m; termination_criteria_chain::reset(); }
protected:
int max_m;
int iterations_m;
};
/// @brief Termination criteria based on the number
/// of iterations without an improvement.
///
/// This termination criteria terminates the tabu-search
/// after "max" number of itarations without a single
/// global improvement.
class noimprove_termination_criteria
: public termination_criteria_chain
{
public:
noimprove_termination_criteria(int max, gol_type epsilon = 1e-7)
: termination_criteria_chain(),
best_cost_m(std::numeric_limits<gol_type>::max()),
max_noimprove_m(max),
iterations_left_m(max),
total_iterations_m(0),
resets_m(0),
second_guess_m(0),
epsilon_m(epsilon)
{}
noimprove_termination_criteria
(termination_criteria_chain* next, int max, gol_type epsilon = 1e-7)
: termination_criteria_chain(next),
best_cost_m(std::numeric_limits<gol_type>::max()),
max_noimprove_m(max),
iterations_left_m(max),
total_iterations_m(0),
resets_m(0),
second_guess_m(0),
epsilon_m(epsilon)
{ }
bool
operator()(const feasible_solution& fs);
void reset()
{ iterations_left_m = max_noimprove_m;
second_guess_m = total_iterations_m = resets_m = 0;
best_cost_m = std::numeric_limits<gol_type>::max();
termination_criteria_chain::reset();
}
int second_guess() const { return second_guess_m; }
int iteration() const { return total_iterations_m; }
int resets() const { return resets_m; }
protected:
gol_type best_cost_m;
int max_noimprove_m;
int iterations_left_m;
int total_iterations_m;
int resets_m;
int second_guess_m;
gol_type epsilon_m;
};
/// @brief Termination criteria based on cost value
///
/// This termination criteria terminates the tabu-search
/// when a certain threshold is reached
class threshold_termination_criteria
: public termination_criteria_chain
{
public:
threshold_termination_criteria(gol_type level, gol_type epsilon = 1e-7)
: termination_criteria_chain(),
level_m(level),
epsilon_m(epsilon)
{ }
threshold_termination_criteria
(termination_criteria_chain* next, gol_type level, gol_type epsilon = 1e-7)
: termination_criteria_chain(next),
level_m(level),
epsilon_m(epsilon)
{ }
bool
operator()(const feasible_solution& fs)
{
mets::gol_type current_cost =
dynamic_cast<const evaluable_solution&>(fs).cost_function();
if(current_cost < level_m + epsilon_m)
return true;
return termination_criteria_chain::operator()(fs);
}
void reset()
{ termination_criteria_chain::reset(); }
protected:
gol_type level_m;
gol_type epsilon_m;
};
/// The mets::forever termination criterion will never terminate the
/// search.
///
/// This can be used in the mets::simulated_annealing to stop only
/// when the temperature reaches 0 or in the mets::tabu_search if we
/// want to stop for another reason (e.g. some components or
/// observer raises an exception).
///
/// The forever termination criterion cannot be chained. When
/// chained behaviour is undetermined.
class forever : public termination_criteria_chain
{
public:
forever() : termination_criteria_chain() {}
bool
operator()(const feasible_solution& /*fs*/)
{ return false; }
void reset()
{ termination_criteria_chain::reset(); }
};
/// @}
}
//________________________________________________________________________
inline bool
mets::termination_criteria_chain::operator()(const feasible_solution& fs)
{
if(next_m)
return next_m->operator()(fs);
else
return false;
}
//________________________________________________________________________
inline void
mets::termination_criteria_chain::reset()
{
if(next_m) next_m->reset();
}
//________________________________________________________________________
inline bool
mets::noimprove_termination_criteria::operator()(const feasible_solution& fs)
{
mets::gol_type current_cost =
dynamic_cast<const evaluable_solution&>(fs).cost_function();
if(current_cost < best_cost_m - epsilon_m)
{
best_cost_m = current_cost;
second_guess_m = std::max(second_guess_m,
(max_noimprove_m - iterations_left_m));
iterations_left_m = max_noimprove_m;
resets_m++;
}
if(iterations_left_m <= 0)
return true;
total_iterations_m++;
--iterations_left_m;
return termination_criteria_chain::operator()(fs);
}
#endif