- TripletSparseMatrix in BlockRandomAccessSparseMatrix is replaced with
BlockSparseMatrix
- BlockSparseMatrix::ToCompressedRowSparseMatrix is performed in a
direct sort-less way
Change-Id: Ib951fda1b9394050e2c47a9721172c5e3c674801
1. Add threading to all three subclasses of BlockRandomAccessMatrix.
i.e. BlockRandomAccessDenseMatrix, BlockRandomAccessSparseMatrix
and BlockRandomAccessDenseMatrix.
For BlockRandomAccessDenseMatrix and BlockRandomAccessSparseMatrix
this just means SetZero is parallelized. Which by itself is no
big deal, but by doing so, the constructor for all three subclasses
become uniform.
BlockRandomAccessSparseMatrix::SymmetricRightMultiplyAndAccumulate
maybe threaded in the future if needed.
BlockRandomAccessDiagonalMatrix is the biggest beneficiary. SetZero
Invert and RightMultiplyAndAccumulate are all threaded now.
2. Change the storage in BlockRandomAccessDiagonalMatrix from
TripletSparseMatrix to CompressedRowSparseMatrix. This has no
performance implications since we do not really use the capabilities
of the underlying matrix indexing representation. This is a forward
looking change when we decide to transfer this matrix to the GPU,
a CompressedRowSparseMatrix will save on a data conversion.
3. Use std::unique_ptr as needed and eliminate the need for custom
destructors.
4. Modify CompressedRowSparseMatrix::CreateBlockDiagonalMatrix to
take a nullptr as the data vector.
Fixes https://github.com/ceres-solver/ceres-solver/issues/936
Fixes https://github.com/ceres-solver/ceres-solver/issues/935
Change-Id: Ia6487f2d924fbe669835bdcc38abf2b451bda4ee
Previously some matrices used Block to keep track of
row/column block sizes and some would just use ints, and
then compute the position of each row and column from it.
By uniformly using Block everywhere, we reduce duplicate
computation and data copies.
I also cleaned up a bunch of c++17 related stuff as I edited
these files.
Change-Id: I4c86b1593fd4c91f9057fbb38314f62f303e0477
* Added CudaCgnrSolver, a new CUDA-accelerated CGNR.
* To use CudaCgnrSolver, the user must select CGNR as the linear_solver
and CUDA_SPARSE as the sparse_linear_algebra_library.
* Updated ConjugateGradientSolver to work with an array of pointers to
scratch to support CudaVectors as scratch.
* Moved CUDA initialization to run in Solver::Solve as needed.
Some performance comparisons on an Ubuntu 20.04 desktop with an
Intel i9-9940X CPU @ 3.30GHz, and an nVidia Quadro RTX 6000,
all configurations run with 24 threads, and 10 iterations.
=================================================
CGNR + CUDA_SPARSE + IDENTITY Preconditioner
problem-1778-993923-pre.txt
=================================================
Cost:
Initial 2.563973e+08
Final 1.724755e+06
Change 2.546725e+08
Minimizer iterations 11
Successful steps 7
Unsuccessful steps 4
Time (in seconds):
Preprocessor 4.020158
Residual only evaluation 1.567092 (10)
Jacobian & residual evaluation 7.847130 (7)
Linear solver 31.688898 (10)
Minimizer 46.834987
Postprocessor 0.353974
Total 51.209120
=================================================
SPARSE_SCHUR (CPU) + SUITE_SPARSE + AMD
problem-1778-993923-pre.txt
=================================================
Cost:
Initial 2.563973e+08
Final 1.651617e+06
Change 2.547457e+08
Minimizer iterations 11
Successful steps 11
Unsuccessful steps 0
Time (in seconds):
Preprocessor 35.812003
Residual only evaluation 1.658980 (10)
Jacobian & residual evaluation 12.218799 (11)
Linear solver 76.409992 (10)
Minimizer 98.809773
Postprocessor 0.372712
Total 134.994489
=================================================
ITERATIVE_SCHUR (CPU) + JACOBI Preconditioner
problem-1778-993923-pre.txt
=================================================
Cost:
Initial 2.563973e+08
Final 1.684447e+06
Change 2.547128e+08
Minimizer iterations 11
Successful steps 8
Unsuccessful steps 3
Time (in seconds):
Preprocessor 15.331614
Residual only evaluation 1.606114 (10)
Jacobian & residual evaluation 8.502166 (8)
Linear solver 351.910080 (10)
Minimizer 368.797327
Postprocessor 0.363536
Total 384.492478
=================================================
CGNR + CUDA_SPARSE + IDENTITY Preconditioner
problem-13682-4456117-pre.txt
=================================================
Cost:
Initial 1.126372e+09
Final 2.269329e+07
Change 1.103678e+09
Minimizer iterations 11
Successful steps 7
Unsuccessful steps 4
Time (in seconds):
Preprocessor 19.140087
Residual only evaluation 8.721920 (10)
Jacobian & residual evaluation 41.955923 (7)
Linear solver 214.121861 (10)
Minimizer 296.636890
Postprocessor 1.971827
Total 317.748804
Change-Id: I3a09f31aa6903f661e91f595afd39d427583e856
Clang emits the following warning:
internal/ceres/schur_complement_solver.cc:71:51: warning: class with destructor marked 'final' cannot be inherited from [-Wfinal-dtor-non-final-class]
virtual ~BlockRandomAccessSparseMatrixAdapter() final {}
Change-Id: I9da96711fd1e924ce8504988ff171cb476b6222f
These methods were historically poorly named and every time I read code
I get confused whether they are just multiplying or multiplying and
adding. Clarifying them also gives us the changce to introduce
RightMultiply and LeftMultiply methods in the base class which will
simplify a number call sites in a subsequent CL.
Fixes https://github.com/ceres-solver/ceres-solver/issues/855
Change-Id: Ice4fb483f1acd02527a6dd753ef0c5a66037f4b0
1. Convert it from a class to a template function. Where the
template parameter is "DenseVectorType". This allows us
to have a single implementation of Conjugate Gradients
without worrying about where the matrix and the vectors
are stored or what their internal representation is.
For the case of CPU based vectors, we abstract operations
on Eigen vectors using eigen_vector_ops.
2. Introduce ConjugateGradientsLinearOperator which is
templated on DenseVectorType. It is the matrix vector
multiplication abstraction.
3. Port the tests and all usages of ConjugateGradientsSolver
to this new implementation.
4. Introduce Eigen::Vector based RightMultiply and LeftMultiply
methods into LinearOperator which by default delete to the
bare pointer based interfaces.
5. Add an identity preconditioner.
These changes are being made in preparation for adding a CUDA
based CGNR solver.
Change-Id: I9da36dc6c131856dd1a4aa7e645aaf12d25dd79b
Do not define trivial constructors or destructors unless necessary
(e.g., for implementing pimpl) following the rule of zero. Define
virtual base class destructors out-of-line to avoid emitting vtables in
every translation unit.
Change-Id: Iea2d8978e62a8ee5a97b86cbb4e858d56e0fb274
Applied changes correspond to clang-tidy fixes
stemming from the modernize-use-equals-default check.
Change-Id: I254b0908a76d464131564b637cd0e42a6b03fb5a
virtual can be ambiguous. Applied changes correspond to clang-tidy fixes
stemming from the modernize-use-override check.
Change-Id: I973afd4680a5df587419777504aeb94467196b89
Like SparseCholesky, the DenseCholesky interface abstracts
away the solution of dense linear systems using Cholesky factorization.
This allows the client code to not worry about the type of dense
linear algebra library being used.
DenseNormalCholeskySolver and DenseSchurComplementSolver code
is considerably simpler as a result.
Change-Id: Ie15f09ee376d5f9a64609e6a55ad83e99c76352a
- Change formatting standard to Cpp11. Main difference is not having
the space between two closing >> for nested templates. We don't
choose c++14, because older versions of clang-format (version 9
and earlier) don't know this value yet, and it doesn't make a
difference in the formatting.
- Apply clang-format to all (non generated) internal source files.
- Manually fix some code sections (clang-format on/off) and c-strings
- Exclude some embedded external files with very different formatting
(gtest/gmock)
- Add script to format all source files
Change-Id: Ic6cea41575ad6e37c9e136dbce176b0d505dc44d
A number of algorithms like the SchurEliminator do not need
access to the full BlockSparseMatrix interface. They only
need read only access to the values array and the block structure.
This change introduces, BlockSparseDataMatrix a struct that carries
these two bits of information and modifies the Schur type algorithms
to use it.
What this change will allow us to do, in a subsequent CL is to
take the values array of a BlockSparseMatrix and pair it with
a different blocks structure for subset preconditioning.
Change-Id: I1808f12531b586c9ff4d6a70b3d390c7b0d9f441
Also run clang-format on schur_complement_solver.cc.
A more elaborate integration is not done, because it will cause
binary bloat.
Change-Id: Idf4e9e1794f7401a39f05fb145ec06459209a1e1
1. Add Solver::Options::use_mixed_precision_solves,
and Solver::Options::max_num_refinement_iterations.
2. Make SparseCholesky::Create return a unique_ptr.
3. SparseCholesky::Create now takes LinearSolver::Options
as an argument.
4. IterativeRefiner's constructor does not require num_cols
as an argument.
5. SparseNormalCholeskySolver now uses a separate rhs vector.
This basic implementation results in a 10% reduction in solver time
and 30% reduction in linear solver memory usage.
Change-Id: I6830f32cae2febf082d2733262eb2c9f0482b0ea
There was a bug in the trust region preprocessor where no fill
reducing ordering was computed for the case of SPARSE_SCHUR + CX_SPARSE
but this was not signaled to SchurComplementSolver, so it was using
a naive/natural ordering. To fix this two changes are made:
1. TrustRegionProcessor's logic for signaling the ordering to the
linear solver has been re-worked. The surrounding code has also
been re-organized for better readability.
2. In SchurComplementSolver::SolveReducedSystem the row and column
block structure has been added to the CompressedRowSparseMatrix
containing the Schur complement so that block AMD can be used.
As a result of these changes the linear solve time for
problem-744-543562-pre.txt has been brought down from 58 seconds to
35 seconds.
Change-Id: I4d82efce05175260f97b1f925f8a1b4a9d650cae
SchurComplementSolver implements a variant of ITERATIVE_SCHUR
when explicit_schur_complement is set to true. In this case
the SparseCholesky object should not be instantiated. Even
though there is no CPU cost, it can be the case that ITERATIVE_SCHUR
is being used when there are not sparse linear algebra libraries
are available, and this can result in a crash.
Change-Id: I349d5f79201782689b3ab0ccc2c5001804b44c7b
SparseCholesky is an interface to sparse cholesky factorization
routines across sparse linear algebra libraries. Each sparse
linear algebra library is responsible for implementing its own
instance of this interface.
As a result the various places - SparseNormalCholeskySolver,
SparseSchurComplementSolver and VisibilityBasedPreconditioner
are significantly simplified.
Change-Id: I8b465705eae83bba9e1adfffcc741a05c70faf2e
1. Convert a CompressedRowSparseMatrix constructor which
takes a TripletSparseMatrix as input into a factory method
which allows the input to be transposed.
2. Move the random matrix creation routine for CompressedRowSparseMatrix
from being a standalone function to a static method.
3. Add a corresponding random matrix generation static method to
TripletSparseMatrix.
4. Add a new constructor to TripletSparseMatrix, which takes as input
the row, col and values arrays.
Change-Id: Iec7b184646818f432a5e6822bea3b2f3128a82aa
SchurEliminator::Init now takes a bool that tells it whether
it can assume that the diagonal blocks it is inverting can
be assumed to be full rank or not.
This information is then passed onto InvertPSDMatrix.
Change-Id: I26037b6233f2aad5584fed245f631c3959928afe
Print information about the fill-in when using Eigen to perform
Simplicial sparse Cholesky factorization.
Change-Id: I09013b7f4ed2f6c55ae8ce8922447e23d63580a8
Eigen upstream was broken a little while ago, and it seemed to be
the case that we needed a fix for using the LLT factorization on
ARM.
This has been fixed and AFAIK there are no stable eigen releases
with this bug in it.
For full gore, see
http://eigen.tuxfamily.org/bz/show_bug.cgi?id=992
In light of the fix, the extra layer of indirection introduced earlier
is not needed and we are reverting to normal programming.
Change-Id: I16929d2145253b38339b573b27b6b8fabd523704
It seems that Eigen's LLT factorization is broken on ARM.
This patch enables the use of LDLT factorization instead of LLT
factorization. The switch is controlled at compile time using a
preprocessor define - CERES_USE_EIGEN_LDLT.
By default we continue to use LLT factorization though.
To make the switching easier without introducing the Cholesky factorization
based inversion and linear system solve routines have been abstracted into
two new functions.
Android.mk has been updated to enable the LDLT factorization, but
the cmake file has not been updated as I will leave it to Alex's
capable hands to do proper detection of ARM as a target platform.
Change-Id: Iffe3abd2ce894de2a388b454df3da909b482d5e5
Since Ceres is moving to using GitHub for issues, and the Google
Code URL in the current copyright header will soon become invalid,
update all the headers.
Change-Id: I1fce70375d1bcf098591f07b4d8f01a5c1e0789c
For historical reasons we had a "using namespace std;" in port.h. This
is generally a bad idea. So removing it and along the way doing a bunch
of cpplint cleanup.
Change-Id: Ia125601a55ae62695e247fb0250df4c6f86c46c6
Up till now ITERATIVE_SCHUR evaluates matrix-vector products
between the Schur complement and a vector implicitly by exploiting
the algebraic expression for the Schur complement.
This cost of this evaluation scales with the number of non-zeros
in the Jacobian.
For small to medium sized problems there is a sweet spot where
computing the Schur complement is cheap enough that it is much
more efficient to explicitly compute it and use it for evaluating
the matrix-vector products.
This changes implements support for an explicit Schur complement
in ITERATIVE_SCHUR in combination with the SCHUR_JACOBI preconditioner.
API wise a new bool Solver::Options::use_explicit_schur_complement
has been added.
The implementation extends the SparseSchurComplementSolver to use
Conjugate Gradients.
Example speedup:
use_explicit_schur_complement = false
Time (in seconds):
Preprocessor 0.585
Residual evaluation 0.319
Jacobian evaluation 1.590
Linear solver 25.685
Minimizer 27.990
Postprocessor 0.010
Total 28.585
use_explicit_schur_complement = true
Time (in seconds):
Preprocessor 0.638
Residual evaluation 0.318
Jacobian evaluation 1.507
Linear solver 5.930
Minimizer 8.144
Postprocessor 0.010
Total 8.791
Which indicates an end-to-end speedup of more than 3x, with the linear
solver being sped up by > 4x.
The idea to explore this optimization was inspired by the recent paper:
Mining structure fragments for smart bundle adjustment
L. Carlone, P. Alcantarilla, H. Chiu, K. Zsolt, F. Dellaert
British Machine Vision Conference, 2014
which uses a more complicated algorithm to compute parts of the
Schur complement to speed up the matrix-vector product.
Change-Id: I95324af0ab351faa1600f5204039a1d2a64ae61d
The instrumentation revealed that EIGEN_SPARSE can be upto
an order of magnitude slower than CX_SPARSE on some bundle
adjustment problems.
The problem comes down to the quality of AMD ordering that
CXSparse/Eigen implements. It does particularly badly
on the Schur complement. In the CXSparse implementation
we got around this by considering the block sparsity structure
and computing the AMD ordering on it and lifting it to the
full matrix.
This is currently not possible with the release version of
Eigen, as the support for using preordered/natural orderings
is in the master branch but has not been released yet.
Change-Id: I25588d3e723e50606f327db5759f174f58439e29
SPARSE_NORMAL_CHOLESKY and SPARSE_SCHUR can now be used
with EIGEN_SPARSE as the backend.
The performance is not as good as CXSparse. This needs to be
investigated. Is it because the quality of AMD ordering that
we are computing is not as good as the one for CXSparse? This
could be because we are working with the scalar matrix instead
of the block matrix.
Also, the upper/lower triangular story is not completely clear.
Both of these issues will be benchmarked and tackled in the
near future.
Also included in this change is a bunch of cleanup to the
SparseNormalCholeskySolver and SparseSchurComplementSolver
classes around the use of the of defines used to conditionally
compile out parts of the code.
The system_test has been updated to test EIGEN_SPARSE also.
Change-Id: I46a57e9c4c97782696879e0b15cfc7a93fe5496a
cholmod_lhs was not being freed before
the SolveImpl function returned if the
underlying linear solver failed.
Change-Id: Id45db4f69a2821e43dc00fc975718584d426f44d