mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-29 08:34:37 +08:00
Add mixed precision solves for SUITE_SPARSE
Starting with SuiteSparse version 7.4.0 CHOLMOD has support for single
precision matrices. This allows us to have single precision and mixed
precision solves when using the SUITE_SPARSE backend.
This CL also fixes sparse_cholesky_test which was completely broken for
single precision testing.
Sample performance on my Mac.
/usr/bin/time -l ./bin/bundle_adjuster --input=../../Downloads/problem-3068-310854-pre.txt
<SNIP>
Cost:
Initial 9.099334e+07
Final 4.161838e+06
Change 8.683150e+07
Minimizer iterations 6
Successful steps 4
Unsuccessful steps 2
Time (in seconds):
Preprocessor 2.528222
Residual only evaluation 0.142804 (5)
Jacobian & residual evaluation 0.424014 (4)
Linear solver 54.083396 (5)
Minimizer 54.895752
Postprocessor 0.024564
Total 57.448539
Termination: NO_CONVERGENCE (Maximum number of iterations reached. Number of iterations: 5.)
59.04 real 341.24 user 5.49 sys
5776375808 maximum resident set size
<SNIP>
616329634071 instructions retired
929475980510 cycles elapsed
5375034560 peak memory footprint
/usr/bin/time -l ./bin/bundle_adjuster --input=../../Downloads/problem-3068-310854-pre.txt -mixed_precision_solves
<SNIP>
Cost:
Initial 9.099334e+07
Final 4.148930e+06
Change 8.684441e+07
Minimizer iterations 6
Successful steps 4
Unsuccessful steps 2
Time (in seconds):
Preprocessor 2.580217
Residual only evaluation 0.144098 (5)
Jacobian & residual evaluation 0.396723 (4)
Linear solver 23.636074 (5)
Minimizer 24.427163
Postprocessor 0.023790
Total 27.031170
Termination: NO_CONVERGENCE (Maximum number of iterations reached. Number of iterations: 5.)
28.58 real 128.53 user 2.37 sys
4818386944 maximum resident set size
<SNIP>
395186936091 instructions retired
368802808856 cycles elapsed
4327029824 peak memory footprint
Change-Id: I1f137b0dd12da8da7f9ced338dd8f20f4bbdf99d
This commit is contained in:
@@ -374,6 +374,11 @@ if (SUITESPARSE)
|
||||
message("-- Found SuiteSparse ${SuiteSparse_VERSION}, "
|
||||
"building with SuiteSparse.")
|
||||
|
||||
if (SuiteSparse_VERSION VERSION_LESS 7.4.0)
|
||||
list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CHOLMOD_FLOAT)
|
||||
else()
|
||||
message("-- CHOLMOD supports single precision factorization")
|
||||
endif()
|
||||
if (SuiteSparse_NO_CMAKE OR NOT SuiteSparse_DIR)
|
||||
install(FILES ${Ceres_SOURCE_DIR}/cmake/FindSuiteSparse.cmake
|
||||
${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake
|
||||
|
||||
@@ -77,6 +77,11 @@
|
||||
// If defined, Ceres was compiled with a version of SuiteSparse/CHOLMOD without
|
||||
// the Partition module (requires METIS).
|
||||
@CERES_NO_CHOLMOD_PARTITION@
|
||||
|
||||
// If defined Ceres was compiled with a version of SuiteSparse that does not
|
||||
// support single precision solves in CHOLMOD.
|
||||
@CERES_NO_CHOLMOD_FLOAT@
|
||||
|
||||
// If defined Ceres was compiled without support for METIS via Eigen.
|
||||
@CERES_NO_EIGEN_METIS@
|
||||
|
||||
|
||||
@@ -905,20 +905,12 @@ iterations of iterative refinement are controlled by
|
||||
:member:`Solver::Options::max_num_refinement_iterations`. The default
|
||||
value of this parameter is zero, which means if
|
||||
:member:`Solver::Options::use_mixed_precision_solves` is ``true``,
|
||||
then no iterative refinement is performed. Usually 2-3 refinement
|
||||
iterations are enough.
|
||||
then no iterative refinement is performed. Usually 1-3 refinement
|
||||
iterations are enough, depending upon the conditioning of your
|
||||
problem.
|
||||
|
||||
Mixed precision solves are available in the following linear solver
|
||||
configurations:
|
||||
|
||||
1. ``DENSE_NORMAL_CHOLESKY`` + ``EIGEN``/ ``LAPACK`` / ``CUDA``.
|
||||
2. ``DENSE_SCHUR`` + ``EIGEN``/ ``LAPACK`` / ``CUDA``.
|
||||
3. ``SPARSE_NORMAL_CHOLESKY`` + ``EIGEN_SPARSE`` / ``ACCELERATE_SPARSE``
|
||||
4. ``SPARSE_SCHUR`` + ``EIGEN_SPARSE`` / ``ACCELERATE_SPARSE``
|
||||
|
||||
Mixed precision solves area not available when using ``SUITE_SPARSE``
|
||||
as the sparse linear algebra backend because SuiteSparse/CHOLMOD does
|
||||
not support single precision solves.
|
||||
If :member:`Solver::Options::max_num_refinement_iterations = 0`, then
|
||||
the Gauss-Newton step is computed in single precision.
|
||||
|
||||
.. _section-preconditioner:
|
||||
|
||||
|
||||
+12
-11
@@ -569,23 +569,24 @@ class CERES_EXPORT Solver {
|
||||
// This settings only affects the SPARSE_NORMAL_CHOLESKY solver.
|
||||
bool dynamic_sparsity = false;
|
||||
|
||||
// If use_mixed_precision_solves is true, the Gauss-Newton matrix
|
||||
// is computed in double precision, but its factorization is
|
||||
// computed in single precision. This can result in significant
|
||||
// time and memory savings at the cost of some accuracy in the
|
||||
// Gauss-Newton step. Iterative refinement is used to recover some
|
||||
// of this accuracy back.
|
||||
// If use_mixed_precision_solves is true, the Gauss-Newton matrix is
|
||||
// computed in double precision, but its factorization is computed in single
|
||||
// precision. This can result in significant time and memory savings at the
|
||||
// cost of some accuracy in the Gauss-Newton step. Iterative refinement is
|
||||
// used to recover some of this accuracy back.
|
||||
//
|
||||
// If use_mixed_precision_solves is true, we recommend setting
|
||||
// max_num_refinement_iterations to 2-3.
|
||||
// max_num_refinement_iterations to 1-3, depending on your problem. If
|
||||
// max_num_refinement_iterations = 0, then the Gauss-Newton step is computed
|
||||
// in single precision.
|
||||
//
|
||||
// This options is available when linear solver uses sparse or dense
|
||||
// cholesky factorization, except when sparse_linear_algebra_library_type =
|
||||
// SUITE_SPARSE.
|
||||
// cholesky factorization.
|
||||
bool use_mixed_precision_solves = false;
|
||||
|
||||
// Number steps of the iterative refinement process to run when
|
||||
// computing the Gauss-Newton step.
|
||||
// Number steps of the iterative refinement process to run when computing
|
||||
// the Gauss-Newton step. This is most useful when used in conjunction with
|
||||
// use_mixed_precision = true.
|
||||
int max_num_refinement_iterations = 0;
|
||||
|
||||
// Minimum number of iterations for which the linear solver should
|
||||
|
||||
@@ -184,7 +184,6 @@ add_library(ceres_internal OBJECT
|
||||
fake_bundle_adjustment_jacobian.cc
|
||||
file.cc
|
||||
first_order_function.cc
|
||||
float_suitesparse.cc
|
||||
function_sample.cc
|
||||
gradient_checking_cost_function.cc
|
||||
gradient_problem_solver.cc
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2023 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/float_suitesparse.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/log/log.h"
|
||||
|
||||
#if !defined(CERES_NO_SUITESPARSE)
|
||||
|
||||
namespace ceres::internal {
|
||||
|
||||
std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create(
|
||||
OrderingType ordering_type) {
|
||||
LOG(FATAL) << "FloatSuiteSparseCholesky is not available.";
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace ceres::internal
|
||||
|
||||
#endif // !defined(CERES_NO_SUITESPARSE)
|
||||
@@ -1,59 +0,0 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2023 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)
|
||||
|
||||
#ifndef CERES_INTERNAL_FLOAT_SUITESPARSE_H_
|
||||
#define CERES_INTERNAL_FLOAT_SUITESPARSE_H_
|
||||
|
||||
// This include must come before any #ifndef check on Ceres compile options.
|
||||
// clang-format off
|
||||
#include "ceres/internal/config.h"
|
||||
// clang-format on
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ceres/internal/export.h"
|
||||
#include "ceres/sparse_cholesky.h"
|
||||
|
||||
#if !defined(CERES_NO_SUITESPARSE)
|
||||
|
||||
namespace ceres::internal {
|
||||
|
||||
// Fake implementation of a single precision Sparse Cholesky using
|
||||
// SuiteSparse.
|
||||
class CERES_NO_EXPORT FloatSuiteSparseCholesky : public SparseCholesky {
|
||||
public:
|
||||
static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
|
||||
};
|
||||
|
||||
} // namespace ceres::internal
|
||||
|
||||
#endif // !defined(CERES_NO_SUITESPARSE)
|
||||
|
||||
#endif // CERES_INTERNAL_FLOAT_SUITESPARSE_H_
|
||||
@@ -195,11 +195,16 @@ bool OptionsAreValidForSparseCholeskyBasedSolver(const Solver::Options& options,
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
if (options.use_mixed_precision_solves &&
|
||||
options.sparse_linear_algebra_library_type == SUITE_SPARSE) {
|
||||
*error = absl::StrFormat(kMixedFormat, solver_name, library_name);
|
||||
*error =
|
||||
"This version of SuiteSparse does not support single precision "
|
||||
"Cholesky factorization. So use_mixed_precision_solves is not "
|
||||
"supported with SUITE_SPARSE";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options.dynamic_sparsity &&
|
||||
options.sparse_linear_algebra_library_type == ACCELERATE_SPARSE) {
|
||||
|
||||
@@ -511,7 +511,11 @@ TEST(Solver, SparseNormalCholeskyOptionsSuiteSparse) {
|
||||
options.sparse_linear_algebra_library_type)) {
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = false;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
|
||||
options.use_mixed_precision_solves = false;
|
||||
options.dynamic_sparsity = true;
|
||||
@@ -519,7 +523,11 @@ TEST(Solver, SparseNormalCholeskyOptionsSuiteSparse) {
|
||||
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = true;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CERES_NO_CHOLMOD_PARTITION
|
||||
@@ -532,7 +540,11 @@ TEST(Solver, SparseNormalCholeskyOptionsSuiteSparse) {
|
||||
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = false;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
|
||||
options.use_mixed_precision_solves = false;
|
||||
options.dynamic_sparsity = true;
|
||||
@@ -540,7 +552,11 @@ TEST(Solver, SparseNormalCholeskyOptionsSuiteSparse) {
|
||||
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = true;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
options.linear_solver_ordering_type = NESDIS;
|
||||
@@ -729,7 +745,11 @@ TEST(Solver, SparseSchurOptionsSuiteSparse) {
|
||||
options.sparse_linear_algebra_library_type)) {
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = false;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
|
||||
options.use_mixed_precision_solves = false;
|
||||
options.dynamic_sparsity = true;
|
||||
@@ -750,7 +770,11 @@ TEST(Solver, SparseSchurOptionsSuiteSparse) {
|
||||
|
||||
options.use_mixed_precision_solves = true;
|
||||
options.dynamic_sparsity = false;
|
||||
#ifdef CERES_NO_CHOLMOD_FLOAT
|
||||
EXPECT_FALSE(options.IsValid(&message));
|
||||
#else
|
||||
EXPECT_TRUE(options.IsValid(&message));
|
||||
#endif
|
||||
|
||||
options.use_mixed_precision_solves = false;
|
||||
options.dynamic_sparsity = true;
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "ceres/accelerate_sparse.h"
|
||||
#include "ceres/cuda_sparse_cholesky.h"
|
||||
#include "ceres/eigensparse.h"
|
||||
#include "ceres/float_suitesparse.h"
|
||||
#include "ceres/iterative_refiner.h"
|
||||
#include "ceres/suitesparse.h"
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ bool ComputeExpectedSolution(const CompressedRowSparseMatrix& lhs,
|
||||
|
||||
void SparseCholeskySolverUnitTest(
|
||||
const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
|
||||
const bool use_single_precision,
|
||||
const OrderingType ordering_type,
|
||||
const bool use_block_structure,
|
||||
const int num_blocks,
|
||||
@@ -120,16 +121,20 @@ void SparseCholeskySolverUnitTest(
|
||||
const int max_block_size,
|
||||
const double block_density,
|
||||
std::mt19937& prng) {
|
||||
LinearSolver::Options sparse_cholesky_options;
|
||||
sparse_cholesky_options.sparse_linear_algebra_library_type =
|
||||
sparse_linear_algebra_library_type;
|
||||
sparse_cholesky_options.ordering_type = ordering_type;
|
||||
#ifndef CERES_NO_CUDSS
|
||||
ContextImpl context;
|
||||
sparse_cholesky_options.context = &context;
|
||||
std::string error;
|
||||
CHECK(context.InitCuda(&error)) << error;
|
||||
#endif // CERES_NO_CUDSS
|
||||
|
||||
LinearSolver::Options sparse_cholesky_options;
|
||||
sparse_cholesky_options.sparse_linear_algebra_library_type =
|
||||
sparse_linear_algebra_library_type;
|
||||
sparse_cholesky_options.ordering_type = ordering_type;
|
||||
sparse_cholesky_options.max_num_refinement_iterations = 0;
|
||||
sparse_cholesky_options.use_mixed_precision_solves = use_single_precision;
|
||||
|
||||
auto sparse_cholesky = SparseCholesky::Create(sparse_cholesky_options);
|
||||
const CompressedRowSparseMatrix::StorageType storage_type =
|
||||
sparse_cholesky->StorageType();
|
||||
@@ -156,22 +161,30 @@ void SparseCholeskySolverUnitTest(
|
||||
LinearSolverTerminationType::SUCCESS);
|
||||
Matrix eigen_lhs;
|
||||
lhs->ToDenseMatrix(&eigen_lhs);
|
||||
EXPECT_NEAR((actual - expected).norm() / actual.norm(),
|
||||
0.0,
|
||||
std::numeric_limits<double>::epsilon() * 20)
|
||||
const double kTolerance =
|
||||
(use_single_precision ? std::numeric_limits<float>::epsilon()
|
||||
: std::numeric_limits<double>::epsilon()) *
|
||||
20;
|
||||
|
||||
EXPECT_NEAR((actual - expected).norm() / actual.norm(), 0.0, kTolerance)
|
||||
<< "\n"
|
||||
<< eigen_lhs;
|
||||
}
|
||||
|
||||
// SparseLinearAlgebraLibraryType
|
||||
// FLOAT/DOUBLE
|
||||
// OrderingType
|
||||
// BlockStructure
|
||||
using Param =
|
||||
::testing::tuple<SparseLinearAlgebraLibraryType, OrderingType, bool>;
|
||||
::testing::tuple<SparseLinearAlgebraLibraryType, bool, OrderingType, bool>;
|
||||
|
||||
std::string ParamInfoToString(testing::TestParamInfo<Param> info) {
|
||||
Param param = info.param;
|
||||
std::stringstream ss;
|
||||
ss << SparseLinearAlgebraLibraryTypeToString(::testing::get<0>(param)) << "_"
|
||||
<< ::testing::get<1>(param) << "_"
|
||||
<< (::testing::get<2>(param) ? "UseBlockStructure" : "NoBlockStructure");
|
||||
<< (::testing::get<1>(param) ? "FLOAT" : "DOUBLE") << "_"
|
||||
<< ::testing::get<2>(param) << "_"
|
||||
<< (::testing::get<3>(param) ? "UseBlockStructure" : "NoBlockStructure");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -198,6 +211,7 @@ TEST_P(SparseCholeskyTest, FactorAndSolve) {
|
||||
SparseCholeskySolverUnitTest(::testing::get<0>(param),
|
||||
::testing::get<1>(param),
|
||||
::testing::get<2>(param),
|
||||
::testing::get<3>(param),
|
||||
num_blocks,
|
||||
kMinBlockSize,
|
||||
kMaxBlockSize,
|
||||
@@ -214,38 +228,29 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
SuiteSparseCholesky,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(SUITE_SPARSE),
|
||||
#if defined(CERES_NO_CHOLMOD_FLOAT)
|
||||
::testing::Values(false),
|
||||
#else
|
||||
::testing::Values(false, true),
|
||||
#endif // defined(CERES_NO_CHOLMOD_FLOAT)
|
||||
#if defined(CERES_NO_CHOLMOD_PARTITION)
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NATURAL),
|
||||
#else
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NESDIS,
|
||||
OrderingType::NATURAL),
|
||||
#endif // defined(CERES_NO_CHOLMOD_PARTITION)
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
#endif
|
||||
|
||||
#if !defined(CERES_NO_SUITESPARSE) && !defined(CERES_NO_CHOLMOD_PARTITION)
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SuiteSparseCholeskyMETIS,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(SUITE_SPARSE),
|
||||
::testing::Values(OrderingType::NESDIS),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
#endif // !defined(CERES_NO_SUITESPARSE) &&
|
||||
// !defined(CERES_NO_CHOLMOD_PARTITION)
|
||||
#endif // !defined(CERES_NO_SUITESPARSE)
|
||||
|
||||
#ifndef CERES_NO_ACCELERATE_SPARSE
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AccelerateSparseCholesky,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(ACCELERATE_SPARSE),
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NESDIS,
|
||||
OrderingType::NATURAL),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AccelerateSparseCholeskySingle,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(ACCELERATE_SPARSE),
|
||||
::testing::Values(false, true),
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NESDIS,
|
||||
OrderingType::NATURAL),
|
||||
@@ -258,59 +263,30 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
EigenSparseCholesky,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(EIGEN_SPARSE),
|
||||
::testing::Values(false, true),
|
||||
#if defined(CERES_NO_EIGEN_METIS)
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NATURAL),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
FloatEigenSparseCholesky,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(EIGEN_SPARSE),
|
||||
#else
|
||||
::testing::Values(OrderingType::AMD,
|
||||
OrderingType::NATURAL),
|
||||
OrderingType::NATURAL,
|
||||
OrderingType::NESDIS),
|
||||
#endif // defined(CERES_NO_EIGEN_METIS)
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
#endif // CERES_USE_EIGEN_SPARSE
|
||||
|
||||
#ifndef CERES_NO_CUDSS
|
||||
using CuDSSCholeskyDouble = CudaSparseCholesky<double>;
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CuDSSCholeskyDouble,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(CUDA_SPARSE),
|
||||
::testing::Values(OrderingType::AMD),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
|
||||
using CuDSSCholeskySingle = CudaSparseCholesky<float>;
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CuDSSCholeskySingle,
|
||||
CudaCholesky,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(CUDA_SPARSE),
|
||||
::testing::Values(false, true),
|
||||
::testing::Values(OrderingType::AMD),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
#endif // CERES_NO_CUDSS
|
||||
|
||||
#if defined(CERES_USE_EIGEN_SPARSE) && !defined(CERES_NO_EIGEN_METIS)
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
EigenSparseCholeskyMETIS,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(EIGEN_SPARSE),
|
||||
::testing::Values(OrderingType::NESDIS),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
EigenSparseCholeskySingleMETIS,
|
||||
SparseCholeskyTest,
|
||||
::testing::Combine(::testing::Values(EIGEN_SPARSE),
|
||||
::testing::Values(OrderingType::NESDIS),
|
||||
::testing::Values(true, false)),
|
||||
ParamInfoToString);
|
||||
#endif // defined(CERES_USE_EIGEN_SPARSE) && !defined(CERES_NO_EIGEN_METIS)
|
||||
|
||||
class MockSparseCholesky : public SparseCholesky {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(StorageType, CompressedRowSparseMatrix::StorageType());
|
||||
|
||||
@@ -250,9 +250,9 @@ bool SuiteSparse::BlockOrdering(const cholmod_sparse* A,
|
||||
block_matrix.i = reinterpret_cast<void*>(block_rows.data());
|
||||
block_matrix.x = nullptr;
|
||||
block_matrix.stype = A->stype;
|
||||
block_matrix.itype = CHOLMOD_INT;
|
||||
block_matrix.itype = A->itype;
|
||||
block_matrix.xtype = CHOLMOD_PATTERN;
|
||||
block_matrix.dtype = CHOLMOD_DOUBLE;
|
||||
block_matrix.dtype = A->dtype;
|
||||
block_matrix.sorted = 1;
|
||||
block_matrix.packed = 1;
|
||||
|
||||
@@ -466,6 +466,154 @@ LinearSolverTerminationType SuiteSparseCholesky::Solve(const double* rhs,
|
||||
return LinearSolverTerminationType::SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef CERES_NO_CHOLMOD_FLOAT
|
||||
|
||||
std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create(
|
||||
const OrderingType ordering_type) {
|
||||
return std::unique_ptr<SparseCholesky>(
|
||||
new FloatSuiteSparseCholesky(ordering_type));
|
||||
}
|
||||
|
||||
FloatSuiteSparseCholesky::FloatSuiteSparseCholesky(
|
||||
const OrderingType ordering_type)
|
||||
: ordering_type_(ordering_type), factor_(nullptr) {}
|
||||
|
||||
FloatSuiteSparseCholesky::~FloatSuiteSparseCholesky() {
|
||||
if (factor_ != nullptr) {
|
||||
ss_.Free(factor_);
|
||||
}
|
||||
}
|
||||
|
||||
LinearSolverTerminationType FloatSuiteSparseCholesky::Factorize(
|
||||
CompressedRowSparseMatrix* lhs, std::string* message) {
|
||||
if (lhs == nullptr) {
|
||||
*message = "Failure: Input lhs is nullptr.";
|
||||
return LinearSolverTerminationType::FATAL_ERROR;
|
||||
}
|
||||
|
||||
cholmod_sparse cholmod_lhs = ss_.CreateSparseMatrixTransposeView(lhs);
|
||||
float_lhs_values_ =
|
||||
ConstVectorRef(lhs->values(), lhs->num_nonzeros()).cast<float>();
|
||||
cholmod_lhs.dtype = CHOLMOD_SINGLE;
|
||||
cholmod_lhs.x = reinterpret_cast<void*>(float_lhs_values_.data());
|
||||
|
||||
// If a factorization does not exist, compute the symbolic
|
||||
// factorization first.
|
||||
//
|
||||
// If the ordering type is NATURAL, then there is no fill reducing
|
||||
// ordering to be computed, regardless of block structure, so we can
|
||||
// just call the scalar version of symbolic factorization. For
|
||||
// SuiteSparse this is the common case since we have already
|
||||
// pre-ordered the columns of the Jacobian.
|
||||
//
|
||||
// Similarly regardless of ordering type, if there is no block
|
||||
// structure in the matrix we call the scalar version of symbolic
|
||||
// factorization.
|
||||
if (factor_ == nullptr) {
|
||||
if (ordering_type_ == OrderingType::NATURAL ||
|
||||
(lhs->col_blocks().empty() || lhs->row_blocks().empty())) {
|
||||
factor_ = ss_.AnalyzeCholesky(&cholmod_lhs, ordering_type_, message);
|
||||
} else {
|
||||
factor_ = ss_.BlockAnalyzeCholesky(&cholmod_lhs,
|
||||
ordering_type_,
|
||||
lhs->col_blocks(),
|
||||
lhs->row_blocks(),
|
||||
message);
|
||||
}
|
||||
}
|
||||
|
||||
if (factor_ == nullptr) {
|
||||
return LinearSolverTerminationType::FATAL_ERROR;
|
||||
}
|
||||
|
||||
// Compute and return the numeric factorization.
|
||||
return ss_.Cholesky(&cholmod_lhs, factor_, message);
|
||||
}
|
||||
|
||||
CompressedRowSparseMatrix::StorageType FloatSuiteSparseCholesky::StorageType()
|
||||
const {
|
||||
return ((ordering_type_ == OrderingType::NATURAL)
|
||||
? CompressedRowSparseMatrix::StorageType::UPPER_TRIANGULAR
|
||||
: CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR);
|
||||
}
|
||||
|
||||
LinearSolverTerminationType FloatSuiteSparseCholesky::Solve(
|
||||
const double* rhs, double* solution, std::string* message) {
|
||||
// Error checking
|
||||
if (factor_ == nullptr) {
|
||||
*message = "Solve called without a call to Factorize first.";
|
||||
return LinearSolverTerminationType::FATAL_ERROR;
|
||||
}
|
||||
|
||||
const int num_cols = factor_->n;
|
||||
cholmod_dense cholmod_rhs = ss_.CreateDenseVectorView(rhs, num_cols);
|
||||
|
||||
float_rhs_ = ConstVectorRef(rhs, num_cols).cast<float>();
|
||||
cholmod_rhs.dtype = CHOLMOD_SINGLE;
|
||||
cholmod_rhs.x = reinterpret_cast<void*>(float_rhs_.data());
|
||||
|
||||
cholmod_dense* cholmod_dense_solution =
|
||||
ss_.Solve(factor_, &cholmod_rhs, message);
|
||||
|
||||
if (cholmod_dense_solution == nullptr) {
|
||||
return LinearSolverTerminationType::FAILURE;
|
||||
}
|
||||
|
||||
CHECK_EQ(cholmod_dense_solution->dtype, CHOLMOD_SINGLE);
|
||||
VectorRef(solution, num_cols) =
|
||||
Eigen::Map<Eigen::VectorXf>(
|
||||
reinterpret_cast<float*>(cholmod_dense_solution->x), num_cols)
|
||||
.cast<double>();
|
||||
ss_.Free(cholmod_dense_solution);
|
||||
return LinearSolverTerminationType::SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create(
|
||||
const OrderingType ordering_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FloatSuiteSparseCholesky::FloatSuiteSparseCholesky(
|
||||
const OrderingType ordering_type)
|
||||
: ordering_type_(ordering_type), factor_(nullptr) {}
|
||||
|
||||
FloatSuiteSparseCholesky::~FloatSuiteSparseCholesky() {
|
||||
if (factor_ != nullptr) {
|
||||
ss_.Free(factor_);
|
||||
}
|
||||
}
|
||||
|
||||
LinearSolverTerminationType FloatSuiteSparseCholesky::Factorize(
|
||||
CompressedRowSparseMatrix* lhs, std::string* message) {
|
||||
*message =
|
||||
"Single precision Cholesky factorization is not supported. If "
|
||||
"you are seeing this failure, then you have discovered a Ceres "
|
||||
"Solver bug. Please get in touch with the Ceres Solver team at "
|
||||
"ceres-solver@googlegroups.com";
|
||||
return LinearSolverTerminationType::FATAL_ERROR;
|
||||
}
|
||||
|
||||
CompressedRowSparseMatrix::StorageType FloatSuiteSparseCholesky::StorageType()
|
||||
const {
|
||||
return ((ordering_type_ == OrderingType::NATURAL)
|
||||
? CompressedRowSparseMatrix::StorageType::UPPER_TRIANGULAR
|
||||
: CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR);
|
||||
}
|
||||
|
||||
LinearSolverTerminationType FloatSuiteSparseCholesky::Solve(
|
||||
const double* rhs, double* solution, std::string* message) {
|
||||
*message =
|
||||
"Single precision Cholesky factorization is not supported. If "
|
||||
"you are seeing this failure, then you have discovered a Ceres "
|
||||
"Solver bug. Please get in touch with the Ceres Solver team at "
|
||||
"ceres-solver@googlegroups.com";
|
||||
return LinearSolverTerminationType::FATAL_ERROR;
|
||||
}
|
||||
|
||||
#endif // CERES_NO_CHOLMOD_FLOAT
|
||||
|
||||
} // namespace ceres::internal
|
||||
|
||||
#endif // CERES_NO_SUITESPARSE
|
||||
|
||||
@@ -264,7 +264,30 @@ class CERES_NO_EXPORT SuiteSparseCholesky final : public SparseCholesky {
|
||||
|
||||
const OrderingType ordering_type_;
|
||||
SuiteSparse ss_;
|
||||
cholmod_factor* factor_;
|
||||
cholmod_factor* factor_ = nullptr;
|
||||
};
|
||||
|
||||
class CERES_NO_EXPORT FloatSuiteSparseCholesky final : public SparseCholesky {
|
||||
public:
|
||||
static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type);
|
||||
|
||||
// SparseCholesky interface.
|
||||
~FloatSuiteSparseCholesky() override;
|
||||
CompressedRowSparseMatrix::StorageType StorageType() const final;
|
||||
LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
|
||||
std::string* message) final;
|
||||
LinearSolverTerminationType Solve(const double* rhs,
|
||||
double* solution,
|
||||
std::string* message) final;
|
||||
|
||||
private:
|
||||
explicit FloatSuiteSparseCholesky(const OrderingType ordering_type);
|
||||
|
||||
const OrderingType ordering_type_;
|
||||
SuiteSparse ss_;
|
||||
cholmod_factor* factor_ = nullptr;
|
||||
Eigen::VectorXf float_lhs_values_;
|
||||
Eigen::VectorXf float_rhs_;
|
||||
};
|
||||
|
||||
} // namespace ceres::internal
|
||||
|
||||
Reference in New Issue
Block a user