diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp index 900728804..f360c6753 100644 --- a/src/libslic3r/Optimize/NLoptOptimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -40,30 +40,70 @@ struct IsNLoptAlg> { static const constexpr bool value = true; }; +// NLopt can wrap any of its algorithms to use the augmented lagrangian method +// for deriving an object function from all equality and inequality constraints +// This way one can use algorithms that do not support these constraints natively +template struct NLoptAUGLAG {}; + +template +struct IsNLoptAlg>> { + static const constexpr bool value = true; +}; + +template struct IsNLoptAlg>> { + static const constexpr bool value = true; +}; + template using NLoptOnly = std::enable_if_t::value, T>; +template struct GetNLoptAlg_ { + static constexpr nlopt_algorithm Local = NLOPT_NUM_ALGORITHMS; + static constexpr nlopt_algorithm Global = NLOPT_NUM_ALGORITHMS; + static constexpr bool IsAUGLAG = false; +}; + +template struct GetNLoptAlg_> { + static constexpr nlopt_algorithm Local = NLOPT_NUM_ALGORITHMS; + static constexpr nlopt_algorithm Global = a; + static constexpr bool IsAUGLAG = false; +}; + +template +struct GetNLoptAlg_> { + static constexpr nlopt_algorithm Local = l; + static constexpr nlopt_algorithm Global = g; + static constexpr bool IsAUGLAG = false; +}; + +template constexpr nlopt_algorithm GetNLoptAlg_Global = GetNLoptAlg_>::Global; +template constexpr nlopt_algorithm GetNLoptAlg_Local = GetNLoptAlg_>::Local; +template constexpr bool IsAUGLAG = GetNLoptAlg_>::IsAUGLAG; + +template struct GetNLoptAlg_> { + static constexpr nlopt_algorithm Local = GetNLoptAlg_Local; + static constexpr nlopt_algorithm Global = GetNLoptAlg_Global; + static constexpr bool IsAUGLAG = true; +}; enum class OptDir { MIN, MAX }; // Where to optimize -struct NLopt { // Helper RAII class for nlopt_opt +struct NLoptRAII { // Helper RAII class for nlopt_opt nlopt_opt ptr = nullptr; - template explicit NLopt(A&&...a) + template explicit NLoptRAII(A&&...a) { ptr = nlopt_create(std::forward(a)...); } - NLopt(const NLopt&) = delete; - NLopt(NLopt&&) = delete; - NLopt& operator=(const NLopt&) = delete; - NLopt& operator=(NLopt&&) = delete; + NLoptRAII(const NLoptRAII&) = delete; + NLoptRAII(NLoptRAII&&) = delete; + NLoptRAII& operator=(const NLoptRAII&) = delete; + NLoptRAII& operator=(NLoptRAII&&) = delete; - ~NLopt() { nlopt_destroy(ptr); } + ~NLoptRAII() { nlopt_destroy(ptr); } }; -template class NLoptOpt {}; - // Map a generic function to each argument following the mapping function template Fn for_each_argument(Fn &&fn, Args&&...args) @@ -96,10 +136,10 @@ auto wrap_tup(const std::tuple &tup) return std::tuple...>(tup); } -// Optimizers based on NLopt. -template class NLoptOpt> { -protected: +template> +class NLoptOpt { StopCriteria m_stopcr; + StopCriteria m_loc_stopcr; OptDir m_dir = OptDir::MIN; static constexpr double ConstraintEps = 1e-6; @@ -154,7 +194,7 @@ protected: } template - static void set_up(NLopt &nl, + static void set_up(NLoptRAII &nl, const Bounds &bounds, const StopCriteria &stopcr) { @@ -180,7 +220,7 @@ protected: } template - Result optimize(NLopt &nl, Fn &&fn, const Input &initvals, + Result optimize(NLoptRAII &nl, Fn &&fn, const Input &initvals, const std::tuple &equalities, const std::tuple &inequalities) { @@ -221,36 +261,6 @@ protected: return r; } -public: - - template - Result optimize(Func&& func, - const Input &initvals, - const Bounds& bounds, - const std::tuple &equalities, - const std::tuple &inequalities) - { - NLopt nl{alg, N}; - set_up(nl, bounds, m_stopcr); - - return optimize(nl, std::forward(func), initvals, - equalities, inequalities); - } - - explicit NLoptOpt(const StopCriteria &stopcr = {}) : m_stopcr(stopcr) {} - - void set_criteria(const StopCriteria &cr) { m_stopcr = cr; } - const StopCriteria &get_criteria() const noexcept { return m_stopcr; } - void set_dir(OptDir dir) noexcept { m_dir = dir; } - - void seed(long s) { nlopt_srand(s); } -}; - -template -class NLoptOpt>: public NLoptOpt> -{ - using Base = NLoptOpt>; - StopCriteria m_loc_stopcr; public: template @@ -260,22 +270,59 @@ public: const std::tuple &equalities, const std::tuple &inequalities) { - NLopt nl_glob{glob, N}, nl_loc{loc, N}; + if constexpr (IsAUGLAG) { + NLoptRAII nl_wrap{NLOPT_AUGLAG, N}; + set_up(nl_wrap, bounds, get_criteria()); - Base::set_up(nl_glob, bounds, Base::get_criteria()); - Base::set_up(nl_loc, bounds, m_loc_stopcr); - nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr); + NLoptRAII nl_glob{GetNLoptAlg_Global, N}; + set_up(nl_glob, bounds, get_criteria()); + nlopt_set_local_optimizer(nl_wrap.ptr, nl_glob.ptr); - return Base::optimize(nl_glob, std::forward(f), initvals, - equalities, inequalities); + if constexpr (GetNLoptAlg_Local < NLOPT_NUM_ALGORITHMS) { + NLoptRAII nl_loc{GetNLoptAlg_Local, N}; + set_up(nl_loc, bounds, m_loc_stopcr); + nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr); + + return optimize(nl_wrap, std::forward(f), initvals, + equalities, inequalities); + } else { + return optimize(nl_wrap, std::forward(f), initvals, + equalities, inequalities); + } + } else { + NLoptRAII nl_glob{GetNLoptAlg_Global, N}; + set_up(nl_glob, bounds, get_criteria()); + + if constexpr (GetNLoptAlg_Local < NLOPT_NUM_ALGORITHMS) { + NLoptRAII nl_loc{GetNLoptAlg_Local, N}; + set_up(nl_loc, bounds, m_loc_stopcr); + nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr); + + return optimize(nl_glob, std::forward(f), initvals, + equalities, inequalities); + } else { + return optimize(nl_glob, std::forward(f), initvals, + equalities, inequalities); + } + } + + assert(false); + + return {}; } - explicit NLoptOpt(StopCriteria stopcr = {}) - : Base{stopcr}, m_loc_stopcr{stopcr} + explicit NLoptOpt(const StopCriteria &stopcr_glob = {}) + : m_stopcr(stopcr_glob) {} + void set_criteria(const StopCriteria &cr) { m_stopcr = cr; } + const StopCriteria &get_criteria() const noexcept { return m_stopcr; } + void set_loc_criteria(const StopCriteria &cr) { m_loc_stopcr = cr; } const StopCriteria &get_loc_criteria() const noexcept { return m_loc_stopcr; } + + void set_dir(OptDir dir) noexcept { m_dir = dir; } + void seed(long s) { nlopt_srand(s); } }; template struct AlgFeatures_ { @@ -345,8 +392,12 @@ using AlgNLoptORIG_DIRECT = detail::NLoptAlg; using AlgNLoptISRES = detail::NLoptAlg; using AlgNLoptAGS = detail::NLoptAlg; -using AlgNLoptMLSL = detail::NLoptAlgComb; -using AlgNLoptMLSL_Cobyla = detail::NLoptAlgComb; +using AlgNLoptMLSL = detail::NLoptAlgComb; +using AlgNLoptMLSL_Cobyla = detail::NLoptAlgComb; +using AlgNLoptGenetic_Subplx = detail::NLoptAlgComb; + +// To craft auglag algorithms (constraint support through object function transformation) +using detail::NLoptAUGLAG; namespace detail { @@ -370,6 +421,11 @@ template<> struct AlgFeatures_ { static constexpr bool SupportsEqualities = true; }; +template struct AlgFeatures_> { + static constexpr bool SupportsInequalities = true; + static constexpr bool SupportsEqualities = true; +}; + } // namespace detail }} // namespace Slic3r::opt