| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2015 Google Inc. All rights reserved.// http://ceres-solver.org///// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// * Redistributions of source code must retain the above copyright notice,//   this list of conditions and the following disclaimer.// * Redistributions in binary form must reproduce the above copyright notice,//   this list of conditions and the following disclaimer in the documentation//   and/or other materials provided with the distribution.// * Neither the name of Google Inc. nor the names of its contributors may be//   used to endorse or promote products derived from this software without//   specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE// POSSIBILITY OF SUCH DAMAGE.//// Author: sameeragarwal@google.com (Sameer Agarwal)#include "ceres/linear_least_squares_problems.h"#include <cstdio>#include <memory>#include <string>#include <vector>#include "ceres/block_sparse_matrix.h"#include "ceres/block_structure.h"#include "ceres/casts.h"#include "ceres/file.h"#include "ceres/stringprintf.h"#include "ceres/triplet_sparse_matrix.h"#include "ceres/types.h"#include "glog/logging.h"namespace ceres {namespace internal {using std::string;LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) {  switch (id) {    case 0:      return LinearLeastSquaresProblem0();    case 1:      return LinearLeastSquaresProblem1();    case 2:      return LinearLeastSquaresProblem2();    case 3:      return LinearLeastSquaresProblem3();    case 4:      return LinearLeastSquaresProblem4();    default:      LOG(FATAL) << "Unknown problem id requested " << id;  }  return NULL;}/*A = [1   2]    [3   4]    [6 -10]b = [  8      18     -18]x = [2     3]D = [1     2]x_D = [1.78448275;       2.82327586;] */LinearLeastSquaresProblem* LinearLeastSquaresProblem0() {  LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;  TripletSparseMatrix* A = new TripletSparseMatrix(3, 2, 6);  problem->b.reset(new double[3]);  problem->D.reset(new double[2]);  problem->x.reset(new double[2]);  problem->x_D.reset(new double[2]);  int* Ai = A->mutable_rows();  int* Aj = A->mutable_cols();  double* Ax = A->mutable_values();  int counter = 0;  for (int i = 0; i < 3; ++i) {    for (int j = 0; j < 2; ++j) {      Ai[counter] = i;      Aj[counter] = j;      ++counter;    }  }  Ax[0] = 1.;  Ax[1] = 2.;  Ax[2] = 3.;  Ax[3] = 4.;  Ax[4] = 6;  Ax[5] = -10;  A->set_num_nonzeros(6);  problem->A.reset(A);  problem->b[0] = 8;  problem->b[1] = 18;  problem->b[2] = -18;  problem->x[0] = 2.0;  problem->x[1] = 3.0;  problem->D[0] = 1;  problem->D[1] = 2;  problem->x_D[0] = 1.78448275;  problem->x_D[1] = 2.82327586;  return problem;}/*      A = [1 0  | 2 0 0           3 0  | 0 4 0           0 5  | 0 0 6           0 7  | 8 0 0           0 9  | 1 0 0           0 0  | 1 1 1]      b = [0           1           2           3           4           5]      c = A'* b = [ 3                   67                   33                    9                   17]      A'A = [10    0    2   12   0              0  155   65    0  30              2   65   70    1   1             12    0    1   17   1              0   30    1    1  37]      S = [ 42.3419  -1.4000  -11.5806            -1.4000   2.6000    1.0000            11.5806   1.0000   31.1935]      r = [ 4.3032            5.4000            5.0323]      S\r = [ 0.2102              2.1367              0.1388]      A\b = [-2.3061              0.3172              0.2102              2.1367              0.1388]*/// The following two functions create a TripletSparseMatrix and a// BlockSparseMatrix version of this problem.// TripletSparseMatrix version.LinearLeastSquaresProblem* LinearLeastSquaresProblem1() {  int num_rows = 6;  int num_cols = 5;  LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;  TripletSparseMatrix* A =      new TripletSparseMatrix(num_rows, num_cols, num_rows * num_cols);  problem->b.reset(new double[num_rows]);  problem->D.reset(new double[num_cols]);  problem->num_eliminate_blocks = 2;  int* rows = A->mutable_rows();  int* cols = A->mutable_cols();  double* values = A->mutable_values();  int nnz = 0;  // Row 1  {    rows[nnz] = 0;    cols[nnz] = 0;    values[nnz++] = 1;    rows[nnz] = 0;    cols[nnz] = 2;    values[nnz++] = 2;  }  // Row 2  {    rows[nnz] = 1;    cols[nnz] = 0;    values[nnz++] = 3;    rows[nnz] = 1;    cols[nnz] = 3;    values[nnz++] = 4;  }  // Row 3  {    rows[nnz] = 2;    cols[nnz] = 1;    values[nnz++] = 5;    rows[nnz] = 2;    cols[nnz] = 4;    values[nnz++] = 6;  }  // Row 4  {    rows[nnz] = 3;    cols[nnz] = 1;    values[nnz++] = 7;    rows[nnz] = 3;    cols[nnz] = 2;    values[nnz++] = 8;  }  // Row 5  {    rows[nnz] = 4;    cols[nnz] = 1;    values[nnz++] = 9;    rows[nnz] = 4;    cols[nnz] = 2;    values[nnz++] = 1;  }  // Row 6  {    rows[nnz] = 5;    cols[nnz] = 2;    values[nnz++] = 1;    rows[nnz] = 5;    cols[nnz] = 3;    values[nnz++] = 1;    rows[nnz] = 5;    cols[nnz] = 4;    values[nnz++] = 1;  }  A->set_num_nonzeros(nnz);  CHECK(A->IsValid());  problem->A.reset(A);  for (int i = 0; i < num_cols; ++i) {    problem->D.get()[i] = 1;  }  for (int i = 0; i < num_rows; ++i) {    problem->b.get()[i] = i;  }  return problem;}// BlockSparseMatrix versionLinearLeastSquaresProblem* LinearLeastSquaresProblem2() {  int num_rows = 6;  int num_cols = 5;  LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;  problem->b.reset(new double[num_rows]);  problem->D.reset(new double[num_cols]);  problem->num_eliminate_blocks = 2;  CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;  std::unique_ptr<double[]> values(new double[num_rows * num_cols]);  for (int c = 0; c < num_cols; ++c) {    bs->cols.push_back(Block());    bs->cols.back().size = 1;    bs->cols.back().position = c;  }  int nnz = 0;  // Row 1  {    values[nnz++] = 1;    values[nnz++] = 2;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 0;    row.cells.push_back(Cell(0, 0));    row.cells.push_back(Cell(2, 1));  }  // Row 2  {    values[nnz++] = 3;    values[nnz++] = 4;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 1;    row.cells.push_back(Cell(0, 2));    row.cells.push_back(Cell(3, 3));  }  // Row 3  {    values[nnz++] = 5;    values[nnz++] = 6;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 2;    row.cells.push_back(Cell(1, 4));    row.cells.push_back(Cell(4, 5));  }  // Row 4  {    values[nnz++] = 7;    values[nnz++] = 8;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 3;    row.cells.push_back(Cell(1, 6));    row.cells.push_back(Cell(2, 7));  }  // Row 5  {    values[nnz++] = 9;    values[nnz++] = 1;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 4;    row.cells.push_back(Cell(1, 8));    row.cells.push_back(Cell(2, 9));  }  // Row 6  {    values[nnz++] = 1;    values[nnz++] = 1;    values[nnz++] = 1;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 5;    row.cells.push_back(Cell(2, 10));    row.cells.push_back(Cell(3, 11));    row.cells.push_back(Cell(4, 12));  }  BlockSparseMatrix* A = new BlockSparseMatrix(bs);  memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));  for (int i = 0; i < num_cols; ++i) {    problem->D.get()[i] = 1;  }  for (int i = 0; i < num_rows; ++i) {    problem->b.get()[i] = i;  }  problem->A.reset(A);  return problem;}/*      A = [1 0           3 0           0 5           0 7           0 9           0 0]      b = [0           1           2           3           4           5]*/// BlockSparseMatrix versionLinearLeastSquaresProblem* LinearLeastSquaresProblem3() {  int num_rows = 5;  int num_cols = 2;  LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;  problem->b.reset(new double[num_rows]);  problem->D.reset(new double[num_cols]);  problem->num_eliminate_blocks = 2;  CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;  std::unique_ptr<double[]> values(new double[num_rows * num_cols]);  for (int c = 0; c < num_cols; ++c) {    bs->cols.push_back(Block());    bs->cols.back().size = 1;    bs->cols.back().position = c;  }  int nnz = 0;  // Row 1  {    values[nnz++] = 1;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 0;    row.cells.push_back(Cell(0, 0));  }  // Row 2  {    values[nnz++] = 3;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 1;    row.cells.push_back(Cell(0, 1));  }  // Row 3  {    values[nnz++] = 5;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 2;    row.cells.push_back(Cell(1, 2));  }  // Row 4  {    values[nnz++] = 7;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 3;    row.cells.push_back(Cell(1, 3));  }  // Row 5  {    values[nnz++] = 9;    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 4;    row.cells.push_back(Cell(1, 4));  }  BlockSparseMatrix* A = new BlockSparseMatrix(bs);  memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));  for (int i = 0; i < num_cols; ++i) {    problem->D.get()[i] = 1;  }  for (int i = 0; i < num_rows; ++i) {    problem->b.get()[i] = i;  }  problem->A.reset(A);  return problem;}/*      A = [1 2 0 0 0 1 1           1 4 0 0 0 5 6           0 0 9 0 0 3 1]      b = [0           1           2]*/// BlockSparseMatrix version//// This problem has the unique property that it has two different// sized f-blocks, but only one of them occurs in the rows involving// the one e-block. So performing Schur elimination on this problem// tests the Schur Eliminator's ability to handle non-e-block rows// correctly when their structure does not conform to the static// structure determined by DetectStructure.//// NOTE: This problem is too small and rank deficient to be solved without// the diagonal regularization.LinearLeastSquaresProblem* LinearLeastSquaresProblem4() {  int num_rows = 3;  int num_cols = 7;  LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem;  problem->b.reset(new double[num_rows]);  problem->D.reset(new double[num_cols]);  problem->num_eliminate_blocks = 1;  CompressedRowBlockStructure* bs = new CompressedRowBlockStructure;  std::unique_ptr<double[]> values(new double[num_rows * num_cols]);  // Column block structure  bs->cols.push_back(Block());  bs->cols.back().size = 2;  bs->cols.back().position = 0;  bs->cols.push_back(Block());  bs->cols.back().size = 3;  bs->cols.back().position = 2;  bs->cols.push_back(Block());  bs->cols.back().size = 2;  bs->cols.back().position = 5;  int nnz = 0;  // Row 1 & 2  {    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 2;    row.block.position = 0;    row.cells.push_back(Cell(0, nnz));    values[nnz++] = 1;    values[nnz++] = 2;    values[nnz++] = 1;    values[nnz++] = 4;    row.cells.push_back(Cell(2, nnz));    values[nnz++] = 1;    values[nnz++] = 1;    values[nnz++] = 5;    values[nnz++] = 6;  }  // Row 3  {    bs->rows.push_back(CompressedRow());    CompressedRow& row = bs->rows.back();    row.block.size = 1;    row.block.position = 2;    row.cells.push_back(Cell(1, nnz));    values[nnz++] = 9;    values[nnz++] = 0;    values[nnz++] = 0;    row.cells.push_back(Cell(2, nnz));    values[nnz++] = 3;    values[nnz++] = 1;  }  BlockSparseMatrix* A = new BlockSparseMatrix(bs);  memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));  for (int i = 0; i < num_cols; ++i) {    problem->D.get()[i] = (i + 1) * 100;  }  for (int i = 0; i < num_rows; ++i) {    problem->b.get()[i] = i;  }  problem->A.reset(A);  return problem;}namespace {bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A,                                            const double* D,                                            const double* b,                                            const double* x,                                            int num_eliminate_blocks) {  CHECK(A != nullptr);  Matrix AA;  A->ToDenseMatrix(&AA);  LOG(INFO) << "A^T: \n" << AA.transpose();  if (D != NULL) {    LOG(INFO) << "A's appended diagonal:\n" << ConstVectorRef(D, A->num_cols());  }  if (b != NULL) {    LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows());  }  if (x != NULL) {    LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols());  }  return true;}void WriteArrayToFileOrDie(const string& filename,                           const double* x,                           const int size) {  CHECK(x != nullptr);  VLOG(2) << "Writing array to: " << filename;  FILE* fptr = fopen(filename.c_str(), "w");  CHECK(fptr != nullptr);  for (int i = 0; i < size; ++i) {    fprintf(fptr, "%17f\n", x[i]);  }  fclose(fptr);}bool DumpLinearLeastSquaresProblemToTextFile(const string& filename_base,                                             const SparseMatrix* A,                                             const double* D,                                             const double* b,                                             const double* x,                                             int num_eliminate_blocks) {  CHECK(A != nullptr);  LOG(INFO) << "writing to: " << filename_base << "*";  string matlab_script;  StringAppendF(&matlab_script,                "function lsqp = load_trust_region_problem()\n");  StringAppendF(&matlab_script, "lsqp.num_rows = %d;\n", A->num_rows());  StringAppendF(&matlab_script, "lsqp.num_cols = %d;\n", A->num_cols());  {    string filename = filename_base + "_A.txt";    FILE* fptr = fopen(filename.c_str(), "w");    CHECK(fptr != nullptr);    A->ToTextFile(fptr);    fclose(fptr);    StringAppendF(        &matlab_script, "tmp = load('%s', '-ascii');\n", filename.c_str());    StringAppendF(        &matlab_script,        "lsqp.A = sparse(tmp(:, 1) + 1, tmp(:, 2) + 1, tmp(:, 3), %d, %d);\n",        A->num_rows(),        A->num_cols());  }  if (D != NULL) {    string filename = filename_base + "_D.txt";    WriteArrayToFileOrDie(filename, D, A->num_cols());    StringAppendF(        &matlab_script, "lsqp.D = load('%s', '-ascii');\n", filename.c_str());  }  if (b != NULL) {    string filename = filename_base + "_b.txt";    WriteArrayToFileOrDie(filename, b, A->num_rows());    StringAppendF(        &matlab_script, "lsqp.b = load('%s', '-ascii');\n", filename.c_str());  }  if (x != NULL) {    string filename = filename_base + "_x.txt";    WriteArrayToFileOrDie(filename, x, A->num_cols());    StringAppendF(        &matlab_script, "lsqp.x = load('%s', '-ascii');\n", filename.c_str());  }  string matlab_filename = filename_base + ".m";  WriteStringToFileOrDie(matlab_script, matlab_filename);  return true;}}  // namespacebool DumpLinearLeastSquaresProblem(const string& filename_base,                                   DumpFormatType dump_format_type,                                   const SparseMatrix* A,                                   const double* D,                                   const double* b,                                   const double* x,                                   int num_eliminate_blocks) {  switch (dump_format_type) {    case CONSOLE:      return DumpLinearLeastSquaresProblemToConsole(          A, D, b, x, num_eliminate_blocks);    case TEXTFILE:      return DumpLinearLeastSquaresProblemToTextFile(          filename_base, A, D, b, x, num_eliminate_blocks);    default:      LOG(FATAL) << "Unknown DumpFormatType " << dump_format_type;  }  return true;}}  // namespace internal}  // namespace ceres
 |