BambuStudio/libnest2d/include/libnest2d/optimizer.hpp

255 lines
7.6 KiB
C++

#ifndef OPTIMIZER_HPP
#define OPTIMIZER_HPP
#include <tuple>
#include <functional>
#include <limits>
#include <libnest2d/common.hpp>
namespace libnest2d { namespace opt {
using std::forward;
using std::tuple;
using std::make_tuple;
/// A Type trait for upper and lower limit of a numeric type.
template<class T, class B = void >
struct limits {
inline static T min() { return std::numeric_limits<T>::min(); }
inline static T max() { return std::numeric_limits<T>::max(); }
};
template<class T>
struct limits<T, enable_if_t<std::numeric_limits<T>::has_infinity, void>> {
inline static T min() { return -std::numeric_limits<T>::infinity(); }
inline static T max() { return std::numeric_limits<T>::infinity(); }
};
/// An interval of possible input values for optimization
template<class T>
class Bound {
T min_;
T max_;
public:
Bound(const T& min = limits<T>::min(),
const T& max = limits<T>::max()): min_(min), max_(max) {}
inline const T min() const BP2D_NOEXCEPT { return min_; }
inline const T max() const BP2D_NOEXCEPT { return max_; }
};
/**
* Helper function to make a Bound object with its type deduced automatically.
*/
template<class T>
inline Bound<T> bound(const T& min, const T& max) { return Bound<T>(min, max); }
/**
* This is the type of an input tuple for the object function. It holds the
* values and their type in each dimension.
*/
template<class...Args> using Input = tuple<Args...>;
template<class...Args>
inline tuple<Args...> initvals(Args...args) { return make_tuple(args...); }
/**
* @brief Specific optimization methods for which a default optimizer
* implementation can be instantiated.
*/
enum class Method {
L_SIMPLEX,
L_SUBPLEX,
G_GENETIC,
G_PARTICLE_SWARM
//...
};
/**
* @brief Info about result of an optimization. These codes are exactly the same
* as the nlopt codes for convinience.
*/
enum ResultCodes {
FAILURE = -1, /* generic failure code */
INVALID_ARGS = -2,
OUT_OF_MEMORY = -3,
ROUNDOFF_LIMITED = -4,
FORCED_STOP = -5,
SUCCESS = 1, /* generic success code */
STOPVAL_REACHED = 2,
FTOL_REACHED = 3,
XTOL_REACHED = 4,
MAXEVAL_REACHED = 5,
MAXTIME_REACHED = 6
};
/**
* \brief A type to hold the complete result of the optimization.
*/
template<class...Args>
struct Result {
ResultCodes resultcode;
tuple<Args...> optimum;
double score;
};
/**
* @brief A type for specifying the stop criteria.
*/
struct StopCriteria {
/// If the absolute value difference between two scores.
double absolute_score_difference = std::nan("");
/// If the relative value difference between two scores.
double relative_score_difference = std::nan("");
/// Stop if this value or better is found.
double stop_score = std::nan("");
/// A predicate that if evaluates to true, the optimization should terminate
/// and the best result found prior to termination should be returned.
std::function<bool()> stop_condition = [] { return false; };
/// The max allowed number of iterations.
unsigned max_iterations = 0;
};
/**
* \brief The Optimizer base class with CRTP pattern.
*/
template<class Subclass>
class Optimizer {
protected:
enum class OptDir{
MIN,
MAX
} dir_;
StopCriteria stopcr_;
public:
inline explicit Optimizer(const StopCriteria& scr = {}): stopcr_(scr) {}
/**
* \brief Optimize for minimum value of the provided objectfunction.
* \param objectfunction The function that will be searched for the minimum
* return value.
* \param initvals A tuple with the initial values for the search
* \param bounds A parameter pack with the bounds for each dimension.
* \return Returns a Result<Args...> structure.
* An example call would be:
* auto result = opt.optimize_min(
* [](tuple<double> x) // object function
* {
* return std::pow(std::get<0>(x), 2);
* },
* make_tuple(-0.5), // initial value
* {-1.0, 1.0} // search space bounds
* );
*/
template<class Func, class...Args>
inline Result<Args...> optimize_min(Func&& objectfunction,
Input<Args...> initvals,
Bound<Args>... bounds)
{
dir_ = OptDir::MIN;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction), initvals, bounds... );
}
template<class Func, class...Args>
inline Result<Args...> optimize_min(Func&& objectfunction,
Input<Args...> initvals)
{
dir_ = OptDir::MIN;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction), initvals, Bound<Args>()... );
}
template<class...Args, class Func>
inline Result<Args...> optimize_min(Func&& objectfunction)
{
dir_ = OptDir::MIN;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction),
Input<Args...>(),
Bound<Args>()... );
}
/// Same as optimize_min but optimizes for maximum function value.
template<class Func, class...Args>
inline Result<Args...> optimize_max(Func&& objectfunction,
Input<Args...> initvals,
Bound<Args>... bounds)
{
dir_ = OptDir::MAX;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction), initvals, bounds... );
}
template<class Func, class...Args>
inline Result<Args...> optimize_max(Func&& objectfunction,
Input<Args...> initvals)
{
dir_ = OptDir::MAX;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction), initvals, Bound<Args>()... );
}
template<class...Args, class Func>
inline Result<Args...> optimize_max(Func&& objectfunction)
{
dir_ = OptDir::MAX;
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
forward<Func>(objectfunction),
Input<Args...>(),
Bound<Args>()... );
}
};
// Just to be able to instantiate an unimplemented method and generate compile
// error.
template<class T = void>
class DummyOptimizer : public Optimizer<DummyOptimizer<T>> {
friend class Optimizer<DummyOptimizer<T>>;
public:
DummyOptimizer() {
static_assert(always_false<T>::value, "Optimizer unimplemented!");
}
DummyOptimizer(const StopCriteria&) {
static_assert(always_false<T>::value, "Optimizer unimplemented!");
}
template<class Func, class...Args>
Result<Args...> optimize(Func&& /*func*/,
tuple<Args...> /*initvals*/,
Bound<Args>... /*args*/)
{
return Result<Args...>();
}
};
// Specializing this struct will tell what kind of optimizer to generate for
// a given method
template<Method m> struct OptimizerSubclass { using Type = DummyOptimizer<>; };
/// Optimizer type based on the method provided in parameter m.
template<Method m> using TOptimizer = typename OptimizerSubclass<m>::Type;
/// Global optimizer with an explicitly specified local method.
template<Method m>
inline TOptimizer<m> GlobalOptimizer(Method, const StopCriteria& scr = {})
{ // Need to be specialized in order to do anything useful.
return TOptimizer<m>(scr);
}
}
}
#endif // OPTIMIZER_HPP