blob: 36cffec02782be2b61a98cda659f0af1a757f5ad [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// 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/linear_least_squares_problems.h"
32
33#include <cstdio>
34#include <memory>
35#include <string>
36#include <vector>
37
38#include "ceres/block_sparse_matrix.h"
39#include "ceres/block_structure.h"
40#include "ceres/casts.h"
41#include "ceres/file.h"
42#include "ceres/stringprintf.h"
43#include "ceres/triplet_sparse_matrix.h"
44#include "ceres/types.h"
45#include "glog/logging.h"
46
Austin Schuh3de38b02024-06-25 18:25:10 -070047namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080048
Austin Schuh3de38b02024-06-25 18:25:10 -070049std::unique_ptr<LinearLeastSquaresProblem>
50CreateLinearLeastSquaresProblemFromId(int id) {
Austin Schuh70cc9552019-01-21 19:46:48 -080051 switch (id) {
52 case 0:
53 return LinearLeastSquaresProblem0();
54 case 1:
55 return LinearLeastSquaresProblem1();
56 case 2:
57 return LinearLeastSquaresProblem2();
58 case 3:
59 return LinearLeastSquaresProblem3();
60 case 4:
61 return LinearLeastSquaresProblem4();
Austin Schuh3de38b02024-06-25 18:25:10 -070062 case 5:
63 return LinearLeastSquaresProblem5();
64 case 6:
65 return LinearLeastSquaresProblem6();
Austin Schuh70cc9552019-01-21 19:46:48 -080066 default:
67 LOG(FATAL) << "Unknown problem id requested " << id;
68 }
Austin Schuh3de38b02024-06-25 18:25:10 -070069 return nullptr;
Austin Schuh70cc9552019-01-21 19:46:48 -080070}
71
72/*
73A = [1 2]
74 [3 4]
75 [6 -10]
76
77b = [ 8
78 18
79 -18]
80
81x = [2
82 3]
83
84D = [1
85 2]
86
87x_D = [1.78448275;
88 2.82327586;]
89 */
Austin Schuh3de38b02024-06-25 18:25:10 -070090std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0() {
91 auto problem = std::make_unique<LinearLeastSquaresProblem>();
Austin Schuh70cc9552019-01-21 19:46:48 -080092
Austin Schuh3de38b02024-06-25 18:25:10 -070093 auto A = std::make_unique<TripletSparseMatrix>(3, 2, 6);
94 problem->b = std::make_unique<double[]>(3);
95 problem->D = std::make_unique<double[]>(2);
Austin Schuh70cc9552019-01-21 19:46:48 -080096
Austin Schuh3de38b02024-06-25 18:25:10 -070097 problem->x = std::make_unique<double[]>(2);
98 problem->x_D = std::make_unique<double[]>(2);
Austin Schuh70cc9552019-01-21 19:46:48 -080099
100 int* Ai = A->mutable_rows();
101 int* Aj = A->mutable_cols();
102 double* Ax = A->mutable_values();
103
104 int counter = 0;
105 for (int i = 0; i < 3; ++i) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800106 for (int j = 0; j < 2; ++j) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800107 Ai[counter] = i;
108 Aj[counter] = j;
109 ++counter;
110 }
111 }
112
113 Ax[0] = 1.;
114 Ax[1] = 2.;
115 Ax[2] = 3.;
116 Ax[3] = 4.;
117 Ax[4] = 6;
118 Ax[5] = -10;
119 A->set_num_nonzeros(6);
Austin Schuh3de38b02024-06-25 18:25:10 -0700120 problem->A = std::move(A);
Austin Schuh70cc9552019-01-21 19:46:48 -0800121
122 problem->b[0] = 8;
123 problem->b[1] = 18;
124 problem->b[2] = -18;
125
126 problem->x[0] = 2.0;
127 problem->x[1] = 3.0;
128
129 problem->D[0] = 1;
130 problem->D[1] = 2;
131
132 problem->x_D[0] = 1.78448275;
133 problem->x_D[1] = 2.82327586;
134 return problem;
135}
136
Austin Schuh70cc9552019-01-21 19:46:48 -0800137/*
138 A = [1 0 | 2 0 0
139 3 0 | 0 4 0
140 0 5 | 0 0 6
141 0 7 | 8 0 0
142 0 9 | 1 0 0
143 0 0 | 1 1 1]
144
145 b = [0
146 1
147 2
148 3
149 4
150 5]
151
152 c = A'* b = [ 3
153 67
154 33
155 9
156 17]
157
158 A'A = [10 0 2 12 0
159 0 155 65 0 30
160 2 65 70 1 1
161 12 0 1 17 1
162 0 30 1 1 37]
163
Austin Schuh3de38b02024-06-25 18:25:10 -0700164 cond(A'A) = 200.36
165
Austin Schuh70cc9552019-01-21 19:46:48 -0800166 S = [ 42.3419 -1.4000 -11.5806
167 -1.4000 2.6000 1.0000
Austin Schuh3de38b02024-06-25 18:25:10 -0700168 -11.5806 1.0000 31.1935]
Austin Schuh70cc9552019-01-21 19:46:48 -0800169
170 r = [ 4.3032
171 5.4000
Austin Schuh3de38b02024-06-25 18:25:10 -0700172 4.0323]
Austin Schuh70cc9552019-01-21 19:46:48 -0800173
174 S\r = [ 0.2102
175 2.1367
176 0.1388]
177
178 A\b = [-2.3061
179 0.3172
180 0.2102
181 2.1367
182 0.1388]
183*/
184// The following two functions create a TripletSparseMatrix and a
185// BlockSparseMatrix version of this problem.
186
187// TripletSparseMatrix version.
Austin Schuh3de38b02024-06-25 18:25:10 -0700188std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1() {
Austin Schuh70cc9552019-01-21 19:46:48 -0800189 int num_rows = 6;
190 int num_cols = 5;
191
Austin Schuh3de38b02024-06-25 18:25:10 -0700192 auto problem = std::make_unique<LinearLeastSquaresProblem>();
193
194 auto A = std::make_unique<TripletSparseMatrix>(
195 num_rows, num_cols, num_rows * num_cols);
196 problem->b = std::make_unique<double[]>(num_rows);
197 problem->D = std::make_unique<double[]>(num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800198 problem->num_eliminate_blocks = 2;
199
Austin Schuh3de38b02024-06-25 18:25:10 -0700200 problem->x = std::make_unique<double[]>(num_cols);
201 problem->x[0] = -2.3061;
202 problem->x[1] = 0.3172;
203 problem->x[2] = 0.2102;
204 problem->x[3] = 2.1367;
205 problem->x[4] = 0.1388;
206
Austin Schuh70cc9552019-01-21 19:46:48 -0800207 int* rows = A->mutable_rows();
208 int* cols = A->mutable_cols();
209 double* values = A->mutable_values();
210
211 int nnz = 0;
212
213 // Row 1
214 {
215 rows[nnz] = 0;
216 cols[nnz] = 0;
217 values[nnz++] = 1;
218
219 rows[nnz] = 0;
220 cols[nnz] = 2;
221 values[nnz++] = 2;
222 }
223
224 // Row 2
225 {
226 rows[nnz] = 1;
227 cols[nnz] = 0;
228 values[nnz++] = 3;
229
230 rows[nnz] = 1;
231 cols[nnz] = 3;
232 values[nnz++] = 4;
233 }
234
235 // Row 3
236 {
237 rows[nnz] = 2;
238 cols[nnz] = 1;
239 values[nnz++] = 5;
240
241 rows[nnz] = 2;
242 cols[nnz] = 4;
243 values[nnz++] = 6;
244 }
245
246 // Row 4
247 {
248 rows[nnz] = 3;
249 cols[nnz] = 1;
250 values[nnz++] = 7;
251
252 rows[nnz] = 3;
253 cols[nnz] = 2;
254 values[nnz++] = 8;
255 }
256
257 // Row 5
258 {
259 rows[nnz] = 4;
260 cols[nnz] = 1;
261 values[nnz++] = 9;
262
263 rows[nnz] = 4;
264 cols[nnz] = 2;
265 values[nnz++] = 1;
266 }
267
268 // Row 6
269 {
270 rows[nnz] = 5;
271 cols[nnz] = 2;
272 values[nnz++] = 1;
273
274 rows[nnz] = 5;
275 cols[nnz] = 3;
276 values[nnz++] = 1;
277
278 rows[nnz] = 5;
279 cols[nnz] = 4;
280 values[nnz++] = 1;
281 }
282
283 A->set_num_nonzeros(nnz);
284 CHECK(A->IsValid());
285
Austin Schuh3de38b02024-06-25 18:25:10 -0700286 problem->A = std::move(A);
Austin Schuh70cc9552019-01-21 19:46:48 -0800287
288 for (int i = 0; i < num_cols; ++i) {
289 problem->D.get()[i] = 1;
290 }
291
292 for (int i = 0; i < num_rows; ++i) {
293 problem->b.get()[i] = i;
294 }
295
296 return problem;
297}
298
299// BlockSparseMatrix version
Austin Schuh3de38b02024-06-25 18:25:10 -0700300std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2() {
Austin Schuh70cc9552019-01-21 19:46:48 -0800301 int num_rows = 6;
302 int num_cols = 5;
303
Austin Schuh3de38b02024-06-25 18:25:10 -0700304 auto problem = std::make_unique<LinearLeastSquaresProblem>();
Austin Schuh70cc9552019-01-21 19:46:48 -0800305
Austin Schuh3de38b02024-06-25 18:25:10 -0700306 problem->b = std::make_unique<double[]>(num_rows);
307 problem->D = std::make_unique<double[]>(num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800308 problem->num_eliminate_blocks = 2;
309
Austin Schuh3de38b02024-06-25 18:25:10 -0700310 problem->x = std::make_unique<double[]>(num_cols);
311 problem->x[0] = -2.3061;
312 problem->x[1] = 0.3172;
313 problem->x[2] = 0.2102;
314 problem->x[3] = 2.1367;
315 problem->x[4] = 0.1388;
316
317 auto* bs = new CompressedRowBlockStructure;
318 auto values = std::make_unique<double[]>(num_rows * num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800319
320 for (int c = 0; c < num_cols; ++c) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700321 bs->cols.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800322 bs->cols.back().size = 1;
323 bs->cols.back().position = c;
324 }
325
326 int nnz = 0;
327
328 // Row 1
329 {
330 values[nnz++] = 1;
331 values[nnz++] = 2;
332
Austin Schuh3de38b02024-06-25 18:25:10 -0700333 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800334 CompressedRow& row = bs->rows.back();
335 row.block.size = 1;
336 row.block.position = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700337 row.cells.emplace_back(0, 0);
338 row.cells.emplace_back(2, 1);
Austin Schuh70cc9552019-01-21 19:46:48 -0800339 }
340
341 // Row 2
342 {
343 values[nnz++] = 3;
344 values[nnz++] = 4;
345
Austin Schuh3de38b02024-06-25 18:25:10 -0700346 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800347 CompressedRow& row = bs->rows.back();
348 row.block.size = 1;
349 row.block.position = 1;
Austin Schuh3de38b02024-06-25 18:25:10 -0700350 row.cells.emplace_back(0, 2);
351 row.cells.emplace_back(3, 3);
Austin Schuh70cc9552019-01-21 19:46:48 -0800352 }
353
354 // Row 3
355 {
356 values[nnz++] = 5;
357 values[nnz++] = 6;
358
Austin Schuh3de38b02024-06-25 18:25:10 -0700359 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800360 CompressedRow& row = bs->rows.back();
361 row.block.size = 1;
362 row.block.position = 2;
Austin Schuh3de38b02024-06-25 18:25:10 -0700363 row.cells.emplace_back(1, 4);
364 row.cells.emplace_back(4, 5);
Austin Schuh70cc9552019-01-21 19:46:48 -0800365 }
366
367 // Row 4
368 {
369 values[nnz++] = 7;
370 values[nnz++] = 8;
371
Austin Schuh3de38b02024-06-25 18:25:10 -0700372 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800373 CompressedRow& row = bs->rows.back();
374 row.block.size = 1;
375 row.block.position = 3;
Austin Schuh3de38b02024-06-25 18:25:10 -0700376 row.cells.emplace_back(1, 6);
377 row.cells.emplace_back(2, 7);
Austin Schuh70cc9552019-01-21 19:46:48 -0800378 }
379
380 // Row 5
381 {
382 values[nnz++] = 9;
383 values[nnz++] = 1;
384
Austin Schuh3de38b02024-06-25 18:25:10 -0700385 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800386 CompressedRow& row = bs->rows.back();
387 row.block.size = 1;
388 row.block.position = 4;
Austin Schuh3de38b02024-06-25 18:25:10 -0700389 row.cells.emplace_back(1, 8);
390 row.cells.emplace_back(2, 9);
Austin Schuh70cc9552019-01-21 19:46:48 -0800391 }
392
393 // Row 6
394 {
395 values[nnz++] = 1;
396 values[nnz++] = 1;
397 values[nnz++] = 1;
398
Austin Schuh3de38b02024-06-25 18:25:10 -0700399 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800400 CompressedRow& row = bs->rows.back();
401 row.block.size = 1;
402 row.block.position = 5;
Austin Schuh3de38b02024-06-25 18:25:10 -0700403 row.cells.emplace_back(2, 10);
404 row.cells.emplace_back(3, 11);
405 row.cells.emplace_back(4, 12);
Austin Schuh70cc9552019-01-21 19:46:48 -0800406 }
407
Austin Schuh3de38b02024-06-25 18:25:10 -0700408 auto A = std::make_unique<BlockSparseMatrix>(bs);
Austin Schuh70cc9552019-01-21 19:46:48 -0800409 memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
410
411 for (int i = 0; i < num_cols; ++i) {
412 problem->D.get()[i] = 1;
413 }
414
415 for (int i = 0; i < num_rows; ++i) {
416 problem->b.get()[i] = i;
417 }
418
Austin Schuh3de38b02024-06-25 18:25:10 -0700419 problem->A = std::move(A);
Austin Schuh70cc9552019-01-21 19:46:48 -0800420
421 return problem;
422}
423
Austin Schuh70cc9552019-01-21 19:46:48 -0800424/*
425 A = [1 0
426 3 0
427 0 5
428 0 7
429 0 9
430 0 0]
431
432 b = [0
433 1
434 2
435 3
436 4
437 5]
438*/
439// BlockSparseMatrix version
Austin Schuh3de38b02024-06-25 18:25:10 -0700440std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3() {
Austin Schuh70cc9552019-01-21 19:46:48 -0800441 int num_rows = 5;
442 int num_cols = 2;
443
Austin Schuh3de38b02024-06-25 18:25:10 -0700444 auto problem = std::make_unique<LinearLeastSquaresProblem>();
Austin Schuh70cc9552019-01-21 19:46:48 -0800445
Austin Schuh3de38b02024-06-25 18:25:10 -0700446 problem->b = std::make_unique<double[]>(num_rows);
447 problem->D = std::make_unique<double[]>(num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800448 problem->num_eliminate_blocks = 2;
449
Austin Schuh3de38b02024-06-25 18:25:10 -0700450 auto* bs = new CompressedRowBlockStructure;
451 auto values = std::make_unique<double[]>(num_rows * num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800452
453 for (int c = 0; c < num_cols; ++c) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700454 bs->cols.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800455 bs->cols.back().size = 1;
456 bs->cols.back().position = c;
457 }
458
459 int nnz = 0;
460
461 // Row 1
462 {
463 values[nnz++] = 1;
Austin Schuh3de38b02024-06-25 18:25:10 -0700464 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800465 CompressedRow& row = bs->rows.back();
466 row.block.size = 1;
467 row.block.position = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700468 row.cells.emplace_back(0, 0);
Austin Schuh70cc9552019-01-21 19:46:48 -0800469 }
470
471 // Row 2
472 {
473 values[nnz++] = 3;
Austin Schuh3de38b02024-06-25 18:25:10 -0700474 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800475 CompressedRow& row = bs->rows.back();
476 row.block.size = 1;
477 row.block.position = 1;
Austin Schuh3de38b02024-06-25 18:25:10 -0700478 row.cells.emplace_back(0, 1);
Austin Schuh70cc9552019-01-21 19:46:48 -0800479 }
480
481 // Row 3
482 {
483 values[nnz++] = 5;
Austin Schuh3de38b02024-06-25 18:25:10 -0700484 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800485 CompressedRow& row = bs->rows.back();
486 row.block.size = 1;
487 row.block.position = 2;
Austin Schuh3de38b02024-06-25 18:25:10 -0700488 row.cells.emplace_back(1, 2);
Austin Schuh70cc9552019-01-21 19:46:48 -0800489 }
490
491 // Row 4
492 {
493 values[nnz++] = 7;
Austin Schuh3de38b02024-06-25 18:25:10 -0700494 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800495 CompressedRow& row = bs->rows.back();
496 row.block.size = 1;
497 row.block.position = 3;
Austin Schuh3de38b02024-06-25 18:25:10 -0700498 row.cells.emplace_back(1, 3);
Austin Schuh70cc9552019-01-21 19:46:48 -0800499 }
500
501 // Row 5
502 {
503 values[nnz++] = 9;
Austin Schuh3de38b02024-06-25 18:25:10 -0700504 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800505 CompressedRow& row = bs->rows.back();
506 row.block.size = 1;
507 row.block.position = 4;
Austin Schuh3de38b02024-06-25 18:25:10 -0700508 row.cells.emplace_back(1, 4);
Austin Schuh70cc9552019-01-21 19:46:48 -0800509 }
510
Austin Schuh3de38b02024-06-25 18:25:10 -0700511 auto A = std::make_unique<BlockSparseMatrix>(bs);
Austin Schuh70cc9552019-01-21 19:46:48 -0800512 memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
513
514 for (int i = 0; i < num_cols; ++i) {
515 problem->D.get()[i] = 1;
516 }
517
518 for (int i = 0; i < num_rows; ++i) {
519 problem->b.get()[i] = i;
520 }
521
Austin Schuh3de38b02024-06-25 18:25:10 -0700522 problem->A = std::move(A);
Austin Schuh70cc9552019-01-21 19:46:48 -0800523
524 return problem;
525}
526
527/*
528 A = [1 2 0 0 0 1 1
529 1 4 0 0 0 5 6
530 0 0 9 0 0 3 1]
531
532 b = [0
533 1
534 2]
535*/
536// BlockSparseMatrix version
537//
538// This problem has the unique property that it has two different
539// sized f-blocks, but only one of them occurs in the rows involving
540// the one e-block. So performing Schur elimination on this problem
541// tests the Schur Eliminator's ability to handle non-e-block rows
542// correctly when their structure does not conform to the static
543// structure determined by DetectStructure.
544//
545// NOTE: This problem is too small and rank deficient to be solved without
546// the diagonal regularization.
Austin Schuh3de38b02024-06-25 18:25:10 -0700547std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4() {
Austin Schuh70cc9552019-01-21 19:46:48 -0800548 int num_rows = 3;
549 int num_cols = 7;
550
Austin Schuh3de38b02024-06-25 18:25:10 -0700551 auto problem = std::make_unique<LinearLeastSquaresProblem>();
Austin Schuh70cc9552019-01-21 19:46:48 -0800552
Austin Schuh3de38b02024-06-25 18:25:10 -0700553 problem->b = std::make_unique<double[]>(num_rows);
554 problem->D = std::make_unique<double[]>(num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800555 problem->num_eliminate_blocks = 1;
556
Austin Schuh3de38b02024-06-25 18:25:10 -0700557 auto* bs = new CompressedRowBlockStructure;
558 auto values = std::make_unique<double[]>(num_rows * num_cols);
Austin Schuh70cc9552019-01-21 19:46:48 -0800559
560 // Column block structure
Austin Schuh3de38b02024-06-25 18:25:10 -0700561 bs->cols.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800562 bs->cols.back().size = 2;
563 bs->cols.back().position = 0;
564
Austin Schuh3de38b02024-06-25 18:25:10 -0700565 bs->cols.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800566 bs->cols.back().size = 3;
567 bs->cols.back().position = 2;
568
Austin Schuh3de38b02024-06-25 18:25:10 -0700569 bs->cols.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800570 bs->cols.back().size = 2;
571 bs->cols.back().position = 5;
572
573 int nnz = 0;
574
575 // Row 1 & 2
576 {
Austin Schuh3de38b02024-06-25 18:25:10 -0700577 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800578 CompressedRow& row = bs->rows.back();
579 row.block.size = 2;
580 row.block.position = 0;
581
Austin Schuh3de38b02024-06-25 18:25:10 -0700582 row.cells.emplace_back(0, nnz);
Austin Schuh70cc9552019-01-21 19:46:48 -0800583 values[nnz++] = 1;
584 values[nnz++] = 2;
585 values[nnz++] = 1;
586 values[nnz++] = 4;
587
Austin Schuh3de38b02024-06-25 18:25:10 -0700588 row.cells.emplace_back(2, nnz);
Austin Schuh70cc9552019-01-21 19:46:48 -0800589 values[nnz++] = 1;
590 values[nnz++] = 1;
591 values[nnz++] = 5;
592 values[nnz++] = 6;
593 }
594
595 // Row 3
596 {
Austin Schuh3de38b02024-06-25 18:25:10 -0700597 bs->rows.emplace_back();
Austin Schuh70cc9552019-01-21 19:46:48 -0800598 CompressedRow& row = bs->rows.back();
599 row.block.size = 1;
600 row.block.position = 2;
601
Austin Schuh3de38b02024-06-25 18:25:10 -0700602 row.cells.emplace_back(1, nnz);
Austin Schuh70cc9552019-01-21 19:46:48 -0800603 values[nnz++] = 9;
604 values[nnz++] = 0;
605 values[nnz++] = 0;
606
Austin Schuh3de38b02024-06-25 18:25:10 -0700607 row.cells.emplace_back(2, nnz);
Austin Schuh70cc9552019-01-21 19:46:48 -0800608 values[nnz++] = 3;
609 values[nnz++] = 1;
610 }
611
Austin Schuh3de38b02024-06-25 18:25:10 -0700612 auto A = std::make_unique<BlockSparseMatrix>(bs);
Austin Schuh70cc9552019-01-21 19:46:48 -0800613 memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
614
615 for (int i = 0; i < num_cols; ++i) {
616 problem->D.get()[i] = (i + 1) * 100;
617 }
618
619 for (int i = 0; i < num_rows; ++i) {
620 problem->b.get()[i] = i;
621 }
622
Austin Schuh3de38b02024-06-25 18:25:10 -0700623 problem->A = std::move(A);
624 return problem;
625}
626
627/*
628A problem with block-diagonal F'F.
629
630 A = [1 0 | 0 0 2
631 3 0 | 0 0 4
632 0 -1 | 0 1 0
633 0 -3 | 0 1 0
634 0 -1 | 3 0 0
635 0 -2 | 1 0 0]
636
637 b = [0
638 1
639 2
640 3
641 4
642 5]
643
644 c = A'* b = [ 22
645 -25
646 17
647 7
648 4]
649
650 A'A = [10 0 0 0 10
651 0 15 -5 -4 0
652 0 -5 10 0 0
653 0 -4 0 2 0
654 10 0 0 0 20]
655
656 cond(A'A) = 41.402
657
658 S = [ 8.3333 -1.3333 0
659 -1.3333 0.9333 0
660 0 0 10.0000]
661
662 r = [ 8.6667
663 -1.6667
664 1.0000]
665
666 S\r = [ 0.9778
667 -0.3889
668 0.1000]
669
670 A\b = [ 0.2
671 -1.4444
672 0.9777
673 -0.3888
674 0.1]
675*/
676
677std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem5() {
678 int num_rows = 6;
679 int num_cols = 5;
680
681 auto problem = std::make_unique<LinearLeastSquaresProblem>();
682 problem->b = std::make_unique<double[]>(num_rows);
683 problem->D = std::make_unique<double[]>(num_cols);
684 problem->num_eliminate_blocks = 2;
685
686 // TODO: add x
687 problem->x = std::make_unique<double[]>(num_cols);
688 problem->x[0] = 0.2;
689 problem->x[1] = -1.4444;
690 problem->x[2] = 0.9777;
691 problem->x[3] = -0.3888;
692 problem->x[4] = 0.1;
693
694 auto* bs = new CompressedRowBlockStructure;
695 auto values = std::make_unique<double[]>(num_rows * num_cols);
696
697 for (int c = 0; c < num_cols; ++c) {
698 bs->cols.emplace_back();
699 bs->cols.back().size = 1;
700 bs->cols.back().position = c;
701 }
702
703 int nnz = 0;
704
705 // Row 1
706 {
707 values[nnz++] = -1;
708 values[nnz++] = 2;
709
710 bs->rows.emplace_back();
711 CompressedRow& row = bs->rows.back();
712 row.block.size = 1;
713 row.block.position = 0;
714 row.cells.emplace_back(0, 0);
715 row.cells.emplace_back(4, 1);
716 }
717
718 // Row 2
719 {
720 values[nnz++] = 3;
721 values[nnz++] = 4;
722
723 bs->rows.emplace_back();
724 CompressedRow& row = bs->rows.back();
725 row.block.size = 1;
726 row.block.position = 1;
727 row.cells.emplace_back(0, 2);
728 row.cells.emplace_back(4, 3);
729 }
730
731 // Row 3
732 {
733 values[nnz++] = -1;
734 values[nnz++] = 1;
735
736 bs->rows.emplace_back();
737 CompressedRow& row = bs->rows.back();
738 row.block.size = 1;
739 row.block.position = 2;
740 row.cells.emplace_back(1, 4);
741 row.cells.emplace_back(3, 5);
742 }
743
744 // Row 4
745 {
746 values[nnz++] = -3;
747 values[nnz++] = 1;
748
749 bs->rows.emplace_back();
750 CompressedRow& row = bs->rows.back();
751 row.block.size = 1;
752 row.block.position = 3;
753 row.cells.emplace_back(1, 6);
754 row.cells.emplace_back(3, 7);
755 }
756
757 // Row 5
758 {
759 values[nnz++] = -1;
760 values[nnz++] = 3;
761
762 bs->rows.emplace_back();
763 CompressedRow& row = bs->rows.back();
764 row.block.size = 1;
765 row.block.position = 4;
766 row.cells.emplace_back(1, 8);
767 row.cells.emplace_back(2, 9);
768 }
769
770 // Row 6
771 {
772 // values[nnz++] = 2;
773 values[nnz++] = -2;
774 values[nnz++] = 1;
775
776 bs->rows.emplace_back();
777 CompressedRow& row = bs->rows.back();
778 row.block.size = 1;
779 row.block.position = 5;
780 // row.cells.emplace_back(0, 10);
781 row.cells.emplace_back(1, 10);
782 row.cells.emplace_back(2, 11);
783 }
784
785 auto A = std::make_unique<BlockSparseMatrix>(bs);
786 memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
787
788 for (int i = 0; i < num_cols; ++i) {
789 problem->D.get()[i] = 1;
790 }
791
792 for (int i = 0; i < num_rows; ++i) {
793 problem->b.get()[i] = i;
794 }
795
796 problem->A = std::move(A);
797
798 return problem;
799}
800
801/*
802 A = [1 2 0 0 0 1 1
803 1 4 0 0 0 5 6
804 3 4 0 0 0 7 8
805 5 6 0 0 0 9 0
806 0 0 9 0 0 3 1]
807
808 b = [0
809 1
810 2
811 3
812 4]
813*/
814// BlockSparseMatrix version
815//
816// This problem has the unique property that it has two different
817// sized f-blocks, but only one of them occurs in the rows involving
818// the one e-block. So performing Schur elimination on this problem
819// tests the Schur Eliminator's ability to handle non-e-block rows
820// correctly when their structure does not conform to the static
821// structure determined by DetectStructure.
822//
823// Additionally, this problem has the first row of the last row block of E being
824// larger than number of row blocks in E
825//
826// NOTE: This problem is too small and rank deficient to be solved without
827// the diagonal regularization.
828std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem6() {
829 int num_rows = 5;
830 int num_cols = 7;
831
832 auto problem = std::make_unique<LinearLeastSquaresProblem>();
833
834 problem->b = std::make_unique<double[]>(num_rows);
835 problem->D = std::make_unique<double[]>(num_cols);
836 problem->num_eliminate_blocks = 1;
837
838 auto* bs = new CompressedRowBlockStructure;
839 auto values = std::make_unique<double[]>(num_rows * num_cols);
840
841 // Column block structure
842 bs->cols.emplace_back();
843 bs->cols.back().size = 2;
844 bs->cols.back().position = 0;
845
846 bs->cols.emplace_back();
847 bs->cols.back().size = 3;
848 bs->cols.back().position = 2;
849
850 bs->cols.emplace_back();
851 bs->cols.back().size = 2;
852 bs->cols.back().position = 5;
853
854 int nnz = 0;
855
856 // Row 1 & 2
857 {
858 bs->rows.emplace_back();
859 CompressedRow& row = bs->rows.back();
860 row.block.size = 2;
861 row.block.position = 0;
862
863 row.cells.emplace_back(0, nnz);
864 values[nnz++] = 1;
865 values[nnz++] = 2;
866 values[nnz++] = 1;
867 values[nnz++] = 4;
868
869 row.cells.emplace_back(2, nnz);
870 values[nnz++] = 1;
871 values[nnz++] = 1;
872 values[nnz++] = 5;
873 values[nnz++] = 6;
874 }
875
876 // Row 3 & 4
877 {
878 bs->rows.emplace_back();
879 CompressedRow& row = bs->rows.back();
880 row.block.size = 2;
881 row.block.position = 2;
882
883 row.cells.emplace_back(0, nnz);
884 values[nnz++] = 3;
885 values[nnz++] = 4;
886 values[nnz++] = 5;
887 values[nnz++] = 6;
888
889 row.cells.emplace_back(2, nnz);
890 values[nnz++] = 7;
891 values[nnz++] = 8;
892 values[nnz++] = 9;
893 values[nnz++] = 0;
894 }
895
896 // Row 5
897 {
898 bs->rows.emplace_back();
899 CompressedRow& row = bs->rows.back();
900 row.block.size = 1;
901 row.block.position = 4;
902
903 row.cells.emplace_back(1, nnz);
904 values[nnz++] = 9;
905 values[nnz++] = 0;
906 values[nnz++] = 0;
907
908 row.cells.emplace_back(2, nnz);
909 values[nnz++] = 3;
910 values[nnz++] = 1;
911 }
912
913 auto A = std::make_unique<BlockSparseMatrix>(bs);
914 memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
915
916 for (int i = 0; i < num_cols; ++i) {
917 problem->D.get()[i] = (i + 1) * 100;
918 }
919
920 for (int i = 0; i < num_rows; ++i) {
921 problem->b.get()[i] = i;
922 }
923
924 problem->A = std::move(A);
Austin Schuh70cc9552019-01-21 19:46:48 -0800925 return problem;
926}
927
928namespace {
929bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A,
930 const double* D,
931 const double* b,
932 const double* x,
Austin Schuh3de38b02024-06-25 18:25:10 -0700933 int /*num_eliminate_blocks*/) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800934 CHECK(A != nullptr);
935 Matrix AA;
936 A->ToDenseMatrix(&AA);
937 LOG(INFO) << "A^T: \n" << AA.transpose();
938
Austin Schuh3de38b02024-06-25 18:25:10 -0700939 if (D != nullptr) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800940 LOG(INFO) << "A's appended diagonal:\n" << ConstVectorRef(D, A->num_cols());
Austin Schuh70cc9552019-01-21 19:46:48 -0800941 }
942
Austin Schuh3de38b02024-06-25 18:25:10 -0700943 if (b != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800944 LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows());
945 }
946
Austin Schuh3de38b02024-06-25 18:25:10 -0700947 if (x != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800948 LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols());
949 }
950 return true;
951}
952
Austin Schuh3de38b02024-06-25 18:25:10 -0700953void WriteArrayToFileOrDie(const std::string& filename,
Austin Schuh70cc9552019-01-21 19:46:48 -0800954 const double* x,
955 const int size) {
956 CHECK(x != nullptr);
957 VLOG(2) << "Writing array to: " << filename;
958 FILE* fptr = fopen(filename.c_str(), "w");
959 CHECK(fptr != nullptr);
960 for (int i = 0; i < size; ++i) {
961 fprintf(fptr, "%17f\n", x[i]);
962 }
963 fclose(fptr);
964}
965
Austin Schuh3de38b02024-06-25 18:25:10 -0700966bool DumpLinearLeastSquaresProblemToTextFile(const std::string& filename_base,
Austin Schuh70cc9552019-01-21 19:46:48 -0800967 const SparseMatrix* A,
968 const double* D,
969 const double* b,
970 const double* x,
Austin Schuh3de38b02024-06-25 18:25:10 -0700971 int /*num_eliminate_blocks*/) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800972 CHECK(A != nullptr);
973 LOG(INFO) << "writing to: " << filename_base << "*";
974
Austin Schuh3de38b02024-06-25 18:25:10 -0700975 std::string matlab_script;
Austin Schuh70cc9552019-01-21 19:46:48 -0800976 StringAppendF(&matlab_script,
977 "function lsqp = load_trust_region_problem()\n");
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800978 StringAppendF(&matlab_script, "lsqp.num_rows = %d;\n", A->num_rows());
979 StringAppendF(&matlab_script, "lsqp.num_cols = %d;\n", A->num_cols());
Austin Schuh70cc9552019-01-21 19:46:48 -0800980
981 {
Austin Schuh3de38b02024-06-25 18:25:10 -0700982 std::string filename = filename_base + "_A.txt";
Austin Schuh70cc9552019-01-21 19:46:48 -0800983 FILE* fptr = fopen(filename.c_str(), "w");
984 CHECK(fptr != nullptr);
985 A->ToTextFile(fptr);
986 fclose(fptr);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800987 StringAppendF(
988 &matlab_script, "tmp = load('%s', '-ascii');\n", filename.c_str());
Austin Schuh70cc9552019-01-21 19:46:48 -0800989 StringAppendF(
990 &matlab_script,
991 "lsqp.A = sparse(tmp(:, 1) + 1, tmp(:, 2) + 1, tmp(:, 3), %d, %d);\n",
992 A->num_rows(),
993 A->num_cols());
994 }
995
Austin Schuh3de38b02024-06-25 18:25:10 -0700996 if (D != nullptr) {
997 std::string filename = filename_base + "_D.txt";
Austin Schuh70cc9552019-01-21 19:46:48 -0800998 WriteArrayToFileOrDie(filename, D, A->num_cols());
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800999 StringAppendF(
1000 &matlab_script, "lsqp.D = load('%s', '-ascii');\n", filename.c_str());
Austin Schuh70cc9552019-01-21 19:46:48 -08001001 }
1002
Austin Schuh3de38b02024-06-25 18:25:10 -07001003 if (b != nullptr) {
1004 std::string filename = filename_base + "_b.txt";
Austin Schuh70cc9552019-01-21 19:46:48 -08001005 WriteArrayToFileOrDie(filename, b, A->num_rows());
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001006 StringAppendF(
1007 &matlab_script, "lsqp.b = load('%s', '-ascii');\n", filename.c_str());
Austin Schuh70cc9552019-01-21 19:46:48 -08001008 }
1009
Austin Schuh3de38b02024-06-25 18:25:10 -07001010 if (x != nullptr) {
1011 std::string filename = filename_base + "_x.txt";
Austin Schuh70cc9552019-01-21 19:46:48 -08001012 WriteArrayToFileOrDie(filename, x, A->num_cols());
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001013 StringAppendF(
1014 &matlab_script, "lsqp.x = load('%s', '-ascii');\n", filename.c_str());
Austin Schuh70cc9552019-01-21 19:46:48 -08001015 }
1016
Austin Schuh3de38b02024-06-25 18:25:10 -07001017 std::string matlab_filename = filename_base + ".m";
Austin Schuh70cc9552019-01-21 19:46:48 -08001018 WriteStringToFileOrDie(matlab_script, matlab_filename);
1019 return true;
1020}
1021} // namespace
1022
Austin Schuh3de38b02024-06-25 18:25:10 -07001023bool DumpLinearLeastSquaresProblem(const std::string& filename_base,
Austin Schuh70cc9552019-01-21 19:46:48 -08001024 DumpFormatType dump_format_type,
1025 const SparseMatrix* A,
1026 const double* D,
1027 const double* b,
1028 const double* x,
1029 int num_eliminate_blocks) {
1030 switch (dump_format_type) {
1031 case CONSOLE:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001032 return DumpLinearLeastSquaresProblemToConsole(
1033 A, D, b, x, num_eliminate_blocks);
Austin Schuh70cc9552019-01-21 19:46:48 -08001034 case TEXTFILE:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001035 return DumpLinearLeastSquaresProblemToTextFile(
1036 filename_base, A, D, b, x, num_eliminate_blocks);
Austin Schuh70cc9552019-01-21 19:46:48 -08001037 default:
1038 LOG(FATAL) << "Unknown DumpFormatType " << dump_format_type;
1039 }
1040
1041 return true;
1042}
1043
Austin Schuh3de38b02024-06-25 18:25:10 -07001044} // namespace ceres::internal