#ifndef OPTIMIZER_HPP #define OPTIMIZER_HPP #include #include #include #include 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 struct limits { inline static T min() { return std::numeric_limits::min(); } inline static T max() { return std::numeric_limits::max(); } }; template struct limits::has_infinity, void>> { inline static T min() { return -std::numeric_limits::infinity(); } inline static T max() { return std::numeric_limits::infinity(); } }; /// An interval of possible input values for optimization template class Bound { T min_; T max_; public: Bound(const T& min = limits::min(), const T& max = limits::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 inline Bound bound(const T& min, const T& max) { return Bound(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 using Input = tuple; template inline tuple 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 struct Result { ResultCodes resultcode; tuple 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 stop_condition = [] { return false; }; /// The max allowed number of iterations. unsigned max_iterations = 0; }; /** * \brief The Optimizer base class with CRTP pattern. */ template 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 structure. * An example call would be: * auto result = opt.optimize_min( * [](tuple 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 inline Result optimize_min(Func&& objectfunction, Input initvals, Bound... bounds) { dir_ = OptDir::MIN; return static_cast(this)->template optimize( forward(objectfunction), initvals, bounds... ); } template inline Result optimize_min(Func&& objectfunction, Input initvals) { dir_ = OptDir::MIN; return static_cast(this)->template optimize( forward(objectfunction), initvals, Bound()... ); } template inline Result optimize_min(Func&& objectfunction) { dir_ = OptDir::MIN; return static_cast(this)->template optimize( forward(objectfunction), Input(), Bound()... ); } /// Same as optimize_min but optimizes for maximum function value. template inline Result optimize_max(Func&& objectfunction, Input initvals, Bound... bounds) { dir_ = OptDir::MAX; return static_cast(this)->template optimize( forward(objectfunction), initvals, bounds... ); } template inline Result optimize_max(Func&& objectfunction, Input initvals) { dir_ = OptDir::MAX; return static_cast(this)->template optimize( forward(objectfunction), initvals, Bound()... ); } template inline Result optimize_max(Func&& objectfunction) { dir_ = OptDir::MAX; return static_cast(this)->template optimize( forward(objectfunction), Input(), Bound()... ); } }; // Just to be able to instantiate an unimplemented method and generate compile // error. template class DummyOptimizer : public Optimizer> { friend class Optimizer>; public: DummyOptimizer() { static_assert(always_false::value, "Optimizer unimplemented!"); } DummyOptimizer(const StopCriteria&) { static_assert(always_false::value, "Optimizer unimplemented!"); } template Result optimize(Func&& /*func*/, tuple /*initvals*/, Bound... /*args*/) { return Result(); } }; // Specializing this struct will tell what kind of optimizer to generate for // a given method template struct OptimizerSubclass { using Type = DummyOptimizer<>; }; /// Optimizer type based on the method provided in parameter m. template using TOptimizer = typename OptimizerSubclass::Type; /// Global optimizer with an explicitly specified local method. template inline TOptimizer GlobalOptimizer(Method, const StopCriteria& scr = {}) { // Need to be specialized in order to do anything useful. return TOptimizer(scr); } } } #endif // OPTIMIZER_HPP