Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 1 | // Ceres Solver - A fast non-linear least squares minimizer |
| 2 | // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. |
| 3 | // http://code.google.com/p/ceres-solver/ |
| 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 | // |
| 31 | // Tests shared across evaluators. The tests try all combinations of linear |
| 32 | // solver and num_eliminate_blocks (for schur-based solvers). |
| 33 | |
| 34 | #include "ceres/evaluator.h" |
| 35 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 36 | #include "ceres/casts.h" |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 37 | #include "ceres/cost_function.h" |
| 38 | #include "ceres/crs_matrix.h" |
| 39 | #include "ceres/internal/eigen.h" |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 40 | #include "ceres/internal/scoped_ptr.h" |
| 41 | #include "ceres/local_parameterization.h" |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 42 | #include "ceres/problem_impl.h" |
| 43 | #include "ceres/program.h" |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 44 | #include "ceres/sized_cost_function.h" |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 45 | #include "ceres/sparse_matrix.h" |
| 46 | #include "ceres/types.h" |
| 47 | #include "gtest/gtest.h" |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 48 | |
| 49 | namespace ceres { |
| 50 | namespace internal { |
| 51 | |
| 52 | // TODO(keir): Consider pushing this into a common test utils file. |
| 53 | template<int kFactor, int kNumResiduals, |
| 54 | int N0 = 0, int N1 = 0, int N2 = 0, bool kSucceeds = true> |
| 55 | class ParameterIgnoringCostFunction |
| 56 | : public SizedCostFunction<kNumResiduals, N0, N1, N2> { |
| 57 | typedef SizedCostFunction<kNumResiduals, N0, N1, N2> Base; |
| 58 | public: |
| 59 | virtual bool Evaluate(double const* const* parameters, |
| 60 | double* residuals, |
| 61 | double** jacobians) const { |
| 62 | for (int i = 0; i < Base::num_residuals(); ++i) { |
| 63 | residuals[i] = i + 1; |
| 64 | } |
| 65 | if (jacobians) { |
| 66 | for (int k = 0; k < Base::parameter_block_sizes().size(); ++k) { |
| 67 | // The jacobians here are full sized, but they are transformed in the |
| 68 | // evaluator into the "local" jacobian. In the tests, the "subset |
| 69 | // constant" parameterization is used, which should pick out columns |
| 70 | // from these jacobians. Put values in the jacobian that make this |
| 71 | // obvious; in particular, make the jacobians like this: |
| 72 | // |
| 73 | // 1 2 3 4 ... |
| 74 | // 1 2 3 4 ... .* kFactor |
| 75 | // 1 2 3 4 ... |
| 76 | // |
| 77 | // where the multiplication by kFactor makes it easier to distinguish |
| 78 | // between Jacobians of different residuals for the same parameter. |
| 79 | if (jacobians[k] != NULL) { |
| 80 | MatrixRef jacobian(jacobians[k], |
| 81 | Base::num_residuals(), |
| 82 | Base::parameter_block_sizes()[k]); |
| 83 | for (int j = 0; j < Base::parameter_block_sizes()[k]; ++j) { |
| 84 | jacobian.col(j).setConstant(kFactor * (j + 1)); |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | return kSucceeds; |
| 90 | } |
| 91 | }; |
| 92 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 93 | struct ExpectedEvaluation { |
| 94 | int num_rows; |
| 95 | int num_cols; |
| 96 | double cost; |
| 97 | const double residuals[50]; |
| 98 | const double gradient[50]; |
| 99 | const double jacobian[200]; |
| 100 | }; |
| 101 | |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 102 | void CompareEvaluations(int expected_num_rows, |
| 103 | int expected_num_cols, |
| 104 | double expected_cost, |
| 105 | const double* expected_residuals, |
| 106 | const double* expected_gradient, |
| 107 | const double* expected_jacobian, |
| 108 | const double actual_cost, |
| 109 | const double* actual_residuals, |
| 110 | const double* actual_gradient, |
| 111 | const double* actual_jacobian) { |
| 112 | EXPECT_EQ(expected_cost, actual_cost); |
| 113 | |
| 114 | if (expected_residuals != NULL) { |
| 115 | ConstVectorRef expected_residuals_vector(expected_residuals, |
| 116 | expected_num_rows); |
| 117 | ConstVectorRef actual_residuals_vector(actual_residuals, |
| 118 | expected_num_rows); |
| 119 | EXPECT_TRUE((actual_residuals_vector.array() == |
| 120 | expected_residuals_vector.array()).all()) |
| 121 | << "Actual:\n" << actual_residuals_vector |
| 122 | << "\nExpected:\n" << expected_residuals_vector; |
| 123 | } |
| 124 | |
| 125 | if (expected_gradient != NULL) { |
| 126 | ConstVectorRef expected_gradient_vector(expected_gradient, |
| 127 | expected_num_cols); |
| 128 | ConstVectorRef actual_gradient_vector(actual_gradient, |
| 129 | expected_num_cols); |
| 130 | |
| 131 | EXPECT_TRUE((actual_gradient_vector.array() == |
| 132 | expected_gradient_vector.array()).all()) |
| 133 | << "Actual:\n" << actual_gradient_vector.transpose() |
| 134 | << "\nExpected:\n" << expected_gradient_vector.transpose(); |
| 135 | } |
| 136 | |
| 137 | if (expected_jacobian != NULL) { |
| 138 | ConstMatrixRef expected_jacobian_matrix(expected_jacobian, |
| 139 | expected_num_rows, |
| 140 | expected_num_cols); |
| 141 | ConstMatrixRef actual_jacobian_matrix(actual_jacobian, |
| 142 | expected_num_rows, |
| 143 | expected_num_cols); |
| 144 | EXPECT_TRUE((actual_jacobian_matrix.array() == |
| 145 | expected_jacobian_matrix.array()).all()) |
| 146 | << "Actual:\n" << actual_jacobian_matrix |
| 147 | << "\nExpected:\n" << expected_jacobian_matrix; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 152 | struct EvaluatorTest |
| 153 | : public ::testing::TestWithParam<pair<LinearSolverType, int> > { |
| 154 | Evaluator* CreateEvaluator(Program* program) { |
| 155 | // This program is straight from the ProblemImpl, and so has no index/offset |
| 156 | // yet; compute it here as required by the evalutor implementations. |
| 157 | program->SetParameterOffsetsAndIndex(); |
| 158 | |
| 159 | VLOG(1) << "Creating evaluator with type: " << GetParam().first |
| 160 | << " and num_eliminate_blocks: " << GetParam().second; |
| 161 | Evaluator::Options options; |
| 162 | options.linear_solver_type = GetParam().first; |
| 163 | options.num_eliminate_blocks = GetParam().second; |
| 164 | string error; |
| 165 | return Evaluator::Create(options, program, &error); |
| 166 | } |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 167 | |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 168 | void EvaluateAndCompare(ProblemImpl *problem, |
| 169 | int expected_num_rows, |
| 170 | int expected_num_cols, |
| 171 | double expected_cost, |
| 172 | const double* expected_residuals, |
| 173 | const double* expected_gradient, |
| 174 | const double* expected_jacobian) { |
| 175 | scoped_ptr<Evaluator> evaluator( |
| 176 | CreateEvaluator(problem->mutable_program())); |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 177 | int num_residuals = expected_num_rows; |
| 178 | int num_parameters = expected_num_cols; |
| 179 | |
| 180 | double cost = -1; |
| 181 | |
| 182 | Vector residuals(num_residuals); |
| 183 | residuals.setConstant(-2000); |
| 184 | |
| 185 | Vector gradient(num_parameters); |
| 186 | gradient.setConstant(-3000); |
| 187 | |
| 188 | scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian()); |
| 189 | |
| 190 | ASSERT_EQ(expected_num_rows, evaluator->NumResiduals()); |
| 191 | ASSERT_EQ(expected_num_cols, evaluator->NumEffectiveParameters()); |
| 192 | ASSERT_EQ(expected_num_rows, jacobian->num_rows()); |
| 193 | ASSERT_EQ(expected_num_cols, jacobian->num_cols()); |
| 194 | |
| 195 | vector<double> state(evaluator->NumParameters()); |
| 196 | |
| 197 | ASSERT_TRUE(evaluator->Evaluate( |
| 198 | &state[0], |
| 199 | &cost, |
| 200 | expected_residuals != NULL ? &residuals[0] : NULL, |
| 201 | expected_gradient != NULL ? &gradient[0] : NULL, |
| 202 | expected_jacobian != NULL ? jacobian.get() : NULL)); |
| 203 | |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 204 | Matrix actual_jacobian; |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 205 | if (expected_jacobian != NULL) { |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 206 | jacobian->ToDenseMatrix(&actual_jacobian); |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 207 | } |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 208 | |
| 209 | CompareEvaluations(expected_num_rows, |
| 210 | expected_num_cols, |
| 211 | expected_cost, |
| 212 | expected_residuals, |
| 213 | expected_gradient, |
| 214 | expected_jacobian, |
| 215 | cost, |
| 216 | &residuals[0], |
| 217 | &gradient[0], |
| 218 | actual_jacobian.data()); |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 219 | } |
| 220 | |
| 221 | // Try all combinations of parameters for the evaluator. |
| 222 | void CheckAllEvaluationCombinations(const ExpectedEvaluation &expected) { |
| 223 | for (int i = 0; i < 8; ++i) { |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 224 | EvaluateAndCompare(&problem, |
| 225 | expected.num_rows, |
| 226 | expected.num_cols, |
| 227 | expected.cost, |
| 228 | (i & 1) ? expected.residuals : NULL, |
| 229 | (i & 2) ? expected.gradient : NULL, |
| 230 | (i & 4) ? expected.jacobian : NULL); |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 231 | } |
| 232 | } |
| 233 | |
| 234 | // The values are ignored completely by the cost function. |
| 235 | double x[2]; |
| 236 | double y[3]; |
| 237 | double z[4]; |
| 238 | |
| 239 | ProblemImpl problem; |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 240 | }; |
| 241 | |
| 242 | void SetSparseMatrixConstant(SparseMatrix* sparse_matrix, double value) { |
| 243 | VectorRef(sparse_matrix->mutable_values(), |
| 244 | sparse_matrix->num_nonzeros()).setConstant(value); |
| 245 | } |
| 246 | |
| 247 | TEST_P(EvaluatorTest, SingleResidualProblem) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 248 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>, |
| 249 | NULL, |
| 250 | x, y, z); |
| 251 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 252 | ExpectedEvaluation expected = { |
| 253 | // Rows/columns |
| 254 | 3, 9, |
| 255 | // Cost |
| 256 | 7.0, |
| 257 | // Residuals |
| 258 | { 1.0, 2.0, 3.0 }, |
| 259 | // Gradient |
| 260 | { 6.0, 12.0, // x |
| 261 | 6.0, 12.0, 18.0, // y |
| 262 | 6.0, 12.0, 18.0, 24.0, // z |
| 263 | }, |
| 264 | // Jacobian |
| 265 | // x y z |
| 266 | { 1, 2, 1, 2, 3, 1, 2, 3, 4, |
| 267 | 1, 2, 1, 2, 3, 1, 2, 3, 4, |
| 268 | 1, 2, 1, 2, 3, 1, 2, 3, 4 |
| 269 | } |
| 270 | }; |
| 271 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 272 | } |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 273 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 274 | TEST_P(EvaluatorTest, SingleResidualProblemWithPermutedParameters) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 275 | // Add the parameters in explicit order to force the ordering in the program. |
| 276 | problem.AddParameterBlock(x, 2); |
| 277 | problem.AddParameterBlock(y, 3); |
| 278 | problem.AddParameterBlock(z, 4); |
| 279 | |
| 280 | // Then use a cost function which is similar to the others, but swap around |
| 281 | // the ordering of the parameters to the cost function. This shouldn't affect |
| 282 | // the jacobian evaluation, but requires explicit handling in the evaluators. |
| 283 | // At one point the compressed row evaluator had a bug that went undetected |
| 284 | // for a long time, since by chance most users added parameters to the problem |
| 285 | // in the same order that they occured as parameters to a cost function. |
| 286 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 4, 3, 2>, |
| 287 | NULL, |
| 288 | z, y, x); |
| 289 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 290 | ExpectedEvaluation expected = { |
| 291 | // Rows/columns |
| 292 | 3, 9, |
| 293 | // Cost |
| 294 | 7.0, |
| 295 | // Residuals |
| 296 | { 1.0, 2.0, 3.0 }, |
| 297 | // Gradient |
| 298 | { 6.0, 12.0, // x |
| 299 | 6.0, 12.0, 18.0, // y |
| 300 | 6.0, 12.0, 18.0, 24.0, // z |
| 301 | }, |
| 302 | // Jacobian |
| 303 | // x y z |
| 304 | { 1, 2, 1, 2, 3, 1, 2, 3, 4, |
| 305 | 1, 2, 1, 2, 3, 1, 2, 3, 4, |
| 306 | 1, 2, 1, 2, 3, 1, 2, 3, 4 |
| 307 | } |
| 308 | }; |
| 309 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 310 | } |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 311 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 312 | TEST_P(EvaluatorTest, SingleResidualProblemWithNuisanceParameters) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 313 | // These parameters are not used. |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 314 | double a[2]; |
| 315 | double b[1]; |
| 316 | double c[1]; |
| 317 | double d[3]; |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 318 | |
| 319 | // Add the parameters in a mixed order so the Jacobian is "checkered" with the |
| 320 | // values from the other parameters. |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 321 | problem.AddParameterBlock(a, 2); |
| 322 | problem.AddParameterBlock(x, 2); |
| 323 | problem.AddParameterBlock(b, 1); |
| 324 | problem.AddParameterBlock(y, 3); |
| 325 | problem.AddParameterBlock(c, 1); |
| 326 | problem.AddParameterBlock(z, 4); |
| 327 | problem.AddParameterBlock(d, 3); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 328 | |
| 329 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>, |
| 330 | NULL, |
| 331 | x, y, z); |
| 332 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 333 | ExpectedEvaluation expected = { |
| 334 | // Rows/columns |
| 335 | 3, 16, |
| 336 | // Cost |
| 337 | 7.0, |
| 338 | // Residuals |
| 339 | { 1.0, 2.0, 3.0 }, |
| 340 | // Gradient |
| 341 | { 0.0, 0.0, // a |
| 342 | 6.0, 12.0, // x |
| 343 | 0.0, // b |
| 344 | 6.0, 12.0, 18.0, // y |
| 345 | 0.0, // c |
| 346 | 6.0, 12.0, 18.0, 24.0, // z |
| 347 | 0.0, 0.0, 0.0, // d |
| 348 | }, |
| 349 | // Jacobian |
| 350 | // a x b y c z d |
| 351 | { 0, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 0, 0, |
| 352 | 0, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 0, 0, |
| 353 | 0, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 0, 0 |
| 354 | } |
| 355 | }; |
| 356 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 357 | } |
| 358 | |
| 359 | TEST_P(EvaluatorTest, MultipleResidualProblem) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 360 | // Add the parameters in explicit order to force the ordering in the program. |
| 361 | problem.AddParameterBlock(x, 2); |
| 362 | problem.AddParameterBlock(y, 3); |
| 363 | problem.AddParameterBlock(z, 4); |
| 364 | |
| 365 | // f(x, y) in R^2 |
| 366 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>, |
| 367 | NULL, |
| 368 | x, y); |
| 369 | |
| 370 | // g(x, z) in R^3 |
| 371 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>, |
| 372 | NULL, |
| 373 | x, z); |
| 374 | |
| 375 | // h(y, z) in R^4 |
| 376 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>, |
| 377 | NULL, |
| 378 | y, z); |
| 379 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 380 | ExpectedEvaluation expected = { |
| 381 | // Rows/columns |
| 382 | 9, 9, |
| 383 | // Cost |
| 384 | // f g h |
| 385 | ( 1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0, |
| 386 | // Residuals |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 387 | { 1.0, 2.0, // f |
| 388 | 1.0, 2.0, 3.0, // g |
| 389 | 1.0, 2.0, 3.0, 4.0 // h |
| 390 | }, |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 391 | // Gradient |
| 392 | { 15.0, 30.0, // x |
| 393 | 33.0, 66.0, 99.0, // y |
| 394 | 42.0, 84.0, 126.0, 168.0 // z |
| 395 | }, |
| 396 | // Jacobian |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 397 | // x y z |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 398 | { /* f(x, y) */ 1, 2, 1, 2, 3, 0, 0, 0, 0, |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 399 | 1, 2, 1, 2, 3, 0, 0, 0, 0, |
| 400 | |
| 401 | /* g(x, z) */ 2, 4, 0, 0, 0, 2, 4, 6, 8, |
| 402 | 2, 4, 0, 0, 0, 2, 4, 6, 8, |
| 403 | 2, 4, 0, 0, 0, 2, 4, 6, 8, |
| 404 | |
| 405 | /* h(y, z) */ 0, 0, 3, 6, 9, 3, 6, 9, 12, |
| 406 | 0, 0, 3, 6, 9, 3, 6, 9, 12, |
| 407 | 0, 0, 3, 6, 9, 3, 6, 9, 12, |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 408 | 0, 0, 3, 6, 9, 3, 6, 9, 12 |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 409 | } |
| 410 | }; |
| 411 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 412 | } |
| 413 | |
| 414 | TEST_P(EvaluatorTest, MultipleResidualsWithLocalParameterizations) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 415 | // Add the parameters in explicit order to force the ordering in the program. |
| 416 | problem.AddParameterBlock(x, 2); |
| 417 | |
| 418 | // Fix y's first dimension. |
| 419 | vector<int> y_fixed; |
| 420 | y_fixed.push_back(0); |
| 421 | problem.AddParameterBlock(y, 3, new SubsetParameterization(3, y_fixed)); |
| 422 | |
| 423 | // Fix z's second dimension. |
| 424 | vector<int> z_fixed; |
| 425 | z_fixed.push_back(1); |
| 426 | problem.AddParameterBlock(z, 4, new SubsetParameterization(4, z_fixed)); |
| 427 | |
| 428 | // f(x, y) in R^2 |
| 429 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>, |
| 430 | NULL, |
| 431 | x, y); |
| 432 | |
| 433 | // g(x, z) in R^3 |
| 434 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>, |
| 435 | NULL, |
| 436 | x, z); |
| 437 | |
| 438 | // h(y, z) in R^4 |
| 439 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>, |
| 440 | NULL, |
| 441 | y, z); |
| 442 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 443 | ExpectedEvaluation expected = { |
| 444 | // Rows/columns |
| 445 | 9, 7, |
| 446 | // Cost |
| 447 | // f g h |
| 448 | ( 1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0, |
| 449 | // Residuals |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 450 | { 1.0, 2.0, // f |
| 451 | 1.0, 2.0, 3.0, // g |
| 452 | 1.0, 2.0, 3.0, 4.0 // h |
| 453 | }, |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 454 | // Gradient |
| 455 | { 15.0, 30.0, // x |
| 456 | 66.0, 99.0, // y |
| 457 | 42.0, 126.0, 168.0 // z |
| 458 | }, |
| 459 | // Jacobian |
| 460 | // x y z |
| 461 | { /* f(x, y) */ 1, 2, 2, 3, 0, 0, 0, |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 462 | 1, 2, 2, 3, 0, 0, 0, |
| 463 | |
| 464 | /* g(x, z) */ 2, 4, 0, 0, 2, 6, 8, |
| 465 | 2, 4, 0, 0, 2, 6, 8, |
| 466 | 2, 4, 0, 0, 2, 6, 8, |
| 467 | |
| 468 | /* h(y, z) */ 0, 0, 6, 9, 3, 9, 12, |
| 469 | 0, 0, 6, 9, 3, 9, 12, |
| 470 | 0, 0, 6, 9, 3, 9, 12, |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 471 | 0, 0, 6, 9, 3, 9, 12 |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 472 | } |
| 473 | }; |
| 474 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 475 | } |
| 476 | |
| 477 | TEST_P(EvaluatorTest, MultipleResidualProblemWithSomeConstantParameters) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 478 | // The values are ignored completely by the cost function. |
| 479 | double x[2]; |
| 480 | double y[3]; |
| 481 | double z[4]; |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 482 | |
| 483 | // Add the parameters in explicit order to force the ordering in the program. |
| 484 | problem.AddParameterBlock(x, 2); |
| 485 | problem.AddParameterBlock(y, 3); |
| 486 | problem.AddParameterBlock(z, 4); |
| 487 | |
| 488 | // f(x, y) in R^2 |
| 489 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>, |
| 490 | NULL, |
| 491 | x, y); |
| 492 | |
| 493 | // g(x, z) in R^3 |
| 494 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>, |
| 495 | NULL, |
| 496 | x, z); |
| 497 | |
| 498 | // h(y, z) in R^4 |
| 499 | problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>, |
| 500 | NULL, |
| 501 | y, z); |
| 502 | |
| 503 | // For this test, "z" is constant. |
| 504 | problem.SetParameterBlockConstant(z); |
| 505 | |
| 506 | // Create the reduced program which is missing the fixed "z" variable. |
| 507 | // Normally, the preprocessing of the program that happens in solver_impl |
| 508 | // takes care of this, but we don't want to invoke the solver here. |
| 509 | Program reduced_program; |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 510 | vector<ParameterBlock*>* parameter_blocks = |
| 511 | problem.mutable_program()->mutable_parameter_blocks(); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 512 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 513 | // "z" is the last parameter; save it for later and pop it off temporarily. |
| 514 | // Note that "z" will still get read during evaluation, so it cannot be |
| 515 | // deleted at this point. |
| 516 | ParameterBlock* parameter_block_z = parameter_blocks->back(); |
| 517 | parameter_blocks->pop_back(); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 518 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 519 | ExpectedEvaluation expected = { |
| 520 | // Rows/columns |
| 521 | 9, 5, |
| 522 | // Cost |
| 523 | // f g h |
| 524 | ( 1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0, |
| 525 | // Residuals |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 526 | { 1.0, 2.0, // f |
| 527 | 1.0, 2.0, 3.0, // g |
| 528 | 1.0, 2.0, 3.0, 4.0 // h |
| 529 | }, |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 530 | // Gradient |
| 531 | { 15.0, 30.0, // x |
| 532 | 33.0, 66.0, 99.0, // y |
| 533 | }, |
| 534 | // Jacobian |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 535 | // x y |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 536 | { /* f(x, y) */ 1, 2, 1, 2, 3, |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 537 | 1, 2, 1, 2, 3, |
| 538 | |
| 539 | /* g(x, z) */ 2, 4, 0, 0, 0, |
| 540 | 2, 4, 0, 0, 0, |
| 541 | 2, 4, 0, 0, 0, |
| 542 | |
| 543 | /* h(y, z) */ 0, 0, 3, 6, 9, |
| 544 | 0, 0, 3, 6, 9, |
| 545 | 0, 0, 3, 6, 9, |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 546 | 0, 0, 3, 6, 9 |
| 547 | } |
| 548 | }; |
| 549 | CheckAllEvaluationCombinations(expected); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 550 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 551 | // Restore parameter block z, so it will get freed in a consistent way. |
| 552 | parameter_blocks->push_back(parameter_block_z); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 553 | } |
| 554 | |
| 555 | TEST_P(EvaluatorTest, EvaluatorAbortsForResidualsThatFailToEvaluate) { |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 556 | // Switch the return value to failure. |
| 557 | problem.AddResidualBlock( |
| 558 | new ParameterIgnoringCostFunction<20, 3, 2, 3, 4, false>, NULL, x, y, z); |
| 559 | |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 560 | // The values are ignored. |
| 561 | double state[9]; |
| 562 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 563 | scoped_ptr<Evaluator> evaluator(CreateEvaluator(problem.mutable_program())); |
| 564 | scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian()); |
| 565 | double cost; |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 566 | EXPECT_FALSE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL)); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | // In the pairs, the first argument is the linear solver type, and the second |
| 570 | // argument is num_eliminate_blocks. Changing the num_eliminate_blocks only |
| 571 | // makes sense for the schur-based solvers. |
| 572 | // |
| 573 | // Try all values of num_eliminate_blocks that make sense given that in the |
| 574 | // tests a maximum of 4 parameter blocks are present. |
| 575 | INSTANTIATE_TEST_CASE_P( |
| 576 | LinearSolvers, |
| 577 | EvaluatorTest, |
| 578 | ::testing::Values(make_pair(DENSE_QR, 0), |
| 579 | make_pair(DENSE_SCHUR, 0), |
| 580 | make_pair(DENSE_SCHUR, 1), |
| 581 | make_pair(DENSE_SCHUR, 2), |
| 582 | make_pair(DENSE_SCHUR, 3), |
| 583 | make_pair(DENSE_SCHUR, 4), |
| 584 | make_pair(SPARSE_SCHUR, 0), |
| 585 | make_pair(SPARSE_SCHUR, 1), |
| 586 | make_pair(SPARSE_SCHUR, 2), |
| 587 | make_pair(SPARSE_SCHUR, 3), |
| 588 | make_pair(SPARSE_SCHUR, 4), |
| 589 | make_pair(ITERATIVE_SCHUR, 0), |
| 590 | make_pair(ITERATIVE_SCHUR, 1), |
| 591 | make_pair(ITERATIVE_SCHUR, 2), |
| 592 | make_pair(ITERATIVE_SCHUR, 3), |
| 593 | make_pair(ITERATIVE_SCHUR, 4), |
| 594 | make_pair(SPARSE_NORMAL_CHOLESKY, 0))); |
| 595 | |
| 596 | // Simple cost function used to check if the evaluator is sensitive to |
| 597 | // state changes. |
| 598 | class ParameterSensitiveCostFunction : public SizedCostFunction<2, 2> { |
| 599 | public: |
| 600 | virtual bool Evaluate(double const* const* parameters, |
| 601 | double* residuals, |
| 602 | double** jacobians) const { |
| 603 | double x1 = parameters[0][0]; |
| 604 | double x2 = parameters[0][1]; |
| 605 | residuals[0] = x1 * x1; |
| 606 | residuals[1] = x2 * x2; |
| 607 | |
| 608 | if (jacobians != NULL) { |
| 609 | double* jacobian = jacobians[0]; |
| 610 | if (jacobian != NULL) { |
| 611 | jacobian[0] = 2.0 * x1; |
| 612 | jacobian[1] = 0.0; |
| 613 | jacobian[2] = 0.0; |
| 614 | jacobian[3] = 2.0 * x2; |
| 615 | } |
| 616 | } |
| 617 | return true; |
| 618 | } |
| 619 | }; |
| 620 | |
| 621 | TEST(Evaluator, EvaluatorRespectsParameterChanges) { |
| 622 | ProblemImpl problem; |
| 623 | |
| 624 | double x[2]; |
| 625 | x[0] = 1.0; |
| 626 | x[1] = 1.0; |
| 627 | |
| 628 | problem.AddResidualBlock(new ParameterSensitiveCostFunction(), NULL, x); |
| 629 | Program* program = problem.mutable_program(); |
| 630 | program->SetParameterOffsetsAndIndex(); |
| 631 | |
| 632 | Evaluator::Options options; |
| 633 | options.linear_solver_type = DENSE_QR; |
| 634 | options.num_eliminate_blocks = 0; |
| 635 | string error; |
| 636 | scoped_ptr<Evaluator> evaluator(Evaluator::Create(options, program, &error)); |
| 637 | scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian()); |
| 638 | |
| 639 | ASSERT_EQ(2, jacobian->num_rows()); |
| 640 | ASSERT_EQ(2, jacobian->num_cols()); |
| 641 | |
| 642 | double state[2]; |
| 643 | state[0] = 2.0; |
| 644 | state[1] = 3.0; |
| 645 | |
| 646 | // The original state of a residual block comes from the user's |
| 647 | // state. So the original state is 1.0, 1.0, and the only way we get |
| 648 | // the 2.0, 3.0 results in the following tests is if it respects the |
| 649 | // values in the state vector. |
| 650 | |
| 651 | // Cost only; no residuals and no jacobian. |
| 652 | { |
| 653 | double cost = -1; |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 654 | ASSERT_TRUE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL)); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 655 | EXPECT_EQ(48.5, cost); |
| 656 | } |
| 657 | |
| 658 | // Cost and residuals, no jacobian. |
| 659 | { |
| 660 | double cost = -1; |
| 661 | double residuals[2] = { -2, -2 }; |
Keir Mierle | f44907f | 2012-07-06 13:52:32 -0700 | [diff] [blame] | 662 | ASSERT_TRUE(evaluator->Evaluate(state, &cost, residuals, NULL, NULL)); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 663 | EXPECT_EQ(48.5, cost); |
| 664 | EXPECT_EQ(4, residuals[0]); |
| 665 | EXPECT_EQ(9, residuals[1]); |
| 666 | } |
| 667 | |
| 668 | // Cost, residuals, and jacobian. |
| 669 | { |
| 670 | double cost = -1; |
| 671 | double residuals[2] = { -2, -2}; |
| 672 | SetSparseMatrixConstant(jacobian.get(), -1); |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 673 | ASSERT_TRUE(evaluator->Evaluate(state, |
| 674 | &cost, |
| 675 | residuals, |
| 676 | NULL, |
| 677 | jacobian.get())); |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 678 | EXPECT_EQ(48.5, cost); |
| 679 | EXPECT_EQ(4, residuals[0]); |
| 680 | EXPECT_EQ(9, residuals[1]); |
| 681 | Matrix actual_jacobian; |
| 682 | jacobian->ToDenseMatrix(&actual_jacobian); |
| 683 | |
| 684 | Matrix expected_jacobian(2, 2); |
| 685 | expected_jacobian |
| 686 | << 2 * state[0], 0, |
| 687 | 0, 2 * state[1]; |
| 688 | |
| 689 | EXPECT_TRUE((actual_jacobian.array() == expected_jacobian.array()).all()) |
| 690 | << "Actual:\n" << actual_jacobian |
| 691 | << "\nExpected:\n" << expected_jacobian; |
| 692 | } |
| 693 | } |
| 694 | |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 695 | // Simple cost function used for testing Evaluator::Evaluate. |
| 696 | // |
| 697 | // r_i = i - (j + 1) * x_ij^2 |
| 698 | template <int kNumResiduals, int kNumParameterBlocks > |
| 699 | class QuadraticCostFunction : public CostFunction { |
| 700 | public: |
| 701 | QuadraticCostFunction() { |
| 702 | CHECK_GT(kNumResiduals, 0); |
| 703 | CHECK_GT(kNumParameterBlocks, 0); |
| 704 | set_num_residuals(kNumResiduals); |
| 705 | for (int i = 0; i < kNumParameterBlocks; ++i) { |
| 706 | mutable_parameter_block_sizes()->push_back(kNumResiduals); |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | virtual bool Evaluate(double const* const* parameters, |
| 711 | double* residuals, |
| 712 | double** jacobians) const { |
| 713 | for (int i = 0; i < kNumResiduals; ++i) { |
| 714 | residuals[i] = i; |
| 715 | for (int j = 0; j < kNumParameterBlocks; ++j) { |
| 716 | residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i]; |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | if (jacobians == NULL) { |
| 721 | return true; |
| 722 | } |
| 723 | |
| 724 | for (int j = 0; j < kNumParameterBlocks; ++j) { |
| 725 | if (jacobians[j] != NULL) { |
| 726 | MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) = |
| 727 | (-2.0 * (j + 1.0) * |
| 728 | ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal(); |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | return true; |
| 733 | } |
| 734 | }; |
| 735 | |
| 736 | // Convert a CRSMatrix to a dense Eigen matrix. |
| 737 | void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) { |
| 738 | Matrix& m = *CHECK_NOTNULL(output); |
| 739 | m.resize(input.num_rows, input.num_cols); |
| 740 | m.setZero(); |
| 741 | for (int row = 0; row < input.num_rows; ++row) { |
| 742 | for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) { |
| 743 | const int col = input.cols[j]; |
| 744 | m(row, col) = input.values[j]; |
| 745 | } |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | |
| 750 | class StaticEvaluateTest : public ::testing::Test { |
| 751 | protected: |
| 752 | void SetUp() { |
| 753 | for (int i = 0; i < 6; ++i) { |
| 754 | parameters_[i] = static_cast<double>(i + 1); |
| 755 | } |
| 756 | |
| 757 | CostFunction* cost_function = new QuadraticCostFunction<2, 2>; |
| 758 | |
| 759 | // f(x, y) |
| 760 | problem_.AddResidualBlock(cost_function, |
| 761 | NULL, |
| 762 | parameters_, |
| 763 | parameters_ + 2); |
| 764 | // g(y, z) |
| 765 | problem_.AddResidualBlock(cost_function, |
| 766 | NULL, parameters_ + 2, |
| 767 | parameters_ + 4); |
| 768 | // h(z, x) |
| 769 | problem_.AddResidualBlock(cost_function, |
| 770 | NULL, |
| 771 | parameters_ + 4, |
| 772 | parameters_); |
| 773 | } |
| 774 | |
| 775 | |
| 776 | |
| 777 | void EvaluateAndCompare(const int expected_num_rows, |
| 778 | const int expected_num_cols, |
| 779 | const double expected_cost, |
| 780 | const double* expected_residuals, |
| 781 | const double* expected_gradient, |
| 782 | const double* expected_jacobian) { |
| 783 | double cost; |
| 784 | vector<double> residuals; |
| 785 | vector<double> gradient; |
| 786 | CRSMatrix jacobian; |
| 787 | |
| 788 | EXPECT_TRUE(Evaluator::Evaluate( |
| 789 | problem_.mutable_program(), |
| 790 | 1, |
| 791 | &cost, |
| 792 | expected_residuals != NULL ? &residuals : NULL, |
| 793 | expected_gradient != NULL ? &gradient : NULL, |
| 794 | expected_jacobian != NULL ? &jacobian : NULL)); |
| 795 | |
| 796 | if (expected_residuals != NULL) { |
| 797 | EXPECT_EQ(residuals.size(), expected_num_rows); |
| 798 | } |
| 799 | |
| 800 | if (expected_gradient != NULL) { |
| 801 | EXPECT_EQ(gradient.size(), expected_num_cols); |
| 802 | } |
| 803 | |
| 804 | if (expected_jacobian != NULL) { |
| 805 | EXPECT_EQ(jacobian.num_rows, expected_num_rows); |
| 806 | EXPECT_EQ(jacobian.num_cols, expected_num_cols); |
| 807 | } |
| 808 | |
| 809 | Matrix dense_jacobian; |
| 810 | if (expected_jacobian != NULL) { |
| 811 | CRSToDenseMatrix(jacobian, &dense_jacobian); |
| 812 | } |
| 813 | |
| 814 | CompareEvaluations(expected_num_rows, |
| 815 | expected_num_cols, |
| 816 | expected_cost, |
| 817 | expected_residuals, |
| 818 | expected_gradient, |
| 819 | expected_jacobian, |
| 820 | cost, |
Sameer Agarwal | fba3844 | 2012-09-07 17:04:53 -0700 | [diff] [blame] | 821 | residuals.size() > 0 ? &residuals[0] : NULL, |
| 822 | gradient.size() > 0 ? &gradient[0] : NULL, |
Sameer Agarwal | 4997cbc | 2012-07-02 12:44:34 -0700 | [diff] [blame] | 823 | dense_jacobian.data()); |
| 824 | } |
| 825 | |
| 826 | void CheckAllEvaluationCombinations(const ExpectedEvaluation& expected ) { |
| 827 | for (int i = 0; i < 8; ++i) { |
| 828 | EvaluateAndCompare(expected.num_rows, |
| 829 | expected.num_cols, |
| 830 | expected.cost, |
| 831 | (i & 1) ? expected.residuals : NULL, |
| 832 | (i & 2) ? expected.gradient : NULL, |
| 833 | (i & 4) ? expected.jacobian : NULL); |
| 834 | } |
| 835 | |
| 836 | |
| 837 | double new_parameters[6]; |
| 838 | for (int i = 0; i < 6; ++i) { |
| 839 | new_parameters[i] = 0.0; |
| 840 | } |
| 841 | |
| 842 | problem_.mutable_program()->StateVectorToParameterBlocks(new_parameters); |
| 843 | |
| 844 | for (int i = 0; i < 8; ++i) { |
| 845 | EvaluateAndCompare(expected.num_rows, |
| 846 | expected.num_cols, |
| 847 | expected.cost, |
| 848 | (i & 1) ? expected.residuals : NULL, |
| 849 | (i & 2) ? expected.gradient : NULL, |
| 850 | (i & 4) ? expected.jacobian : NULL); |
| 851 | } |
| 852 | } |
| 853 | |
| 854 | ProblemImpl problem_; |
| 855 | double parameters_[6]; |
| 856 | }; |
| 857 | |
| 858 | |
| 859 | TEST_F(StaticEvaluateTest, MultipleParameterAndResidualBlocks) { |
| 860 | ExpectedEvaluation expected = { |
| 861 | // Rows/columns |
| 862 | 6, 6, |
| 863 | // Cost |
| 864 | 7607.0, |
| 865 | // Residuals |
| 866 | { -19.0, -35.0, // f |
| 867 | -59.0, -87.0, // g |
| 868 | -27.0, -43.0 // h |
| 869 | }, |
| 870 | // Gradient |
| 871 | { 146.0, 484.0, // x |
| 872 | 582.0, 1256.0, // y |
| 873 | 1450.0, 2604.0, // z |
| 874 | }, |
| 875 | // Jacobian |
| 876 | // x y z |
| 877 | { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0, |
| 878 | 0.0, -4.0, 0.0, -16.0, 0.0, 0.0, |
| 879 | /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0, |
| 880 | 0.0, 0.0, 0.0, -8.0, 0.0, -24.0, |
| 881 | /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, |
| 882 | 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 |
| 883 | } |
| 884 | }; |
| 885 | |
| 886 | CheckAllEvaluationCombinations(expected); |
| 887 | } |
| 888 | |
| 889 | TEST_F(StaticEvaluateTest, ConstantParameterBlock) { |
| 890 | ExpectedEvaluation expected = { |
| 891 | // Rows/columns |
| 892 | 6, 6, |
| 893 | // Cost |
| 894 | 7607.0, |
| 895 | // Residuals |
| 896 | { -19.0, -35.0, // f |
| 897 | -59.0, -87.0, // g |
| 898 | -27.0, -43.0 // h |
| 899 | }, |
| 900 | |
| 901 | // Gradient |
| 902 | { 146.0, 484.0, // x |
| 903 | 0.0, 0.0, // y |
| 904 | 1450.0, 2604.0, // z |
| 905 | }, |
| 906 | |
| 907 | // Jacobian |
| 908 | // x y z |
| 909 | { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, |
| 910 | 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, |
| 911 | /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0, |
| 912 | 0.0, 0.0, 0.0, 0.0, 0.0, -24.0, |
| 913 | /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, |
| 914 | 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 |
| 915 | } |
| 916 | }; |
| 917 | |
| 918 | problem_.SetParameterBlockConstant(parameters_ + 2); |
| 919 | CheckAllEvaluationCombinations(expected); |
| 920 | } |
| 921 | |
| 922 | TEST_F(StaticEvaluateTest, LocalParameterization) { |
| 923 | ExpectedEvaluation expected = { |
| 924 | // Rows/columns |
| 925 | 6, 5, |
| 926 | // Cost |
| 927 | 7607.0, |
| 928 | // Residuals |
| 929 | { -19.0, -35.0, // f |
| 930 | -59.0, -87.0, // g |
| 931 | -27.0, -43.0 // h |
| 932 | }, |
| 933 | // Gradient |
| 934 | { 146.0, 484.0, // x |
| 935 | 1256.0, // y with SubsetParameterization |
| 936 | 1450.0, 2604.0, // z |
| 937 | }, |
| 938 | // Jacobian |
| 939 | // x y z |
| 940 | { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, |
| 941 | 0.0, -4.0, -16.0, 0.0, 0.0, |
| 942 | /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0, |
| 943 | 0.0, 0.0, -8.0, 0.0, -24.0, |
| 944 | /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0, |
| 945 | 0.0, -8.0, 0.0, 0.0, -12.0 |
| 946 | } |
| 947 | }; |
| 948 | |
| 949 | vector<int> constant_parameters; |
| 950 | constant_parameters.push_back(0); |
| 951 | problem_.SetParameterization(parameters_ + 2, |
| 952 | new SubsetParameterization(2, |
| 953 | constant_parameters)); |
| 954 | |
| 955 | CheckAllEvaluationCombinations(expected); |
| 956 | } |
| 957 | |
Keir Mierle | 8ebb073 | 2012-04-30 23:09:08 -0700 | [diff] [blame] | 958 | } // namespace internal |
| 959 | } // namespace ceres |