// -*- C++ -*-
/*
Copyright (c) 2008 University of North Carolina at Chapel Hill
This file is part of SSBA (Simple Sparse Bundle Adjustment).
SSBA is free software: you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
SSBA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along
with SSBA. If not, see .
*/
#ifndef V3D_OPTIMIZATION_H
#define V3D_OPTIMIZATION_H
#include "Math/v3d_linear.h"
#include "Math/v3d_mathutilities.h"
#include
#include
namespace V3D
{
enum
{
LEVENBERG_OPTIMIZER_TIMEOUT = 0,
LEVENBERG_OPTIMIZER_SMALL_UPDATE = 1,
LEVENBERG_OPTIMIZER_CONVERGED = 2
};
extern int optimizerVerbosenessLevel;
struct LevenbergOptimizerCommon
{
LevenbergOptimizerCommon()
: status(LEVENBERG_OPTIMIZER_TIMEOUT), currentIteration(0), maxIterations(50),
tau(1e-3), lambda(1e-3),
gradientThreshold(1e-10), updateThreshold(1e-10),
_nu(2.0)
{ }
virtual ~LevenbergOptimizerCommon() {}
// See Madsen et al., "Methods for non-linear least squares problems."
virtual void increaseLambda()
{
lambda *= _nu; _nu *= 2.0;
}
virtual void decreaseLambda(double const rho)
{
double const r = 2*rho - 1.0;
lambda *= std::max(1.0/3.0, 1 - r*r*r);
if (lambda < 1e-10) lambda = 1e-10;
_nu = 2;
}
bool applyGradientStoppingCriteria(double maxGradient) const
{
return maxGradient < gradientThreshold;
}
bool applyUpdateStoppingCriteria(double paramLength, double updateLength) const
{
return updateLength < updateThreshold * (paramLength + updateThreshold);
}
int status;
int currentIteration, maxIterations;
double tau, lambda;
double gradientThreshold, updateThreshold;
protected:
double _nu;
}; // end struct LevenbergOptimizerCommon
# if defined(V3DLIB_ENABLE_SUITESPARSE)
struct SparseLevenbergOptimizer : public LevenbergOptimizerCommon
{
SparseLevenbergOptimizer(int measurementDimension,
int nParametersA, int paramDimensionA,
int nParametersB, int paramDimensionB,
int paramDimensionC,
std::vector const& correspondingParamA,
std::vector const& correspondingParamB)
: LevenbergOptimizerCommon(),
_nMeasurements(correspondingParamA.size()),
_measurementDimension(measurementDimension),
_nParametersA(nParametersA), _paramDimensionA(paramDimensionA),
_nParametersB(nParametersB), _paramDimensionB(paramDimensionB),
_paramDimensionC(paramDimensionC),
_nNonvaryingA(0), _nNonvaryingB(0), _nNonvaryingC(0),
_correspondingParamA(correspondingParamA),
_correspondingParamB(correspondingParamB)
{
assert(correspondingParamA.size() == correspondingParamB.size());
}
~SparseLevenbergOptimizer() { }
void setNonvaryingCounts(int nNonvaryingA, int nNonvaryingB, int nNonvaryingC)
{
_nNonvaryingA = nNonvaryingA;
_nNonvaryingB = nNonvaryingB;
_nNonvaryingC = nNonvaryingC;
}
void getNonvaryingCounts(int& nNonvaryingA, int& nNonvaryingB, int& nNonvaryingC) const
{
nNonvaryingA = _nNonvaryingA;
nNonvaryingB = _nNonvaryingB;
nNonvaryingC = _nNonvaryingC;
}
void minimize();
virtual void evalResidual(VectorArray& residuals) = 0;
virtual void fillWeights(VectorArray const& residuals, Vector& w)
{
(void)residuals;
std::fill(w.begin(), w.end(), 1.0);
}
void fillAllJacobians(Vector const& w,
MatrixArray& Ak,
MatrixArray& Bk,
MatrixArray& Ck)
{
int const nVaryingA = _nParametersA - _nNonvaryingA;
int const nVaryingB = _nParametersB - _nNonvaryingB;
int const nVaryingC = _paramDimensionC - _nNonvaryingC;
for (unsigned k = 0; k < _nMeasurements; ++k)
{
int const i = _correspondingParamA[k];
int const j = _correspondingParamB[k];
if (i < _nNonvaryingA && j < _nNonvaryingB) continue;
fillJacobians(Ak[k], Bk[k], Ck[k], i, j, k);
} // end for (k)
if (nVaryingA > 0)
{
for (unsigned k = 0; k < _nMeasurements; ++k)
scaleMatrixIP(w[k], Ak[k]);
}
if (nVaryingB > 0)
{
for (unsigned k = 0; k < _nMeasurements; ++k)
scaleMatrixIP(w[k], Bk[k]);
}
if (nVaryingC > 0)
{
for (unsigned k = 0; k < _nMeasurements; ++k)
scaleMatrixIP(w[k], Ck[k]);
}
} // end fillAllJacobians()
virtual void setupJacobianGathering() { }
virtual void fillJacobians(Matrix& Ak, Matrix& Bk, Matrix& Ck,
int i, int j, int k) = 0;
virtual double getParameterLength() const = 0;
virtual void updateParametersA(VectorArray const& deltaAi) = 0;
virtual void updateParametersB(VectorArray const& deltaBj) = 0;
virtual void updateParametersC(Vector const& deltaC) = 0;
virtual void saveAllParameters() = 0;
virtual void restoreAllParameters() = 0;
int currentIteration, maxIterations;
protected:
void serializeNonZerosJtJ(std::vector >& dst) const;
void setupSparseJtJ();
void fillSparseJtJ(MatrixArray const& Ui, MatrixArray const& Vj, MatrixArray const& Wk,
Matrix const& Z, Matrix const& X, Matrix const& Y);
int const _nMeasurements, _measurementDimension;
int const _nParametersA, _paramDimensionA;
int const _nParametersB, _paramDimensionB;
int const _paramDimensionC;
int _nNonvaryingA, _nNonvaryingB, _nNonvaryingC;
std::vector const& _correspondingParamA;
std::vector const& _correspondingParamB;
std::vector > _jointNonzerosW;
std::vector _jointIndexW;
std::vector _JtJ_Lp, _JtJ_Parent, _JtJ_Lnz;
std::vector _perm_JtJ, _invPerm_JtJ;
CCS_Matrix _JtJ;
}; // end struct SparseLevenbergOptimizer
struct StdSparseLevenbergOptimizer : public SparseLevenbergOptimizer
{
StdSparseLevenbergOptimizer(int measurementDimension,
int nParametersA, int paramDimensionA,
int nParametersB, int paramDimensionB,
int paramDimensionC,
std::vector const& correspondingParamA,
std::vector const& correspondingParamB)
: SparseLevenbergOptimizer(measurementDimension, nParametersA, paramDimensionA,
nParametersB, paramDimensionB, paramDimensionC,
correspondingParamA, correspondingParamB),
curParametersA(nParametersA, paramDimensionA), savedParametersA(nParametersA, paramDimensionA),
curParametersB(nParametersB, paramDimensionB), savedParametersB(nParametersB, paramDimensionB),
curParametersC(paramDimensionC), savedParametersC(paramDimensionC)
{ }
virtual double getParameterLength() const
{
double res = 0.0;
for (int i = 0; i < _nParametersA; ++i) res += sqrNorm_L2(curParametersA[i]);
for (int j = 0; j < _nParametersB; ++j) res += sqrNorm_L2(curParametersB[j]);
res += sqrNorm_L2(curParametersC);
return sqrt(res);
}
virtual void updateParametersA(VectorArray const& deltaAi)
{
for (int i = 0; i < _nParametersA; ++i) addVectors(deltaAi[i], curParametersA[i], curParametersA[i]);
}
virtual void updateParametersB(VectorArray const& deltaBj)
{
for (int j = 0; j < _nParametersB; ++j) addVectors(deltaBj[j], curParametersB[j], curParametersB[j]);
}
virtual void updateParametersC(Vector const& deltaC)
{
addVectors(deltaC, curParametersC, curParametersC);
}
virtual void saveAllParameters()
{
for (int i = 0; i < _nParametersA; ++i) savedParametersA[i] = curParametersA[i];
for (int j = 0; j < _nParametersB; ++j) savedParametersB[j] = curParametersB[j];
savedParametersC = curParametersC;
}
virtual void restoreAllParameters()
{
for (int i = 0; i < _nParametersA; ++i) curParametersA[i] = savedParametersA[i];
for (int j = 0; j < _nParametersB; ++j) curParametersB[j] = savedParametersB[j];
curParametersC = savedParametersC;
}
VectorArray curParametersA, savedParametersA;
VectorArray curParametersB, savedParametersB;
Vector curParametersC, savedParametersC;
}; // end struct StdSparseLevenbergOptimizer
# endif
} // end namespace V3D
#endif