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/
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions are met:
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.
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.
29
// Author: sameeragarwal@google.com (Sameer Agarwal)
31
#include "ceres/linear_least_squares_problems.h"
36
#include <glog/logging.h>
37
#include "ceres/block_sparse_matrix.h"
38
#include "ceres/block_structure.h"
39
#include "ceres/casts.h"
40
#include "ceres/compressed_row_sparse_matrix.h"
41
#include "ceres/file.h"
42
#include "ceres/matrix_proto.h"
43
#include "ceres/triplet_sparse_matrix.h"
44
#include "ceres/stringprintf.h"
45
#include "ceres/internal/scoped_ptr.h"
46
#include "ceres/types.h"
51
LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) {
54
return LinearLeastSquaresProblem0();
56
return LinearLeastSquaresProblem1();
58
return LinearLeastSquaresProblem2();
60
return LinearLeastSquaresProblem3();
62
LOG(FATAL) << "Unknown problem id requested " << id;
66
#ifndef CERES_DONT_HAVE_PROTOCOL_BUFFERS
67
LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromFile(
68
const string& filename) {
69
LinearLeastSquaresProblemProto problem_proto;
71
string serialized_proto;
72
ReadFileToStringOrDie(filename, &serialized_proto);
73
CHECK(problem_proto.ParseFromString(serialized_proto));
76
LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;
77
const SparseMatrixProto& A = problem_proto.a();
79
if (A.has_block_matrix()) {
80
problem->A.reset(new BlockSparseMatrix(A));
81
} else if (A.has_triplet_matrix()) {
82
problem->A.reset(new TripletSparseMatrix(A));
84
problem->A.reset(new CompressedRowSparseMatrix(A));
87
if (problem_proto.b_size() > 0) {
88
problem->b.reset(new double[problem_proto.b_size()]);
89
for (int i = 0; i < problem_proto.b_size(); ++i) {
90
problem->b[i] = problem_proto.b(i);
94
if (problem_proto.d_size() > 0) {
95
problem->D.reset(new double[problem_proto.d_size()]);
96
for (int i = 0; i < problem_proto.d_size(); ++i) {
97
problem->D[i] = problem_proto.d(i);
101
if (problem_proto.d_size() > 0) {
102
if (problem_proto.x_size() > 0) {
103
problem->x_D.reset(new double[problem_proto.x_size()]);
104
for (int i = 0; i < problem_proto.x_size(); ++i) {
105
problem->x_D[i] = problem_proto.x(i);
109
if (problem_proto.x_size() > 0) {
110
problem->x.reset(new double[problem_proto.x_size()]);
111
for (int i = 0; i < problem_proto.x_size(); ++i) {
112
problem->x[i] = problem_proto.x(i);
117
problem->num_eliminate_blocks = 0;
118
if (problem_proto.has_num_eliminate_blocks()) {
119
problem->num_eliminate_blocks = problem_proto.num_eliminate_blocks();
125
LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromFile(
126
const string& filename) {
128
<< "Loading a least squares problem from disk requires "
129
<< "Ceres to be built with Protocol Buffers support.";
132
#endif // CERES_DONT_HAVE_PROTOCOL_BUFFERS
152
LinearLeastSquaresProblem* LinearLeastSquaresProblem0() {
153
LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;
155
TripletSparseMatrix* A = new TripletSparseMatrix(3, 2, 6);
156
problem->b.reset(new double[3]);
157
problem->D.reset(new double[2]);
159
problem->x.reset(new double[2]);
160
problem->x_D.reset(new double[2]);
162
int* Ai = A->mutable_rows();
163
int* Aj = A->mutable_cols();
164
double* Ax = A->mutable_values();
167
for (int i = 0; i < 3; ++i) {
168
for (int j = 0; j< 2; ++j) {
181
A->set_num_nonzeros(6);
194
problem->x_D[0] = 1.78448275;
195
problem->x_D[1] = 2.82327586;
227
S = [ 42.3419 -1.4000 -11.5806
228
-1.4000 2.6000 1.0000
229
11.5806 1.0000 31.1935]
245
// The following two functions create a TripletSparseMatrix and a
246
// BlockSparseMatrix version of this problem.
248
// TripletSparseMatrix version.
249
LinearLeastSquaresProblem* LinearLeastSquaresProblem1() {
253
LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;
254
TripletSparseMatrix* A = new TripletSparseMatrix(num_rows,
256
num_rows * num_cols);
257
problem->b.reset(new double[num_rows]);
258
problem->D.reset(new double[num_cols]);
259
problem->num_eliminate_blocks = 2;
261
int* rows = A->mutable_rows();
262
int* cols = A->mutable_cols();
263
double* values = A->mutable_values();
337
A->set_num_nonzeros(nnz);
342
for (int i = 0; i < num_cols; ++i) {
343
problem->D.get()[i] = 1;
346
for (int i = 0; i < num_rows; ++i) {
347
problem->b.get()[i] = i;
353
// BlockSparseMatrix version
354
LinearLeastSquaresProblem* LinearLeastSquaresProblem2() {
358
LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;
360
problem->b.reset(new double[num_rows]);
361
problem->D.reset(new double[num_cols]);
362
problem->num_eliminate_blocks = 2;
364
CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;
365
scoped_array<double> values(new double[num_rows * num_cols]);
367
for (int c = 0; c < num_cols; ++c) {
368
bs->cols.push_back(Block());
369
bs->cols.back().size = 1;
370
bs->cols.back().position = c;
380
bs->rows.push_back(CompressedRow());
381
CompressedRow& row = bs->rows.back();
383
row.block.position = 0;
384
row.cells.push_back(Cell(0, 0));
385
row.cells.push_back(Cell(2, 1));
393
bs->rows.push_back(CompressedRow());
394
CompressedRow& row = bs->rows.back();
396
row.block.position = 1;
397
row.cells.push_back(Cell(0, 2));
398
row.cells.push_back(Cell(3, 3));
406
bs->rows.push_back(CompressedRow());
407
CompressedRow& row = bs->rows.back();
409
row.block.position = 2;
410
row.cells.push_back(Cell(1, 4));
411
row.cells.push_back(Cell(4, 5));
419
bs->rows.push_back(CompressedRow());
420
CompressedRow& row = bs->rows.back();
422
row.block.position = 3;
423
row.cells.push_back(Cell(1, 6));
424
row.cells.push_back(Cell(2, 7));
432
bs->rows.push_back(CompressedRow());
433
CompressedRow& row = bs->rows.back();
435
row.block.position = 4;
436
row.cells.push_back(Cell(1, 8));
437
row.cells.push_back(Cell(2, 9));
446
bs->rows.push_back(CompressedRow());
447
CompressedRow& row = bs->rows.back();
449
row.block.position = 5;
450
row.cells.push_back(Cell(2, 10));
451
row.cells.push_back(Cell(3, 11));
452
row.cells.push_back(Cell(4, 12));
455
BlockSparseMatrix* A = new BlockSparseMatrix(bs);
456
memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
458
for (int i = 0; i < num_cols; ++i) {
459
problem->D.get()[i] = 1;
462
for (int i = 0; i < num_rows; ++i) {
463
problem->b.get()[i] = i;
487
// BlockSparseMatrix version
488
LinearLeastSquaresProblem* LinearLeastSquaresProblem3() {
492
LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;
494
problem->b.reset(new double[num_rows]);
495
problem->D.reset(new double[num_cols]);
496
problem->num_eliminate_blocks = 2;
498
CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;
499
scoped_array<double> values(new double[num_rows * num_cols]);
501
for (int c = 0; c < num_cols; ++c) {
502
bs->cols.push_back(Block());
503
bs->cols.back().size = 1;
504
bs->cols.back().position = c;
512
bs->rows.push_back(CompressedRow());
513
CompressedRow& row = bs->rows.back();
515
row.block.position = 0;
516
row.cells.push_back(Cell(0, 0));
522
bs->rows.push_back(CompressedRow());
523
CompressedRow& row = bs->rows.back();
525
row.block.position = 1;
526
row.cells.push_back(Cell(0, 1));
532
bs->rows.push_back(CompressedRow());
533
CompressedRow& row = bs->rows.back();
535
row.block.position = 2;
536
row.cells.push_back(Cell(1, 2));
542
bs->rows.push_back(CompressedRow());
543
CompressedRow& row = bs->rows.back();
545
row.block.position = 3;
546
row.cells.push_back(Cell(1, 3));
552
bs->rows.push_back(CompressedRow());
553
CompressedRow& row = bs->rows.back();
555
row.block.position = 4;
556
row.cells.push_back(Cell(1, 4));
559
BlockSparseMatrix* A = new BlockSparseMatrix(bs);
560
memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
562
for (int i = 0; i < num_cols; ++i) {
563
problem->D.get()[i] = 1;
566
for (int i = 0; i < num_rows; ++i) {
567
problem->b.get()[i] = i;
575
bool DumpLinearLeastSquaresProblemToConsole(const string& directory,
577
const SparseMatrix* A,
581
int num_eliminate_blocks) {
584
A->ToDenseMatrix(&AA);
585
LOG(INFO) << "A^T: \n" << AA.transpose();
588
LOG(INFO) << "A's appended diagonal:\n"
589
<< ConstVectorRef(D, A->num_cols());
593
LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows());
597
LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols());
602
#ifndef CERES_DONT_HAVE_PROTOCOL_BUFFERS
603
bool DumpLinearLeastSquaresProblemToProtocolBuffer(const string& directory,
605
const SparseMatrix* A,
609
int num_eliminate_blocks) {
611
LinearLeastSquaresProblemProto lsqp;
612
A->ToProto(lsqp.mutable_a());
615
for (int i = 0; i < A->num_cols(); ++i) {
621
for (int i = 0; i < A->num_rows(); ++i) {
627
for (int i = 0; i < A->num_cols(); ++i) {
632
lsqp.set_num_eliminate_blocks(num_eliminate_blocks);
633
string format_string = JoinPath(directory,
634
"lm_iteration_%03d.lsqp");
636
StringPrintf(format_string.c_str(), iteration);
637
LOG(INFO) << "Dumping least squares problem for iteration " << iteration
638
<< " to disk. File: " << filename;
639
WriteStringToFileOrDie(lsqp.SerializeAsString(), filename);
643
bool DumpLinearLeastSquaresProblemToProtocolBuffer(const string& directory,
645
const SparseMatrix* A,
649
int num_eliminate_blocks) {
650
LOG(ERROR) << "Dumping least squares problems is only "
651
<< "supported when Ceres is compiled with "
652
<< "protocol buffer support.";
657
void WriteArrayToFileOrDie(const string& filename,
661
VLOG(2) << "Writing array to: " << filename;
662
FILE* fptr = fopen(filename.c_str(), "w");
664
for (int i = 0; i < size; ++i) {
665
fprintf(fptr, "%17f\n", x[i]);
670
bool DumpLinearLeastSquaresProblemToTextFile(const string& directory,
672
const SparseMatrix* A,
676
int num_eliminate_blocks) {
678
string format_string = JoinPath(directory,
679
"lm_iteration_%03d");
680
string filename_prefix =
681
StringPrintf(format_string.c_str(), iteration);
683
LOG(INFO) << "writing to: " << filename_prefix << "*";
685
string matlab_script;
686
StringAppendF(&matlab_script,
687
"function lsqp = lm_iteration_%03d()\n",
691
StringAppendF(&matlab_script,
692
"lsqp.num_rows = %d;\n", A->num_rows());
693
StringAppendF(&matlab_script,
694
"lsqp.num_cols = %d;\n", A->num_cols());
697
string filename = filename_prefix + "_A.txt";
698
FILE* fptr = fopen(filename.c_str(), "w");
702
StringAppendF(&matlab_script,
703
"tmp = load('%s', '-ascii');\n", filename.c_str());
706
"lsqp.A = sparse(tmp(:, 1) + 1, tmp(:, 2) + 1, tmp(:, 3), %d, %d);\n",
713
string filename = filename_prefix + "_D.txt";
714
WriteArrayToFileOrDie(filename, D, A->num_cols());
715
StringAppendF(&matlab_script,
716
"lsqp.D = load('%s', '-ascii');\n", filename.c_str());
720
string filename = filename_prefix + "_b.txt";
721
WriteArrayToFileOrDie(filename, b, A->num_rows());
722
StringAppendF(&matlab_script,
723
"lsqp.b = load('%s', '-ascii');\n", filename.c_str());
727
string filename = filename_prefix + "_x.txt";
728
WriteArrayToFileOrDie(filename, x, A->num_cols());
729
StringAppendF(&matlab_script,
730
"lsqp.x = load('%s', '-ascii');\n", filename.c_str());
733
string matlab_filename = filename_prefix + ".m";
734
WriteStringToFileOrDie(matlab_script, matlab_filename);
738
bool DumpLinearLeastSquaresProblem(const string& directory,
740
DumpFormatType dump_format_type,
741
const SparseMatrix* A,
745
int num_eliminate_blocks) {
746
switch (dump_format_type) {
748
return DumpLinearLeastSquaresProblemToConsole(directory,
751
num_eliminate_blocks);
753
return DumpLinearLeastSquaresProblemToProtocolBuffer(
757
num_eliminate_blocks);
759
return DumpLinearLeastSquaresProblemToTextFile(directory,
762
num_eliminate_blocks);
764
LOG(FATAL) << "Unknown DumpFormatType " << dump_format_type;
770
} // namespace internal