6#include <tinyopt/cost.h>
7#include <tinyopt/solvers/base.h>
8#include <tinyopt/traits.h>
10#include "tinyopt/math.h"
12namespace tinyopt::solvers {
14template <
typename Hessian_t = MatX>
16 :
public SolverBase<typename Hessian_t::Scalar, SQRT(traits::params_trait<Hessian_t>::Dims)> {
18 static constexpr bool IsNLLS =
true;
19 static constexpr bool FirstOrder =
false;
20 using Base = SolverBase<
typename Hessian_t::Scalar, SQRT(traits::params_trait<Hessian_t>::Dims)>;
21 using Scalar =
typename Hessian_t::Scalar;
22 static constexpr Index Dims = SQRT(traits::params_trait<Hessian_t>::Dims);
25 using H_t = Hessian_t;
27 using Grad_t = Vector<Scalar, Dims>;
31 explicit SolverGN(
const Options &options = {}) : Base(options) {}
43 template <
int D = Dims, std::enable_if_t<D == Dynamic,
int> = 0>
45 if (dims == Dynamic) {
46 throw std::invalid_argument(
"Dimensions cannot be Dynamic here");
48 if (grad_.rows() != dims || H_.rows() != dims) {
49 H_.resize(dims, dims);
58 template <
int D = Dims, std::enable_if_t<D != Dynamic,
int> = 0>
61 throw std::invalid_argument(
"Error: Static and Dynamic Dimensions must match");
63 if constexpr (traits::is_sparse_matrix_v<H_t>) {
64 H_.resize(dims, dims);
79 template <
typename X_t>
81 if constexpr (Dims == Dynamic) {
82 const int dims = traits::params_trait<X_t>::dims(x);
83 if (grad_.rows() != dims) {
84 if (options_.log.enable) TINYOPT_LOG(
"Need to resize the system");
92 template <
typename X_t,
typename AccFunc>
93 inline Scalar
Evaluate(
const X_t &x,
const AccFunc &acc,
bool save) {
96 Cost cost = acc(x, nul, H);
98 if (save) this->cost_ = cost;
103 template <
typename X_t,
typename AccFunc>
105 this->cost_ = acc(x, grad_, H_);
107 return this->cost_.isValid();
112 template <
typename X_t,
typename AccFunc>
113 inline bool Build(
const X_t &x,
const AccFunc &acc_func,
bool resize_and_clear =
true) {
115 if (resize_and_clear) {
122 if (!success)
return false;
125 this->
Clamp(grad_, options_.opt.grad_clipping);
128 if (options_.hessian.check_min_H_diag > 0 &&
129 (H_.diagonal().cwiseAbs().array() < options_.hessian.check_min_H_diag).any()) {
130 if (options_.log.enable) TINYOPT_LOG(
"❌ Hessian has very low diagonal coefficients");
136 if (!options_.hessian.H_is_full && RequiresFullMatrix(options_.linear_solver))
137 CompleteSymmetricMatrix(H_);
139#if defined(TINYOPT_ENABLE_SUITESPARSE)
140 if constexpr (traits::is_sparse_matrix_v<H_t>) {
141 if (options_.linear_solver == LinearSolverMethod::SuiteSparse) H_.makeCompressed();
148 inline std::optional<Vector<Scalar, Dims>>
Solve()
const override {
149 if (!this->cost().isValid())
return std::nullopt;
151 const auto dx_ = tinyopt::SolveLinearSystem(H_, -grad_, options_.linear_solver,
152 options_.svd_relative_threshold);
155 if (options_.log.enable && options_.log.print_failure) {
156 TINYOPT_LOG(
"❌ Failed solve linear system");
157 TINYOPT_LOG(
"grad = \n{}", grad_);
158 TINYOPT_LOG(
"H = \n{}", H_);
163 virtual Index dims()
const override {
return grad_.size(); }
168 const auto &H = use_damped ? H_ :
Hessian();
169 if constexpr (traits::is_sparse_matrix_v<H_t>)
170 return sqrt(InvCov(H).value().coeffs().maxCoeff());
172 return sqrt(InvCov(H).value().maxCoeff());
176 virtual std::optional<H_t>
Covariance()
const {
return InvCov(H_); }
181 const H_t &H()
const {
return H_; }
182 H_t &H() {
return H_; }
184 const Grad_t &Gradient()
const {
return grad_; }
185 Grad_t &Gradient() {
return grad_; }
186 Scalar GradientNorm()
const {
return grad_.norm(); }
187 Scalar GradientSquaredNorm()
const {
return grad_.squaredNorm(); }
void NormalizeCost(Cost &cost)
Eventually normalize the cost.
Definition base.h:41
bool Clamp(Grad_t &g, Scalar minmax) const
Clamp the gradient 'g' to within [-minmax, minmax], if minmax is not 0. Returns true if 'g' was clamp...
Definition base.h:30
bool Accumulate(const X_t &x, const AccFunc &acc)
Accumulate residuals and update the gradient, returns true on success.
Definition gn.h:104
Scalar Evaluate(const X_t &x, const AccFunc &acc, bool save)
Accumulate residuals and return the final error.
Definition gn.h:93
bool ResizeIfNeeded(const X_t &x)
Check whether we need to resize the system (gradient), return true if it did.
Definition gn.h:80
virtual void reset()
Reset the solver state and clear gradient & hessian.
Definition gn.h:40
void clear()
Definition gn.h:72
virtual std::optional< H_t > Covariance() const
Latest Covariance estimate.
Definition gn.h:176
void InitWith(const Grad_t &g, const H_t &h)
Initialize solver with specific gradient and hessian.
Definition gn.h:34
bool resize(int dims=Dims)
Resize H and grad if needed, return true if they were resized.
Definition gn.h:59
bool resize(int dims)
Resize H and grad if needed, return true if they were resized.
Definition gn.h:44
bool Build(const X_t &x, const AccFunc &acc_func, bool resize_and_clear=true)
Definition gn.h:113
Scalar MaxStdDev(bool use_damped=true) const
Definition gn.h:167
std::optional< Vector< Scalar, Dims > > Solve() const override
Solve the linear system dx = -H^-1 * grad, returns nullopt on failure.
Definition gn.h:148
const H_t & Hessian() const
Definition gn.h:179