Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/libslic3r/Optimize/NLoptOptimizer.hpp')
-rw-r--r--src/libslic3r/Optimize/NLoptOptimizer.hpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/libslic3r/Optimize/NLoptOptimizer.hpp b/src/libslic3r/Optimize/NLoptOptimizer.hpp
new file mode 100644
index 000000000..826b1632a
--- /dev/null
+++ b/src/libslic3r/Optimize/NLoptOptimizer.hpp
@@ -0,0 +1,233 @@
+#ifndef NLOPTOPTIMIZER_HPP
+#define NLOPTOPTIMIZER_HPP
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4267)
+#endif
+#include <nlopt.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include <utility>
+
+#include <libslic3r/Optimize/Optimizer.hpp>
+
+namespace Slic3r { namespace opt {
+
+namespace detail {
+
+// Helper types for NLopt algorithm selection in template contexts
+template<nlopt_algorithm alg> struct NLoptAlg {};
+
+// NLopt can combine multiple algorithms if one is global an other is a local
+// method. This is how template specializations can be informed about this fact.
+template<nlopt_algorithm gl_alg, nlopt_algorithm lc_alg = NLOPT_LN_NELDERMEAD>
+struct NLoptAlgComb {};
+
+template<class M> struct IsNLoptAlg {
+ static const constexpr bool value = false;
+};
+
+template<nlopt_algorithm a> struct IsNLoptAlg<NLoptAlg<a>> {
+ static const constexpr bool value = true;
+};
+
+template<nlopt_algorithm a1, nlopt_algorithm a2>
+struct IsNLoptAlg<NLoptAlgComb<a1, a2>> {
+ static const constexpr bool value = true;
+};
+
+template<class M, class T = void>
+using NLoptOnly = std::enable_if_t<IsNLoptAlg<M>::value, T>;
+
+
+enum class OptDir { MIN, MAX }; // Where to optimize
+
+struct NLopt { // Helper RAII class for nlopt_opt
+ nlopt_opt ptr = nullptr;
+
+ template<class...A> explicit NLopt(A&&...a)
+ {
+ ptr = nlopt_create(std::forward<A>(a)...);
+ }
+
+ NLopt(const NLopt&) = delete;
+ NLopt(NLopt&&) = delete;
+ NLopt& operator=(const NLopt&) = delete;
+ NLopt& operator=(NLopt&&) = delete;
+
+ ~NLopt() { nlopt_destroy(ptr); }
+};
+
+template<class Method> class NLoptOpt {};
+
+// Optimizers based on NLopt.
+template<nlopt_algorithm alg> class NLoptOpt<NLoptAlg<alg>> {
+protected:
+ StopCriteria m_stopcr;
+ OptDir m_dir;
+
+ template<class Fn> using TOptData =
+ std::tuple<std::remove_reference_t<Fn>*, NLoptOpt*, nlopt_opt>;
+
+ template<class Fn, size_t N>
+ static double optfunc(unsigned n, const double *params,
+ double *gradient,
+ void *data)
+ {
+ assert(n >= N);
+
+ auto tdata = static_cast<TOptData<Fn>*>(data);
+
+ if (std::get<1>(*tdata)->m_stopcr.stop_condition())
+ nlopt_force_stop(std::get<2>(*tdata));
+
+ auto fnptr = std::get<0>(*tdata);
+ auto funval = to_arr<N>(params);
+
+ double scoreval = 0.;
+ using RetT = decltype((*fnptr)(funval));
+ if constexpr (std::is_convertible_v<RetT, ScoreGradient<N>>) {
+ ScoreGradient<N> score = (*fnptr)(funval);
+ for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i];
+ scoreval = score.score;
+ } else {
+ scoreval = (*fnptr)(funval);
+ }
+
+ return scoreval;
+ }
+
+ template<size_t N>
+ void set_up(NLopt &nl, const Bounds<N>& bounds)
+ {
+ std::array<double, N> lb, ub;
+
+ for (size_t i = 0; i < N; ++i) {
+ lb[i] = bounds[i].min();
+ ub[i] = bounds[i].max();
+ }
+
+ nlopt_set_lower_bounds(nl.ptr, lb.data());
+ nlopt_set_upper_bounds(nl.ptr, ub.data());
+
+ double abs_diff = m_stopcr.abs_score_diff();
+ double rel_diff = m_stopcr.rel_score_diff();
+ double stopval = m_stopcr.stop_score();
+ if(!std::isnan(abs_diff)) nlopt_set_ftol_abs(nl.ptr, abs_diff);
+ if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
+ if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
+
+ if(this->m_stopcr.max_iterations() > 0)
+ nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations());
+ }
+
+ template<class Fn, size_t N>
+ Result<N> optimize(NLopt &nl, Fn &&fn, const Input<N> &initvals)
+ {
+ Result<N> r;
+
+ TOptData<Fn> data = std::make_tuple(&fn, this, nl.ptr);
+
+ switch(m_dir) {
+ case OptDir::MIN:
+ nlopt_set_min_objective(nl.ptr, optfunc<Fn, N>, &data); break;
+ case OptDir::MAX:
+ nlopt_set_max_objective(nl.ptr, optfunc<Fn, N>, &data); break;
+ }
+
+ r.optimum = initvals;
+ r.resultcode = nlopt_optimize(nl.ptr, r.optimum.data(), &r.score);
+
+ return r;
+ }
+
+public:
+
+ template<class Func, size_t N>
+ Result<N> optimize(Func&& func,
+ const Input<N> &initvals,
+ const Bounds<N>& bounds)
+ {
+ NLopt nl{alg, N};
+ set_up(nl, bounds);
+
+ return optimize(nl, std::forward<Func>(func), initvals);
+ }
+
+ explicit NLoptOpt(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<nlopt_algorithm glob, nlopt_algorithm loc>
+class NLoptOpt<NLoptAlgComb<glob, loc>>: public NLoptOpt<NLoptAlg<glob>>
+{
+ using Base = NLoptOpt<NLoptAlg<glob>>;
+public:
+
+ template<class Fn, size_t N>
+ Result<N> optimize(Fn&& f,
+ const Input<N> &initvals,
+ const Bounds<N>& bounds)
+ {
+ NLopt nl_glob{glob, N}, nl_loc{loc, N};
+
+ Base::set_up(nl_glob, bounds);
+ Base::set_up(nl_loc, bounds);
+ nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr);
+
+ return Base::optimize(nl_glob, std::forward<Fn>(f), initvals);
+ }
+
+ explicit NLoptOpt(StopCriteria stopcr = {}) : Base{stopcr} {}
+};
+
+} // namespace detail;
+
+// Optimizers based on NLopt.
+template<class M> class Optimizer<M, detail::NLoptOnly<M>> {
+ detail::NLoptOpt<M> m_opt;
+
+public:
+
+ Optimizer& to_max() { m_opt.set_dir(detail::OptDir::MAX); return *this; }
+ Optimizer& to_min() { m_opt.set_dir(detail::OptDir::MIN); return *this; }
+
+ template<class Func, size_t N>
+ Result<N> optimize(Func&& func,
+ const Input<N> &initvals,
+ const Bounds<N>& bounds)
+ {
+ return m_opt.optimize(std::forward<Func>(func), initvals, bounds);
+ }
+
+ explicit Optimizer(StopCriteria stopcr = {}) : m_opt(stopcr) {}
+
+ Optimizer &set_criteria(const StopCriteria &cr)
+ {
+ m_opt.set_criteria(cr); return *this;
+ }
+
+ const StopCriteria &get_criteria() const { return m_opt.get_criteria(); }
+
+ void seed(long s) { m_opt.seed(s); }
+};
+
+// Predefinded NLopt algorithms
+using AlgNLoptGenetic = detail::NLoptAlgComb<NLOPT_GN_ESCH>;
+using AlgNLoptSubplex = detail::NLoptAlg<NLOPT_LN_SBPLX>;
+using AlgNLoptSimplex = detail::NLoptAlg<NLOPT_LN_NELDERMEAD>;
+using AlgNLoptDIRECT = detail::NLoptAlg<NLOPT_GN_DIRECT>;
+using AlgNLoptMLSL = detail::NLoptAlg<NLOPT_GN_MLSL>;
+
+}} // namespace Slic3r::opt
+
+#endif // NLOPTOPTIMIZER_HPP