Add cuDSS as sparse Cholesky solver
cuDSS could be used as an alternative for SuiteSparse and EigenSparse
in case if CUDA capable GPU is available.
Change-Id: I7a567093ce91363478118153e181134ed5804573
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index c7e2cfa..eb1463b 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -82,6 +82,13 @@
apt-get install -y nvidia-cuda-toolkit
echo "CUDACXX=/usr/lib/nvidia-cuda-toolkit/bin/nvcc" >> $GITHUB_ENV
+ - name: Setup cuDSS
+ if: matrix.gpu == 'cuda'
+ run: |
+ wget https://developer.download.nvidia.com/compute/cudss/redist/libcudss/linux-x86_64/libcudss-linux-x86_64-0.3.0.9_cuda12-archive.tar.xz
+ tar -xf libcudss-linux-x86_64-0.3.0.9_cuda12-archive.tar.xz
+ echo "cudss_DIR=${{github.workspace}}/libcudss-linux-x86_64-0.3.0.9_cuda12-archive/lib/cmake/cudss" >> $GITHUB_ENV
+
- name: Cache Build
id: cache-build
uses: actions/cache@v4
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index eaffa79..182ed30 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -133,6 +133,17 @@
echo "CUDA_PATH_V12_5=$CUDA_PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "$CUDA_PATH/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+ - name: Setup cuDSS
+ if: matrix.gpu == 'cuda'
+ run: |
+ Invoke-WebRequest https://developer.download.nvidia.com/compute/cudss/redist/libcudss/windows-x86_64/libcudss-windows-x86_64-0.3.0.9_cuda12-archive.zip -OutFile libcudss-windows-x86_64-0.3.0.9_cuda12-archive.zip
+ Expand-Archive -Path .\libcudss-windows-x86_64-0.3.0.9_cuda12-archive.zip -DestinationPath "C:/"
+ Remove-Item .\libcudss-windows-x86_64-0.3.0.9_cuda12-archive.zip
+ $CUDSS_PATH = "C:/libcudss-windows-x86_64-0.3.0.9_cuda12-archive/"
+ $CUDSS_CMAKE_CONFIG = "$CUDSS_PATH/lib/cmake/cudss/cudss-config.cmake"
+ echo "cudss_DIR=$CUDSS_PATH/lib/cmake/cudss" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+ echo "$CUDSS_PATH/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+
- name: Cache gflags
id: cache-gflags
uses: actions/cache@v4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6cbc942..b92437c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -278,10 +278,21 @@
CUDA::cudart${CERES_CUDA_TARGET_SUFFIX}
CUDA::cusolver${CERES_CUDA_TARGET_SUFFIX}
CUDA::cusparse${CERES_CUDA_TARGET_SUFFIX})
+
+ find_package(cudss CONFIG)
+ if (cudss_FOUND)
+ set(cudss_DEPENDENCY "find_dependency(cudss ${cudss_VERSION})")
+ list(APPEND CERES_CUDA_LIBRARIES cudss${CERES_CUDA_TARGET_SUFFIX})
+ else (cudss_FOUND)
+ list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDSS)
+ message("-- Did not find cuDSS, SPARSE_SCHUR and SPARSE_NORMAL_CHOLESKY with CUDA_SPARSE will not be available.")
+ endif (cudss_FOUND)
+
unset (CERES_CUDA_TARGET_SUFFIX)
set(CMAKE_CUDA_RUNTIME_LIBRARY NONE)
else (USE_CUDA)
message("-- Building without CUDA.")
+ list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDSS)
list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDA)
endif (USE_CUDA)
@@ -380,10 +391,10 @@
endif()
# Ensure that the user understands they have disabled all sparse libraries.
-if (NOT SUITESPARSE AND NOT EIGENSPARSE AND NOT ACCELERATESPARSE)
+if (NOT SUITESPARSE AND NOT EIGENSPARSE AND NOT ACCELERATESPARSE AND NOT cudss_FOUND)
message(" ===============================================================")
- message(" Compiling without any sparse library: SuiteSparse, ")
- message(" EigenSparse & Apple's Accelerate are all disabled or unavailable. ")
+ message(" Compiling without any sparse library: SuiteSparse, EigenSparse")
+ message(" Apple's Accelerate & cuDSS are all disabled or unavailable. ")
message(" No sparse linear solvers (SPARSE_NORMAL_CHOLESKY & SPARSE_SCHUR)")
message(" will be available when Ceres is used.")
message(" ===============================================================")
diff --git a/cmake/CeresCompileOptionsToComponents.cmake b/cmake/CeresCompileOptionsToComponents.cmake
index 64634d5..f4ed9ee 100644
--- a/cmake/CeresCompileOptionsToComponents.cmake
+++ b/cmake/CeresCompileOptionsToComponents.cmake
@@ -79,6 +79,8 @@
add_to_output_if_not_found(CURRENT_CERES_COMPILE_OPTIONS ${CERES_COMPONENTS_VAR}
CERES_NO_ACCELERATE_SPARSE "AccelerateSparse;SparseLinearAlgebraLibrary")
add_to_output_if_not_found(CURRENT_CERES_COMPILE_OPTIONS ${CERES_COMPONENTS_VAR}
+ CERES_NO_CUDSS "cuDSS;SparseLinearAlgebraLibrary")
+ add_to_output_if_not_found(CURRENT_CERES_COMPILE_OPTIONS ${CERES_COMPONENTS_VAR}
CERES_RESTRICT_SCHUR_SPECIALIZATION "SchurSpecializations")
# Remove duplicates of SparseLinearAlgebraLibrary if multiple sparse backends
# are present.
diff --git a/cmake/CeresConfig.cmake.in b/cmake/CeresConfig.cmake.in
index ceb7e26..b929c82 100644
--- a/cmake/CeresConfig.cmake.in
+++ b/cmake/CeresConfig.cmake.in
@@ -180,6 +180,7 @@
@METIS_DEPENDENCY@
@SuiteSparse_DEPENDENCY@
@CUDAToolkit_DEPENDENCY@
+@cudss_DEPENDENCY@
@Threads_DEPENDENCY@
# As imported CMake targets are not re-exported when a dependent target is
diff --git a/cmake/config.h.in b/cmake/config.h.in
index 1566795..51db4a0 100644
--- a/cmake/config.h.in
+++ b/cmake/config.h.in
@@ -53,11 +53,15 @@
// If defined, Ceres was compiled without CUDA.
@CERES_NO_CUDA@
+// If defined, Ceres was compiled without cuDSS.
+@CERES_NO_CUDSS@
+
// If defined, Ceres was compiled without Apple's Accelerate framework solvers.
@CERES_NO_ACCELERATE_SPARSE@
#if defined(CERES_NO_SUITESPARSE) && \
defined(CERES_NO_ACCELERATE_SPARSE) && \
+ defined(CERES_NO_CUDSS) && \
!defined(CERES_USE_EIGEN_SPARSE) // NOLINT
// If defined Ceres was compiled without any sparse linear algebra support.
#define CERES_NO_SPARSE
@@ -90,6 +94,9 @@
#if defined(CERES_USE_EIGEN_SPARSE)
#error CERES_NO_SPARSE requires !CERES_USE_EIGEN_SPARSE
#endif
+#if !defined(CERES_NO_CUDSS)
+#error CERES_NO_SPARSE required CERES_NO_CUDSS
+#endif
#endif
#endif // CERES_PUBLIC_INTERNAL_CONFIG_H_
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index 582ae2e..8df0d8a 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -96,8 +96,7 @@
"Use power series expansion to initialize the solution in ITERATIVE_SCHUR linear solver.");
DEFINE_string(sparse_linear_algebra_library, "suite_sparse",
- "Options are: suite_sparse, accelerate_sparse, eigen_sparse, and "
- "cuda_sparse.");
+ "Options are: suite_sparse, accelerate_sparse, eigen_sparse and cuda_sparse");
DEFINE_string(dense_linear_algebra_library, "eigen",
"Options are: eigen, lapack, and cuda");
DEFINE_string(ordering_type, "amd", "Options are: amd, nesdis");
@@ -381,6 +380,7 @@
int main(int argc, char** argv) {
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
+
if (CERES_GET_FLAG(FLAGS_input).empty()) {
LOG(ERROR) << "Usage: bundle_adjuster --input=bal_problem";
return 1;
diff --git a/include/ceres/types.h b/include/ceres/types.h
index 6e19c51..f5b66a9 100644
--- a/include/ceres/types.h
+++ b/include/ceres/types.h
@@ -175,7 +175,7 @@
// Apple's Accelerate framework sparse linear algebra routines.
ACCELERATE_SPARSE,
- // Nvidia's cuSPARSE library.
+ // Nvidia's cuDSS and cuSPARSE libraries.
CUDA_SPARSE,
// No sparse linear solver should be used. This does not necessarily
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index f4e40fb..6c1fe93 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -170,6 +170,7 @@
cuda_partitioned_block_sparse_crs_view.cc
cuda_block_structure.cc
cuda_sparse_matrix.cc
+ cuda_sparse_cholesky.cc
cuda_vector.cc
dense_cholesky.cc
dense_normal_cholesky_solver.cc
diff --git a/internal/ceres/context_impl.cc b/internal/ceres/context_impl.cc
index 2b9d9cc..073dc7a 100644
--- a/internal/ceres/context_impl.cc
+++ b/internal/ceres/context_impl.cc
@@ -60,6 +60,12 @@
cusparseDestroy(cusparse_handle_);
cusparse_handle_ = nullptr;
}
+#ifndef CERES_NO_CUDSS
+ if (cudss_handle_ != nullptr) {
+ cudssDestroy(cudss_handle_);
+ cudss_handle_ = nullptr;
+ }
+#endif // CERES_NO_CUDSS
for (auto& s : streams_) {
if (s != nullptr) {
cudaStreamDestroy(s);
@@ -157,6 +163,13 @@
return false;
}
event_logger.AddEvent("cusparseCreate");
+#ifndef CERES_NO_CUDSS
+ if (cudssCreate(&cudss_handle_) != CUDSS_STATUS_SUCCESS) {
+ *message = "CUDA initialization failed because cudssCreate() failed.";
+ TearDown();
+ return false;
+ }
+#endif // CERES_NO_CUDSS
for (auto& s : streams_) {
if (cudaStreamCreateWithFlags(&s, cudaStreamNonBlocking) != cudaSuccess) {
*message =
@@ -172,7 +185,11 @@
cublasSetStream(cublas_handle_, DefaultStream()) !=
CUBLAS_STATUS_SUCCESS ||
cusparseSetStream(cusparse_handle_, DefaultStream()) !=
- CUSPARSE_STATUS_SUCCESS) {
+ CUSPARSE_STATUS_SUCCESS
+#ifndef CERES_NO_CUDSS
+ || cudssSetStream(cudss_handle_, DefaultStream()) != CUDSS_STATUS_SUCCESS
+#endif // CERES_NO_CUDSS
+ ) {
*message = "CUDA initialization failed because SetStream failed.";
TearDown();
return false;
diff --git a/internal/ceres/context_impl.h b/internal/ceres/context_impl.h
index 46692e6..cc301ee 100644
--- a/internal/ceres/context_impl.h
+++ b/internal/ceres/context_impl.h
@@ -47,6 +47,9 @@
#include "cuda_runtime.h"
#include "cusolverDn.h"
#include "cusparse.h"
+#ifndef CERES_NO_CUDSS
+#include "cudss.h"
+#endif // CERES_NO_CUDSS
#endif // CERES_NO_CUDA
#include "ceres/thread_pool.h"
@@ -122,6 +125,9 @@
cusolverDnHandle_t cusolver_handle_ = nullptr;
cublasHandle_t cublas_handle_ = nullptr;
+#ifndef CERES_NO_CUDSS
+ cudssHandle_t cudss_handle_ = nullptr;
+#endif // CERES_NO_CUDSS
// Default stream.
// Kernel invocations and memory copies on this stream can be left without
diff --git a/internal/ceres/cuda_buffer.h b/internal/ceres/cuda_buffer.h
index 40048fd..95504ca 100644
--- a/internal/ceres/cuda_buffer.h
+++ b/internal/ceres/cuda_buffer.h
@@ -36,6 +36,8 @@
#ifndef CERES_NO_CUDA
+#include <cstddef>
+#include <utility>
#include <vector>
#include "cuda_runtime.h"
@@ -165,6 +167,55 @@
size_t size_ = 0;
ContextImpl* context_ = nullptr;
};
+
+// This class wraps host memory region allocated via cudaMallocHost. Such memory
+// region is page-locked, hence enabling direct transfer to/from device,
+// avoiding implicit buffering under the hood of CUDA API.
+template <typename T>
+class CudaPinnedHostBuffer {
+ public:
+ CudaPinnedHostBuffer() noexcept = default;
+ CudaPinnedHostBuffer(int size) { Reserve(size); }
+ CudaPinnedHostBuffer(CudaPinnedHostBuffer&& other) noexcept
+ : data_(std::exchange(other.data_, nullptr)),
+ size_(std::exchange(other.size_, 0)) {}
+ CudaPinnedHostBuffer(const CudaPinnedHostBuffer&) = delete;
+ CudaPinnedHostBuffer& operator=(const CudaPinnedHostBuffer&) = delete;
+ CudaPinnedHostBuffer& operator=(CudaPinnedHostBuffer&& other) noexcept {
+ Free();
+ data_ = std::exchange(other.data_, nullptr);
+ size_ = std::exchange(other.size_, 0);
+ return *this;
+ }
+ ~CudaPinnedHostBuffer() { Free(); }
+
+ void Reserve(const std::size_t size) {
+ if (size > size_) {
+ Free();
+ CHECK_EQ(cudaMallocHost(&data_, size * sizeof(T)), cudaSuccess)
+ << "Failed to allocate " << size * sizeof(T)
+ << " bytes of pinned host memory";
+ size_ = size;
+ }
+ }
+
+ T* data() noexcept { return data_; }
+ const T* data() const noexcept { return data_; }
+ std::size_t size() const noexcept { return size_; }
+
+ private:
+ void Free() {
+ if (data_ != nullptr) {
+ CHECK_EQ(cudaFreeHost(data_), cudaSuccess);
+ data_ = nullptr;
+ size_ = 0;
+ }
+ }
+
+ T* data_ = nullptr;
+ std::size_t size_ = 0;
+};
+
} // namespace ceres::internal
#endif // CERES_NO_CUDA
diff --git a/internal/ceres/cuda_sparse_cholesky.cc b/internal/ceres/cuda_sparse_cholesky.cc
new file mode 100644
index 0000000..d4a285b
--- /dev/null
+++ b/internal/ceres/cuda_sparse_cholesky.cc
@@ -0,0 +1,485 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2024 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: markshachkov@gmail.com (Mark Shachkov)
+
+#include "ceres/cuda_sparse_cholesky.h"
+
+#ifndef CERES_NO_CUDSS
+
+#include <cstddef>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "Eigen/Core"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/cuda_buffer.h"
+#include "ceres/linear_solver.h"
+#include "cudss.h"
+
+namespace ceres::internal {
+
+inline std::string cuDSSStatusToString(cudssStatus_t status) {
+ switch (status) {
+ case CUDSS_STATUS_SUCCESS:
+ return "CUDSS_STATUS_SUCCESS";
+ case CUDSS_STATUS_NOT_INITIALIZED:
+ return "CUDSS_STATUS_NOT_INITIALIZED";
+ case CUDSS_STATUS_ALLOC_FAILED:
+ return "CUDSS_STATUS_ALLOC_FAILED";
+ case CUDSS_STATUS_INVALID_VALUE:
+ return "CUDSS_STATUS_INVALID_VALUE";
+ case CUDSS_STATUS_NOT_SUPPORTED:
+ return "CUDSS_STATUS_NOT_SUPPORTED";
+ case CUDSS_STATUS_EXECUTION_FAILED:
+ return "CUDSS_STATUS_EXECUTION_FAILED";
+ case CUDSS_STATUS_INTERNAL_ERROR:
+ return "CUDSS_STATUS_INTERNAL_ERROR";
+ default:
+ return "unknown cuDSS status: " + std::to_string(status);
+ }
+}
+
+#define CUDSS_STATUS_CHECK(IN) \
+ if (cudssStatus_t status = IN; status != CUDSS_STATUS_SUCCESS) { \
+ CHECK(false) << "Got error: " << cuDSSStatusToString(status); \
+ }
+
+#define CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(IN, additional_message) \
+ if (cudssStatus_t status = IN; status != CUDSS_STATUS_SUCCESS) { \
+ *message = std::string(additional_message) + \
+ " Got error: " + cuDSSStatusToString(status); \
+ return factorize_result_ = LinearSolverTerminationType::FATAL_ERROR; \
+ }
+
+#define CUDSS_STATUS_OK_OR_RETURN_CUDSS_STATUS(IN) \
+ if (cudssStatus_t status = IN; status != CUDSS_STATUS_SUCCESS) { \
+ return status; \
+ }
+
+class CERES_NO_EXPORT CuDSSMatrixBase {
+ public:
+ CuDSSMatrixBase() = default;
+ CuDSSMatrixBase(const CuDSSMatrixBase&) = delete;
+ CuDSSMatrixBase(CuDSSMatrixBase&&) = delete;
+ CuDSSMatrixBase& operator=(const CuDSSMatrixBase&) = delete;
+ CuDSSMatrixBase& operator=(CuDSSMatrixBase&&) = delete;
+ ~CuDSSMatrixBase() { CUDSS_STATUS_CHECK(Free()); }
+
+ cudssStatus_t Free() noexcept {
+ if (matrix_) {
+ const auto status = cudssMatrixDestroy(matrix_);
+ matrix_ = nullptr;
+ return status;
+ }
+
+ return CUDSS_STATUS_SUCCESS;
+ }
+
+ cudssMatrix_t Get() const noexcept { return matrix_; }
+
+ protected:
+ cudssMatrix_t matrix_{nullptr};
+};
+
+class CERES_NO_EXPORT CuDSSMatrixCSR : public CuDSSMatrixBase {
+ public:
+ cudssStatus_t Reset(int64_t num_rows,
+ int64_t num_cols,
+ int64_t num_nonzeros,
+ void* rows_start,
+ void* rows_end,
+ void* cols,
+ void* values,
+ cudaDataType_t index_type,
+ cudaDataType_t value_type,
+ cudssMatrixType_t matrix_type,
+ cudssMatrixViewType_t matrix_storage_type,
+ cudssIndexBase_t index_base) {
+ CUDSS_STATUS_OK_OR_RETURN_CUDSS_STATUS(Free());
+
+ return cudssMatrixCreateCsr(&matrix_,
+ num_rows,
+ num_cols,
+ num_nonzeros,
+ rows_start,
+ rows_end,
+ cols,
+ values,
+ index_type,
+ value_type,
+ matrix_type,
+ matrix_storage_type,
+ index_base);
+ }
+};
+
+class CERES_NO_EXPORT CuDSSMatrixDense : public CuDSSMatrixBase {
+ public:
+ cudssStatus_t Reset(int64_t num_rows,
+ int64_t num_cols,
+ int64_t leading_dimension_size,
+ void* values,
+ cudaDataType_t value_type,
+ cudssLayout_t layout) {
+ CUDSS_STATUS_OK_OR_RETURN_CUDSS_STATUS(Free());
+
+ return cudssMatrixCreateDn(&matrix_,
+ num_rows,
+ num_cols,
+ leading_dimension_size,
+ values,
+ value_type,
+ layout);
+ }
+};
+
+struct CudssContext {
+ CudssContext(cudssHandle_t cudss_handle) : cudss_handle_(cudss_handle) {
+ CUDSS_STATUS_CHECK(cudssConfigCreate(&solver_config_));
+ CUDSS_STATUS_CHECK(cudssDataCreate(cudss_handle_, &solver_data_));
+ }
+ CudssContext(const CudssContext&) = delete;
+ CudssContext(CudssContext&&) = delete;
+ CudssContext& operator=(const CudssContext&) = delete;
+ CudssContext& operator=(CudssContext&&) = delete;
+ ~CudssContext() {
+ CUDSS_STATUS_CHECK(cudssDataDestroy(cudss_handle_, solver_data_));
+ CUDSS_STATUS_CHECK(cudssConfigDestroy(solver_config_));
+ }
+
+ cudssHandle_t cudss_handle_{nullptr};
+ cudssConfig_t solver_config_{nullptr};
+ cudssData_t solver_data_{nullptr};
+};
+
+template <typename Scalar>
+class CERES_NO_EXPORT CudaSparseCholeskyImpl final : public SparseCholesky {
+ public:
+ static_assert(std::is_same_v<Scalar, float> || std::is_same_v<Scalar, double>,
+ "Scalar type is unsuported by cuDSS");
+ static constexpr cudaDataType_t kCuDSSScalar =
+ std::is_same_v<Scalar, float> ? CUDA_R_32F : CUDA_R_64F;
+
+ CudaSparseCholeskyImpl(ContextImpl* context)
+ : context_(context),
+ lhs_cols_d_(context_),
+ lhs_rows_d_(context_),
+ lhs_values_d_(context_),
+ rhs_d_(context_),
+ x_d_(context_) {}
+ CudaSparseCholeskyImpl(const CudaSparseCholeskyImpl&) = delete;
+ CudaSparseCholeskyImpl(CudaSparseCholeskyImpl&&) = delete;
+ CudaSparseCholeskyImpl& operator=(const CudaSparseCholeskyImpl&) = delete;
+ CudaSparseCholeskyImpl& operator=(CudaSparseCholeskyImpl&&) = delete;
+ ~CudaSparseCholeskyImpl() = default;
+
+ CompressedRowSparseMatrix::StorageType StorageType() const {
+ return CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR;
+ }
+
+ LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
+ std::string* message) {
+ if (lhs->num_rows() != lhs->num_cols()) {
+ *message = "lhs matrix must be square";
+ return factorize_result_ = LinearSolverTerminationType::FATAL_ERROR;
+ }
+ if (lhs->storage_type() != StorageType()) {
+ *message = "lhs matrix must be lower triangular";
+ return factorize_result_ = LinearSolverTerminationType::FATAL_ERROR;
+ }
+
+ // If, after previous attempt to factorize, cudssDataGet(CUDSS_DATA_INFO)
+ // returned a numerical error, such error will be preserved by cuDSS 0.3.0
+ // and returned by cudssDataGet(CUDSS_DATA_INFO) even after correctly
+ // factorizeable matrix is provided. Such behaviour forces us to reset a
+ // cudssData_t object (managed by CudssContext) and to loose a result of
+ // anylyze stage, thus we have to perform analyze one more time.
+ // TODO: do not re-perform analyze in case of failed factorization numerics
+ if (analyze_result_ != LinearSolverTerminationType::SUCCESS ||
+ factorize_result_ != LinearSolverTerminationType::SUCCESS) {
+ analyze_result_ = Analyze(lhs, message);
+ if (analyze_result_ != LinearSolverTerminationType::SUCCESS) {
+ return analyze_result_;
+ }
+ }
+ CHECK_NE(cudss_context_.get(), nullptr);
+
+ ConvertAndCopyToDevice(lhs->values(), lhs_values_h_.data(), lhs_values_d_);
+
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ cudssExecute(context_->cudss_handle_,
+ CUDSS_PHASE_FACTORIZATION,
+ cudss_context_->solver_config_,
+ cudss_context_->solver_data_,
+ cudss_lhs_.Get(),
+ cudss_x_.Get(),
+ cudss_rhs_.Get()),
+ "cudssExecute with CUDSS_PHASE_FACTORIZATION failed");
+
+ int cudss_data_info;
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ GetCudssDataInfo(cudss_data_info),
+ "cudssDataGet with CUDSS_DATA_INFO failed");
+ const auto factorization_status =
+ static_cast<cudssStatus_t>(cudss_data_info);
+
+ if (factorization_status == CUDSS_STATUS_SUCCESS) {
+ return factorize_result_ = LinearSolverTerminationType::SUCCESS;
+ }
+
+ if (cudss_data_info > 0) {
+ return factorize_result_ = LinearSolverTerminationType::FAILURE;
+ }
+
+ return factorize_result_ = LinearSolverTerminationType::FATAL_ERROR;
+ }
+
+ LinearSolverTerminationType Solve(const double* rhs,
+ double* solution,
+ std::string* message) {
+ CHECK_NE(cudss_context_.get(), nullptr);
+
+ if (factorize_result_ != LinearSolverTerminationType::SUCCESS) {
+ *message = "Factorize did not complete successfully previously.";
+ return factorize_result_;
+ }
+
+ ConvertAndCopyToDevice(rhs, rhs_h_.data(), rhs_d_);
+
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ cudssExecute(context_->cudss_handle_,
+ CUDSS_PHASE_SOLVE,
+ cudss_context_->solver_config_,
+ cudss_context_->solver_data_,
+ cudss_lhs_.Get(),
+ cudss_x_.Get(),
+ cudss_rhs_.Get()),
+ "cudssExecute with CUDSS_PHASE_SOLVE failed");
+
+ ConvertAndCopyToHost(x_d_, x_h_.data(), solution);
+
+ int cudss_data_info;
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ GetCudssDataInfo(cudss_data_info),
+ "cudssDataGet with CUDSS_DATA_INFO failed");
+ const auto solve_status = static_cast<cudssStatus_t>(cudss_data_info);
+
+ if (solve_status != CUDSS_STATUS_SUCCESS) {
+ return LinearSolverTerminationType::FAILURE;
+ }
+
+ return LinearSolverTerminationType::SUCCESS;
+ }
+
+ private:
+ cudssStatus_t GetCudssDataInfo(int& cudss_data_info) {
+ CHECK_NE(cudss_context_.get(), nullptr);
+ std::size_t size_written = 0;
+ CUDSS_STATUS_OK_OR_RETURN_CUDSS_STATUS(
+ cudssDataGet(context_->cudss_handle_,
+ cudss_context_->solver_data_,
+ CUDSS_DATA_INFO,
+ &cudss_data_info,
+ sizeof(cudss_data_info),
+ &size_written));
+ // TODO: enable following check after cudssDataGet will be fixed
+ // CHECK_EQ(size_written, sizeof(cudss_data_info));
+
+ return CUDSS_STATUS_SUCCESS;
+ }
+
+ LinearSolverTerminationType Analyze(const CompressedRowSparseMatrix* lhs,
+ std::string* message) {
+ if (auto status = SetupCudssMatrices(lhs, message);
+ status != LinearSolverTerminationType::SUCCESS) {
+ return status;
+ }
+
+ lhs_rows_d_.CopyFromCpu(lhs->rows(), lhs->num_rows() + 1);
+ lhs_cols_d_.CopyFromCpu(lhs->cols(), lhs->num_nonzeros());
+
+ // Analyze and factorization results are stored in cudssData_t (managed by
+ // CudssContext). Given that cuDSS 0.3.0 does not reset it's error state in
+ // case of failed numerics at factorization stage, we have to reset
+ // cudssData_t and to recompute an analyze stage while trying to factorize a
+ // rescaled matrix with the same structure.
+ // TODO: move creation of CudssContext to ctor of CudaSparseCholeskyImpl
+ cudss_context_ = std::make_unique<CudssContext>(context_->cudss_handle_);
+
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ cudssExecute(context_->cudss_handle_,
+ CUDSS_PHASE_ANALYSIS,
+ cudss_context_->solver_config_,
+ cudss_context_->solver_data_,
+ cudss_lhs_.Get(),
+ cudss_x_.Get(),
+ cudss_rhs_.Get()),
+ "cudssExecute with CUDSS_PHASE_ANALYSIS failed");
+
+ return LinearSolverTerminationType::SUCCESS;
+ }
+
+ // Resize buffers and setup cuDSS structs that describe the type and storage
+ // configuration of linear system operands.
+ LinearSolverTerminationType SetupCudssMatrices(
+ const CompressedRowSparseMatrix* lhs, std::string* message) {
+ const auto num_rows = lhs->num_rows();
+ const auto num_nonzeros = lhs->num_nonzeros();
+
+ if constexpr (std::is_same_v<Scalar, float>) {
+ lhs_values_h_.Reserve(num_nonzeros);
+ rhs_h_.Reserve(num_rows);
+ x_h_.Reserve(num_rows);
+ }
+
+ lhs_rows_d_.Reserve(num_rows + 1);
+ lhs_cols_d_.Reserve(num_nonzeros);
+ lhs_values_d_.Reserve(num_nonzeros);
+ rhs_d_.Reserve(num_rows);
+ x_d_.Reserve(num_rows);
+
+ static constexpr auto kFailedToCreateCuDSSMatrix =
+ "cudssMatrixCreate() call failed";
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(cudss_lhs_.Reset(num_rows,
+ num_rows,
+ num_nonzeros,
+ lhs_rows_d_.data(),
+ nullptr,
+ lhs_cols_d_.data(),
+ lhs_values_d_.data(),
+ CUDA_R_32I,
+ kCuDSSScalar,
+ CUDSS_MTYPE_SPD,
+ CUDSS_MVIEW_LOWER,
+ CUDSS_BASE_ZERO),
+ kFailedToCreateCuDSSMatrix);
+
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ cudss_rhs_.Reset(num_rows,
+ 1,
+ num_rows,
+ rhs_d_.data(),
+ kCuDSSScalar,
+ CUDSS_LAYOUT_COL_MAJOR),
+ kFailedToCreateCuDSSMatrix);
+
+ CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR(
+ cudss_x_.Reset(num_rows,
+ 1,
+ num_rows,
+ x_d_.data(),
+ kCuDSSScalar,
+ CUDSS_LAYOUT_COL_MAJOR),
+ kFailedToCreateCuDSSMatrix);
+
+ return LinearSolverTerminationType::SUCCESS;
+ }
+
+ template <typename S, typename D>
+ void Convert(const S* source, D* destination, size_t size) {
+ Eigen::Map<Eigen::Matrix<D, Eigen::Dynamic, 1>>(destination, size) =
+ Eigen::Map<const Eigen::Matrix<S, Eigen::Dynamic, 1>>(source, size)
+ .template cast<D>();
+ }
+
+ void ConvertAndCopyToDevice(const double* source,
+ Scalar* intermediate,
+ CudaBuffer<Scalar>& destination) {
+ const auto size = destination.size();
+ if constexpr (std::is_same_v<Scalar, double>) {
+ destination.CopyFromCpu(source, size);
+ } else {
+ Convert(source, intermediate, size);
+ destination.CopyFromCpu(intermediate, size);
+ }
+ }
+
+ void ConvertAndCopyToHost(const CudaBuffer<Scalar>& source,
+ Scalar* intermediate,
+ double* destination) {
+ const auto size = source.size();
+ if constexpr (std::is_same_v<Scalar, double>) {
+ source.CopyToCpu(destination, source.size());
+ } else {
+ source.CopyToCpu(intermediate, source.size());
+ Convert(intermediate, destination, size);
+ }
+ }
+
+ ContextImpl* context_{nullptr};
+ std::unique_ptr<CudssContext> cudss_context_;
+ CuDSSMatrixCSR cudss_lhs_;
+ CuDSSMatrixDense cudss_rhs_;
+ CuDSSMatrixDense cudss_x_;
+
+ CudaPinnedHostBuffer<Scalar> lhs_values_h_;
+ CudaPinnedHostBuffer<Scalar> rhs_h_;
+ CudaPinnedHostBuffer<Scalar> x_h_;
+ CudaBuffer<int> lhs_rows_d_;
+ CudaBuffer<int> lhs_cols_d_;
+ CudaBuffer<Scalar> lhs_values_d_;
+ CudaBuffer<Scalar> rhs_d_;
+ CudaBuffer<Scalar> x_d_;
+
+ LinearSolverTerminationType analyze_result_ =
+ LinearSolverTerminationType::FATAL_ERROR;
+ LinearSolverTerminationType factorize_result_ =
+ LinearSolverTerminationType::FATAL_ERROR;
+};
+
+template <typename Scalar>
+std::unique_ptr<SparseCholesky> CudaSparseCholesky<Scalar>::Create(
+ ContextImpl* context, const OrderingType ordering_type) {
+ if (ordering_type == OrderingType::NESDIS) {
+ LOG(FATAL)
+ << "Congratulations you have found a bug in Ceres Solver. Please "
+ "report it to the Ceres Solver developers.";
+ return nullptr;
+ }
+
+ if (context == nullptr || !context->IsCudaInitialized()) {
+ LOG(FATAL) << "CudaSparseCholesky requires CUDA context to be initialized";
+ return nullptr;
+ }
+
+ return std::make_unique<CudaSparseCholeskyImpl<Scalar>>(context);
+}
+
+template class CudaSparseCholesky<float>;
+template class CudaSparseCholesky<double>;
+
+} // namespace ceres::internal
+
+#undef CUDSS_STATUS_CHECK
+#undef CUDSS_STATUS_OK_OR_RETURN_FATAL_ERROR
+#undef CUDSS_STATUS_OK_OR_RETURN_CUDSS_STATUS
+
+#endif // CERES_NO_CUDSS
diff --git a/internal/ceres/cuda_sparse_cholesky.h b/internal/ceres/cuda_sparse_cholesky.h
new file mode 100644
index 0000000..e147633
--- /dev/null
+++ b/internal/ceres/cuda_sparse_cholesky.h
@@ -0,0 +1,66 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2024 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: markshachkov@gmail.com (Mark Shachkov)
+//
+// A C++ interface to cuDSS.
+
+#ifndef CERES_INTERNAL_CUDA_SPARSE_CHOLESKY_H_
+#define CERES_INTERNAL_CUDA_SPARSE_CHOLESKY_H_
+
+// This include must come before any #ifndef check on Ceres compile options.
+#include "ceres/internal/config.h"
+
+#ifndef CERES_NO_CUDSS
+
+#include <memory>
+
+#include "ceres/internal/export.h"
+#include "ceres/linear_solver.h"
+#include "ceres/sparse_cholesky.h"
+
+namespace ceres::internal {
+
+// This class is a factory for implementation of sparse cholesky that uses cuDSS
+// on CUDA capable GPU's to solve sparse linear systems. Scalar controls the
+// precision used during computations, currently float and double are supported.
+// Details of implementation are incapsulated into cuda_sparse_cholesky.cc
+template <typename Scalar = double>
+class CERES_NO_EXPORT CudaSparseCholesky : public SparseCholesky {
+ public:
+ static constexpr bool IsNestedDissectionAvailable() noexcept { return false; }
+
+ static std::unique_ptr<SparseCholesky> Create(
+ ContextImpl* context, const OrderingType ordering_type);
+};
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+
+#endif // CERES_INTERNAL_CUDA_SPARSE_CHOLESKY_H_
diff --git a/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc b/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
index d77d7f7..6dd0adf 100644
--- a/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
+++ b/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
@@ -46,6 +46,7 @@
#include "ceres/triplet_sparse_matrix.h"
#include "ceres/types.h"
#include "ceres/wall_time.h"
+#include "cuda_sparse_cholesky.h"
#ifdef CERES_USE_EIGEN_SPARSE
#include "Eigen/SparseCholesky"
@@ -88,6 +89,9 @@
case EIGEN_SPARSE:
summary = SolveImplUsingEigen(A, x);
break;
+ case CUDA_SPARSE:
+ summary = SolveImplUsingCuda(A, x);
+ break;
default:
LOG(FATAL) << "Unsupported sparse linear algebra library for "
<< "dynamic sparsity: "
@@ -231,4 +235,63 @@
#endif
}
+LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImplUsingCuda(
+ CompressedRowSparseMatrix* A, double* rhs_and_solution) {
+#ifdef CERES_NO_CUDSS
+ (void)A;
+ (void)rhs_and_solution;
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 0;
+ summary.termination_type = LinearSolverTerminationType::FATAL_ERROR;
+ summary.message =
+ "SPARSE_NORMAL_CHOLESKY cannot be used with CUDA_SPARSE "
+ "because Ceres was not built with support for cuDSS. "
+ "This requires enabling building with -DUSE_CUDA=ON and ensuring that "
+ "cuDSS is found.";
+ return summary;
+#else
+
+ EventLogger event_logger("DynamicSparseNormalCholeskySolver::cuDSS::Solve");
+
+ // TODO: Consider computing A^T*A on device via cuSPARSE
+ // https://github.com/ceres-solver/ceres-solver/issues/1066
+ Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>> a(
+ A->num_rows(),
+ A->num_cols(),
+ A->num_nonzeros(),
+ A->mutable_rows(),
+ A->mutable_cols(),
+ A->mutable_values());
+ Eigen::SparseMatrix<double, Eigen::RowMajor> ata =
+ (a.transpose() * a).triangularView<Eigen::Lower>();
+
+ CompressedRowSparseMatrix lhs(ata.rows(), ata.cols(), ata.nonZeros());
+ std::copy_n(ata.outerIndexPtr(), lhs.num_rows() + 1, lhs.mutable_rows());
+ std::copy_n(ata.innerIndexPtr(), lhs.num_nonzeros(), lhs.mutable_cols());
+ std::copy_n(ata.valuePtr(), lhs.num_nonzeros(), lhs.mutable_values());
+ lhs.set_storage_type(
+ CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR);
+ event_logger.AddEvent("Compute A^T * A");
+
+ auto sparse_cholesky = CudaSparseCholesky<double>::Create(
+ options_.context, options_.ordering_type);
+
+ LinearSolver::Summary summary;
+ summary.num_iterations = 1;
+ summary.termination_type = sparse_cholesky->Factorize(&lhs, &summary.message);
+ if (summary.termination_type != LinearSolverTerminationType::SUCCESS) {
+ return summary;
+ }
+ event_logger.AddEvent("Analyze");
+
+ const Vector rhs = ConstVectorRef(rhs_and_solution, A->num_cols());
+ summary.termination_type =
+ sparse_cholesky->Solve(rhs.data(), rhs_and_solution, &summary.message);
+ event_logger.AddEvent("Solve");
+
+ return summary;
+#endif
+}
+
} // namespace ceres::internal
diff --git a/internal/ceres/dynamic_sparse_normal_cholesky_solver.h b/internal/ceres/dynamic_sparse_normal_cholesky_solver.h
index 394ba2a..e5e8a28 100644
--- a/internal/ceres/dynamic_sparse_normal_cholesky_solver.h
+++ b/internal/ceres/dynamic_sparse_normal_cholesky_solver.h
@@ -70,6 +70,9 @@
LinearSolver::Summary SolveImplUsingEigen(CompressedRowSparseMatrix* A,
double* rhs_and_solution);
+ LinearSolver::Summary SolveImplUsingCuda(CompressedRowSparseMatrix* A,
+ double* rhs_and_solution);
+
const LinearSolver::Options options_;
};
diff --git a/internal/ceres/dynamic_sparsity_test.cc b/internal/ceres/dynamic_sparsity_test.cc
index 0c29595..ae56037 100644
--- a/internal/ceres/dynamic_sparsity_test.cc
+++ b/internal/ceres/dynamic_sparsity_test.cc
@@ -426,38 +426,41 @@
Solver::Options options;
options.max_num_iterations = 100;
- options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
- // Only SuiteSparse & EigenSparse currently support dynamic sparsity.
- options.sparse_linear_algebra_library_type =
+ SparseLinearAlgebraLibraryType sparse_library_types[] = {
#if !defined(CERES_NO_SUITESPARSE)
- ceres::SUITE_SPARSE;
-#elif defined(CERES_USE_EIGEN_SPARSE)
- ceres::EIGEN_SPARSE;
+ ceres::SUITE_SPARSE,
#endif
-
- // First, solve `X` and `t` jointly with dynamic_sparsity = true.
- Matrix X0 = X;
- Vector t0 = t;
- options.dynamic_sparsity = false;
- Solver::Summary static_summary;
- Solve(options, &problem, &static_summary);
- EXPECT_EQ(static_summary.termination_type, CONVERGENCE)
- << static_summary.FullReport();
-
- X = X0;
- t = t0;
- options.dynamic_sparsity = true;
- Solver::Summary dynamic_summary;
- Solve(options, &problem, &dynamic_summary);
- EXPECT_EQ(dynamic_summary.termination_type, CONVERGENCE)
- << dynamic_summary.FullReport();
-
- EXPECT_NEAR(static_summary.final_cost,
- dynamic_summary.final_cost,
- std::numeric_limits<double>::epsilon())
- << "Static: \n"
- << static_summary.FullReport() << "\nDynamic: \n"
- << dynamic_summary.FullReport();
+#if defined(CERES_USE_EIGEN_SPARSE)
+ ceres::EIGEN_SPARSE,
+#endif
+#if !defined(CERES_NO_CUDSS)
+ ceres::CUDA_SPARSE,
+#endif
+ };
+ for (auto type : sparse_library_types) {
+ options.sparse_linear_algebra_library_type = type;
+ // First, solve `X` and `t` jointly with dynamic_sparsity = true.
+ Matrix X0 = X;
+ Vector t0 = t;
+ options.dynamic_sparsity = false;
+ Solver::Summary static_summary;
+ Solve(options, &problem, &static_summary);
+ EXPECT_EQ(static_summary.termination_type, CONVERGENCE)
+ << static_summary.FullReport();
+ X = X0;
+ t = t0;
+ options.dynamic_sparsity = true;
+ Solver::Summary dynamic_summary;
+ Solve(options, &problem, &dynamic_summary);
+ EXPECT_EQ(dynamic_summary.termination_type, CONVERGENCE)
+ << dynamic_summary.FullReport();
+ EXPECT_NEAR(static_summary.final_cost,
+ dynamic_summary.final_cost,
+ std::numeric_limits<double>::epsilon())
+ << "Static: \n"
+ << static_summary.FullReport() << "\nDynamic: \n"
+ << dynamic_summary.FullReport();
+ }
}
} // namespace ceres::internal
diff --git a/internal/ceres/evaluator.cc b/internal/ceres/evaluator.cc
index 64eb4c5..9226dc5 100644
--- a/internal/ceres/evaluator.cc
+++ b/internal/ceres/evaluator.cc
@@ -63,7 +63,11 @@
options, program);
case DENSE_SCHUR:
case SPARSE_SCHUR:
- case ITERATIVE_SCHUR:
+ case ITERATIVE_SCHUR: {
+ return std::make_unique<
+ ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>>(
+ options, program);
+ }
case CGNR: {
if (options.sparse_linear_algebra_library_type == CUDA_SPARSE) {
return std::make_unique<ProgramEvaluator<ScratchEvaluatePreparer,
diff --git a/internal/ceres/generate_bundle_adjustment_tests.py b/internal/ceres/generate_bundle_adjustment_tests.py
index ac83bc3..0359b40 100644
--- a/internal/ceres/generate_bundle_adjustment_tests.py
+++ b/internal/ceres/generate_bundle_adjustment_tests.py
@@ -53,9 +53,11 @@
('SPARSE_NORMAL_CHOLESKY', 'SUITE_SPARSE'),
('SPARSE_NORMAL_CHOLESKY', 'EIGEN_SPARSE'),
('SPARSE_NORMAL_CHOLESKY', 'ACCELERATE_SPARSE'),
+ ('SPARSE_NORMAL_CHOLESKY', 'CUDA_SPARSE'),
('SPARSE_SCHUR', 'SUITE_SPARSE'),
('SPARSE_SCHUR', 'EIGEN_SPARSE'),
('SPARSE_SCHUR', 'ACCELERATE_SPARSE'),
+ ('SPARSE_SCHUR', 'CUDA_SPARSE')
]
ITERATIVE_SOLVER_CONFIGS = [
@@ -66,9 +68,11 @@
('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_JACOBI'),
('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_JACOBI'),
('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_JACOBI'),
+ ('ITERATIVE_SCHUR', 'CUDA_SPARSE', 'CLUSTER_JACOBI'),
('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_TRIDIAGONAL'),
('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_TRIDIAGONAL'),
('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_TRIDIAGONAL'),
+ ('ITERATIVE_SCHUR', 'CUDA_SPARSE', 'CLUSTER_TRIDIAGONAL'),
]
FILENAME_SHORTENING_MAP = dict(
@@ -83,6 +87,7 @@
SUITE_SPARSE='suitesparse',
EIGEN_SPARSE='eigensparse',
ACCELERATE_SPARSE='acceleratesparse',
+ CUDA_SPARSE='cudasparse',
IDENTITY='identity',
JACOBI='jacobi',
SCHUR_JACOBI='schurjacobi',
@@ -217,6 +222,14 @@
elif sparse_backend == 'EIGEN_SPARSE':
preprocessor_conditions_begin.append('#ifdef CERES_USE_EIGEN_SPARSE')
preprocessor_conditions_end.insert(0, '#endif // CERES_USE_EIGEN_SPARSE')
+ elif sparse_backend == 'CUDA_SPARSE':
+ preprocessor_conditions_begin.append('#ifndef CERES_NO_CUDA')
+ preprocessor_conditions_end.insert(0, '#endif // CERES_NO_CUDA')
+ if linear_solver == 'SPARSE_SCHUR' or linear_solver == 'SPARSE_NORMAL_CHOLESKY' or (
+ linear_solver == 'ITERATIVE_SCHUR' and (
+ preconditioner == 'CLUSTER_JACOBI' or preconditioner == 'CLUSTER_TRIDIAGONAL')):
+ preprocessor_conditions_begin.append('#ifndef CERES_NO_CUDSS')
+ preprocessor_conditions_end.insert(0, '#endif // CERES_NO_CUDSS')
if dense_backend == "LAPACK":
preprocessor_conditions_begin.append('#ifndef CERES_NO_LAPACK')
diff --git a/internal/ceres/generated_bundle_adjustment_tests/CMakeLists.txt b/internal/ceres/generated_bundle_adjustment_tests/CMakeLists.txt
index 5f4f65e..8da71b4 100644
--- a/internal/ceres/generated_bundle_adjustment_tests/CMakeLists.txt
+++ b/internal/ceres/generated_bundle_adjustment_tests/CMakeLists.txt
@@ -41,69 +41,85 @@
ceres_test(ba_sparsecholesky_suitesparse_auto)
ceres_test(ba_sparsecholesky_eigensparse_auto)
ceres_test(ba_sparsecholesky_acceleratesparse_auto)
+ceres_test(ba_sparsecholesky_cudasparse_auto)
ceres_test(ba_sparseschur_suitesparse_auto)
ceres_test(ba_sparseschur_eigensparse_auto)
ceres_test(ba_sparseschur_acceleratesparse_auto)
+ceres_test(ba_sparseschur_cudasparse_auto)
ceres_test(ba_iterschur_jacobi_auto)
ceres_test(ba_iterschur_schurjacobi_auto)
ceres_test(ba_iterschur_spse_auto)
ceres_test(ba_iterschur_suitesparse_clustjacobi_auto)
ceres_test(ba_iterschur_eigensparse_clustjacobi_auto)
ceres_test(ba_iterschur_acceleratesparse_clustjacobi_auto)
+ceres_test(ba_iterschur_cudasparse_clustjacobi_auto)
ceres_test(ba_iterschur_suitesparse_clusttri_auto)
ceres_test(ba_iterschur_eigensparse_clusttri_auto)
ceres_test(ba_iterschur_acceleratesparse_clusttri_auto)
+ceres_test(ba_iterschur_cudasparse_clusttri_auto)
ceres_test(ba_denseschur_eigen_auto_threads)
ceres_test(ba_denseschur_lapack_auto_threads)
ceres_test(ba_denseschur_cuda_auto_threads)
ceres_test(ba_sparsecholesky_suitesparse_auto_threads)
ceres_test(ba_sparsecholesky_eigensparse_auto_threads)
ceres_test(ba_sparsecholesky_acceleratesparse_auto_threads)
+ceres_test(ba_sparsecholesky_cudasparse_auto_threads)
ceres_test(ba_sparseschur_suitesparse_auto_threads)
ceres_test(ba_sparseschur_eigensparse_auto_threads)
ceres_test(ba_sparseschur_acceleratesparse_auto_threads)
+ceres_test(ba_sparseschur_cudasparse_auto_threads)
ceres_test(ba_iterschur_jacobi_auto_threads)
ceres_test(ba_iterschur_schurjacobi_auto_threads)
ceres_test(ba_iterschur_spse_auto_threads)
ceres_test(ba_iterschur_suitesparse_clustjacobi_auto_threads)
ceres_test(ba_iterschur_eigensparse_clustjacobi_auto_threads)
ceres_test(ba_iterschur_acceleratesparse_clustjacobi_auto_threads)
+ceres_test(ba_iterschur_cudasparse_clustjacobi_auto_threads)
ceres_test(ba_iterschur_suitesparse_clusttri_auto_threads)
ceres_test(ba_iterschur_eigensparse_clusttri_auto_threads)
ceres_test(ba_iterschur_acceleratesparse_clusttri_auto_threads)
+ceres_test(ba_iterschur_cudasparse_clusttri_auto_threads)
ceres_test(ba_denseschur_eigen_user)
ceres_test(ba_denseschur_lapack_user)
ceres_test(ba_denseschur_cuda_user)
ceres_test(ba_sparsecholesky_suitesparse_user)
ceres_test(ba_sparsecholesky_eigensparse_user)
ceres_test(ba_sparsecholesky_acceleratesparse_user)
+ceres_test(ba_sparsecholesky_cudasparse_user)
ceres_test(ba_sparseschur_suitesparse_user)
ceres_test(ba_sparseschur_eigensparse_user)
ceres_test(ba_sparseschur_acceleratesparse_user)
+ceres_test(ba_sparseschur_cudasparse_user)
ceres_test(ba_iterschur_jacobi_user)
ceres_test(ba_iterschur_schurjacobi_user)
ceres_test(ba_iterschur_spse_user)
ceres_test(ba_iterschur_suitesparse_clustjacobi_user)
ceres_test(ba_iterschur_eigensparse_clustjacobi_user)
ceres_test(ba_iterschur_acceleratesparse_clustjacobi_user)
+ceres_test(ba_iterschur_cudasparse_clustjacobi_user)
ceres_test(ba_iterschur_suitesparse_clusttri_user)
ceres_test(ba_iterschur_eigensparse_clusttri_user)
ceres_test(ba_iterschur_acceleratesparse_clusttri_user)
+ceres_test(ba_iterschur_cudasparse_clusttri_user)
ceres_test(ba_denseschur_eigen_user_threads)
ceres_test(ba_denseschur_lapack_user_threads)
ceres_test(ba_denseschur_cuda_user_threads)
ceres_test(ba_sparsecholesky_suitesparse_user_threads)
ceres_test(ba_sparsecholesky_eigensparse_user_threads)
ceres_test(ba_sparsecholesky_acceleratesparse_user_threads)
+ceres_test(ba_sparsecholesky_cudasparse_user_threads)
ceres_test(ba_sparseschur_suitesparse_user_threads)
ceres_test(ba_sparseschur_eigensparse_user_threads)
ceres_test(ba_sparseschur_acceleratesparse_user_threads)
+ceres_test(ba_sparseschur_cudasparse_user_threads)
ceres_test(ba_iterschur_jacobi_user_threads)
ceres_test(ba_iterschur_schurjacobi_user_threads)
ceres_test(ba_iterschur_spse_user_threads)
ceres_test(ba_iterschur_suitesparse_clustjacobi_user_threads)
ceres_test(ba_iterschur_eigensparse_clustjacobi_user_threads)
ceres_test(ba_iterschur_acceleratesparse_clustjacobi_user_threads)
+ceres_test(ba_iterschur_cudasparse_clustjacobi_user_threads)
ceres_test(ba_iterschur_suitesparse_clusttri_user_threads)
ceres_test(ba_iterschur_eigensparse_clusttri_user_threads)
ceres_test(ba_iterschur_acceleratesparse_clusttri_user_threads)
+ceres_test(ba_iterschur_cudasparse_clusttri_user_threads)
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_test.cc
new file mode 100644
index 0000000..1719aee
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterJacobi_AutomaticOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_JACOBI;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_threads_test.cc
new file mode 100644
index 0000000..c0c3489
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_auto_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterJacobi_AutomaticOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_JACOBI;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_test.cc
new file mode 100644
index 0000000..554a432
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterJacobi_UserOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_JACOBI;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_threads_test.cc
new file mode 100644
index 0000000..1e39f65
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clustjacobi_user_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterJacobi_UserOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_JACOBI;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_test.cc
new file mode 100644
index 0000000..6addad5
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterTridiagonal_AutomaticOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_TRIDIAGONAL;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_threads_test.cc
new file mode 100644
index 0000000..d2fefb8
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_auto_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterTridiagonal_AutomaticOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_TRIDIAGONAL;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_test.cc
new file mode 100644
index 0000000..ae04eec
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterTridiagonal_UserOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_TRIDIAGONAL;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_threads_test.cc
new file mode 100644
index 0000000..cad2051
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_iterschur_cudasparse_clusttri_user_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ IterativeSchur_CudaSparse_ClusterTridiagonal_UserOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = ITERATIVE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = CLUSTER_TRIDIAGONAL;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_test.cc
new file mode 100644
index 0000000..77f2fc1
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseNormalCholesky_CudaSparse_AutomaticOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_threads_test.cc
new file mode 100644
index 0000000..f436544
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_auto_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseNormalCholesky_CudaSparse_AutomaticOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_test.cc
new file mode 100644
index 0000000..dea6202
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseNormalCholesky_CudaSparse_UserOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_threads_test.cc
new file mode 100644
index 0000000..90a69e8
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparsecholesky_cudasparse_user_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseNormalCholesky_CudaSparse_UserOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_test.cc
new file mode 100644
index 0000000..5c45977
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseSchur_CudaSparse_AutomaticOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = SPARSE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_threads_test.cc
new file mode 100644
index 0000000..b866acb
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_auto_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseSchur_CudaSparse_AutomaticOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = SPARSE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kAutomaticOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_test.cc
new file mode 100644
index 0000000..a7e5e14
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseSchur_CudaSparse_UserOrdering) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 1;
+ options->linear_solver_type = SPARSE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_threads_test.cc b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_threads_test.cc
new file mode 100644
index 0000000..7319b28
--- /dev/null
+++ b/internal/ceres/generated_bundle_adjustment_tests/ba_sparseschur_cudasparse_user_threads_test.cc
@@ -0,0 +1,67 @@
+// 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.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// ========================================
+//
+// This file is generated using generate_bundle_adjustment_tests.py.
+
+#include "ceres/bundle_adjustment_test_util.h"
+#include "ceres/internal/config.h"
+#include "gtest/gtest.h"
+
+#ifndef CERES_NO_CUDA
+#ifndef CERES_NO_CUDSS
+
+namespace ceres::internal {
+
+TEST_F(BundleAdjustmentTest,
+ SparseSchur_CudaSparse_UserOrdering_Threads) { // NOLINT
+ BundleAdjustmentProblem bundle_adjustment_problem;
+ Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
+ options->eta = 0.01;
+ options->num_threads = 4;
+ options->linear_solver_type = SPARSE_SCHUR;
+ options->dense_linear_algebra_library_type = EIGEN;
+ options->sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options->preconditioner_type = IDENTITY;
+ if (kUserOrdering) {
+ options->linear_solver_ordering = nullptr;
+ }
+ Problem* problem = bundle_adjustment_problem.mutable_problem();
+ RunSolverForConfigAndExpectResidualsMatch(*options, problem);
+}
+
+} // namespace ceres::internal
+
+#endif // CERES_NO_CUDSS
+#endif // CERES_NO_CUDA
diff --git a/internal/ceres/reorder_program.cc b/internal/ceres/reorder_program.cc
index 44c4e46..268c477 100644
--- a/internal/ceres/reorder_program.cc
+++ b/internal/ceres/reorder_program.cc
@@ -576,7 +576,9 @@
// would involve performing two symbolic factorisations instead of one
// which would have a negative overall impact on performance.
return true;
-
+ } else if (sparse_linear_algebra_library_type == CUDA_SPARSE) {
+ // cuDSS has the same limitation as Accelerate
+ return true;
} else if (sparse_linear_algebra_library_type == EIGEN_SPARSE) {
OrderingForSparseNormalCholeskyUsingEigenSparse(
linear_solver_ordering_type,
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 611e465..fadb17b 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -41,6 +41,7 @@
#include "ceres/casts.h"
#include "ceres/context.h"
#include "ceres/context_impl.h"
+#include "ceres/cuda_sparse_cholesky.h"
#include "ceres/detect_structure.h"
#include "ceres/eigensparse.h"
#include "ceres/gradient_checking_cost_function.h"
@@ -113,7 +114,12 @@
internal::SuiteSparse::IsNestedDissectionAvailable()) ||
(type == ACCELERATE_SPARSE) ||
((type == EIGEN_SPARSE) &&
- internal::EigenSparse::IsNestedDissectionAvailable()));
+ internal::EigenSparse::IsNestedDissectionAvailable())
+#ifndef CERES_NO_CUDSS
+ || ((type == CUDA_SPARSE) &&
+ internal::CudaSparseCholesky<>::IsNestedDissectionAvailable())
+#endif
+ );
}
bool IsIterativeSolver(LinearSolverType type) {
@@ -168,8 +174,15 @@
return false;
}
- if (!IsSparseLinearAlgebraLibraryTypeAvailable(
+ if (IsSparseLinearAlgebraLibraryTypeAvailable(
options.sparse_linear_algebra_library_type)) {
+ if (options.sparse_linear_algebra_library_type == CUDA_SPARSE) {
+#if defined(CERES_NO_CUDSS)
+ *error = StringPrintf(kNoLibraryFormat, solver_name, library_name);
+ return false;
+#endif
+ }
+ } else {
*error = StringPrintf(kNoLibraryFormat, solver_name, library_name);
return false;
}
@@ -678,7 +691,12 @@
options.linear_solver_type == DENSE_QR) {
return (options.dense_linear_algebra_library_type == CUDA);
}
- if (options.linear_solver_type == CGNR) {
+ if (options.linear_solver_type == CGNR ||
+ options.linear_solver_type == SPARSE_SCHUR ||
+ options.linear_solver_type == SPARSE_NORMAL_CHOLESKY ||
+ (options.linear_solver_type == ITERATIVE_SCHUR &&
+ (options.preconditioner_type == CLUSTER_JACOBI ||
+ options.preconditioner_type == CLUSTER_TRIDIAGONAL))) {
return (options.sparse_linear_algebra_library_type == CUDA_SPARSE);
}
return false;
diff --git a/internal/ceres/solver_utils.cc b/internal/ceres/solver_utils.cc
index f5fbf05..ac22210 100644
--- a/internal/ceres/solver_utils.cc
+++ b/internal/ceres/solver_utils.cc
@@ -36,6 +36,9 @@
#include "ceres/version.h"
#ifndef CERES_NO_CUDA
#include "cuda_runtime.h"
+#ifndef CERES_NO_CUDSS
+#include "cudss.h"
+#endif // CERES_NO_CUDSS
#endif // CERES_NO_CUDA
namespace ceres::internal {
@@ -79,6 +82,11 @@
#ifndef CERES_NO_CUDA
"-cuda-(" CERES_TO_STRING(CUDART_VERSION) ")"
+#ifndef CERES_NO_CUDSS
+ "-cudss-(" CERES_SEMVER_VERSION(CUDSS_VERSION_MAJOR,
+ CUDSS_VERSION_MINOR,
+ CUDSS_VERSION_PATCH) ")"
+#endif // CERES_NO_CUDSS
#endif
;
// clang-format on
diff --git a/internal/ceres/sparse_cholesky.cc b/internal/ceres/sparse_cholesky.cc
index 4f1bf87..ecfeb29 100644
--- a/internal/ceres/sparse_cholesky.cc
+++ b/internal/ceres/sparse_cholesky.cc
@@ -34,6 +34,7 @@
#include <utility>
#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"
@@ -87,6 +88,19 @@
LOG(FATAL) << "Ceres was compiled without support for Apple's Accelerate "
<< "framework solvers.";
#endif
+ case CUDA_SPARSE:
+#ifndef CERES_NO_CUDSS
+ if (options.use_mixed_precision_solves) {
+ sparse_cholesky = CudaSparseCholesky<float>::Create(
+ options.context, options.ordering_type);
+ } else {
+ sparse_cholesky = CudaSparseCholesky<double>::Create(
+ options.context, options.ordering_type);
+ }
+ break;
+#else // CERES_NO_CUDSS
+ LOG(FATAL) << "Ceres was compiled without support for cuDSS.";
+#endif // CERES_NO_CUDSS
default:
LOG(FATAL) << "Unknown sparse linear algebra library type : "
diff --git a/internal/ceres/sparse_cholesky_test.cc b/internal/ceres/sparse_cholesky_test.cc
index d0d962e..bb5c7c8 100644
--- a/internal/ceres/sparse_cholesky_test.cc
+++ b/internal/ceres/sparse_cholesky_test.cc
@@ -39,6 +39,7 @@
#include "Eigen/SparseCore"
#include "ceres/block_sparse_matrix.h"
#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/cuda_sparse_cholesky.h"
#include "ceres/inner_product_computer.h"
#include "ceres/internal/config.h"
#include "ceres/internal/eigen.h"
@@ -117,6 +118,12 @@
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
auto sparse_cholesky = SparseCholesky::Create(sparse_cholesky_options);
const CompressedRowSparseMatrix::StorageType storage_type =
sparse_cholesky->StorageType();
@@ -251,7 +258,7 @@
ParamInfoToString);
INSTANTIATE_TEST_SUITE_P(
- EigenSparseCholeskySingle,
+ FloatEigenSparseCholesky,
SparseCholeskyTest,
::testing::Combine(::testing::Values(EIGEN_SPARSE),
::testing::Values(OrderingType::AMD,
@@ -260,6 +267,26 @@
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,
+ SparseCholeskyTest,
+ ::testing::Combine(::testing::Values(CUDA_SPARSE),
+ ::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,
diff --git a/internal/ceres/sparse_normal_cholesky_solver_test.cc b/internal/ceres/sparse_normal_cholesky_solver_test.cc
index 3396e34..2ff3f6e 100644
--- a/internal/ceres/sparse_normal_cholesky_solver_test.cc
+++ b/internal/ceres/sparse_normal_cholesky_solver_test.cc
@@ -88,8 +88,9 @@
EXPECT_EQ(summary.termination_type, LinearSolverTerminationType::SUCCESS);
+ const double eps = options.use_mixed_precision_solves ? 2e-6 : 1e-8;
for (int i = 0; i < A_->num_cols(); ++i) {
- EXPECT_NEAR(expected_solution(i), actual_solution(i), 1e-8)
+ EXPECT_NEAR(expected_solution(i), actual_solution(i), eps)
<< "\nExpected: " << expected_solution.transpose()
<< "\nActual: " << actual_solution.transpose();
}
@@ -177,4 +178,31 @@
}
#endif // CERES_USE_EIGEN_SPARSE
+#ifndef CERES_NO_CUDSS
+TEST_F(SparseNormalCholeskySolverTest, SparseNormalCholeskyUsingCuDSSSingle) {
+ LinearSolver::Options options;
+ options.sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options.type = SPARSE_NORMAL_CHOLESKY;
+ options.ordering_type = OrderingType::AMD;
+ options.use_mixed_precision_solves = true;
+ ContextImpl context;
+ options.context = &context;
+ std::string error;
+ CHECK(context.InitCuda(&error)) << error;
+ TestSolver(options);
+}
+
+TEST_F(SparseNormalCholeskySolverTest, SparseNormalCholeskyUsingCuDSSDouble) {
+ LinearSolver::Options options;
+ options.sparse_linear_algebra_library_type = CUDA_SPARSE;
+ options.type = SPARSE_NORMAL_CHOLESKY;
+ options.ordering_type = OrderingType::AMD;
+ ContextImpl context;
+ options.context = &context;
+ std::string error;
+ CHECK(context.InitCuda(&error)) << error;
+ TestSolver(options);
+}
+#endif // CERES_USE_EIGEN_SPARSE
+
} // namespace ceres::internal
diff --git a/internal/ceres/visibility_based_preconditioner.cc b/internal/ceres/visibility_based_preconditioner.cc
index 42e8a6e..34d6e0c 100644
--- a/internal/ceres/visibility_based_preconditioner.cc
+++ b/internal/ceres/visibility_based_preconditioner.cc
@@ -100,6 +100,8 @@
sparse_cholesky_options.sparse_linear_algebra_library_type =
options_.sparse_linear_algebra_library_type;
sparse_cholesky_options.ordering_type = options_.ordering_type;
+ sparse_cholesky_options.context = options_.context;
+
sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options);
const time_t init_time = time(nullptr);