From 056e7400278cd81bf93aee67c6a751a0e9ddbbfc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Nov 2022 18:25:39 +0100 Subject: [PATCH] Better handling of nonlinear constraints in nlopt wrapper --- src/libslic3r/Optimize/NLoptOptimizer.hpp | 147 ++++++++++++++++------ 1 file changed, 106 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp index 46a0d0648..900728804 100644 --- a/src/libslic3r/Optimize/NLoptOptimizer.hpp +++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp @@ -74,18 +74,28 @@ Fn for_each_argument(Fn &&fn, Args&&...args) return fn; } -template -Fn for_each_in_tuple(Fn fn, const std::tuple &tup) +// Call fn on each element of the input tuple tup. +template +Fn for_each_in_tuple(Fn fn, Tup &&tup) { - auto arg = std::tuple_cat(std::make_tuple(fn), tup); - auto mpfn = [](auto fn, auto...pack) { - return for_each_argument(fn, pack...); + auto mpfn = [&fn](auto&...pack) { + for_each_argument(fn, pack...); }; - std::apply(mpfn, arg); + + std::apply(mpfn, tup); return fn; } +// Wrap each element of the tuple tup into a wrapper class W and return +// a new tuple with each element being of type W where T_i is the type of +// i-th element of tup. +template class W, class...Args> +auto wrap_tup(const std::tuple &tup) +{ + return std::tuple...>(tup); +} + // Optimizers based on NLopt. template class NLoptOpt> { protected: @@ -94,32 +104,38 @@ protected: static constexpr double ConstraintEps = 1e-6; - template using TOptData = - std::tuple*, NLoptOpt*, nlopt_opt>; + template struct OptData { + Fn fn; + NLoptOpt *self = nullptr; + nlopt_opt opt_raw = nullptr; + + OptData(const Fn &f): fn{f} {} + + OptData(const Fn &f, NLoptOpt *s, nlopt_opt nlopt_raw) + : fn{f}, self{s}, opt_raw{nlopt_raw} {} + }; template static double optfunc(unsigned n, const double *params, - double *gradient, - void *data) + double *gradient, void *data) { assert(n == N); - auto tdata = static_cast*>(data); + auto tdata = static_cast*>(data); - if (std::get<1>(*tdata)->m_stopcr.stop_condition()) - nlopt_force_stop(std::get<2>(*tdata)); + if (tdata->self->m_stopcr.stop_condition()) + nlopt_force_stop(tdata->opt_raw); - auto fnptr = std::get<0>(*tdata); auto funval = to_arr(params); double scoreval = 0.; - using RetT = decltype((*fnptr)(funval)); + using RetT = decltype(tdata->fn(funval)); if constexpr (std::is_convertible_v>) { - ScoreGradient score = (*fnptr)(funval); + ScoreGradient score = tdata->fn(funval); for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i]; scoreval = score.score; } else { - scoreval = (*fnptr)(funval); + scoreval = tdata->fn(funval); } return scoreval; @@ -127,17 +143,14 @@ protected: template static double constrain_func(unsigned n, const double *params, - double *gradient, - void *data) + double *gradient, void *data) { assert(n == N); - auto tdata = static_cast*>(data); - - auto &fnptr = std::get<0>(*tdata); + auto tdata = static_cast*>(data); auto funval = to_arr(params); - return (*fnptr)(funval); + return tdata->fn(funval); } template @@ -173,22 +186,27 @@ protected: { Result r; - TOptData data = std::make_tuple(&fn, this, nl.ptr); + OptData data {fn, this, nl.ptr}; - auto do_for_each_eq = [this, &nl](auto &&arg) { - auto data = std::make_tuple(&arg, this, nl.ptr); - using F = std::remove_cv_t; - nlopt_add_equality_constraint (nl.ptr, constrain_func, &data, ConstraintEps); + auto do_for_each_eq = [this, &nl](auto &arg) { + arg.self = this; + arg.opt_raw = nl.ptr; + using F = decltype(arg.fn); + nlopt_add_equality_constraint (nl.ptr, constrain_func, &arg, ConstraintEps); }; - auto do_for_each_ineq = [this, &nl](auto &&arg) { - auto data = std::make_tuple(&arg, this, nl.ptr); - using F = std::remove_cv_t; - nlopt_add_inequality_constraint (nl.ptr, constrain_func, &data, ConstraintEps); + auto do_for_each_ineq = [this, &nl](auto &arg) { + arg.self = this; + arg.opt_raw = nl.ptr; + using F = decltype(arg.fn); + nlopt_add_inequality_constraint (nl.ptr, constrain_func, &arg, ConstraintEps); }; - for_each_in_tuple(do_for_each_eq, equalities); - for_each_in_tuple(do_for_each_ineq, inequalities); + auto eq_data = wrap_tup(equalities); + for_each_in_tuple(do_for_each_eq, eq_data); + + auto ineq_data = wrap_tup(inequalities); + for_each_in_tuple(do_for_each_ineq, ineq_data); switch(m_dir) { case OptDir::MIN: @@ -260,8 +278,19 @@ public: const StopCriteria &get_loc_criteria() const noexcept { return m_loc_stopcr; } }; +template struct AlgFeatures_ { + static constexpr bool SupportsInequalities = false; + static constexpr bool SupportsEqualities = false; +}; + } // namespace detail; +template constexpr bool SupportsEqualities = + detail::AlgFeatures_>::SupportsEqualities; + +template constexpr bool SupportsInequalities = + detail::AlgFeatures_>::SupportsInequalities; + // Optimizers based on NLopt. template class Optimizer> { detail::NLoptOpt m_opt; @@ -278,6 +307,14 @@ public: const std::tuple &eq_constraints = {}, const std::tuple &ineq_constraint = {}) { + static_assert(std::tuple_size_v> == 0 + || SupportsEqualities, + "Equality constraints are not supported."); + + static_assert(std::tuple_size_v> == 0 + || SupportsInequalities, + "Inequality constraints are not supported."); + return m_opt.optimize(std::forward(func), initvals, bounds, eq_constraints, ineq_constraint); @@ -299,13 +336,41 @@ public: }; // Predefinded NLopt algorithms -using AlgNLoptGenetic = detail::NLoptAlgComb; -using AlgNLoptSubplex = detail::NLoptAlg; -using AlgNLoptSimplex = detail::NLoptAlg; -using AlgNLoptCobyla = detail::NLoptAlg; -using AlgNLoptDIRECT = detail::NLoptAlg; -using AlgNLoptISRES = detail::NLoptAlg; -using AlgNLoptMLSL = detail::NLoptAlgComb; +using AlgNLoptGenetic = detail::NLoptAlgComb; +using AlgNLoptSubplex = detail::NLoptAlg; +using AlgNLoptSimplex = detail::NLoptAlg; +using AlgNLoptCobyla = detail::NLoptAlg; +using AlgNLoptDIRECT = detail::NLoptAlg; +using AlgNLoptORIG_DIRECT = detail::NLoptAlg; +using AlgNLoptISRES = detail::NLoptAlg; +using AlgNLoptAGS = detail::NLoptAlg; + +using AlgNLoptMLSL = detail::NLoptAlgComb; +using AlgNLoptMLSL_Cobyla = detail::NLoptAlgComb; + +namespace detail { + +template<> struct AlgFeatures_ { + static constexpr bool SupportsInequalities = true; + static constexpr bool SupportsEqualities = true; +}; + +template<> struct AlgFeatures_ { + static constexpr bool SupportsInequalities = true; + static constexpr bool SupportsEqualities = false; +}; + +template<> struct AlgFeatures_ { + static constexpr bool SupportsInequalities = true; + static constexpr bool SupportsEqualities = false; +}; + +template<> struct AlgFeatures_ { + static constexpr bool SupportsInequalities = true; + static constexpr bool SupportsEqualities = true; +}; + +} // namespace detail }} // namespace Slic3r::opt