Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 1 | // Ceres Solver - A fast non-linear least squares minimizer |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 2 | // Copyright 2024 Google Inc. All rights reserved. |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 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 | |
| 31 | #include "ceres/autodiff_cost_function.h" |
| 32 | |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 33 | #include <memory> |
| 34 | |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 35 | #include "ceres/array_utils.h" |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 36 | #include "ceres/cost_function.h" |
| 37 | #include "gtest/gtest.h" |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 38 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 39 | namespace ceres::internal { |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 40 | |
| 41 | class BinaryScalarCost { |
| 42 | public: |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 43 | explicit BinaryScalarCost(double a) : a_(a) {} |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 44 | template <typename T> |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 45 | bool operator()(const T* const x, const T* const y, T* cost) const { |
| 46 | cost[0] = x[0] * y[0] + x[1] * y[1] - T(a_); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 47 | return true; |
| 48 | } |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 49 | |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 50 | private: |
| 51 | double a_; |
| 52 | }; |
| 53 | |
| 54 | TEST(AutodiffCostFunction, BilinearDifferentiationTest) { |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 55 | CostFunction* cost_function = |
| 56 | new AutoDiffCostFunction<BinaryScalarCost, 1, 2, 2>( |
| 57 | new BinaryScalarCost(1.0)); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 58 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 59 | auto** parameters = new double*[2]; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 60 | parameters[0] = new double[2]; |
| 61 | parameters[1] = new double[2]; |
| 62 | |
| 63 | parameters[0][0] = 1; |
| 64 | parameters[0][1] = 2; |
| 65 | |
| 66 | parameters[1][0] = 3; |
| 67 | parameters[1][1] = 4; |
| 68 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 69 | auto** jacobians = new double*[2]; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 70 | jacobians[0] = new double[2]; |
| 71 | jacobians[1] = new double[2]; |
| 72 | |
| 73 | double residuals = 0.0; |
| 74 | |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 75 | cost_function->Evaluate(parameters, &residuals, nullptr); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 76 | EXPECT_EQ(10.0, residuals); |
| 77 | |
| 78 | cost_function->Evaluate(parameters, &residuals, jacobians); |
| 79 | EXPECT_EQ(10.0, residuals); |
| 80 | |
| 81 | EXPECT_EQ(3, jacobians[0][0]); |
| 82 | EXPECT_EQ(4, jacobians[0][1]); |
| 83 | EXPECT_EQ(1, jacobians[1][0]); |
| 84 | EXPECT_EQ(2, jacobians[1][1]); |
| 85 | |
| 86 | delete[] jacobians[0]; |
| 87 | delete[] jacobians[1]; |
| 88 | delete[] parameters[0]; |
| 89 | delete[] parameters[1]; |
| 90 | delete[] jacobians; |
| 91 | delete[] parameters; |
| 92 | delete cost_function; |
| 93 | } |
| 94 | |
| 95 | struct TenParameterCost { |
| 96 | template <typename T> |
| 97 | bool operator()(const T* const x0, |
| 98 | const T* const x1, |
| 99 | const T* const x2, |
| 100 | const T* const x3, |
| 101 | const T* const x4, |
| 102 | const T* const x5, |
| 103 | const T* const x6, |
| 104 | const T* const x7, |
| 105 | const T* const x8, |
| 106 | const T* const x9, |
| 107 | T* cost) const { |
| 108 | cost[0] = *x0 + *x1 + *x2 + *x3 + *x4 + *x5 + *x6 + *x7 + *x8 + *x9; |
| 109 | return true; |
| 110 | } |
| 111 | }; |
| 112 | |
| 113 | TEST(AutodiffCostFunction, ManyParameterAutodiffInstantiates) { |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 114 | CostFunction* cost_function = |
| 115 | new AutoDiffCostFunction<TenParameterCost, |
| 116 | 1, |
| 117 | 1, |
| 118 | 1, |
| 119 | 1, |
| 120 | 1, |
| 121 | 1, |
| 122 | 1, |
| 123 | 1, |
| 124 | 1, |
| 125 | 1, |
| 126 | 1>(new TenParameterCost); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 127 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 128 | auto** parameters = new double*[10]; |
| 129 | auto** jacobians = new double*[10]; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 130 | for (int i = 0; i < 10; ++i) { |
| 131 | parameters[i] = new double[1]; |
| 132 | parameters[i][0] = i; |
| 133 | jacobians[i] = new double[1]; |
| 134 | } |
| 135 | |
| 136 | double residuals = 0.0; |
| 137 | |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 138 | cost_function->Evaluate(parameters, &residuals, nullptr); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 139 | EXPECT_EQ(45.0, residuals); |
| 140 | |
| 141 | cost_function->Evaluate(parameters, &residuals, jacobians); |
| 142 | EXPECT_EQ(residuals, 45.0); |
| 143 | for (int i = 0; i < 10; ++i) { |
| 144 | EXPECT_EQ(1.0, jacobians[i][0]); |
| 145 | } |
| 146 | |
| 147 | for (int i = 0; i < 10; ++i) { |
| 148 | delete[] jacobians[i]; |
| 149 | delete[] parameters[i]; |
| 150 | } |
| 151 | delete[] jacobians; |
| 152 | delete[] parameters; |
| 153 | delete cost_function; |
| 154 | } |
| 155 | |
| 156 | struct OnlyFillsOneOutputFunctor { |
| 157 | template <typename T> |
| 158 | bool operator()(const T* x, T* output) const { |
| 159 | output[0] = x[0]; |
| 160 | return true; |
| 161 | } |
| 162 | }; |
| 163 | |
| 164 | TEST(AutoDiffCostFunction, PartiallyFilledResidualShouldFailEvaluation) { |
| 165 | double parameter = 1.0; |
| 166 | double jacobian[2]; |
| 167 | double residuals[2]; |
| 168 | double* parameters[] = {¶meter}; |
| 169 | double* jacobians[] = {jacobian}; |
| 170 | |
| 171 | std::unique_ptr<CostFunction> cost_function( |
| 172 | new AutoDiffCostFunction<OnlyFillsOneOutputFunctor, 2, 1>( |
| 173 | new OnlyFillsOneOutputFunctor)); |
| 174 | InvalidateArray(2, jacobian); |
| 175 | InvalidateArray(2, residuals); |
| 176 | EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, jacobians)); |
| 177 | EXPECT_FALSE(IsArrayValid(2, jacobian)); |
| 178 | EXPECT_FALSE(IsArrayValid(2, residuals)); |
| 179 | } |
| 180 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 181 | TEST(AutodiffCostFunction, ArgumentForwarding) { |
| 182 | // No narrowing conversion warning should be emitted |
| 183 | auto cost_function1 = |
| 184 | std::make_unique<AutoDiffCostFunction<BinaryScalarCost, 1, 2, 2>>(1); |
| 185 | auto cost_function2 = |
| 186 | std::make_unique<AutoDiffCostFunction<BinaryScalarCost, 1, 2, 2>>(2.0); |
| 187 | // Default constructible functor |
| 188 | auto cost_function3 = |
| 189 | std::make_unique<AutoDiffCostFunction<OnlyFillsOneOutputFunctor, 1, 1>>(); |
| 190 | } |
| 191 | |
| 192 | TEST(AutodiffCostFunction, UniquePtrCtor) { |
| 193 | auto cost_function1 = |
| 194 | std::make_unique<AutoDiffCostFunction<BinaryScalarCost, 1, 2, 2>>( |
| 195 | std::make_unique<BinaryScalarCost>(1)); |
| 196 | auto cost_function2 = |
| 197 | std::make_unique<AutoDiffCostFunction<BinaryScalarCost, 1, 2, 2>>( |
| 198 | std::make_unique<BinaryScalarCost>(2.0)); |
| 199 | } |
| 200 | |
| 201 | } // namespace ceres::internal |