numeric_diff_cost_function_test.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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: keir@google.com (Keir Mierle)
  30. // tbennun@gmail.com (Tal Ben-Nun)
  31. #include "ceres/numeric_diff_cost_function.h"
  32. #include <algorithm>
  33. #include <array>
  34. #include <cmath>
  35. #include <memory>
  36. #include <random>
  37. #include <string>
  38. #include <vector>
  39. #include "ceres/array_utils.h"
  40. #include "ceres/numeric_diff_test_utils.h"
  41. #include "ceres/test_util.h"
  42. #include "ceres/types.h"
  43. #include "glog/logging.h"
  44. #include "gtest/gtest.h"
  45. namespace ceres {
  46. namespace internal {
  47. TEST(NumericDiffCostFunction, EasyCaseFunctorCentralDifferences) {
  48. auto cost_function =
  49. std::make_unique<NumericDiffCostFunction<EasyFunctor,
  50. CENTRAL,
  51. 3, // number of residuals
  52. 5, // size of x1
  53. 5 // size of x2
  54. >>(new EasyFunctor);
  55. EasyFunctor functor;
  56. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
  57. }
  58. TEST(NumericDiffCostFunction, EasyCaseFunctorForwardDifferences) {
  59. auto cost_function =
  60. std::make_unique<NumericDiffCostFunction<EasyFunctor,
  61. FORWARD,
  62. 3, // number of residuals
  63. 5, // size of x1
  64. 5 // size of x2
  65. >>(new EasyFunctor);
  66. EasyFunctor functor;
  67. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
  68. }
  69. TEST(NumericDiffCostFunction, EasyCaseFunctorRidders) {
  70. auto cost_function =
  71. std::make_unique<NumericDiffCostFunction<EasyFunctor,
  72. RIDDERS,
  73. 3, // number of residuals
  74. 5, // size of x1
  75. 5 // size of x2
  76. >>(new EasyFunctor);
  77. EasyFunctor functor;
  78. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
  79. }
  80. TEST(NumericDiffCostFunction, EasyCaseCostFunctionCentralDifferences) {
  81. auto cost_function =
  82. std::make_unique<NumericDiffCostFunction<EasyCostFunction,
  83. CENTRAL,
  84. 3, // number of residuals
  85. 5, // size of x1
  86. 5 // size of x2
  87. >>(new EasyCostFunction,
  88. TAKE_OWNERSHIP);
  89. EasyFunctor functor;
  90. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
  91. }
  92. TEST(NumericDiffCostFunction, EasyCaseCostFunctionForwardDifferences) {
  93. auto cost_function =
  94. std::make_unique<NumericDiffCostFunction<EasyCostFunction,
  95. FORWARD,
  96. 3, // number of residuals
  97. 5, // size of x1
  98. 5 // size of x2
  99. >>(new EasyCostFunction,
  100. TAKE_OWNERSHIP);
  101. EasyFunctor functor;
  102. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
  103. }
  104. TEST(NumericDiffCostFunction, EasyCaseCostFunctionRidders) {
  105. auto cost_function =
  106. std::make_unique<NumericDiffCostFunction<EasyCostFunction,
  107. RIDDERS,
  108. 3, // number of residuals
  109. 5, // size of x1
  110. 5 // size of x2
  111. >>(new EasyCostFunction,
  112. TAKE_OWNERSHIP);
  113. EasyFunctor functor;
  114. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
  115. }
  116. TEST(NumericDiffCostFunction, TranscendentalCaseFunctorCentralDifferences) {
  117. auto cost_function =
  118. std::make_unique<NumericDiffCostFunction<TranscendentalFunctor,
  119. CENTRAL,
  120. 2, // number of residuals
  121. 5, // size of x1
  122. 5 // size of x2
  123. >>(new TranscendentalFunctor);
  124. TranscendentalFunctor functor;
  125. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
  126. }
  127. TEST(NumericDiffCostFunction, TranscendentalCaseFunctorForwardDifferences) {
  128. auto cost_function =
  129. std::make_unique<NumericDiffCostFunction<TranscendentalFunctor,
  130. FORWARD,
  131. 2, // number of residuals
  132. 5, // size of x1
  133. 5 // size of x2
  134. >>(new TranscendentalFunctor);
  135. TranscendentalFunctor functor;
  136. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
  137. }
  138. TEST(NumericDiffCostFunction, TranscendentalCaseFunctorRidders) {
  139. NumericDiffOptions options;
  140. // Using a smaller initial step size to overcome oscillatory function
  141. // behavior.
  142. options.ridders_relative_initial_step_size = 1e-3;
  143. auto cost_function =
  144. std::make_unique<NumericDiffCostFunction<TranscendentalFunctor,
  145. RIDDERS,
  146. 2, // number of residuals
  147. 5, // size of x1
  148. 5 // size of x2
  149. >>(
  150. new TranscendentalFunctor, TAKE_OWNERSHIP, 2, options);
  151. TranscendentalFunctor functor;
  152. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
  153. }
  154. TEST(NumericDiffCostFunction,
  155. TranscendentalCaseCostFunctionCentralDifferences) {
  156. auto cost_function =
  157. std::make_unique<NumericDiffCostFunction<TranscendentalCostFunction,
  158. CENTRAL,
  159. 2, // number of residuals
  160. 5, // size of x1
  161. 5 // size of x2
  162. >>(
  163. new TranscendentalCostFunction, TAKE_OWNERSHIP);
  164. TranscendentalFunctor functor;
  165. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
  166. }
  167. TEST(NumericDiffCostFunction,
  168. TranscendentalCaseCostFunctionForwardDifferences) {
  169. auto cost_function =
  170. std::make_unique<NumericDiffCostFunction<TranscendentalCostFunction,
  171. FORWARD,
  172. 2, // number of residuals
  173. 5, // size of x1
  174. 5 // size of x2
  175. >>(
  176. new TranscendentalCostFunction, TAKE_OWNERSHIP);
  177. TranscendentalFunctor functor;
  178. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
  179. }
  180. TEST(NumericDiffCostFunction, TranscendentalCaseCostFunctionRidders) {
  181. NumericDiffOptions options;
  182. // Using a smaller initial step size to overcome oscillatory function
  183. // behavior.
  184. options.ridders_relative_initial_step_size = 1e-3;
  185. auto cost_function =
  186. std::make_unique<NumericDiffCostFunction<TranscendentalCostFunction,
  187. RIDDERS,
  188. 2, // number of residuals
  189. 5, // size of x1
  190. 5 // size of x2
  191. >>(
  192. new TranscendentalCostFunction, TAKE_OWNERSHIP, 2, options);
  193. TranscendentalFunctor functor;
  194. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
  195. }
  196. template <int num_rows, int num_cols>
  197. class SizeTestingCostFunction : public SizedCostFunction<num_rows, num_cols> {
  198. public:
  199. bool Evaluate(double const* const* parameters,
  200. double* residuals,
  201. double** jacobians) const final {
  202. return true;
  203. }
  204. };
  205. // As described in
  206. // http://forum.kde.org/viewtopic.php?f=74&t=98536#p210774
  207. // Eigen3 has restrictions on the Row/Column major storage of vectors,
  208. // depending on their dimensions. This test ensures that the correct
  209. // templates are instantiated for various shapes of the Jacobian
  210. // matrix.
  211. TEST(NumericDiffCostFunction, EigenRowMajorColMajorTest) {
  212. std::unique_ptr<CostFunction> cost_function = std::make_unique<
  213. NumericDiffCostFunction<SizeTestingCostFunction<1, 1>, CENTRAL, 1, 1>>(
  214. new SizeTestingCostFunction<1, 1>, ceres::TAKE_OWNERSHIP);
  215. cost_function = std::make_unique<
  216. NumericDiffCostFunction<SizeTestingCostFunction<2, 1>, CENTRAL, 2, 1>>(
  217. new SizeTestingCostFunction<2, 1>, ceres::TAKE_OWNERSHIP);
  218. cost_function = std::make_unique<
  219. NumericDiffCostFunction<SizeTestingCostFunction<1, 2>, CENTRAL, 1, 2>>(
  220. new SizeTestingCostFunction<1, 2>, ceres::TAKE_OWNERSHIP);
  221. cost_function = std::make_unique<
  222. NumericDiffCostFunction<SizeTestingCostFunction<2, 2>, CENTRAL, 2, 2>>(
  223. new SizeTestingCostFunction<2, 2>, ceres::TAKE_OWNERSHIP);
  224. cost_function = std::make_unique<
  225. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 1>>(
  226. new EasyFunctor, TAKE_OWNERSHIP, 1);
  227. cost_function = std::make_unique<
  228. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 1>>(
  229. new EasyFunctor, TAKE_OWNERSHIP, 2);
  230. cost_function = std::make_unique<
  231. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 2>>(
  232. new EasyFunctor, TAKE_OWNERSHIP, 1);
  233. cost_function = std::make_unique<
  234. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 2>>(
  235. new EasyFunctor, TAKE_OWNERSHIP, 2);
  236. cost_function = std::make_unique<
  237. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 2, 1>>(
  238. new EasyFunctor, TAKE_OWNERSHIP, 1);
  239. cost_function = std::make_unique<
  240. NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 2, 1>>(
  241. new EasyFunctor, TAKE_OWNERSHIP, 2);
  242. }
  243. TEST(NumericDiffCostFunction,
  244. EasyCaseFunctorCentralDifferencesAndDynamicNumResiduals) {
  245. auto cost_function =
  246. std::make_unique<NumericDiffCostFunction<EasyFunctor,
  247. CENTRAL,
  248. ceres::DYNAMIC,
  249. 5, // size of x1
  250. 5 // size of x2
  251. >>(
  252. new EasyFunctor, TAKE_OWNERSHIP, 3);
  253. EasyFunctor functor;
  254. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
  255. }
  256. TEST(NumericDiffCostFunction, ExponentialFunctorRidders) {
  257. auto cost_function =
  258. std::make_unique<NumericDiffCostFunction<ExponentialFunctor,
  259. RIDDERS,
  260. 1, // number of residuals
  261. 1 // size of x1
  262. >>(new ExponentialFunctor);
  263. ExponentialFunctor functor;
  264. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
  265. }
  266. TEST(NumericDiffCostFunction, ExponentialCostFunctionRidders) {
  267. auto cost_function =
  268. std::make_unique<NumericDiffCostFunction<ExponentialCostFunction,
  269. RIDDERS,
  270. 1, // number of residuals
  271. 1 // size of x1
  272. >>(new ExponentialCostFunction);
  273. ExponentialFunctor functor;
  274. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
  275. }
  276. TEST(NumericDiffCostFunction, RandomizedFunctorRidders) {
  277. std::mt19937 prng;
  278. NumericDiffOptions options;
  279. // Larger initial step size is chosen to produce robust results in the
  280. // presence of random noise.
  281. options.ridders_relative_initial_step_size = 10.0;
  282. auto cost_function =
  283. std::make_unique<NumericDiffCostFunction<RandomizedFunctor,
  284. RIDDERS,
  285. 1, // number of residuals
  286. 1 // size of x1
  287. >>(
  288. new RandomizedFunctor(kNoiseFactor, prng),
  289. TAKE_OWNERSHIP,
  290. 1,
  291. options);
  292. RandomizedFunctor functor(kNoiseFactor, prng);
  293. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
  294. }
  295. TEST(NumericDiffCostFunction, RandomizedCostFunctionRidders) {
  296. std::mt19937 prng;
  297. NumericDiffOptions options;
  298. // Larger initial step size is chosen to produce robust results in the
  299. // presence of random noise.
  300. options.ridders_relative_initial_step_size = 10.0;
  301. auto cost_function =
  302. std::make_unique<NumericDiffCostFunction<RandomizedCostFunction,
  303. RIDDERS,
  304. 1, // number of residuals
  305. 1 // size of x1
  306. >>(
  307. new RandomizedCostFunction(kNoiseFactor, prng),
  308. TAKE_OWNERSHIP,
  309. 1,
  310. options);
  311. RandomizedFunctor functor(kNoiseFactor, prng);
  312. functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
  313. }
  314. struct OnlyFillsOneOutputFunctor {
  315. bool operator()(const double* x, double* output) const {
  316. output[0] = x[0];
  317. return true;
  318. }
  319. };
  320. TEST(NumericDiffCostFunction, PartiallyFilledResidualShouldFailEvaluation) {
  321. double parameter = 1.0;
  322. double jacobian[2];
  323. double residuals[2];
  324. double* parameters[] = {&parameter};
  325. double* jacobians[] = {jacobian};
  326. auto cost_function = std::make_unique<
  327. NumericDiffCostFunction<OnlyFillsOneOutputFunctor, CENTRAL, 2, 1>>(
  328. new OnlyFillsOneOutputFunctor);
  329. InvalidateArray(2, jacobian);
  330. InvalidateArray(2, residuals);
  331. EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, jacobians));
  332. EXPECT_FALSE(IsArrayValid(2, residuals));
  333. InvalidateArray(2, residuals);
  334. EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, nullptr));
  335. // We are only testing residuals here, because the Jacobians are
  336. // computed using finite differencing from the residuals, so unless
  337. // we introduce a validation step after every evaluation of
  338. // residuals inside NumericDiffCostFunction, there is no way of
  339. // ensuring that the Jacobian array is invalid.
  340. EXPECT_FALSE(IsArrayValid(2, residuals));
  341. }
  342. TEST(NumericDiffCostFunction, ParameterBlockConstant) {
  343. constexpr int kNumResiduals = 3;
  344. constexpr int kX1 = 5;
  345. constexpr int kX2 = 5;
  346. auto cost_function = std::make_unique<
  347. NumericDiffCostFunction<EasyFunctor, CENTRAL, kNumResiduals, kX1, kX2>>(
  348. new EasyFunctor);
  349. // Prepare the parameters and residuals.
  350. std::array<double, kX1> x1{1e-64, 2.0, 3.0, 4.0, 5.0};
  351. std::array<double, kX2> x2{9.0, 9.0, 5.0, 5.0, 1.0};
  352. std::array<double*, 2> parameter_blocks{x1.data(), x2.data()};
  353. std::vector<double> residuals(kNumResiduals, -100000);
  354. // Evaluate the full jacobian.
  355. std::vector<std::vector<double>> jacobian_full_vect(2);
  356. jacobian_full_vect[0].resize(kNumResiduals * kX1, -100000);
  357. jacobian_full_vect[1].resize(kNumResiduals * kX2, -100000);
  358. {
  359. std::array<double*, 2> jacobian{jacobian_full_vect[0].data(),
  360. jacobian_full_vect[1].data()};
  361. ASSERT_TRUE(cost_function->Evaluate(
  362. parameter_blocks.data(), residuals.data(), jacobian.data()));
  363. }
  364. // Evaluate and check jacobian when first parameter block is constant.
  365. {
  366. std::vector<double> jacobian_vect(kNumResiduals * kX2, -100000);
  367. std::array<double*, 2> jacobian{nullptr, jacobian_vect.data()};
  368. ASSERT_TRUE(cost_function->Evaluate(
  369. parameter_blocks.data(), residuals.data(), jacobian.data()));
  370. for (int i = 0; i < kNumResiduals * kX2; ++i) {
  371. EXPECT_DOUBLE_EQ(jacobian_full_vect[1][i], jacobian_vect[i]);
  372. }
  373. }
  374. // Evaluate and check jacobian when second parameter block is constant.
  375. {
  376. std::vector<double> jacobian_vect(kNumResiduals * kX1, -100000);
  377. std::array<double*, 2> jacobian{jacobian_vect.data(), nullptr};
  378. ASSERT_TRUE(cost_function->Evaluate(
  379. parameter_blocks.data(), residuals.data(), jacobian.data()));
  380. for (int i = 0; i < kNumResiduals * kX1; ++i) {
  381. EXPECT_DOUBLE_EQ(jacobian_full_vect[0][i], jacobian_vect[i]);
  382. }
  383. }
  384. }
  385. } // namespace internal
  386. } // namespace ceres