diff --git a/apps/compare_convergence.cpp b/apps/compare_convergence.cpp index ef2594503d05f28425516aab924e41a6b5c2055d..b154ac487d031e129229dbd475600eff8ef4fecb 100644 --- a/apps/compare_convergence.cpp +++ b/apps/compare_convergence.cpp @@ -25,7 +25,7 @@ int main() { Scalar value_threshold = 1e-8; std::vector<uint32_t> dims = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; - NelderMead<Objective, -1> nm(max_evaluations, value_threshold); + NelderMead<-1> nm(max_evaluations, value_threshold); // We'll set the learning rate in the loop. GradientDescent<-1> gd(0, max_evaluations, gradient_threshold); diff --git a/optimization/optimizers/nelder_mead/main.cpp b/optimization/optimizers/nelder_mead/main.cpp index b2819e2e6980f6f100d5517aa560a9d5e181c107..b70e9897423b1ef498e35c9c5a7342fdad1b79d2 100644 --- a/optimization/optimizers/nelder_mead/main.cpp +++ b/optimization/optimizers/nelder_mead/main.cpp @@ -48,7 +48,7 @@ int main(int const argc, char const** argv) { Objective objective; objective.dim() = dim; - NelderMead<Objective, -1> optimizer(max_evaluations, relative_y_tolerance); + NelderMead<-1> optimizer(max_evaluations, relative_y_tolerance); VectorXs minimum = optimizer.optimize(objective, simplex); std::cout << "n evaluations: " << optimizer.n_evaluations() << '\n'; std::cout << "final point: " << minimum << '\n'; @@ -61,7 +61,7 @@ int main(int const argc, char const** argv) { } if (!vis_file_path.empty()) { - json data = NelderMeadVis<Objective, -1>{optimizer}; + json data = NelderMeadVis<-1>{optimizer}; std::ofstream vis_file(vis_file_path); vis_file << data.dump(4) << '\n'; } diff --git a/optimization/optimizers/nelder_mead/nelder_mead.h b/optimization/optimizers/nelder_mead/nelder_mead.h index 36826b12a30a2508dd615da8bd5d45d022fe6cac..058f34c37a396bc8d8b21e4b0c41b3fafbd3b04e 100644 --- a/optimization/optimizers/nelder_mead/nelder_mead.h +++ b/optimization/optimizers/nelder_mead/nelder_mead.h @@ -9,15 +9,15 @@ namespace optimization { //-------------------------------------------------------------------------------------------------- // TODO record termination condition -template <typename Objective, int32_t D = -1> -class NelderMead : public NelderMeadLog<Objective, D> { +template <int32_t D = -1> +class NelderMead : public NelderMeadLog<D> { public: // vector in the space we're searching - using VectorD = typename NelderMeadLog<Objective, D>::VectorD; + using VectorD = typename NelderMeadLog<D>::VectorD; // vector in one higher dimension (e.g. to store a real value for each simplex vertex) - using VectorN = typename NelderMeadLog<Objective, D>::VectorN; + using VectorN = typename NelderMeadLog<D>::VectorN; // D x N matrix (each column is a VectorD holding a vertex, each row is a VectorN) - using MatrixDN = typename NelderMeadLog<Objective, D>::MatrixDN; + using MatrixDN = typename NelderMeadLog<D>::MatrixDN; NelderMead(uint32_t me = 1000, Scalar ry = 1e-8) : max_evaluations_(me), relative_y_tolerance_(ry) @@ -29,14 +29,17 @@ public: VectorN const& simplex_values() const { return simplex_values_; } // Constructs a simplex from an initial point by offset all coordinate values. + template <typename Objective> VectorD optimize(Objective& objective, VectorD const& initial_point, Scalar offset = 1); // Each row of simplex is a vertex. + template <typename Objective> VectorD optimize(Objective& objective, MatrixDN const& simplex); VectorD const& min_point() const { return simplex_vertices_[i_lowest_]; } Scalar min_value() const { return simplex_values_[i_lowest_]; } private: + template <typename Objective> Scalar try_new_point(Objective& objective, Scalar factor); // For fixed size D (and thus N), dim_ and n_vertices_ are redundant. @@ -63,8 +66,9 @@ private: }; //.................................................................................................. -template <typename Objective, int32_t D> -auto NelderMead<Objective, D>::optimize( +template <int32_t D> +template <typename Objective> +auto NelderMead<D>::optimize( Objective& objective, VectorD const& initial_point, Scalar offset @@ -80,8 +84,9 @@ auto NelderMead<Objective, D>::optimize( } //.................................................................................................. -template <typename Objective, int32_t D> -auto NelderMead<Objective, D>::optimize(Objective& objective, MatrixDN const& simplex) -> VectorD { +template <int32_t D> +template <typename Objective> +auto NelderMead<D>::optimize(Objective& objective, MatrixDN const& simplex) -> VectorD { simplex_vertices_ = simplex; n_vertices_ = simplex_vertices_.cols(); dim_ = simplex_vertices_.rows(); @@ -96,6 +101,8 @@ auto NelderMead<Objective, D>::optimize(Objective& objective, MatrixDN const& si } n_evaluations_ = n_vertices_; + NelderMeadLog<D>::initialize(objective); + while (true) { // Find lowest, highest, and second highest points. i_lowest_ = (simplex_values_[0] < simplex_values_[1]) ? 0 : 1; @@ -116,7 +123,7 @@ auto NelderMead<Objective, D>::optimize(Objective& objective, MatrixDN const& si } // Log the simplex. - NelderMeadLog<Objective, D>::push_back(simplex_vertices_, simplex_values_); + NelderMeadLog<D>::push_back(simplex_vertices_, simplex_values_); // Evaluate termination conditions. Scalar const difference = std::abs(simplex_values_[i_highest_] - simplex_values_[i_lowest_]); @@ -170,8 +177,9 @@ auto NelderMead<Objective, D>::optimize(Objective& objective, MatrixDN const& si } //.................................................................................................. -template <typename Objective, int32_t D> -Scalar NelderMead<Objective, D>::try_new_point(Objective& objective, Scalar factor) { +template <int32_t D> +template <typename Objective> +Scalar NelderMead<D>::try_new_point(Objective& objective, Scalar factor) { // Generate a new point by reflecting/expanding/contracting the worst point. Scalar const t1 = (Scalar(1) - factor) / dim_; Scalar const t2 = factor - t1; diff --git a/optimization/optimizers/nelder_mead/nelder_mead_log.h b/optimization/optimizers/nelder_mead/nelder_mead_log.h index 0e39c7dfdffd6d482c0929449ce1652aee0364cf..4229ad2392ad406ac1fee10376c54f6971dee8fe 100644 --- a/optimization/optimizers/nelder_mead/nelder_mead_log.h +++ b/optimization/optimizers/nelder_mead/nelder_mead_log.h @@ -14,7 +14,7 @@ namespace optimization { //-------------------------------------------------------------------------------------------------- -template <typename Objective, int32_t D> +template <int32_t D> struct NelderMeadState { // If D != -1, N == D + 1. If D == -1, N == -1. static constexpr int32_t N = [D_ = D](){ @@ -48,32 +48,42 @@ struct NelderMeadState { //-------------------------------------------------------------------------------------------------- // This is used as a base class rather than a member so that the empty base class optimization can // be applied (the member would take up space even if it is an empty class). -template <typename Objective, int32_t D> +template <int32_t D> struct NelderMeadLog { - using VectorD = typename NelderMeadState<Objective, D>::VectorD; - using VectorN = typename NelderMeadState<Objective, D>::VectorN; - using MatrixDN = typename NelderMeadState<Objective, D>::MatrixDN; + using VectorD = typename NelderMeadState<D>::VectorD; + using VectorN = typename NelderMeadState<D>::VectorN; + using MatrixDN = typename NelderMeadState<D>::MatrixDN; void reserve(uint32_t n) VIS_ONLY_METHOD; + template <typename Objective> + void initialize(Objective const&) VIS_ONLY_METHOD; void push_back(MatrixDN const& simplex, VectorN const& values) VIS_ONLY_METHOD; void clear() VIS_ONLY_METHOD; #ifdef VISUALIZE - std::vector<NelderMeadState<Objective, D>> states; + std::string objective_name; + std::vector<NelderMeadState<D>> states; #endif }; #ifdef VISUALIZE //.................................................................................................. -template <typename Objective, int32_t D> -void NelderMeadLog<Objective, D>::reserve(uint32_t n) { +template <int32_t D> +void NelderMeadLog<D>::reserve(uint32_t n) { states.reserve(n); } //.................................................................................................. -template <typename Objective, int32_t D> -void NelderMeadLog<Objective, D>::push_back( +template <int32_t D> +template <typename Objective> +void NelderMeadLog<D>::initialize(Objective const&) { + objective_name = Objective::name; +} + +//.................................................................................................. +template <int32_t D> +void NelderMeadLog<D>::push_back( MatrixDN const& simplex, VectorN const& values ) { @@ -82,8 +92,8 @@ void NelderMeadLog<Objective, D>::push_back( //-------------------------------------------------------------------------------------------------- -template <typename Objective, int32_t D> -void to_json(nlohmann::json& j, NelderMeadState<Objective, D> const& state) { +template <int32_t D> +void to_json(nlohmann::json& j, NelderMeadState<D> const& state) { j = nlohmann::json{ {"simplex", transpose_for_json(state.simplex)}, {"values", state.values} @@ -91,11 +101,11 @@ void to_json(nlohmann::json& j, NelderMeadState<Objective, D> const& state) { } //-------------------------------------------------------------------------------------------------- -template <typename Objective, int32_t D> -void to_json(nlohmann::json& j, NelderMeadLog<Objective, D> const& log) { +template <int32_t D> +void to_json(nlohmann::json& j, NelderMeadLog<D> const& log) { j = nlohmann::json{ {"algorithm", "nelder-mead"}, - {"objective", Objective::name}, + {"objective", log.objective_name}, {"data", log.states} }; } diff --git a/optimization/optimizers/nelder_mead/nelder_mead_vis.h b/optimization/optimizers/nelder_mead/nelder_mead_vis.h index a81d4747d52e450da12e93fb4f5bdc5b838d9eef..648b1bca4333edf6c65ca77ebecc59d041d16af3 100644 --- a/optimization/optimizers/nelder_mead/nelder_mead_vis.h +++ b/optimization/optimizers/nelder_mead/nelder_mead_vis.h @@ -8,17 +8,17 @@ namespace optimization { //-------------------------------------------------------------------------------------------------- -template <typename Objective, int32_t D> +template <int32_t D> struct NelderMeadVis { - NelderMeadLog<Objective, D> const& log; + NelderMeadLog<D> const& log; }; //.................................................................................................. -template <typename Objective, int32_t D> -void to_json(nlohmann::json& j, NelderMeadVis<Objective, D> const& vis) { +template <int32_t D> +void to_json(nlohmann::json& j, NelderMeadVis<D> const& vis) { j = nlohmann::json{ {"algorithm", "nelder-mead"}, - {"objective", Objective::name}, + {"objective", vis.log.objective_name}, {"data", { {"polygons", nlohmann::json::array()} }}