10ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Ceres Solver - A fast non-linear least squares minimizer
20ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
30ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// http://code.google.com/p/ceres-solver/
40ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
50ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Redistribution and use in source and binary forms, with or without
60ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// modification, are permitted provided that the following conditions are met:
70ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
80ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Redistributions of source code must retain the above copyright notice,
90ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   this list of conditions and the following disclaimer.
100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Redistributions in binary form must reproduce the above copyright notice,
110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   this list of conditions and the following disclaimer in the documentation
120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   and/or other materials provided with the distribution.
130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Neither the name of Google Inc. nor the names of its contributors may be
140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   used to endorse or promote products derived from this software without
150ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   specific prior written permission.
160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
210ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
220ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
230ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
240ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
250ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
260ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
270ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// POSSIBILITY OF SUCH DAMAGE.
280ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
290ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Author: sameeragarwal@google.com (Sameer Agarwal)
300ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
310ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/schur_eliminator.h"
320ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
330ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "Eigen/Dense"
340ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/block_random_access_dense_matrix.h"
350ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/block_sparse_matrix.h"
360ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/casts.h"
370ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/detect_structure.h"
380ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/internal/eigen.h"
390ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/internal/scoped_ptr.h"
400ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/linear_least_squares_problems.h"
410ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/test_util.h"
420ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/triplet_sparse_matrix.h"
430ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/types.h"
440ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "glog/logging.h"
450ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "gtest/gtest.h"
460ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
470ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// TODO(sameeragarwal): Reduce the size of these tests and redo the
480ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// parameterization to be more efficient.
490ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
500ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongnamespace ceres {
510ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongnamespace internal {
520ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
530ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongclass SchurEliminatorTest : public ::testing::Test {
540ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong protected:
550ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  void SetUpFromId(int id) {
560ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    scoped_ptr<LinearLeastSquaresProblem>
570ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        problem(CreateLinearLeastSquaresProblemFromId(id));
580ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    CHECK_NOTNULL(problem.get());
590ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    SetupHelper(problem.get());
600ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
610ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
620ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  void SetupHelper(LinearLeastSquaresProblem* problem) {
630ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    A.reset(down_cast<BlockSparseMatrix*>(problem->A.release()));
640ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    b.reset(problem->b.release());
650ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    D.reset(problem->D.release());
660ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
670ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    num_eliminate_blocks = problem->num_eliminate_blocks;
680ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    num_eliminate_cols = 0;
690ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const CompressedRowBlockStructure* bs = A->block_structure();
700ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
710ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    for (int i = 0; i < num_eliminate_blocks; ++i) {
720ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      num_eliminate_cols += bs->cols[i].size;
730ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
740ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
750ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
760ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Compute the golden values for the reduced linear system and the
770ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // solution to the linear least squares problem using dense linear
780ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // algebra.
790ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  void ComputeReferenceSolution(const Vector& D) {
800ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix J;
810ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    A->ToDenseMatrix(&J);
820ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    VectorRef f(b.get(), J.rows());
830ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
840ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix H  =  (D.cwiseProduct(D)).asDiagonal();
850ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    H.noalias() += J.transpose() * J;
860ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
870ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const Vector g = J.transpose() * f;
880ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const int schur_size = J.cols() - num_eliminate_cols;
890ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
900ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    lhs_expected.resize(schur_size, schur_size);
910ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    lhs_expected.setZero();
920ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
930ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    rhs_expected.resize(schur_size);
940ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    rhs_expected.setZero();
950ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
960ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    sol_expected.resize(J.cols());
970ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    sol_expected.setZero();
980ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
990ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix P = H.block(0, 0, num_eliminate_cols, num_eliminate_cols);
1000ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix Q = H.block(0,
1010ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       num_eliminate_cols,
1020ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       num_eliminate_cols,
1030ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       schur_size);
1040ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix R = H.block(num_eliminate_cols,
1050ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       num_eliminate_cols,
1060ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       schur_size,
1070ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                       schur_size);
1080ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    int row = 0;
1090ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const CompressedRowBlockStructure* bs = A->block_structure();
1100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    for (int i = 0; i < num_eliminate_blocks; ++i) {
1110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      const int block_size =  bs->cols[i].size;
1120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      P.block(row, row,  block_size, block_size) =
1130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong          P
1140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong          .block(row, row,  block_size, block_size)
115399f7d09e0c45af54b77b4ab9508d6f23759b927Scott Ettinger          .llt()
1160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong          .solve(Matrix::Identity(block_size, block_size));
1170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      row += block_size;
1180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
1190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    lhs_expected
1210ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        .triangularView<Eigen::Upper>() = R - Q.transpose() * P * Q;
1220ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    rhs_expected =
1230ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        g.tail(schur_size) - Q.transpose() * P * g.head(num_eliminate_cols);
124399f7d09e0c45af54b77b4ab9508d6f23759b927Scott Ettinger    sol_expected = H.llt().solve(g);
1250ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1260ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1270ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  void EliminateSolveAndCompare(const VectorRef& diagonal,
1280ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                bool use_static_structure,
1290ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                const double relative_tolerance) {
1300ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const CompressedRowBlockStructure* bs = A->block_structure();
1310ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const int num_col_blocks = bs->cols.size();
1320ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    vector<int> blocks(num_col_blocks - num_eliminate_blocks, 0);
1330ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    for (int i = num_eliminate_blocks; i < num_col_blocks; ++i) {
1340ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      blocks[i - num_eliminate_blocks] = bs->cols[i].size;
1350ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
1360ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1370ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    BlockRandomAccessDenseMatrix lhs(blocks);
1380ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1390ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const int num_cols = A->num_cols();
1400ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const int schur_size = lhs.num_rows();
1410ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1420ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Vector rhs(schur_size);
1430ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1440ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    LinearSolver::Options options;
1450ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    options.elimination_groups.push_back(num_eliminate_blocks);
1460ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    if (use_static_structure) {
1470ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      DetectStructure(*bs,
1480ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                      num_eliminate_blocks,
1490ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                      &options.row_block_size,
1500ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                      &options.e_block_size,
1510ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                      &options.f_block_size);
1520ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
1530ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1540ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    scoped_ptr<SchurEliminatorBase> eliminator;
1550ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    eliminator.reset(SchurEliminatorBase::Create(options));
1560ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    eliminator->Init(num_eliminate_blocks, A->block_structure());
1570ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    eliminator->Eliminate(A.get(), b.get(), diagonal.data(), &lhs, rhs.data());
1580ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1590ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    MatrixRef lhs_ref(lhs.mutable_values(), lhs.num_rows(), lhs.num_cols());
1600ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Vector reduced_sol  =
1610ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        lhs_ref
1620ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        .selfadjointView<Eigen::Upper>()
163399f7d09e0c45af54b77b4ab9508d6f23759b927Scott Ettinger        .llt()
1640ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        .solve(rhs);
1650ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1660ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    // Solution to the linear least squares problem.
1670ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Vector sol(num_cols);
1680ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    sol.setZero();
1690ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    sol.tail(schur_size) = reduced_sol;
1700ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    eliminator->BackSubstitute(A.get(),
1710ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                               b.get(),
1720ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                               diagonal.data(),
1730ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                               reduced_sol.data(),
1740ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                               sol.data());
1750ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1760ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    Matrix delta = (lhs_ref - lhs_expected).selfadjointView<Eigen::Upper>();
1770ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    double diff = delta.norm();
1780ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    EXPECT_NEAR(diff / lhs_expected.norm(), 0.0, relative_tolerance);
1790ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    EXPECT_NEAR((rhs - rhs_expected).norm() / rhs_expected.norm(), 0.0,
1800ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                relative_tolerance);
1810ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    EXPECT_NEAR((sol - sol_expected).norm() / sol_expected.norm(), 0.0,
1820ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                relative_tolerance);
1830ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1840ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1850ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  scoped_ptr<BlockSparseMatrix> A;
1860ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  scoped_array<double> b;
1870ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  scoped_array<double> D;
1880ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  int num_eliminate_blocks;
1890ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  int num_eliminate_cols;
1900ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1910ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  Matrix lhs_expected;
1920ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  Vector rhs_expected;
1930ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  Vector sol_expected;
1940ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong};
1950ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1960ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongTEST_F(SchurEliminatorTest, ScalarProblem) {
1970ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  SetUpFromId(2);
1980ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  Vector zero(A->num_cols());
1990ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  zero.setZero();
2000ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2010ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ComputeReferenceSolution(VectorRef(zero.data(), A->num_cols()));
2020ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  EliminateSolveAndCompare(VectorRef(zero.data(), A->num_cols()), true, 1e-14);
2030ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  EliminateSolveAndCompare(VectorRef(zero.data(), A->num_cols()), false, 1e-14);
2040ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2050ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ComputeReferenceSolution(VectorRef(D.get(), A->num_cols()));
2060ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  EliminateSolveAndCompare(VectorRef(D.get(), A->num_cols()), true, 1e-14);
2070ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  EliminateSolveAndCompare(VectorRef(D.get(), A->num_cols()), false, 1e-14);
2080ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}
2090ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}  // namespace internal
2110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}  // namespace ceres
212