From 52d5468d58b89e3b12b6b0c3ea3e5079c837d459 Mon Sep 17 00:00:00 2001 From: Erik Strand <erik.strand@cba.mit.edu> Date: Mon, 13 Apr 2020 02:12:10 -0400 Subject: [PATCH] Add basic tests for golden section search --- optimization/optimizers/line_search/bracket.h | 12 +++++-- test/CMakeLists.txt | 1 + test/optimizers/line_search/bracket.cpp | 2 +- .../optimizers/line_search/golden_section.cpp | 36 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test/optimizers/line_search/golden_section.cpp diff --git a/optimization/optimizers/line_search/bracket.h b/optimization/optimizers/line_search/bracket.h index 348d376..fb9e1a0 100644 --- a/optimization/optimizers/line_search/bracket.h +++ b/optimization/optimizers/line_search/bracket.h @@ -7,10 +7,18 @@ namespace optimization { //-------------------------------------------------------------------------------------------------- -// TODO: This class should guarantee y_1_ > y_2_ < y_3_ and x_1_ < x_2_ < x_3_ or x_1_ > x_2_ > x_3_ -// (with the "or" evaluted before the "and"). +// TODO: Could be nice for this class to guarantee y_1_ > y_2_ < y_3_ and x_1_ < x_2_ < x_3_ or +// x_1_ > x_2_ > x_3_ (with the "or" evaluted before the "and"). But for now this feels like +// egregious overengineering. class Bracket { public: + Bracket() {} + Bracket(Scalar x_1, Scalar x_2, Scalar x_3, Scalar y_1, Scalar y_2, Scalar y_3) + : x_1_(x_1), x_2_(x_2), x_3_(x_3), y_1_(y_1), y_2_(y_2), y_3_(y_3) + {} + Bracket(Bracket const&) = default; + Bracket& operator=(Bracket const&) = default; + Scalar x_1() const { return x_1_; } Scalar x_2() const { return x_2_; } Scalar x_3() const { return x_3_; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5d2704d..a186b4e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(test main.cpp optimizers/line_search/bracket.cpp + optimizers/line_search/golden_section.cpp ) target_link_libraries(test optimization_lib catch2) diff --git a/test/optimizers/line_search/bracket.cpp b/test/optimizers/line_search/bracket.cpp index 097c754..b2dd1ae 100644 --- a/test/optimizers/line_search/bracket.cpp +++ b/test/optimizers/line_search/bracket.cpp @@ -4,7 +4,7 @@ using namespace optimization; //-------------------------------------------------------------------------------------------------- -TEST_CASE("Bracket", "[bracket]") { +TEST_CASE("Bracket", "[Bracket]") { BracketFinder bracket; SECTION("parabola") { diff --git a/test/optimizers/line_search/golden_section.cpp b/test/optimizers/line_search/golden_section.cpp new file mode 100644 index 0000000..875c659 --- /dev/null +++ b/test/optimizers/line_search/golden_section.cpp @@ -0,0 +1,36 @@ +#include "catch.hpp" +#include "optimizers/line_search/golden_section.h" + +using namespace optimization; + +//-------------------------------------------------------------------------------------------------- +TEST_CASE("GoldenSection", "[GoldenSection]") { + // The 1e-8 sets the absolute tolerance on the width of the bracket. + // There's still a default relative tolerance in effect as well, but these tests don't hit it. + GoldenSection golden_section(1e-8); + Bracket bracket; + Sample<Scalar> result; + + SECTION("parabola") { + struct Parabola { + void eval(Scalar x, Scalar& y) const { y = x * x; } + }; + Parabola parabola; + + bracket = Bracket(-2, -1, 2, 4, 1, 4); + result = golden_section.optimize(parabola, bracket); + // The parabola is flat here so y accuracy had better exceed x accuracy. + REQUIRE(std::abs(result.point) < 1e-8); + REQUIRE(std::abs(result.value) < 1e-16); + // Just a sanity check. + REQUIRE(golden_section.n_evaluations() < 100); + + bracket = Bracket(50, 10, -100, 2500, 100, 10000); + result = golden_section.optimize(parabola, bracket); + // The parabola is flat here so y accuracy had better exceed x accuracy. + REQUIRE(std::abs(result.point) < 1e-8); + REQUIRE(std::abs(result.value) < 1e-16); + // Just a sanity check. + REQUIRE(golden_section.n_evaluations() < 100); + } +} -- GitLab