diff --git a/optimization/optimizers/line_search/bracket.h b/optimization/optimizers/line_search/bracket.h index 348d376a44524dada0e36bc8d6584b7ee7b1372a..fb9e1a089f69aee65a610394f347939d422ba335 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 5d2704d5a8545ed1937f88606ab3408c9ede8342..a186b4e3ab1889131fa97279a9c902f70e2a15c4 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 097c75435edf5e58f4def3fe20887cae74d74339..b2dd1ae4604a507eef79374dbbc0b7205b1e5776 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 0000000000000000000000000000000000000000..875c65923cf5d32407eda3f61afa5fcaf46ed942 --- /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); + } +}