mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-02 17:08:42 +03:00
QIDISlicer1.0.0
This commit is contained in:
142
src/libslic3r/Optimize/BruteforceOptimizer.hpp
Normal file
142
src/libslic3r/Optimize/BruteforceOptimizer.hpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef BRUTEFORCEOPTIMIZER_HPP
|
||||
#define BRUTEFORCEOPTIMIZER_HPP
|
||||
|
||||
#include <libslic3r/Optimize/Optimizer.hpp>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
namespace detail {
|
||||
// Implementing a bruteforce optimizer
|
||||
|
||||
// Return the number of iterations needed to reach a specific grid position (idx)
|
||||
template<size_t N>
|
||||
long num_iter(const std::array<size_t, N> &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<int D, size_t N, class Fn, class Cmp>
|
||||
bool run(std::array<size_t, N> &idx,
|
||||
Result<N> &result,
|
||||
const Bounds<N> &bounds,
|
||||
Fn &&fn,
|
||||
Cmp &&cmp)
|
||||
{
|
||||
if (stc.stop_condition()) return false;
|
||||
|
||||
if constexpr (D < 0) { // Let's evaluate fn
|
||||
Input<N> 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<D - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::forward<Cmp>(cmp)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Fn, size_t N>
|
||||
Result<N> optimize(Fn&& fn,
|
||||
const Input<N> &/*initvals*/,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
std::array<size_t, N> idx = {};
|
||||
Result<N> result;
|
||||
|
||||
if (to_min) {
|
||||
result.score = std::numeric_limits<double>::max();
|
||||
run<int(N) - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::less<double>{});
|
||||
}
|
||||
else {
|
||||
result.score = std::numeric_limits<double>::lowest();
|
||||
run<int(N) - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::greater<double>{});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using AlgBruteForce = detail::AlgBurteForce;
|
||||
|
||||
template<>
|
||||
class Optimizer<AlgBruteForce> {
|
||||
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<class Func, size_t N>
|
||||
Result<N> optimize(Func&& func,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
return m_alg.optimize(std::forward<Func>(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
|
||||
433
src/libslic3r/Optimize/NLoptOptimizer.hpp
Normal file
433
src/libslic3r/Optimize/NLoptOptimizer.hpp
Normal file
@@ -0,0 +1,433 @@
|
||||
#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 "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;
|
||||
};
|
||||
|
||||
// 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<class Alg> struct NLoptAUGLAG {};
|
||||
|
||||
template<nlopt_algorithm a1, nlopt_algorithm a2>
|
||||
struct IsNLoptAlg<NLoptAUGLAG<NLoptAlgComb<a1, a2>>> {
|
||||
static const constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<nlopt_algorithm a> struct IsNLoptAlg<NLoptAUGLAG<NLoptAlg<a>>> {
|
||||
static const constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<class M, class T = void>
|
||||
using NLoptOnly = std::enable_if_t<IsNLoptAlg<M>::value, T>;
|
||||
|
||||
template<class M> struct GetNLoptAlg_ {
|
||||
static constexpr nlopt_algorithm Local = NLOPT_NUM_ALGORITHMS;
|
||||
static constexpr nlopt_algorithm Global = NLOPT_NUM_ALGORITHMS;
|
||||
static constexpr bool IsAUGLAG = false;
|
||||
};
|
||||
|
||||
template<nlopt_algorithm a> struct GetNLoptAlg_<NLoptAlg<a>> {
|
||||
static constexpr nlopt_algorithm Local = NLOPT_NUM_ALGORITHMS;
|
||||
static constexpr nlopt_algorithm Global = a;
|
||||
static constexpr bool IsAUGLAG = false;
|
||||
};
|
||||
|
||||
template<nlopt_algorithm g, nlopt_algorithm l>
|
||||
struct GetNLoptAlg_<NLoptAlgComb<g, l>> {
|
||||
static constexpr nlopt_algorithm Local = l;
|
||||
static constexpr nlopt_algorithm Global = g;
|
||||
static constexpr bool IsAUGLAG = false;
|
||||
};
|
||||
|
||||
template<class M> constexpr nlopt_algorithm GetNLoptAlg_Global = GetNLoptAlg_<remove_cvref_t<M>>::Global;
|
||||
template<class M> constexpr nlopt_algorithm GetNLoptAlg_Local = GetNLoptAlg_<remove_cvref_t<M>>::Local;
|
||||
template<class M> constexpr bool IsAUGLAG = GetNLoptAlg_<remove_cvref_t<M>>::IsAUGLAG;
|
||||
|
||||
template<class M> struct GetNLoptAlg_<NLoptAUGLAG<M>> {
|
||||
static constexpr nlopt_algorithm Local = GetNLoptAlg_Local<M>;
|
||||
static constexpr nlopt_algorithm Global = GetNLoptAlg_Global<M>;
|
||||
static constexpr bool IsAUGLAG = true;
|
||||
};
|
||||
|
||||
enum class OptDir { MIN, MAX }; // Where to optimize
|
||||
|
||||
struct NLoptRAII { // Helper RAII class for nlopt_opt
|
||||
nlopt_opt ptr = nullptr;
|
||||
|
||||
template<class...A> explicit NLoptRAII(A&&...a)
|
||||
{
|
||||
ptr = nlopt_create(std::forward<A>(a)...);
|
||||
}
|
||||
|
||||
NLoptRAII(const NLoptRAII&) = delete;
|
||||
NLoptRAII(NLoptRAII&&) = delete;
|
||||
NLoptRAII& operator=(const NLoptRAII&) = delete;
|
||||
NLoptRAII& operator=(NLoptRAII&&) = delete;
|
||||
|
||||
~NLoptRAII() { nlopt_destroy(ptr); }
|
||||
};
|
||||
|
||||
// Map a generic function to each argument following the mapping function
|
||||
template<class Fn, class...Args>
|
||||
Fn for_each_argument(Fn &&fn, Args&&...args)
|
||||
{
|
||||
// see https://www.fluentcpp.com/2019/03/05/for_each_arg-applying-a-function-to-each-argument-of-a-function-in-cpp/
|
||||
(fn(std::forward<Args>(args)),...);
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
// Call fn on each element of the input tuple tup.
|
||||
template<class Fn, class Tup>
|
||||
Fn for_each_in_tuple(Fn fn, Tup &&tup)
|
||||
{
|
||||
auto mpfn = [&fn](auto&...pack) {
|
||||
for_each_argument(fn, pack...);
|
||||
};
|
||||
|
||||
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<T_i> where T_i is the type of
|
||||
// i-th element of tup.
|
||||
template<template<class> class W, class...Args>
|
||||
auto wrap_tup(const std::tuple<Args...> &tup)
|
||||
{
|
||||
return std::tuple<W<Args>...>(tup);
|
||||
}
|
||||
|
||||
template<class M, class = NLoptOnly<M>>
|
||||
class NLoptOpt {
|
||||
StopCriteria m_stopcr;
|
||||
StopCriteria m_loc_stopcr;
|
||||
OptDir m_dir = OptDir::MIN;
|
||||
|
||||
static constexpr double ConstraintEps = 1e-6;
|
||||
|
||||
template<class Fn> 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<class Fn, size_t N>
|
||||
static double optfunc(unsigned n, const double *params,
|
||||
double *gradient, void *data)
|
||||
{
|
||||
assert(n == N);
|
||||
|
||||
auto tdata = static_cast<OptData<Fn>*>(data);
|
||||
|
||||
if (tdata->self->m_stopcr.stop_condition())
|
||||
nlopt_force_stop(tdata->opt_raw);
|
||||
|
||||
auto funval = to_arr<N>(params);
|
||||
|
||||
double scoreval = 0.;
|
||||
using RetT = decltype(tdata->fn(funval));
|
||||
if constexpr (std::is_convertible_v<RetT, ScoreGradient<N>>) {
|
||||
ScoreGradient<N> score = tdata->fn(funval);
|
||||
for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i];
|
||||
scoreval = score.score;
|
||||
} else {
|
||||
scoreval = tdata->fn(funval);
|
||||
}
|
||||
|
||||
return scoreval;
|
||||
}
|
||||
|
||||
template<class Fn, size_t N>
|
||||
static double constrain_func(unsigned n, const double *params,
|
||||
double *gradient, void *data)
|
||||
{
|
||||
assert(n == N);
|
||||
|
||||
auto tdata = static_cast<OptData<Fn>*>(data);
|
||||
auto funval = to_arr<N>(params);
|
||||
|
||||
return tdata->fn(funval);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
static void set_up(NLoptRAII &nl,
|
||||
const Bounds<N> &bounds,
|
||||
const StopCriteria &stopcr)
|
||||
{
|
||||
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 = stopcr.abs_score_diff();
|
||||
double rel_diff = stopcr.rel_score_diff();
|
||||
double stopval = 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(stopcr.max_iterations() > 0)
|
||||
nlopt_set_maxeval(nl.ptr, stopcr.max_iterations());
|
||||
}
|
||||
|
||||
template<class Fn, size_t N, class...EqFns, class...IneqFns>
|
||||
Result<N> optimize(NLoptRAII &nl, Fn &&fn, const Input<N> &initvals,
|
||||
const std::tuple<EqFns...> &equalities,
|
||||
const std::tuple<IneqFns...> &inequalities)
|
||||
{
|
||||
Result<N> r;
|
||||
|
||||
OptData<Fn> data {fn, this, nl.ptr};
|
||||
|
||||
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<F, N>, &arg, 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<F, N>, &arg, ConstraintEps);
|
||||
};
|
||||
|
||||
auto eq_data = wrap_tup<OptData>(equalities);
|
||||
for_each_in_tuple(do_for_each_eq, eq_data);
|
||||
|
||||
auto ineq_data = wrap_tup<OptData>(inequalities);
|
||||
for_each_in_tuple(do_for_each_ineq, ineq_data);
|
||||
|
||||
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 Fn, size_t N, class...EqFns, class...IneqFns>
|
||||
Result<N> optimize(Fn&& f,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds,
|
||||
const std::tuple<EqFns...> &equalities,
|
||||
const std::tuple<IneqFns...> &inequalities)
|
||||
{
|
||||
if constexpr (IsAUGLAG<M>) {
|
||||
NLoptRAII nl_wrap{NLOPT_AUGLAG, N};
|
||||
set_up(nl_wrap, bounds, get_criteria());
|
||||
|
||||
NLoptRAII nl_glob{GetNLoptAlg_Global<M>, N};
|
||||
set_up(nl_glob, bounds, get_criteria());
|
||||
nlopt_set_local_optimizer(nl_wrap.ptr, nl_glob.ptr);
|
||||
|
||||
if constexpr (GetNLoptAlg_Local<M> < NLOPT_NUM_ALGORITHMS) {
|
||||
NLoptRAII nl_loc{GetNLoptAlg_Local<M>, 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<Fn>(f), initvals,
|
||||
equalities, inequalities);
|
||||
} else {
|
||||
return optimize(nl_wrap, std::forward<Fn>(f), initvals,
|
||||
equalities, inequalities);
|
||||
}
|
||||
} else {
|
||||
NLoptRAII nl_glob{GetNLoptAlg_Global<M>, N};
|
||||
set_up(nl_glob, bounds, get_criteria());
|
||||
|
||||
if constexpr (GetNLoptAlg_Local<M> < NLOPT_NUM_ALGORITHMS) {
|
||||
NLoptRAII nl_loc{GetNLoptAlg_Local<M>, 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<Fn>(f), initvals,
|
||||
equalities, inequalities);
|
||||
} else {
|
||||
return optimize(nl_glob, std::forward<Fn>(f), initvals,
|
||||
equalities, inequalities);
|
||||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
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<class Alg> struct AlgFeatures_ {
|
||||
static constexpr bool SupportsInequalities = false;
|
||||
static constexpr bool SupportsEqualities = false;
|
||||
};
|
||||
|
||||
} // namespace detail;
|
||||
|
||||
template<class Alg> constexpr bool SupportsEqualities =
|
||||
detail::AlgFeatures_<remove_cvref_t<Alg>>::SupportsEqualities;
|
||||
|
||||
template<class Alg> constexpr bool SupportsInequalities =
|
||||
detail::AlgFeatures_<remove_cvref_t<Alg>>::SupportsInequalities;
|
||||
|
||||
// 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, class...EqFns, class...IneqFns>
|
||||
Result<N> optimize(Func&& func,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds,
|
||||
const std::tuple<EqFns...> &eq_constraints = {},
|
||||
const std::tuple<IneqFns...> &ineq_constraint = {})
|
||||
{
|
||||
static_assert(std::tuple_size_v<std::tuple<EqFns...>> == 0
|
||||
|| SupportsEqualities<M>,
|
||||
"Equality constraints are not supported.");
|
||||
|
||||
static_assert(std::tuple_size_v<std::tuple<IneqFns...>> == 0
|
||||
|| SupportsInequalities<M>,
|
||||
"Inequality constraints are not supported.");
|
||||
|
||||
return m_opt.optimize(std::forward<Func>(func), initvals, bounds,
|
||||
eq_constraints,
|
||||
ineq_constraint);
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
void set_loc_criteria(const StopCriteria &cr) { m_opt.set_loc_criteria(cr); }
|
||||
const StopCriteria &get_loc_criteria() const noexcept { return m_opt.get_loc_criteria(); }
|
||||
};
|
||||
|
||||
// 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 AlgNLoptCobyla = detail::NLoptAlg<NLOPT_LN_COBYLA>;
|
||||
using AlgNLoptDIRECT = detail::NLoptAlg<NLOPT_GN_DIRECT>;
|
||||
using AlgNLoptORIG_DIRECT = detail::NLoptAlg<NLOPT_GN_ORIG_DIRECT>;
|
||||
using AlgNLoptISRES = detail::NLoptAlg<NLOPT_GN_ISRES>;
|
||||
using AlgNLoptAGS = detail::NLoptAlg<NLOPT_GN_AGS>;
|
||||
|
||||
using AlgNLoptMLSL_Subplx = detail::NLoptAlgComb<NLOPT_GN_MLSL_LDS, NLOPT_LN_SBPLX>;
|
||||
using AlgNLoptMLSL_Cobyla = detail::NLoptAlgComb<NLOPT_GN_MLSL, NLOPT_LN_COBYLA>;
|
||||
using AlgNLoptGenetic_Subplx = detail::NLoptAlgComb<NLOPT_GN_ESCH, NLOPT_LN_SBPLX>;
|
||||
|
||||
// To craft auglag algorithms (constraint support through object function transformation)
|
||||
using detail::NLoptAUGLAG;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<> struct AlgFeatures_<AlgNLoptCobyla> {
|
||||
static constexpr bool SupportsInequalities = true;
|
||||
static constexpr bool SupportsEqualities = true;
|
||||
};
|
||||
|
||||
template<> struct AlgFeatures_<AlgNLoptISRES> {
|
||||
static constexpr bool SupportsInequalities = true;
|
||||
static constexpr bool SupportsEqualities = false;
|
||||
};
|
||||
|
||||
template<> struct AlgFeatures_<AlgNLoptORIG_DIRECT> {
|
||||
static constexpr bool SupportsInequalities = true;
|
||||
static constexpr bool SupportsEqualities = false;
|
||||
};
|
||||
|
||||
template<> struct AlgFeatures_<AlgNLoptAGS> {
|
||||
static constexpr bool SupportsInequalities = true;
|
||||
static constexpr bool SupportsEqualities = true;
|
||||
};
|
||||
|
||||
template<class M> struct AlgFeatures_<NLoptAUGLAG<M>> {
|
||||
static constexpr bool SupportsInequalities = true;
|
||||
static constexpr bool SupportsEqualities = true;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
#endif // NLOPTOPTIMIZER_HPP
|
||||
201
src/libslic3r/Optimize/Optimizer.hpp
Normal file
201
src/libslic3r/Optimize/Optimizer.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#ifndef OPTIMIZER_HPP
|
||||
#define OPTIMIZER_HPP
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
template<class T, class O = T>
|
||||
using FloatingOnly = std::enable_if_t<std::is_floating_point<T>::value, O>;
|
||||
|
||||
template<class T, class = FloatingOnly<T>>
|
||||
constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
|
||||
|
||||
constexpr float NaNf = NaN<float>;
|
||||
constexpr double NaNd = NaN<double>;
|
||||
|
||||
// A type to hold the complete result of the optimization.
|
||||
template<size_t N> struct Result {
|
||||
int resultcode; // Method dependent
|
||||
std::array<double, N> optimum;
|
||||
double score;
|
||||
};
|
||||
|
||||
// An interval of possible input values for optimization
|
||||
class Bound {
|
||||
double m_min, m_max;
|
||||
|
||||
public:
|
||||
Bound(double min = std::numeric_limits<double>::min(),
|
||||
double max = std::numeric_limits<double>::max())
|
||||
: m_min(min), m_max(max)
|
||||
{}
|
||||
|
||||
double min() const noexcept { return m_min; }
|
||||
double max() const noexcept { return m_max; }
|
||||
};
|
||||
|
||||
// Helper types for optimization function input and bounds
|
||||
template<size_t N> using Input = std::array<double, N>;
|
||||
template<size_t N> using Bounds = std::array<Bound, N>;
|
||||
|
||||
// A type for specifying the stop criteria. Setter methods can be concatenated
|
||||
class StopCriteria {
|
||||
|
||||
// If the absolute value difference between two scores.
|
||||
double m_abs_score_diff = NaNd;
|
||||
|
||||
// If the relative value difference between two scores.
|
||||
double m_rel_score_diff = NaNd;
|
||||
|
||||
// Stop if this value or better is found.
|
||||
double m_stop_score = NaNd;
|
||||
|
||||
// A predicate that if evaluates to true, the optimization should terminate
|
||||
// and the best result found prior to termination should be returned.
|
||||
std::function<bool()> m_stop_condition = [] { return false; };
|
||||
|
||||
// The max allowed number of iterations.
|
||||
unsigned m_max_iterations = 0;
|
||||
|
||||
public:
|
||||
|
||||
StopCriteria & abs_score_diff(double val)
|
||||
{
|
||||
m_abs_score_diff = val; return *this;
|
||||
}
|
||||
|
||||
double abs_score_diff() const { return m_abs_score_diff; }
|
||||
|
||||
StopCriteria & rel_score_diff(double val)
|
||||
{
|
||||
m_rel_score_diff = val; return *this;
|
||||
}
|
||||
|
||||
double rel_score_diff() const { return m_rel_score_diff; }
|
||||
|
||||
StopCriteria & stop_score(double val)
|
||||
{
|
||||
m_stop_score = val; return *this;
|
||||
}
|
||||
|
||||
double stop_score() const { return m_stop_score; }
|
||||
|
||||
StopCriteria & max_iterations(unsigned val)
|
||||
{
|
||||
m_max_iterations = val; return *this;
|
||||
}
|
||||
|
||||
double max_iterations() const { return m_max_iterations; }
|
||||
|
||||
template<class Fn> StopCriteria & stop_condition(Fn &&cond)
|
||||
{
|
||||
m_stop_condition = cond; return *this;
|
||||
}
|
||||
|
||||
bool stop_condition() { return m_stop_condition(); }
|
||||
};
|
||||
|
||||
// Helper class to use optimization methods involving gradient.
|
||||
template<size_t N> struct ScoreGradient {
|
||||
double score;
|
||||
std::optional<std::array<double, N>> gradient;
|
||||
|
||||
ScoreGradient(double s, const std::array<double, N> &grad)
|
||||
: score{s}, gradient{grad}
|
||||
{}
|
||||
};
|
||||
|
||||
// Helper to be used in static_assert.
|
||||
template<class T> struct always_false { enum { value = false }; };
|
||||
|
||||
// Basic interface to optimizer object
|
||||
template<class Method, class Enable = void> class Optimizer {
|
||||
public:
|
||||
|
||||
Optimizer(const StopCriteria &)
|
||||
{
|
||||
static_assert (always_false<Method>::value,
|
||||
"Optimizer unimplemented for given method!");
|
||||
}
|
||||
|
||||
// Switch optimization towards function minimum
|
||||
Optimizer &to_min() { return *this; }
|
||||
|
||||
// Switch optimization towards function maximum
|
||||
Optimizer &to_max() { return *this; }
|
||||
|
||||
// Set criteria for successive optimizations
|
||||
Optimizer &set_criteria(const StopCriteria &) { return *this; }
|
||||
|
||||
// Get current criteria
|
||||
StopCriteria get_criteria() const { return {}; };
|
||||
|
||||
// Find function minimum or maximum for Func which has has signature:
|
||||
// double(const Input<N> &input) and input with dimension N
|
||||
//
|
||||
// Initial starting point can be given as the second parameter.
|
||||
//
|
||||
// For each dimension an interval (Bound) has to be given marking the bounds
|
||||
// for that dimension.
|
||||
//
|
||||
// Optionally, some constraints can be given in the form of double(Input<N>)
|
||||
// functors. The parameters eq_constraints and ineq_constraints can be used
|
||||
// to add equality and inequality (<= 0) constraints to the optimization.
|
||||
// Note that it is up the the particular method if it accepts these
|
||||
// constraints.
|
||||
//
|
||||
// initvals have to be within the specified bounds, otherwise its undefined
|
||||
// behavior.
|
||||
//
|
||||
// Func can return a score of type double or optionally a ScoreGradient
|
||||
// class to indicate the function gradient for a optimization methods that
|
||||
// make use of the gradient.
|
||||
template<class Func, size_t N, class...EqConstraints, class...IneqConstraints>
|
||||
Result<N> optimize(Func&& /*func*/,
|
||||
const Input<N> &/*initvals*/,
|
||||
const Bounds<N>& /*bounds*/,
|
||||
const std::tuple<EqConstraints...> &eq_constraints = {},
|
||||
const std::tuple<IneqConstraints...> &ineq_constraint = {}
|
||||
) { return {}; }
|
||||
|
||||
// optional for randomized methods:
|
||||
void seed(long /*s*/) {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Helper to convert C style array to std::array. The copy should be optimized
|
||||
// away with modern compilers.
|
||||
template<size_t N, class T> auto to_arr(const T *a)
|
||||
{
|
||||
std::array<T, N> r;
|
||||
std::copy(a, a + N, std::begin(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
template<size_t N, class T> auto to_arr(const T (&a) [N])
|
||||
{
|
||||
return to_arr<N>(static_cast<const T *>(a));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Helper functions to create bounds, initial value
|
||||
template<size_t N> Bounds<N> bounds(const Bound (&b) [N]) { return detail::to_arr(b); }
|
||||
template<size_t N> Input<N> initvals(const double (&a) [N]) { return detail::to_arr(a); }
|
||||
template<size_t N> auto score_gradient(double s, const double (&grad)[N])
|
||||
{
|
||||
return ScoreGradient<N>(s, detail::to_arr(grad));
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
#endif // OPTIMIZER_HPP
|
||||
Reference in New Issue
Block a user