blob: 12e62ef21d30d74c1c04a969653addaf88550ae9 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2017 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: richie.stebbing@gmail.com (Richard Stebbing)
30// sameeragarwal@google.com (Sameer Agarwal)
31//
32// Based on examples/ellipse_approximation.cc
33
34#include <cmath>
35#include <vector>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080036
Austin Schuh70cc9552019-01-21 19:46:48 -080037#include "ceres/ceres.h"
38#include "glog/logging.h"
39#include "gtest/gtest.h"
40
41namespace ceres {
42namespace internal {
43
44// Data generated with the following Python code.
45// import numpy as np
46// np.random.seed(1337)
47// t = np.linspace(0.0, 2.0 * np.pi, 212, endpoint=False)
48// t += 2.0 * np.pi * 0.01 * np.random.randn(t.size)
49// theta = np.deg2rad(15)
50// a, b = np.cos(theta), np.sin(theta)
51// R = np.array([[a, -b],
52// [b, a]])
53// Y = np.dot(np.c_[4.0 * np.cos(t), np.sin(t)], R.T)
54
55const int kYRows = 212;
56const int kYCols = 2;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080057// clang-format off
Austin Schuh70cc9552019-01-21 19:46:48 -080058const double kYData[kYRows * kYCols] = {
59 +3.871364e+00, +9.916027e-01,
60 +3.864003e+00, +1.034148e+00,
61 +3.850651e+00, +1.072202e+00,
62 +3.868350e+00, +1.014408e+00,
63 +3.796381e+00, +1.153021e+00,
64 +3.857138e+00, +1.056102e+00,
65 +3.787532e+00, +1.162215e+00,
66 +3.704477e+00, +1.227272e+00,
67 +3.564711e+00, +1.294959e+00,
68 +3.754363e+00, +1.191948e+00,
69 +3.482098e+00, +1.322725e+00,
70 +3.602777e+00, +1.279658e+00,
71 +3.585433e+00, +1.286858e+00,
72 +3.347505e+00, +1.356415e+00,
73 +3.220855e+00, +1.378914e+00,
74 +3.558808e+00, +1.297174e+00,
75 +3.403618e+00, +1.343809e+00,
76 +3.179828e+00, +1.384721e+00,
77 +3.054789e+00, +1.398759e+00,
78 +3.294153e+00, +1.366808e+00,
79 +3.247312e+00, +1.374813e+00,
80 +2.988547e+00, +1.404247e+00,
81 +3.114508e+00, +1.392698e+00,
82 +2.899226e+00, +1.409802e+00,
83 +2.533256e+00, +1.414778e+00,
84 +2.654773e+00, +1.415909e+00,
85 +2.565100e+00, +1.415313e+00,
86 +2.976456e+00, +1.405118e+00,
87 +2.484200e+00, +1.413640e+00,
88 +2.324751e+00, +1.407476e+00,
89 +1.930468e+00, +1.378221e+00,
90 +2.329017e+00, +1.407688e+00,
91 +1.760640e+00, +1.360319e+00,
92 +2.147375e+00, +1.396603e+00,
93 +1.741989e+00, +1.358178e+00,
94 +1.743859e+00, +1.358394e+00,
95 +1.557372e+00, +1.335208e+00,
96 +1.280551e+00, +1.295087e+00,
97 +1.429880e+00, +1.317546e+00,
98 +1.213485e+00, +1.284400e+00,
99 +9.168172e-01, +1.232870e+00,
100 +1.311141e+00, +1.299839e+00,
101 +1.231969e+00, +1.287382e+00,
102 +7.453773e-01, +1.200049e+00,
103 +6.151587e-01, +1.173683e+00,
104 +5.935666e-01, +1.169193e+00,
105 +2.538707e-01, +1.094227e+00,
106 +6.806136e-01, +1.187089e+00,
107 +2.805447e-01, +1.100405e+00,
108 +6.184807e-01, +1.174371e+00,
109 +1.170550e-01, +1.061762e+00,
110 +2.890507e-01, +1.102365e+00,
111 +3.834234e-01, +1.123772e+00,
112 +3.980161e-04, +1.033061e+00,
113 -3.651680e-01, +9.370367e-01,
114 -8.386351e-01, +7.987201e-01,
115 -8.105704e-01, +8.073702e-01,
116 -8.735139e-01, +7.878886e-01,
117 -9.913836e-01, +7.506100e-01,
118 -8.784011e-01, +7.863636e-01,
119 -1.181440e+00, +6.882566e-01,
120 -1.229556e+00, +6.720191e-01,
121 -1.035839e+00, +7.362765e-01,
122 -8.031520e-01, +8.096470e-01,
123 -1.539136e+00, +5.629549e-01,
124 -1.755423e+00, +4.817306e-01,
125 -1.337589e+00, +6.348763e-01,
126 -1.836966e+00, +4.499485e-01,
127 -1.913367e+00, +4.195617e-01,
128 -2.126467e+00, +3.314900e-01,
129 -1.927625e+00, +4.138238e-01,
130 -2.339862e+00, +2.379074e-01,
131 -1.881736e+00, +4.322152e-01,
132 -2.116753e+00, +3.356163e-01,
133 -2.255733e+00, +2.754930e-01,
134 -2.555834e+00, +1.368473e-01,
135 -2.770277e+00, +2.895711e-02,
136 -2.563376e+00, +1.331890e-01,
137 -2.826715e+00, -9.000818e-04,
138 -2.978191e+00, -8.457804e-02,
139 -3.115855e+00, -1.658786e-01,
140 -2.982049e+00, -8.678322e-02,
141 -3.307892e+00, -2.902083e-01,
142 -3.038346e+00, -1.194222e-01,
143 -3.190057e+00, -2.122060e-01,
144 -3.279086e+00, -2.705777e-01,
145 -3.322028e+00, -2.999889e-01,
146 -3.122576e+00, -1.699965e-01,
147 -3.551973e+00, -4.768674e-01,
148 -3.581866e+00, -5.032175e-01,
149 -3.497799e+00, -4.315203e-01,
150 -3.565384e+00, -4.885602e-01,
151 -3.699493e+00, -6.199815e-01,
152 -3.585166e+00, -5.061925e-01,
153 -3.758914e+00, -6.918275e-01,
154 -3.741104e+00, -6.689131e-01,
155 -3.688331e+00, -6.077239e-01,
156 -3.810425e+00, -7.689015e-01,
157 -3.791829e+00, -7.386911e-01,
158 -3.789951e+00, -7.358189e-01,
159 -3.823100e+00, -7.918398e-01,
160 -3.857021e+00, -8.727074e-01,
161 -3.858250e+00, -8.767645e-01,
162 -3.872100e+00, -9.563174e-01,
163 -3.864397e+00, -1.032630e+00,
164 -3.846230e+00, -1.081669e+00,
165 -3.834799e+00, -1.102536e+00,
166 -3.866684e+00, -1.022901e+00,
167 -3.808643e+00, -1.139084e+00,
168 -3.868840e+00, -1.011569e+00,
169 -3.791071e+00, -1.158615e+00,
170 -3.797999e+00, -1.151267e+00,
171 -3.696278e+00, -1.232314e+00,
172 -3.779007e+00, -1.170504e+00,
173 -3.622855e+00, -1.270793e+00,
174 -3.647249e+00, -1.259166e+00,
175 -3.655412e+00, -1.255042e+00,
176 -3.573218e+00, -1.291696e+00,
177 -3.638019e+00, -1.263684e+00,
178 -3.498409e+00, -1.317750e+00,
179 -3.304143e+00, -1.364970e+00,
180 -3.183001e+00, -1.384295e+00,
181 -3.202456e+00, -1.381599e+00,
182 -3.244063e+00, -1.375332e+00,
183 -3.233308e+00, -1.377019e+00,
184 -3.060112e+00, -1.398264e+00,
185 -3.078187e+00, -1.396517e+00,
186 -2.689594e+00, -1.415761e+00,
187 -2.947662e+00, -1.407039e+00,
188 -2.854490e+00, -1.411860e+00,
189 -2.660499e+00, -1.415900e+00,
190 -2.875955e+00, -1.410930e+00,
191 -2.675385e+00, -1.415848e+00,
192 -2.813155e+00, -1.413363e+00,
193 -2.417673e+00, -1.411512e+00,
194 -2.725461e+00, -1.415373e+00,
195 -2.148334e+00, -1.396672e+00,
196 -2.108972e+00, -1.393738e+00,
197 -2.029905e+00, -1.387302e+00,
198 -2.046214e+00, -1.388687e+00,
199 -2.057402e+00, -1.389621e+00,
200 -1.650250e+00, -1.347160e+00,
201 -1.806764e+00, -1.365469e+00,
202 -1.206973e+00, -1.283343e+00,
203 -8.029259e-01, -1.211308e+00,
204 -1.229551e+00, -1.286993e+00,
205 -1.101507e+00, -1.265754e+00,
206 -9.110645e-01, -1.231804e+00,
207 -1.110046e+00, -1.267211e+00,
208 -8.465274e-01, -1.219677e+00,
209 -7.594163e-01, -1.202818e+00,
210 -8.023823e-01, -1.211203e+00,
211 -3.732519e-01, -1.121494e+00,
212 -1.918373e-01, -1.079668e+00,
213 -4.671988e-01, -1.142253e+00,
214 -4.033645e-01, -1.128215e+00,
215 -1.920740e-01, -1.079724e+00,
216 -3.022157e-01, -1.105389e+00,
217 -1.652831e-01, -1.073354e+00,
218 +4.671625e-01, -9.085886e-01,
219 +5.940178e-01, -8.721832e-01,
220 +3.147557e-01, -9.508290e-01,
221 +6.383631e-01, -8.591867e-01,
222 +9.888923e-01, -7.514088e-01,
223 +7.076339e-01, -8.386023e-01,
224 +1.326682e+00, -6.386698e-01,
225 +1.149834e+00, -6.988221e-01,
226 +1.257742e+00, -6.624207e-01,
227 +1.492352e+00, -5.799632e-01,
228 +1.595574e+00, -5.421766e-01,
229 +1.240173e+00, -6.684113e-01,
230 +1.706612e+00, -5.004442e-01,
231 +1.873984e+00, -4.353002e-01,
232 +1.985633e+00, -3.902561e-01,
233 +1.722880e+00, -4.942329e-01,
234 +2.095182e+00, -3.447402e-01,
235 +2.018118e+00, -3.768991e-01,
236 +2.422702e+00, -1.999563e-01,
237 +2.370611e+00, -2.239326e-01,
238 +2.152154e+00, -3.205250e-01,
239 +2.525121e+00, -1.516499e-01,
240 +2.422116e+00, -2.002280e-01,
241 +2.842806e+00, +9.536372e-03,
242 +3.030128e+00, +1.146027e-01,
243 +2.888424e+00, +3.433444e-02,
244 +2.991609e+00, +9.226409e-02,
245 +2.924807e+00, +5.445844e-02,
246 +3.007772e+00, +1.015875e-01,
247 +2.781973e+00, -2.282382e-02,
248 +3.164737e+00, +1.961781e-01,
249 +3.237671e+00, +2.430139e-01,
250 +3.046123e+00, +1.240014e-01,
251 +3.414834e+00, +3.669060e-01,
252 +3.436591e+00, +3.833600e-01,
253 +3.626207e+00, +5.444311e-01,
254 +3.223325e+00, +2.336361e-01,
255 +3.511963e+00, +4.431060e-01,
256 +3.698380e+00, +6.187442e-01,
257 +3.670244e+00, +5.884943e-01,
258 +3.558833e+00, +4.828230e-01,
259 +3.661807e+00, +5.797689e-01,
260 +3.767261e+00, +7.030893e-01,
261 +3.801065e+00, +7.532650e-01,
262 +3.828523e+00, +8.024454e-01,
263 +3.840719e+00, +8.287032e-01,
264 +3.848748e+00, +8.485921e-01,
265 +3.865801e+00, +9.066551e-01,
266 +3.870983e+00, +9.404873e-01,
267 +3.870263e+00, +1.001884e+00,
268 +3.864462e+00, +1.032374e+00,
269 +3.870542e+00, +9.996121e-01,
270 +3.865424e+00, +1.028474e+00
271};
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800272// clang-format on
Austin Schuh70cc9552019-01-21 19:46:48 -0800273
274ConstMatrixRef kY(kYData, kYRows, kYCols);
275
276class PointToLineSegmentContourCostFunction : public CostFunction {
277 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800278 // This class needs to have an Eigen aligned operator new as it contains
279 // fixed-size Eigen types.
280 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
281
Austin Schuh70cc9552019-01-21 19:46:48 -0800282 PointToLineSegmentContourCostFunction(const int num_segments,
283 const Eigen::Vector2d& y)
284 : num_segments_(num_segments), y_(y) {
285 // The first parameter is the preimage position.
286 mutable_parameter_block_sizes()->push_back(1);
287 // The next parameters are the control points for the line segment contour.
288 for (int i = 0; i < num_segments_; ++i) {
289 mutable_parameter_block_sizes()->push_back(2);
290 }
291 set_num_residuals(2);
292 }
293
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800294 bool Evaluate(const double* const* x,
295 double* residuals,
296 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -0800297 // Convert the preimage position `t` into a segment index `i0` and the
298 // line segment interpolation parameter `u`. `i1` is the index of the next
299 // control point.
300 const double t = ModuloNumSegments(*x[0]);
301 CHECK_GE(t, 0.0);
302 CHECK_LT(t, num_segments_);
303 const int i0 = floor(t), i1 = (i0 + 1) % num_segments_;
304 const double u = t - i0;
305
306 // Linearly interpolate between control points `i0` and `i1`.
307 residuals[0] = y_[0] - ((1.0 - u) * x[1 + i0][0] + u * x[1 + i1][0]);
308 residuals[1] = y_[1] - ((1.0 - u) * x[1 + i0][1] + u * x[1 + i1][1]);
309
310 if (jacobians == NULL) {
311 return true;
312 }
313
314 if (jacobians[0] != NULL) {
315 jacobians[0][0] = x[1 + i0][0] - x[1 + i1][0];
316 jacobians[0][1] = x[1 + i0][1] - x[1 + i1][1];
317 }
318 for (int i = 0; i < num_segments_; ++i) {
319 if (jacobians[i + 1] != NULL) {
320 MatrixRef(jacobians[i + 1], 2, 2).setZero();
321 if (i == i0) {
322 jacobians[i + 1][0] = -(1.0 - u);
323 jacobians[i + 1][3] = -(1.0 - u);
324 } else if (i == i1) {
325 jacobians[i + 1][0] = -u;
326 jacobians[i + 1][3] = -u;
327 }
328 }
329 }
330 return true;
331 }
332
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800333 static CostFunction* Create(const int num_segments,
334 const Eigen::Vector2d& y) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800335 return new PointToLineSegmentContourCostFunction(num_segments, y);
336 }
337
338 private:
339 inline double ModuloNumSegments(const double t) const {
340 return t - num_segments_ * floor(t / num_segments_);
341 }
342
343 const int num_segments_;
344 const Eigen::Vector2d y_;
345};
346
347class EuclideanDistanceFunctor {
348 public:
349 explicit EuclideanDistanceFunctor(const double sqrt_weight)
350 : sqrt_weight_(sqrt_weight) {}
351
352 template <typename T>
353 bool operator()(const T* x0, const T* x1, T* residuals) const {
354 residuals[0] = sqrt_weight_ * (x0[0] - x1[0]);
355 residuals[1] = sqrt_weight_ * (x0[1] - x1[1]);
356 return true;
357 }
358
359 static CostFunction* Create(const double sqrt_weight) {
360 return new AutoDiffCostFunction<EuclideanDistanceFunctor, 2, 2, 2>(
361 new EuclideanDistanceFunctor(sqrt_weight));
362 }
363
364 private:
365 const double sqrt_weight_;
366};
367
368TEST(DynamicSparsity, StaticAndDynamicSparsityProduceSameSolution) {
369 // Skip test if there is no sparse linear algebra library.
370 if (!IsSparseLinearAlgebraLibraryTypeAvailable(SUITE_SPARSE) &&
371 !IsSparseLinearAlgebraLibraryTypeAvailable(CX_SPARSE) &&
372 !IsSparseLinearAlgebraLibraryTypeAvailable(EIGEN_SPARSE)) {
373 return;
374 }
375
376 // Problem configuration.
377 const int num_segments = 151;
378 const double regularization_weight = 1e-2;
379
380 // `X` is the matrix of control points which make up the contour of line
381 // segments. The number of control points is equal to the number of line
382 // segments because the contour is closed.
383 //
384 // Initialize `X` to points on the unit circle.
385 Vector w(num_segments + 1);
386 w.setLinSpaced(num_segments + 1, 0.0, 2.0 * M_PI);
387 w.conservativeResize(num_segments);
388 Matrix X(num_segments, 2);
389 X.col(0) = w.array().cos();
390 X.col(1) = w.array().sin();
391
392 // Each data point has an associated preimage position on the line segment
393 // contour. For each data point we initialize the preimage positions to
394 // the index of the closest control point.
395 const int num_observations = kY.rows();
396 Vector t(num_observations);
397 for (int i = 0; i < num_observations; ++i) {
398 (X.rowwise() - kY.row(i)).rowwise().squaredNorm().minCoeff(&t[i]);
399 }
400
401 Problem problem;
402
403 // For each data point add a residual which measures its distance to its
404 // corresponding position on the line segment contour.
405 std::vector<double*> parameter_blocks(1 + num_segments);
406 parameter_blocks[0] = NULL;
407 for (int i = 0; i < num_segments; ++i) {
408 parameter_blocks[i + 1] = X.data() + 2 * i;
409 }
410 for (int i = 0; i < num_observations; ++i) {
411 parameter_blocks[0] = &t[i];
412 problem.AddResidualBlock(
413 PointToLineSegmentContourCostFunction::Create(num_segments, kY.row(i)),
414 NULL,
415 parameter_blocks);
416 }
417
418 // Add regularization to minimize the length of the line segment contour.
419 for (int i = 0; i < num_segments; ++i) {
420 problem.AddResidualBlock(
421 EuclideanDistanceFunctor::Create(sqrt(regularization_weight)),
422 NULL,
423 X.data() + 2 * i,
424 X.data() + 2 * ((i + 1) % num_segments));
425 }
426
427 Solver::Options options;
428 options.max_num_iterations = 100;
429 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
430
431 // First, solve `X` and `t` jointly with dynamic_sparsity = true.
432 Matrix X0 = X;
433 Vector t0 = t;
434 options.dynamic_sparsity = false;
435 Solver::Summary static_summary;
436 Solve(options, &problem, &static_summary);
437 EXPECT_EQ(static_summary.termination_type, CONVERGENCE)
438 << static_summary.FullReport();
439
440 X = X0;
441 t = t0;
442 options.dynamic_sparsity = true;
443 Solver::Summary dynamic_summary;
444 Solve(options, &problem, &dynamic_summary);
445 EXPECT_EQ(dynamic_summary.termination_type, CONVERGENCE)
446 << dynamic_summary.FullReport();
447
448 EXPECT_NEAR(static_summary.final_cost,
449 dynamic_summary.final_cost,
450 std::numeric_limits<double>::epsilon())
451 << "Static: \n"
452 << static_summary.FullReport() << "\nDynamic: \n"
453 << dynamic_summary.FullReport();
454}
455
456} // namespace internal
457} // namespace ceres