mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-29 08:34:37 +08:00
Refactor Covariance::Options::algorithm_type.
THIS IS AN API BREAKING CHANGE. Decouple the algorithm from the sparse linear algebra library being used to perform the computation. Before this change Covariance::AlgorithmType had values DENSE_SVD EIGEN_SPARSE_QR SUITE_SPARSE_QR This has been replaced by two enums now. Covariance::Options::sparse_linear_algebra_library_type which can take values EIGEN_SPARSE, SUITE_SPARSE or CX_SPARSE. The last one is currently not supported. And Covariance::Options::algorithm_type takes values DENSE_SVD SPARSE_QR This sets the stage for future extensions of the covariance computation algorithm. Also as part of this change, the covariance computation chapter has been made a top level chapter on its own instead of being buried deep inside the Solving Non-linear Least Squares problem. Change-Id: Ibfbf60902d8d17694d9ff585047a5a57d329ab22
This commit is contained in:
@@ -27,6 +27,7 @@ reports and feature requests.
|
||||
derivatives
|
||||
nnls_modeling
|
||||
nnls_solving
|
||||
nnls_covariance
|
||||
gradient_solver
|
||||
faqs
|
||||
users
|
||||
|
||||
@@ -2255,365 +2255,3 @@ The three arrays will be:
|
||||
|
||||
If the type of the line search direction is `LBFGS`, then this
|
||||
indicates the rank of the Hessian approximation.
|
||||
|
||||
Covariance Estimation
|
||||
=====================
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
One way to assess the quality of the solution returned by a
|
||||
non-linear least squares solve is to analyze the covariance of the
|
||||
solution.
|
||||
|
||||
Let us consider the non-linear regression problem
|
||||
|
||||
.. math:: y = f(x) + N(0, I)
|
||||
|
||||
i.e., the observation :math:`y` is a random non-linear function of the
|
||||
independent variable :math:`x` with mean :math:`f(x)` and identity
|
||||
covariance. Then the maximum likelihood estimate of :math:`x` given
|
||||
observations :math:`y` is the solution to the non-linear least squares
|
||||
problem:
|
||||
|
||||
.. math:: x^* = \arg \min_x \|f(x)\|^2
|
||||
|
||||
And the covariance of :math:`x^*` is given by
|
||||
|
||||
.. math:: C(x^*) = \left(J'(x^*)J(x^*)\right)^{-1}
|
||||
|
||||
Here :math:`J(x^*)` is the Jacobian of :math:`f` at :math:`x^*`. The
|
||||
above formula assumes that :math:`J(x^*)` has full column rank.
|
||||
|
||||
If :math:`J(x^*)` is rank deficient, then the covariance matrix :math:`C(x^*)`
|
||||
is also rank deficient and is given by the Moore-Penrose pseudo inverse.
|
||||
|
||||
.. math:: C(x^*) = \left(J'(x^*)J(x^*)\right)^{\dagger}
|
||||
|
||||
Note that in the above, we assumed that the covariance matrix for
|
||||
:math:`y` was identity. This is an important assumption. If this is
|
||||
not the case and we have
|
||||
|
||||
.. math:: y = f(x) + N(0, S)
|
||||
|
||||
Where :math:`S` is a positive semi-definite matrix denoting the
|
||||
covariance of :math:`y`, then the maximum likelihood problem to be
|
||||
solved is
|
||||
|
||||
.. math:: x^* = \arg \min_x f'(x) S^{-1} f(x)
|
||||
|
||||
and the corresponding covariance estimate of :math:`x^*` is given by
|
||||
|
||||
.. math:: C(x^*) = \left(J'(x^*) S^{-1} J(x^*)\right)^{-1}
|
||||
|
||||
So, if it is the case that the observations being fitted to have a
|
||||
covariance matrix not equal to identity, then it is the user's
|
||||
responsibility that the corresponding cost functions are correctly
|
||||
scaled, e.g. in the above case the cost function for this problem
|
||||
should evaluate :math:`S^{-1/2} f(x)` instead of just :math:`f(x)`,
|
||||
where :math:`S^{-1/2}` is the inverse square root of the covariance
|
||||
matrix :math:`S`.
|
||||
|
||||
Gauge Invariance
|
||||
----------------
|
||||
|
||||
In structure from motion (3D reconstruction) problems, the
|
||||
reconstruction is ambiguous upto a similarity transform. This is
|
||||
known as a *Gauge Ambiguity*. Handling Gauges correctly requires the
|
||||
use of SVD or custom inversion algorithms. For small problems the
|
||||
user can use the dense algorithm. For more details see the work of
|
||||
Kanatani & Morris [KanataniMorris]_.
|
||||
|
||||
|
||||
:class:`Covariance`
|
||||
-------------------
|
||||
|
||||
:class:`Covariance` allows the user to evaluate the covariance for a
|
||||
non-linear least squares problem and provides random access to its
|
||||
blocks. The computation assumes that the cost functions compute
|
||||
residuals such that their covariance is identity.
|
||||
|
||||
Since the computation of the covariance matrix requires computing the
|
||||
inverse of a potentially large matrix, this can involve a rather large
|
||||
amount of time and memory. However, it is usually the case that the
|
||||
user is only interested in a small part of the covariance
|
||||
matrix. Quite often just the block diagonal. :class:`Covariance`
|
||||
allows the user to specify the parts of the covariance matrix that she
|
||||
is interested in and then uses this information to only compute and
|
||||
store those parts of the covariance matrix.
|
||||
|
||||
Rank of the Jacobian
|
||||
--------------------
|
||||
|
||||
As we noted above, if the Jacobian is rank deficient, then the inverse
|
||||
of :math:`J'J` is not defined and instead a pseudo inverse needs to be
|
||||
computed.
|
||||
|
||||
The rank deficiency in :math:`J` can be *structural* -- columns
|
||||
which are always known to be zero or *numerical* -- depending on the
|
||||
exact values in the Jacobian.
|
||||
|
||||
Structural rank deficiency occurs when the problem contains parameter
|
||||
blocks that are constant. This class correctly handles structural rank
|
||||
deficiency like that.
|
||||
|
||||
Numerical rank deficiency, where the rank of the matrix cannot be
|
||||
predicted by its sparsity structure and requires looking at its
|
||||
numerical values is more complicated. Here again there are two
|
||||
cases.
|
||||
|
||||
a. The rank deficiency arises from overparameterization. e.g., a
|
||||
four dimensional quaternion used to parameterize :math:`SO(3)`,
|
||||
which is a three dimensional manifold. In cases like this, the
|
||||
user should use an appropriate
|
||||
:class:`LocalParameterization`. Not only will this lead to better
|
||||
numerical behaviour of the Solver, it will also expose the rank
|
||||
deficiency to the :class:`Covariance` object so that it can
|
||||
handle it correctly.
|
||||
|
||||
b. More general numerical rank deficiency in the Jacobian requires
|
||||
the computation of the so called Singular Value Decomposition
|
||||
(SVD) of :math:`J'J`. We do not know how to do this for large
|
||||
sparse matrices efficiently. For small and moderate sized
|
||||
problems this is done using dense linear algebra.
|
||||
|
||||
|
||||
:class:`Covariance::Options`
|
||||
|
||||
.. class:: Covariance::Options
|
||||
|
||||
.. member:: int Covariance::Options::num_threads
|
||||
|
||||
Default: ``1``
|
||||
|
||||
Number of threads to be used for evaluating the Jacobian and
|
||||
estimation of covariance.
|
||||
|
||||
.. member:: CovarianceAlgorithmType Covariance::Options::algorithm_type
|
||||
|
||||
Default: ``SUITE_SPARSE_QR`` if ``SuiteSparseQR`` is installed and
|
||||
``EIGEN_SPARSE_QR`` otherwise.
|
||||
|
||||
Ceres supports three different algorithms for covariance
|
||||
estimation, which represent different tradeoffs in speed, accuracy
|
||||
and reliability.
|
||||
|
||||
1. ``DENSE_SVD`` uses ``Eigen``'s ``JacobiSVD`` to perform the
|
||||
computations. It computes the singular value decomposition
|
||||
|
||||
.. math:: U S V^\top = J
|
||||
|
||||
and then uses it to compute the pseudo inverse of J'J as
|
||||
|
||||
.. math:: (J'J)^{\dagger} = V S^{\dagger} V^\top
|
||||
|
||||
It is an accurate but slow method and should only be used for
|
||||
small to moderate sized problems. It can handle full-rank as
|
||||
well as rank deficient Jacobians.
|
||||
|
||||
2. ``EIGEN_SPARSE_QR`` uses the sparse QR factorization algorithm
|
||||
in ``Eigen`` to compute the decomposition
|
||||
|
||||
.. math::
|
||||
|
||||
QR &= J\\
|
||||
\left(J^\top J\right)^{-1} &= \left(R^\top R\right)^{-1}
|
||||
|
||||
It is a moderately fast algorithm for sparse matrices.
|
||||
|
||||
3. ``SUITE_SPARSE_QR`` uses the sparse QR factorization algorithm
|
||||
in ``SuiteSparse``. It uses dense linear algebra and is multi
|
||||
threaded, so for large sparse sparse matrices it is
|
||||
significantly faster than ``EIGEN_SPARSE_QR``.
|
||||
|
||||
Neither ``EIGEN_SPARSE_QR`` nor ``SUITE_SPARSE_QR`` are capable of
|
||||
computing the covariance if the Jacobian is rank deficient.
|
||||
|
||||
.. member:: int Covariance::Options::min_reciprocal_condition_number
|
||||
|
||||
Default: :math:`10^{-14}`
|
||||
|
||||
If the Jacobian matrix is near singular, then inverting :math:`J'J`
|
||||
will result in unreliable results, e.g, if
|
||||
|
||||
.. math::
|
||||
|
||||
J = \begin{bmatrix}
|
||||
1.0& 1.0 \\
|
||||
1.0& 1.0000001
|
||||
\end{bmatrix}
|
||||
|
||||
which is essentially a rank deficient matrix, we have
|
||||
|
||||
.. math::
|
||||
|
||||
(J'J)^{-1} = \begin{bmatrix}
|
||||
2.0471e+14& -2.0471e+14 \\
|
||||
-2.0471e+14 2.0471e+14
|
||||
\end{bmatrix}
|
||||
|
||||
|
||||
This is not a useful result. Therefore, by default
|
||||
:func:`Covariance::Compute` will return ``false`` if a rank
|
||||
deficient Jacobian is encountered. How rank deficiency is detected
|
||||
depends on the algorithm being used.
|
||||
|
||||
1. ``DENSE_SVD``
|
||||
|
||||
.. math:: \frac{\sigma_{\text{min}}}{\sigma_{\text{max}}} < \sqrt{\text{min_reciprocal_condition_number}}
|
||||
|
||||
where :math:`\sigma_{\text{min}}` and
|
||||
:math:`\sigma_{\text{max}}` are the minimum and maxiumum
|
||||
singular values of :math:`J` respectively.
|
||||
|
||||
2. ``EIGEN_SPARSE_QR`` and ``SUITE_SPARSE_QR``
|
||||
|
||||
.. math:: \operatorname{rank}(J) < \operatorname{num\_col}(J)
|
||||
|
||||
Here :\math:`\operatorname{rank}(J)` is the estimate of the
|
||||
rank of `J` returned by the sparse QR factorization
|
||||
algorithm. It is a fairly reliable indication of rank
|
||||
deficiency.
|
||||
|
||||
.. member:: int Covariance::Options::null_space_rank
|
||||
|
||||
When using ``DENSE_SVD``, the user has more control in dealing
|
||||
with singular and near singular covariance matrices.
|
||||
|
||||
As mentioned above, when the covariance matrix is near singular,
|
||||
instead of computing the inverse of :math:`J'J`, the Moore-Penrose
|
||||
pseudoinverse of :math:`J'J` should be computed.
|
||||
|
||||
If :math:`J'J` has the eigen decomposition :math:`(\lambda_i,
|
||||
e_i)`, where :math:`lambda_i` is the :math:`i^\textrm{th}`
|
||||
eigenvalue and :math:`e_i` is the corresponding eigenvector, then
|
||||
the inverse of :math:`J'J` is
|
||||
|
||||
.. math:: (J'J)^{-1} = \sum_i \frac{1}{\lambda_i} e_i e_i'
|
||||
|
||||
and computing the pseudo inverse involves dropping terms from this
|
||||
sum that correspond to small eigenvalues.
|
||||
|
||||
How terms are dropped is controlled by
|
||||
`min_reciprocal_condition_number` and `null_space_rank`.
|
||||
|
||||
If `null_space_rank` is non-negative, then the smallest
|
||||
`null_space_rank` eigenvalue/eigenvectors are dropped irrespective
|
||||
of the magnitude of :math:`\lambda_i`. If the ratio of the
|
||||
smallest non-zero eigenvalue to the largest eigenvalue in the
|
||||
truncated matrix is still below min_reciprocal_condition_number,
|
||||
then the `Covariance::Compute()` will fail and return `false`.
|
||||
|
||||
Setting `null_space_rank = -1` drops all terms for which
|
||||
|
||||
.. math:: \frac{\lambda_i}{\lambda_{\textrm{max}}} < \textrm{min_reciprocal_condition_number}
|
||||
|
||||
This option has no effect on ``EIGEN_SPARSE_QR`` and
|
||||
``SUITE_SPARSE_QR``.
|
||||
|
||||
.. member:: bool Covariance::Options::apply_loss_function
|
||||
|
||||
Default: `true`
|
||||
|
||||
Even though the residual blocks in the problem may contain loss
|
||||
functions, setting ``apply_loss_function`` to false will turn off
|
||||
the application of the loss function to the output of the cost
|
||||
function and in turn its effect on the covariance.
|
||||
|
||||
.. class:: Covariance
|
||||
|
||||
:class:`Covariance::Options` as the name implies is used to control
|
||||
the covariance estimation algorithm. Covariance estimation is a
|
||||
complicated and numerically sensitive procedure. Please read the
|
||||
entire documentation for :class:`Covariance::Options` before using
|
||||
:class:`Covariance`.
|
||||
|
||||
.. function:: bool Covariance::Compute(const vector<pair<const double*, const double*> >& covariance_blocks, Problem* problem)
|
||||
|
||||
Compute a part of the covariance matrix.
|
||||
|
||||
The vector ``covariance_blocks``, indexes into the covariance
|
||||
matrix block-wise using pairs of parameter blocks. This allows the
|
||||
covariance estimation algorithm to only compute and store these
|
||||
blocks.
|
||||
|
||||
Since the covariance matrix is symmetric, if the user passes
|
||||
``<block1, block2>``, then ``GetCovarianceBlock`` can be called with
|
||||
``block1``, ``block2`` as well as ``block2``, ``block1``.
|
||||
|
||||
``covariance_blocks`` cannot contain duplicates. Bad things will
|
||||
happen if they do.
|
||||
|
||||
Note that the list of ``covariance_blocks`` is only used to
|
||||
determine what parts of the covariance matrix are computed. The
|
||||
full Jacobian is used to do the computation, i.e. they do not have
|
||||
an impact on what part of the Jacobian is used for computation.
|
||||
|
||||
The return value indicates the success or failure of the covariance
|
||||
computation. Please see the documentation for
|
||||
:class:`Covariance::Options` for more on the conditions under which
|
||||
this function returns ``false``.
|
||||
|
||||
.. function:: bool GetCovarianceBlock(const double* parameter_block1, const double* parameter_block2, double* covariance_block) const
|
||||
|
||||
Return the block of the cross-covariance matrix corresponding to
|
||||
``parameter_block1`` and ``parameter_block2``.
|
||||
|
||||
Compute must be called before the first call to ``GetCovarianceBlock``
|
||||
and the pair ``<parameter_block1, parameter_block2>`` OR the pair
|
||||
``<parameter_block2, parameter_block1>`` must have been present in the
|
||||
vector covariance_blocks when ``Compute`` was called. Otherwise
|
||||
``GetCovarianceBlock`` will return false.
|
||||
|
||||
``covariance_block`` must point to a memory location that can store
|
||||
a ``parameter_block1_size x parameter_block2_size`` matrix. The
|
||||
returned covariance will be a row-major matrix.
|
||||
|
||||
.. function:: bool GetCovarianceBlockInTangentSpace(const double* parameter_block1, const double* parameter_block2, double* covariance_block) const
|
||||
|
||||
Return the block of the cross-covariance matrix corresponding to
|
||||
``parameter_block1`` and ``parameter_block2``.
|
||||
Returns cross-covariance in the tangent space if a local
|
||||
parameterization is associated with either parameter block;
|
||||
else returns cross-covariance in the ambient space.
|
||||
|
||||
Compute must be called before the first call to ``GetCovarianceBlock``
|
||||
and the pair ``<parameter_block1, parameter_block2>`` OR the pair
|
||||
``<parameter_block2, parameter_block1>`` must have been present in the
|
||||
vector covariance_blocks when ``Compute`` was called. Otherwise
|
||||
``GetCovarianceBlock`` will return false.
|
||||
|
||||
``covariance_block`` must point to a memory location that can store
|
||||
a ``parameter_block1_local_size x parameter_block2_local_size`` matrix. The
|
||||
returned covariance will be a row-major matrix.
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
double x[3];
|
||||
double y[2];
|
||||
|
||||
Problem problem;
|
||||
problem.AddParameterBlock(x, 3);
|
||||
problem.AddParameterBlock(y, 2);
|
||||
<Build Problem>
|
||||
<Solve Problem>
|
||||
|
||||
Covariance::Options options;
|
||||
Covariance covariance(options);
|
||||
|
||||
vector<pair<const double*, const double*> > covariance_blocks;
|
||||
covariance_blocks.push_back(make_pair(x, x));
|
||||
covariance_blocks.push_back(make_pair(y, y));
|
||||
covariance_blocks.push_back(make_pair(x, y));
|
||||
|
||||
CHECK(covariance.Compute(covariance_blocks, &problem));
|
||||
|
||||
double covariance_xx[3 * 3];
|
||||
double covariance_yy[2 * 2];
|
||||
double covariance_xy[3 * 2];
|
||||
covariance.GetCovarianceBlock(x, x, covariance_xx)
|
||||
covariance.GetCovarianceBlock(y, y, covariance_yy)
|
||||
covariance.GetCovarianceBlock(x, y, covariance_xy)
|
||||
|
||||
+28
-22
@@ -200,19 +200,28 @@ class CovarianceImpl;
|
||||
class CERES_EXPORT Covariance {
|
||||
public:
|
||||
struct CERES_EXPORT Options {
|
||||
Options()
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
: algorithm_type(SUITE_SPARSE_QR),
|
||||
#else
|
||||
: algorithm_type(EIGEN_SPARSE_QR),
|
||||
Options() {
|
||||
algorithm_type = SPARSE_QR;
|
||||
|
||||
// Eigen's QR factorization is always available.
|
||||
sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
#if !defined(CERES_NO_SUITESPARSE)
|
||||
sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
#endif
|
||||
min_reciprocal_condition_number(1e-14),
|
||||
null_space_rank(0),
|
||||
num_threads(1),
|
||||
apply_loss_function(true) {
|
||||
|
||||
min_reciprocal_condition_number = 1e-14;
|
||||
null_space_rank = 0;
|
||||
num_threads = 1;
|
||||
apply_loss_function = true;
|
||||
}
|
||||
|
||||
// Ceres supports three different algorithms for covariance
|
||||
// Sparse linear algebra library to use when a sparse matrix
|
||||
// factorization is being used to compute the covariance matrix.
|
||||
//
|
||||
// Currently this only applies to SPARSE_QR.
|
||||
SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type;
|
||||
|
||||
// Ceres supports two different algorithms for covariance
|
||||
// estimation, which represent different tradeoffs in speed,
|
||||
// accuracy and reliability.
|
||||
//
|
||||
@@ -229,22 +238,19 @@ class CERES_EXPORT Covariance {
|
||||
// for small to moderate sized problems. It can handle
|
||||
// full-rank as well as rank deficient Jacobians.
|
||||
//
|
||||
// 2. EIGEN_SPARSE_QR uses the sparse QR factorization algorithm
|
||||
// in Eigen to compute the decomposition
|
||||
// 2. SPARSE_QR uses the sparse QR factorization algorithm
|
||||
// to compute the decomposition
|
||||
//
|
||||
// Q * R = J
|
||||
//
|
||||
// [J'J]^-1 = [R*R']^-1
|
||||
//
|
||||
// It is a moderately fast algorithm for sparse matrices.
|
||||
//
|
||||
// 3. SUITE_SPARSE_QR uses the SuiteSparseQR sparse QR
|
||||
// factorization algorithm. It uses dense linear algebra and is
|
||||
// multi threaded, so for large sparse sparse matrices it is
|
||||
// significantly faster than EIGEN_SPARSE_QR.
|
||||
//
|
||||
// Neither EIGEN_SPARSE_QR not SUITE_SPARSE_QR are capable of
|
||||
// computing the covariance if the Jacobian is rank deficient.
|
||||
// SPARSE_QR is not capable of computing the covariance if the
|
||||
// Jacobian is rank deficient. Depending on the value of
|
||||
// Covariance::Options::sparse_linear_algebra_library_type, either
|
||||
// Eigen's Sparse QR factorization algorithm will be used or
|
||||
// SuiteSparse's high performance SuiteSparseQR algorithm will be
|
||||
// used.
|
||||
CovarianceAlgorithmType algorithm_type;
|
||||
|
||||
// If the Jacobian matrix is near singular, then inverting J'J
|
||||
@@ -270,7 +276,7 @@ class CERES_EXPORT Covariance {
|
||||
// where min_sigma and max_sigma are the minimum and maxiumum
|
||||
// singular values of J respectively.
|
||||
//
|
||||
// 2. SUITE_SPARSE_QR and EIGEN_SPARSE_QR
|
||||
// 2. SPARSE_QR
|
||||
//
|
||||
// rank(J) < num_col(J)
|
||||
//
|
||||
|
||||
+12
-2
@@ -420,8 +420,18 @@ enum LineSearchInterpolationType {
|
||||
|
||||
enum CovarianceAlgorithmType {
|
||||
DENSE_SVD,
|
||||
SUITE_SPARSE_QR,
|
||||
EIGEN_SPARSE_QR
|
||||
SPARSE_QR,
|
||||
|
||||
// TODO(sameeragarwal): Expand this to include
|
||||
// DENSE_QR (Eigen)
|
||||
// DENSE_SCHUR_SVD (Eigen)
|
||||
// SPARSE_SCHUR_LU (Eigen, SuiteSparse)
|
||||
//
|
||||
// Are the following two a good idea? given that they are not rank
|
||||
// revealing.
|
||||
//
|
||||
// DENSE_SCHUR_CHOLESKY (Eigen)
|
||||
// SPARSE_SCHUR_CHOLESKY (Eigen, SuiteSparse)
|
||||
};
|
||||
|
||||
// It is a near impossibility that user code generates this exact
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define CERES_PUBLIC_VERSION_H_
|
||||
|
||||
#define CERES_VERSION_MAJOR 1
|
||||
#define CERES_VERSION_MINOR 12
|
||||
#define CERES_VERSION_MINOR 13
|
||||
#define CERES_VERSION_REVISION 0
|
||||
|
||||
// Classic CPP stringifcation; the extra level of indirection allows the
|
||||
|
||||
@@ -538,24 +538,37 @@ bool CovarianceImpl::ComputeCovarianceSparsity(
|
||||
}
|
||||
|
||||
bool CovarianceImpl::ComputeCovarianceValues() {
|
||||
switch (options_.algorithm_type) {
|
||||
case DENSE_SVD:
|
||||
return ComputeCovarianceValuesUsingDenseSVD();
|
||||
case SUITE_SPARSE_QR:
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
if (options_.algorithm_type == DENSE_SVD) {
|
||||
return ComputeCovarianceValuesUsingDenseSVD();
|
||||
}
|
||||
|
||||
if (options_.algorithm_type == SPARSE_QR) {
|
||||
if (options_.sparse_linear_algebra_library_type == EIGEN_SPARSE) {
|
||||
return ComputeCovarianceValuesUsingEigenSparseQR();
|
||||
}
|
||||
|
||||
if (options_.sparse_linear_algebra_library_type == SUITE_SPARSE) {
|
||||
#if !defined(CERES_NO_SUITESPARSE)
|
||||
return ComputeCovarianceValuesUsingSuiteSparseQR();
|
||||
#else
|
||||
LOG(ERROR) << "SuiteSparse is required to use the "
|
||||
<< "SUITE_SPARSE_QR algorithm.";
|
||||
LOG(ERROR) << "SuiteSparse is required to use the SPARSE_QR algorithm "
|
||||
<< "with "
|
||||
<< "Covariance::Options::sparse_linear_algebra_library_type "
|
||||
<< "= SUITE_SPARSE.";
|
||||
return false;
|
||||
#endif
|
||||
case EIGEN_SPARSE_QR:
|
||||
return ComputeCovarianceValuesUsingEigenSparseQR();
|
||||
default:
|
||||
LOG(ERROR) << "Unsupported covariance estimation algorithm type: "
|
||||
<< CovarianceAlgorithmTypeToString(options_.algorithm_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Unsupported "
|
||||
<< "Covariance::Options::sparse_linear_algebra_library_type "
|
||||
<< "= "
|
||||
<< SparseLinearAlgebraLibraryTypeToString(
|
||||
options_.sparse_linear_algebra_library_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Unsupported Covariance::Options::algorithm_type = "
|
||||
<< CovarianceAlgorithmTypeToString(options_.algorithm_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -629,14 +629,16 @@ TEST_F(CovarianceTest, NormalBehavior) {
|
||||
Covariance::Options options;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -677,14 +679,16 @@ TEST_F(CovarianceTest, ThreadedNormalBehavior) {
|
||||
options.num_threads = 4;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -726,14 +730,16 @@ TEST_F(CovarianceTest, ConstantParameterBlock) {
|
||||
Covariance::Options options;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -781,14 +787,16 @@ TEST_F(CovarianceTest, LocalParameterization) {
|
||||
Covariance::Options options;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -839,14 +847,17 @@ TEST_F(CovarianceTest, LocalParameterizationInTangentSpace) {
|
||||
Covariance::Options options;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -899,14 +910,17 @@ TEST_F(CovarianceTest, LocalParameterizationInTangentSpaceWithConstantBlocks) {
|
||||
Covariance::Options options;
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -978,14 +992,16 @@ TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParameters) {
|
||||
covariance.GetCovarianceMatrix(parameter_blocks, expected_covariance);
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -1005,14 +1021,16 @@ TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParametersThreaded) {
|
||||
covariance.GetCovarianceMatrix(parameter_blocks, expected_covariance);
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocks(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -1043,14 +1061,17 @@ TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParametersInTangentSpace) {
|
||||
expected_covariance);
|
||||
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
options.algorithm_type = SUITE_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = SUITE_SPARSE;
|
||||
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
#endif
|
||||
|
||||
options.algorithm_type = DENSE_SVD;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
|
||||
options.algorithm_type = EIGEN_SPARSE_QR;
|
||||
options.algorithm_type = SPARSE_QR;
|
||||
options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
|
||||
ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
|
||||
}
|
||||
|
||||
@@ -1197,10 +1218,14 @@ class LargeScaleCovarianceTest : public ::testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeAndCompare(CovarianceAlgorithmType algorithm_type,
|
||||
int num_threads) {
|
||||
void ComputeAndCompare(
|
||||
CovarianceAlgorithmType algorithm_type,
|
||||
SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
|
||||
int num_threads) {
|
||||
Covariance::Options options;
|
||||
options.algorithm_type = algorithm_type;
|
||||
options.sparse_linear_algebra_library_type =
|
||||
sparse_linear_algebra_library_type;
|
||||
options.num_threads = num_threads;
|
||||
Covariance covariance(options);
|
||||
EXPECT_TRUE(covariance.Compute(all_covariance_blocks_, &problem_));
|
||||
@@ -1243,7 +1268,7 @@ class LargeScaleCovarianceTest : public ::testing::Test {
|
||||
#if !defined(CERES_NO_SUITESPARSE) && defined(CERES_USE_OPENMP)
|
||||
|
||||
TEST_F(LargeScaleCovarianceTest, Parallel) {
|
||||
ComputeAndCompare(SUITE_SPARSE_QR, 4);
|
||||
ComputeAndCompare(SPARSE_QR, SUITE_SPARSE, 4);
|
||||
}
|
||||
|
||||
#endif // !defined(CERES_NO_SUITESPARSE) && defined(CERES_USE_OPENMP)
|
||||
|
||||
@@ -267,8 +267,7 @@ const char* CovarianceAlgorithmTypeToString(
|
||||
CovarianceAlgorithmType type) {
|
||||
switch (type) {
|
||||
CASESTR(DENSE_SVD);
|
||||
CASESTR(EIGEN_SPARSE_QR);
|
||||
CASESTR(SUITE_SPARSE_QR);
|
||||
CASESTR(SPARSE_QR);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -279,8 +278,7 @@ bool StringToCovarianceAlgorithmType(
|
||||
CovarianceAlgorithmType* type) {
|
||||
UpperCase(&value);
|
||||
STRENUM(DENSE_SVD);
|
||||
STRENUM(EIGEN_SPARSE_QR);
|
||||
STRENUM(SUITE_SPARSE_QR);
|
||||
STRENUM(SPARSE_QR);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user