[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 86661b6 22/22: Validate number of iterations
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 86661b6 22/22: Validate number of iterations |
Date: |
Sun, 6 Jun 2021 21:38:03 -0400 (EDT) |
branch: master
commit 86661b679131242219f8e4ee0aa846a2b2c4c3d0
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Validate number of iterations
Testing the number of iterations has some power to identify errors in
translation from the ALGOL, so it would be foolhardy to omit it.
---
zero_test.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/zero_test.cpp b/zero_test.cpp
index 856bcf4..176806d 100644
--- a/zero_test.cpp
+++ b/zero_test.cpp
@@ -40,6 +40,30 @@ double max_err(double zeta, double tol)
{
return 6.0 * epsilon * std::fabs(zeta) + 2.0 * tol;
}
+
+/// AfMWD eq. 3.3: maximum number of iterations for bisection.
+///
+/// The return value, k+1, is the exact number of function
+/// evaluations unless f vanishes early, as Brent explains in the
+/// paragraph following eq. 3.3 .
+///
+/// static_cast<int> is exact for any number of iterations that
+/// can be counted by an 'int'.
+
+int max_n_iter_bolzano(double a, double b, double tol, double zeta)
+{
+ double delta = 2.0 * epsilon * std::fabs(zeta) + tol;
+ double k = std::ceil(std::log2(std::fabs(b - a) / delta));
+ return 1 + static_cast<int>(k);
+}
+
+/// AfMWD eq. 3.3: maximum number of iterations for Brent's method.
+
+int max_n_iter_brent(double a, double b, double tol, double zeta)
+{
+ int k_plus_one = max_n_iter_bolzano(a, b, tol, zeta);
+ return k_plus_one * k_plus_one - 2;
+}
} // Unnamed namespace.
template<typename F>
@@ -241,12 +265,22 @@ int test_main(int, char*[])
e_nineteenth e_19;
+ // Number of iterations:
+ // 195 Brent's table 4.1 (IBM 360)
+ // 171 x86_64 brent_zero (IEEE 754)
+ // 169 x86_64 decimal_root (differs slightly due to rounding)
double d = brent_zero(-1.0, 4.0, 1.0e-20, e_19);
LMI_TEST(std::fabs(d) <= epsilon);
r = decimal_root(-1.0, 4.0, bias_none, 20, e_19);
LMI_TEST(root_is_valid == r.validity);
LMI_TEST(std::fabs(r.root) <= epsilon);
+ // Assertions labelled 'weak' test the number of iterations
+ // against empirical measurements (with various architectures)
+ // rather than a theoretical maximum. Perhaps they'll always
+ // succeed, because floating-point behavior is determinate;
+ // but small variations betoken no catastrophe.
+ LMI_TEST(169 <= r.n_iter && r.n_iter <= 173); // weak
d = brent_zero(-100.0, 100.0, 0.5, eq_2_1);
double zeta = -100.0;
@@ -256,15 +290,44 @@ int test_main(int, char*[])
r = decimal_root(-100.0, 100.0, bias_none, 0, eq_2_1);
LMI_TEST(root_is_valid == r.validity);
LMI_TEST(-100.0 <= r.root && r.root <= eq_2_1_upper);
+ LMI_TEST(10 == max_n_iter_bolzano(-100.0, 100.0, 0.5, -100.0));
+ LMI_TEST(98 == max_n_iter_brent (-100.0, 100.0, 0.5, -100.0));
+ LMI_TEST(r.n_iter <= 98);
+ LMI_TEST_EQUAL(20, r.n_iter); // weak
+ // Number of iterations required:
+ // 23 for brent_zero() [above]
+ // 20 for decimal_root()
+ // Presumably the difference is due to rounding.
r = decimal_root(-100.0, 100.0, bias_none, 20, eq_2_1);
LMI_TEST(root_is_valid == r.validity);
// Twenty-decimal rounding makes the epsilon term vanish.
LMI_TEST(-100.0 <= r.root && r.root <= zeta + max_err(zeta, 0.0));
+ LMI_TEST( 53 == max_n_iter_bolzano(-100.0, 100.0, 0.0, -100.0));
+ LMI_TEST(2807 == max_n_iter_brent (-100.0, 100.0, 0.0, -100.0));
+ LMI_TEST(r.n_iter <= 2807);
+ LMI_TEST_EQUAL(66, r.n_iter); // weak
r = decimal_root(-1.0, 1.0, bias_none, 13, signum_offset);
LMI_TEST(root_is_valid == r.validity);
LMI_TEST(materially_equal(-1.0 / 3.0, r.root));
+ zeta = 1.0 / 3.0;
+ double tol = 0.5 * 1.0e-13;
+ LMI_TEST_EQUAL( 47, max_n_iter_bolzano(-1.0, 1.0, tol, zeta));
+ LMI_TEST_EQUAL(2207, max_n_iter_brent (-1.0, 1.0, tol, zeta));
+ LMI_TEST(r.n_iter <= 2207);
+ // Here, decimal_root() always chooses the bisection technique.
+ LMI_TEST(46 <= r.n_iter && r.n_iter <= 47); // weak
+
+ r = decimal_root(-1.0, 1.0, bias_none, 16, signum_offset);
+ LMI_TEST(root_is_valid == r.validity);
+ LMI_TEST(materially_equal(-1.0 / 3.0, r.root));
+ tol = 0.5 * 1.0e-16;
+ LMI_TEST_EQUAL( 55, max_n_iter_bolzano(-1.0, 1.0, tol, zeta));
+ LMI_TEST_EQUAL(3023, max_n_iter_brent (-1.0, 1.0, tol, zeta));
+ LMI_TEST(r.n_iter <= 3023);
+ // Here, decimal_root() always chooses the bisection technique.
+ LMI_TEST_EQUAL(55, r.n_iter); // weak
e_former_rounding_problem e_frp;
r = decimal_root(0.12609, 0.12611, bias_lower, 5, e_frp);
- [lmi-commits] [lmi] master 2391573 11/22: Rename a variable, for terseness, (continued)
- [lmi-commits] [lmi] master 2391573 11/22: Rename a variable, for terseness, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master b14e318 18/22: Adjust tolerance for a particular unit-test function, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 1c448d8 20/22: Augment decimal_root()'s return type, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 53c3513 21/22: Reorder unit tests, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 5cf534c 04/22: Constify, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 3865416 10/22: Rearrange certain unit tests, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 1f8316c 16/22: Test actual return value, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 3d4adb3 17/22: Clarify, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 200e756 03/22: Clarify documentation, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 1b0ed06 12/22: Change argument order, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 86661b6 22/22: Validate number of iterations,
Greg Chicares <=
- [lmi-commits] [lmi] master 9027fef 13/22: Refactor, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master c1a020a 14/22: Include appropriate headers, and say why they're included, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 4d9ed2d 19/22: Abstract the Brent error limit, Greg Chicares, 2021/06/06
- [lmi-commits] [lmi] master 776f09c 15/22: Return a struct rather than a std::pair, Greg Chicares, 2021/06/06