LCOV - code coverage report
Current view: top level - app/test - test_Solver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 106 116 91.4 %
Date: 2023-08-17 16:45:52 Functions: 6 7 85.7 %

          Line data    Source code
       1             : #include <catch2/catch_get_random_seed.hpp>
       2             : #include <catch2/catch_test_macros.hpp>
       3             : #include <catch2/matchers/catch_matchers_floating_point.hpp>
       4             : #include <cmath>
       5             : 
       6             : #include "Opt/GeneticIntervalSolver.hpp"
       7             : #include "Opt/GoldenSectionSolver.hpp"
       8             : #include "Opt/QuadraticSolver.hpp"
       9             : #include "Opt/UnivariateSolver.hpp"
      10             : 
      11             : using namespace std;
      12             : 
      13             : using Catch::Matchers::WithinAbs;
      14             : 
      15           6 : void testInterval(
      16             :     const Opt::IntervalSolver::Interval &p,
      17             :     const Opt::IntervalSolver::Interval &psol
      18             : ) {
      19             :     // Check consistency of solution
      20          12 :     REQUIRE(p.first <= p.second);
      21             : 
      22             :     // Check range is completely included in range allowed by error around true solution
      23          12 :     REQUIRE(psol.first <= p.first);
      24          12 :     REQUIRE(p.second <= psol.second);
      25           6 : }
      26             : 
      27           5 : void testIntervalSolverQuadratic(
      28             :     Opt::IntervalSolver &solver,
      29             :     const double        &a,
      30             :     const double        &b,
      31             :     const double        &c,
      32             :     const double        &lmargin,
      33             :     const double        &rmargin,
      34             :     const double        &e
      35             : ) {
      36           5 :     double                         sol = -b / (a * 2);
      37           5 :     Opt::IntervalSolver::Interval  psol(sol - e, sol + e);
      38           5 :     double                         l    = sol - lmargin;
      39           5 :     double                         r    = sol + rmargin;
      40         185 :     Opt::UnivariateSolver::Problem prob = [a, b, c](double x) {
      41         180 :         return (((a)*x + b) * x + c);
      42           5 :     };
      43           5 :     solver.setInterval(l, r);
      44           5 :     solver.setStopCriteria(e);
      45           5 :     Opt::IntervalSolver::Interval p = solver.solveInterval(prob);
      46             : 
      47           5 :     testInterval(p, psol);
      48           5 : }
      49             : 
      50           0 : void testSolverQuadratic(
      51             :     Opt::UnivariateSolver &solver,
      52             :     const double          &a,
      53             :     const double          &b,
      54             :     const double          &c,
      55             :     const double          &e
      56             : ) {
      57           0 :     double                         sol = -b / (a * 2);
      58           0 :     Opt::IntervalSolver::Interval  psol(sol - e, sol + e);
      59           0 :     Opt::UnivariateSolver::Problem prob = [a, b, c](double x) {
      60           0 :         return (((a)*x + b) * x + c);
      61           0 :     };
      62           0 :     solver.setStopCriteria(e);
      63           0 :     double x = solver.solve(prob);
      64             : 
      65           0 :     REQUIRE_THAT(x, WithinAbs(sol, e));
      66           0 : }
      67             : 
      68           6 : TEST_CASE("Golden Section solver", "[golden-section]") {
      69           6 :     Catch::StringMaker<float>::precision = 40;
      70             : 
      71           6 :     Opt::GoldenSectionSolver solver;
      72             : 
      73           7 :     SECTION("Quadratic 1,0,0, error 1e-3, margins 1,1") {
      74           1 :         double a = 1, b = 0, c = 0, e = 1e-3, lmargin = 1, rmargin = 1;
      75           1 :         testIntervalSolverQuadratic(solver, a, b, c, lmargin, rmargin, e);
      76             :     }
      77             : 
      78           7 :     SECTION("Quadratic 1,1,1, error 1e-3, margins 10,100") {
      79           1 :         double a = 1, b = 1, c = 1, e = 1e-3, lmargin = 10, rmargin = 100;
      80           1 :         testIntervalSolverQuadratic(solver, a, b, c, lmargin, rmargin, e);
      81             :     }
      82             : 
      83           7 :     SECTION("Quadratic 1,0,0, error 1e-7, margins 1,1") {
      84           1 :         double a = 1, b = 0, c = 0, e = 1e-7, lmargin = 1, rmargin = 1;
      85           1 :         testIntervalSolverQuadratic(solver, a, b, c, lmargin, rmargin, e);
      86             :     }
      87             : 
      88           7 :     SECTION("Quadratic 1,1,1, error 1e-7, margins 10,100") {
      89           1 :         double a = 1, b = 1, c = 1, e = 1e-7, lmargin = 10, rmargin = 100;
      90           1 :         testIntervalSolverQuadratic(solver, a, b, c, lmargin, rmargin, e);
      91             :     }
      92             : 
      93           7 :     SECTION("Quadratic 123,456,789, error 1e-7, margins 10000,10000") {
      94           1 :         double a = 123, b = 456, c = 789, e = 1e-7, lmargin = 10000, rmargin = 10000;
      95           1 :         testIntervalSolverQuadratic(solver, a, b, c, lmargin, rmargin, e);
      96             :     }
      97             : 
      98           7 :     SECTION("Absolute value, sol 2, error 1e-7, interval -3,14") {
      99           1 :         double                         sol = 2, e = 1e-15, l = -3, r = 14;
     100          80 :         Opt::UnivariateSolver::Problem prob = [sol](double x) {
     101          79 :             return fabs(x - sol);
     102           2 :         };
     103           1 :         solver.setInterval(l, r);
     104           1 :         solver.setStopCriteria(e);
     105           1 :         Opt::IntervalSolver::Interval p = solver.solveInterval(prob);
     106             : 
     107           1 :         testInterval(p, make_pair(sol - e, sol + e));
     108             :     }
     109           6 : }
     110             : 
     111           5 : void testIntervalSolverGenetic(
     112             :     Opt::GeneticIntervalSolver &solver,
     113             :     const double               &a,
     114             :     const double               &b,
     115             :     const double               &c,
     116             :     const double               &lmargin,
     117             :     const double               &rmargin,
     118             :     const double               &e
     119             : ) {
     120           5 :     double                         sol = -b / (a * 2);
     121           5 :     Opt::IntervalSolver::Interval  psol(sol - e, sol + e);
     122           5 :     double                         l    = sol - lmargin;
     123           5 :     double                         r    = sol + rmargin;
     124       36270 :     Opt::UnivariateSolver::Problem prob = [a, b, c](double x) {
     125       36265 :         return (((a)*x + b) * x + c);
     126           5 :     };
     127           5 :     solver.setInterval(l, r);
     128           5 :     solver.setStopCriteria(e);
     129           5 :     Opt::IntervalSolver::Var x = solver.solve(prob);
     130             : 
     131          20 :     REQUIRE_THAT(x, WithinAbs(sol, e));
     132           5 : }
     133             : 
     134           7 : TEST_CASE("Genetic solver", "[genetic]") {
     135           7 :     Catch::StringMaker<float>::precision = 40;
     136             : 
     137           7 :     Opt::GeneticIntervalSolver solver(
     138             :         100,
     139             :         1000,
     140             :         0.5,
     141             :         1000,
     142             :         8,
     143           7 :         std::make_shared<std::mt19937>(0)
     144           7 :     );
     145             : 
     146           8 :     SECTION("Quadratic 1,0,0, error 1e-3, margins 1,1") {
     147           1 :         double a = 1, b = 0, c = 0, e = 1e-3, lmargin = 1, rmargin = 1;
     148           1 :         testIntervalSolverGenetic(solver, a, b, c, lmargin, rmargin, e);
     149             :     }
     150             : 
     151           8 :     SECTION("Quadratic 1,1,1, error 1e-3, margins 10,100") {
     152           1 :         double a = 1, b = 1, c = 1, e = 1e-3, lmargin = 10, rmargin = 100;
     153           1 :         testIntervalSolverGenetic(solver, a, b, c, lmargin, rmargin, e);
     154             :     }
     155             : 
     156           8 :     SECTION("Quadratic 1,0,0, error 1e-7, margins 1,1") {
     157           1 :         double a = 1, b = 0, c = 0, e = 1e-7, lmargin = 1, rmargin = 1;
     158           1 :         testIntervalSolverGenetic(solver, a, b, c, lmargin, rmargin, e);
     159             :     }
     160             : 
     161           8 :     SECTION("Quadratic 1,1,1, error 1e-7, margins 10,100") {
     162           1 :         double a = 1, b = 1, c = 1, e = 1e-7, lmargin = 10, rmargin = 100;
     163           1 :         testIntervalSolverGenetic(solver, a, b, c, lmargin, rmargin, e);
     164             :     }
     165             : 
     166           8 :     SECTION("Quadratic 123,456,789, error 1e-7, margins 10000,10000") {
     167           1 :         double a = 123, b = 456, c = 789, e = 1e-7, lmargin = 10000, rmargin = 10000;
     168           1 :         testIntervalSolverGenetic(solver, a, b, c, lmargin, rmargin, e);
     169             :     }
     170             : 
     171           8 :     SECTION("Absolute value, sol 2, error 1e-7, interval -3,14") {
     172           1 :         double                         sol = 2, e = 1e-15, l = -3, r = 14;
     173       16073 :         Opt::UnivariateSolver::Problem prob = [sol](double x) {
     174       16072 :             return fabs(x - sol);
     175           2 :         };
     176           1 :         solver.setInterval(l, r);
     177           1 :         solver.setStopCriteria(e);
     178           1 :         Opt::IntervalSolver::Var x = solver.solve(prob);
     179             : 
     180           4 :         REQUIRE_THAT(x, WithinAbs(sol, e));
     181             :     }
     182             : 
     183           8 :     SECTION("Quadratic formula with local minima") {
     184        8091 :         Opt::UnivariateSolver::Problem prob = [](double x) {
     185        8091 :             return pow(x, 4) - pow(x, 3) - 8 * pow(x, 2) + 30;
     186           2 :         };
     187           1 :         double l = -5, r = 3;
     188           1 :         double e = 1e-7;
     189             : 
     190           1 :         double sol          = +2.40985257;
     191           1 :         double localMinimum = -1.65985257;
     192             : 
     193             :         // To prove the golden section method cannot solve this case because
     194             :         // the function is not convex
     195           2 :         Opt::GoldenSectionSolver goldenSolver;
     196           1 :         goldenSolver.setInterval(l, r);
     197           1 :         goldenSolver.setStopCriteria(e);
     198           1 :         Opt::IntervalSolver::Var xGS = goldenSolver.solve(prob);
     199           3 :         REQUIRE_THAT(xGS, WithinAbs(localMinimum, e));
     200             : 
     201           1 :         solver.setInterval(l, r);
     202           1 :         solver.setStopCriteria(e);
     203           1 :         Opt::IntervalSolver::Var x = solver.solve(prob);
     204           4 :         REQUIRE_THAT(x, WithinAbs(sol, e));
     205             :     }
     206           7 : }

Generated by: LCOV version 1.14