#ifndef BRUTEFORCEOPTIMIZER_HPP #define BRUTEFORCEOPTIMIZER_HPP #include namespace Slic3r { namespace opt { namespace detail { // Implementing a bruteforce optimizer // Return the number of iterations needed to reach a specific grid position (idx) template long num_iter(const std::array &idx, size_t gridsz) { long ret = 0; for (size_t i = 0; i < N; ++i) ret += idx[i] * std::pow(gridsz, i); return ret; } // Implementation of a grid search where the search interval is sampled in // equidistant points for each dimension. Grid size determines the number of // samples for one dimension so the number of function calls is gridsize ^ dimension. struct AlgBurteForce { bool to_min; StopCriteria stc; size_t gridsz; AlgBurteForce(const StopCriteria &cr, size_t gs): stc{cr}, gridsz{gs} {} // This function is called recursively for each dimension and generates // the grid values for the particular dimension. If D is less than zero, // the object function input values are generated for each dimension and it // can be evaluated. The current best score is compared with the newly // returned score and changed appropriately. template bool run(std::array &idx, Result &result, const Bounds &bounds, Fn &&fn, Cmp &&cmp) { if (stc.stop_condition()) return false; if constexpr (D < 0) { // Let's evaluate fn Input inp; auto max_iter = stc.max_iterations(); if (max_iter && num_iter(idx, gridsz) >= max_iter) return false; for (size_t d = 0; d < N; ++d) { const Bound &b = bounds[d]; double step = (b.max() - b.min()) / (gridsz - 1); inp[d] = b.min() + idx[d] * step; } auto score = fn(inp); if (cmp(score, result.score)) { // Change current score to the new double absdiff = std::abs(score - result.score); result.score = score; result.optimum = inp; // Check if the required precision is reached. if (absdiff < stc.abs_score_diff() || absdiff < stc.rel_score_diff() * std::abs(score)) return false; } } else { for (size_t i = 0; i < gridsz; ++i) { idx[D] = i; // Mark the current grid position and dig down if (!run(idx, result, bounds, std::forward(fn), std::forward(cmp))) return false; } } return true; } template Result optimize(Fn&& fn, const Input &/*initvals*/, const Bounds& bounds) { std::array idx = {}; Result result; if (to_min) { result.score = std::numeric_limits::max(); run(idx, result, bounds, std::forward(fn), std::less{}); } else { result.score = std::numeric_limits::lowest(); run(idx, result, bounds, std::forward(fn), std::greater{}); } return result; } }; } // namespace detail using AlgBruteForce = detail::AlgBurteForce; template<> class Optimizer { AlgBruteForce m_alg; public: Optimizer(const StopCriteria &cr = {}, size_t gridsz = 100) : m_alg{cr, gridsz} {} Optimizer& to_max() { m_alg.to_min = false; return *this; } Optimizer& to_min() { m_alg.to_min = true; return *this; } template Result optimize(Func&& func, const Input &initvals, const Bounds& bounds) { return m_alg.optimize(std::forward(func), initvals, bounds); } Optimizer &set_criteria(const StopCriteria &cr) { m_alg.stc = cr; return *this; } const StopCriteria &get_criteria() const { return m_alg.stc; } }; }} // namespace Slic3r::opt #endif // BRUTEFORCEOPTIMIZER_HPP