corrector_test.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2022 Google Inc. All rights reserved.
  3. // http://ceres-solver.org/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. //
  8. // * Redistributions of source code must retain the above copyright notice,
  9. // this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. // * Neither the name of Google Inc. nor the names of its contributors may be
  14. // used to endorse or promote products derived from this software without
  15. // specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. // POSSIBILITY OF SUCH DAMAGE.
  28. //
  29. // Author: sameeragarwal@google.com (Sameer Agarwal)
  30. #include "ceres/corrector.h"
  31. #include <algorithm>
  32. #include <cmath>
  33. #include <cstring>
  34. #include <random>
  35. #include "ceres/internal/eigen.h"
  36. #include "gtest/gtest.h"
  37. namespace ceres {
  38. namespace internal {
  39. // If rho[1] is zero, the Corrector constructor should crash.
  40. TEST(Corrector, ZeroGradientDeathTest) {
  41. const double kRho[] = {0.0, 0.0, 1.0};
  42. EXPECT_DEATH_IF_SUPPORTED({ Corrector c(1.0, kRho); }, ".*");
  43. }
  44. // If rho[1] is negative, the Corrector constructor should crash.
  45. TEST(Corrector, NegativeGradientDeathTest) {
  46. const double kRho[] = {0.0, -0.1, 1.0};
  47. EXPECT_DEATH_IF_SUPPORTED({ Corrector c(1.0, kRho); }, ".*");
  48. }
  49. TEST(Corrector, ScalarCorrection) {
  50. double residuals = sqrt(3.0);
  51. double jacobian = 10.0;
  52. double sq_norm = residuals * residuals;
  53. const double kRho[] = {sq_norm, 0.1, -0.01};
  54. // In light of the rho'' < 0 clamping now implemented in
  55. // corrector.cc, alpha = 0 whenever rho'' < 0.
  56. const double kAlpha = 0.0;
  57. // Thus the expected value of the residual is
  58. // residual[i] * sqrt(kRho[1]) / (1.0 - kAlpha).
  59. const double kExpectedResidual = residuals * sqrt(kRho[1]) / (1 - kAlpha);
  60. // The jacobian in this case will be
  61. // sqrt(kRho[1]) * (1 - kAlpha) * jacobian.
  62. const double kExpectedJacobian = sqrt(kRho[1]) * (1 - kAlpha) * jacobian;
  63. Corrector c(sq_norm, kRho);
  64. c.CorrectJacobian(1.0, 1.0, &residuals, &jacobian);
  65. c.CorrectResiduals(1.0, &residuals);
  66. ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
  67. ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
  68. }
  69. TEST(Corrector, ScalarCorrectionZeroResidual) {
  70. double residuals = 0.0;
  71. double jacobian = 10.0;
  72. double sq_norm = residuals * residuals;
  73. const double kRho[] = {0.0, 0.1, -0.01};
  74. Corrector c(sq_norm, kRho);
  75. // The alpha equation is
  76. // 1/2 alpha^2 - alpha + 0.0 = 0.
  77. // i.e. alpha = 1.0 - sqrt(1.0).
  78. // alpha = 0.0.
  79. // Thus the expected value of the residual is
  80. // residual[i] * sqrt(kRho[1])
  81. const double kExpectedResidual = residuals * sqrt(kRho[1]);
  82. // The jacobian in this case will be
  83. // sqrt(kRho[1]) * jacobian.
  84. const double kExpectedJacobian = sqrt(kRho[1]) * jacobian;
  85. c.CorrectJacobian(1, 1, &residuals, &jacobian);
  86. c.CorrectResiduals(1, &residuals);
  87. ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
  88. ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
  89. }
  90. // Scaling behaviour for one dimensional functions.
  91. TEST(Corrector, ScalarCorrectionAlphaClamped) {
  92. double residuals = sqrt(3.0);
  93. double jacobian = 10.0;
  94. double sq_norm = residuals * residuals;
  95. const double kRho[] = {3, 0.1, -0.1};
  96. // rho[2] < 0 -> alpha = 0.0
  97. const double kAlpha = 0.0;
  98. // Thus the expected value of the residual is
  99. // residual[i] * sqrt(kRho[1]) / (1.0 - kAlpha).
  100. const double kExpectedResidual = residuals * sqrt(kRho[1]) / (1.0 - kAlpha);
  101. // The jacobian in this case will be scaled by
  102. // sqrt(rho[1]) * (1 - alpha) * J.
  103. const double kExpectedJacobian = sqrt(kRho[1]) * (1.0 - kAlpha) * jacobian;
  104. Corrector c(sq_norm, kRho);
  105. c.CorrectJacobian(1, 1, &residuals, &jacobian);
  106. c.CorrectResiduals(1, &residuals);
  107. ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
  108. ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
  109. }
  110. // Test that the corrected multidimensional residual and jacobians
  111. // match the expected values and the resulting modified normal
  112. // equations match the robustified gauss newton approximation.
  113. TEST(Corrector, MultidimensionalGaussNewtonApproximation) {
  114. double residuals[3];
  115. double jacobian[2 * 3];
  116. double rho[3];
  117. // Eigen matrix references for linear algebra.
  118. MatrixRef jac(jacobian, 3, 2);
  119. VectorRef res(residuals, 3);
  120. // Ground truth values of the modified jacobian and residuals.
  121. Matrix g_jac(3, 2);
  122. Vector g_res(3);
  123. // Ground truth values of the robustified Gauss-Newton
  124. // approximation.
  125. Matrix g_hess(2, 2);
  126. Vector g_grad(2);
  127. // Corrected hessian and gradient implied by the modified jacobian
  128. // and hessians.
  129. Matrix c_hess(2, 2);
  130. Vector c_grad(2);
  131. std::mt19937 prng;
  132. std::uniform_real_distribution<double> uniform01(0.0, 1.0);
  133. for (int iter = 0; iter < 10000; ++iter) {
  134. // Initialize the jacobian and residual.
  135. for (double& jacobian_entry : jacobian) jacobian_entry = uniform01(prng);
  136. for (double& residual : residuals) residual = uniform01(prng);
  137. const double sq_norm = res.dot(res);
  138. rho[0] = sq_norm;
  139. rho[1] = uniform01(prng);
  140. rho[2] = uniform01(
  141. prng, std::uniform_real_distribution<double>::param_type(-1, 1));
  142. // If rho[2] > 0, then the curvature correction to the correction
  143. // and the gauss newton approximation will match. Otherwise, we
  144. // will clamp alpha to 0.
  145. const double kD = 1 + 2 * rho[2] / rho[1] * sq_norm;
  146. const double kAlpha = (rho[2] > 0.0) ? 1 - sqrt(kD) : 0.0;
  147. // Ground truth values.
  148. g_res = sqrt(rho[1]) / (1.0 - kAlpha) * res;
  149. g_jac =
  150. sqrt(rho[1]) * (jac - kAlpha / sq_norm * res * res.transpose() * jac);
  151. g_grad = rho[1] * jac.transpose() * res;
  152. g_hess = rho[1] * jac.transpose() * jac +
  153. 2.0 * rho[2] * jac.transpose() * res * res.transpose() * jac;
  154. Corrector c(sq_norm, rho);
  155. c.CorrectJacobian(3, 2, residuals, jacobian);
  156. c.CorrectResiduals(3, residuals);
  157. // Corrected gradient and hessian.
  158. c_grad = jac.transpose() * res;
  159. c_hess = jac.transpose() * jac;
  160. ASSERT_NEAR((g_res - res).norm(), 0.0, 1e-10);
  161. ASSERT_NEAR((g_jac - jac).norm(), 0.0, 1e-10);
  162. ASSERT_NEAR((g_grad - c_grad).norm(), 0.0, 1e-10);
  163. }
  164. }
  165. TEST(Corrector, MultidimensionalGaussNewtonApproximationZeroResidual) {
  166. double residuals[3];
  167. double jacobian[2 * 3];
  168. double rho[3];
  169. // Eigen matrix references for linear algebra.
  170. MatrixRef jac(jacobian, 3, 2);
  171. VectorRef res(residuals, 3);
  172. // Ground truth values of the modified jacobian and residuals.
  173. Matrix g_jac(3, 2);
  174. Vector g_res(3);
  175. // Ground truth values of the robustified Gauss-Newton
  176. // approximation.
  177. Matrix g_hess(2, 2);
  178. Vector g_grad(2);
  179. // Corrected hessian and gradient implied by the modified jacobian
  180. // and hessians.
  181. Matrix c_hess(2, 2);
  182. Vector c_grad(2);
  183. std::mt19937 prng;
  184. std::uniform_real_distribution<double> uniform01(0.0, 1.0);
  185. for (int iter = 0; iter < 10000; ++iter) {
  186. // Initialize the jacobian.
  187. for (double& jacobian_entry : jacobian) jacobian_entry = uniform01(prng);
  188. // Zero residuals
  189. res.setZero();
  190. const double sq_norm = res.dot(res);
  191. rho[0] = sq_norm;
  192. rho[1] = uniform01(prng);
  193. rho[2] = uniform01(
  194. prng, std::uniform_real_distribution<double>::param_type(-1, 1));
  195. // Ground truth values.
  196. g_res = sqrt(rho[1]) * res;
  197. g_jac = sqrt(rho[1]) * jac;
  198. g_grad = rho[1] * jac.transpose() * res;
  199. g_hess = rho[1] * jac.transpose() * jac +
  200. 2.0 * rho[2] * jac.transpose() * res * res.transpose() * jac;
  201. Corrector c(sq_norm, rho);
  202. c.CorrectJacobian(3, 2, residuals, jacobian);
  203. c.CorrectResiduals(3, residuals);
  204. // Corrected gradient and hessian.
  205. c_grad = jac.transpose() * res;
  206. c_hess = jac.transpose() * jac;
  207. ASSERT_NEAR((g_res - res).norm(), 0.0, 1e-10);
  208. ASSERT_NEAR((g_jac - jac).norm(), 0.0, 1e-10);
  209. ASSERT_NEAR((g_grad - c_grad).norm(), 0.0, 1e-10);
  210. ASSERT_NEAR((g_hess - c_hess).norm(), 0.0, 1e-10);
  211. }
  212. }
  213. } // namespace internal
  214. } // namespace ceres