6#include <tinyopt/cost.h>
7#include <tinyopt/solvers/base.h>
8#include <tinyopt/traits.h>
10namespace tinyopt::solvers {
12template <
typename Gradient_t = VecX>
14 :
public SolverBase<typename Gradient_t::Scalar, traits::params_trait<Gradient_t>::Dims> {
16 static constexpr bool IsNLLS =
false;
17 static constexpr bool FirstOrder =
true;
18 using Base = SolverBase<typename Gradient_t::Scalar, traits::params_trait<Gradient_t>::Dims>;
19 using Scalar =
typename Gradient_t::Scalar;
20 static constexpr Index Dims = traits::params_trait<Gradient_t>::Dims;
22 using Grad_t = Gradient_t;
24 using H_t = std::nullptr_t;
28 explicit SolverGD(
const Options &options = {}) : Base(options) {}
37 template <
int D = Dims, std::enable_if_t<D == Dynamic,
int> = 0>
39 if (dims == Dynamic) {
40 throw std::invalid_argument(
"Dimensions cannot be Dynamic here");
42 if (grad_.rows() != dims) {
51 template <
int D = Dims, std::enable_if_t<D != Dynamic,
int> = 0>
54 throw std::invalid_argument(
"Error: Static and Dynamic Dimensions must match");
56 if constexpr (traits::is_sparse_matrix_v<Grad_t>) {
64 void clear() { grad_.setZero(); }
67 template <
typename X_t>
69 if constexpr (Dims == Dynamic) {
70 const int dims = traits::params_trait<X_t>::dims(x);
71 if (grad_.rows() != dims) {
72 if (options_.log.enable) TINYOPT_LOG(
"Need to resize the system");
80 template <
typename AccFunc>
82 return [&](
const auto &x,
auto &grad) ->
Cost {
83 using X_t =
decltype(x);
84 if constexpr (std::is_invocable_v<AccFunc, const X_t &, std::nullptr_t &>)
88 return acc(x, grad, nul);
94 template <
typename X_t,
typename AccFunc>
95 inline Scalar
Evaluate(
const X_t &x,
const AccFunc &acc,
bool save) {
98 const auto &cost = acc2(x, nul);
100 if (save) this->cost_ = cost;
105 template <
typename X_t,
typename AccFunc>
108 this->cost_ = acc2(x, grad_);
110 return this->cost_.isValid();
115 template <
typename X_t,
typename AccFunc>
116 inline bool Build(
const X_t &x,
const AccFunc &acc,
bool resize_and_clear =
true) {
118 if (resize_and_clear) {
125 this->
Clamp(grad_, options_.opt.grad_clipping);
130 inline std::optional<Vector<Scalar, Dims>>
Solve()
const override {
131 if (!this->cost().isValid())
return std::nullopt;
132 return -options_.gd.lr * grad_;
135 Index dims()
const override {
return grad_.size(); }
137 const Grad_t &Gradient()
const {
return grad_; }
138 Grad_t &Gradient() {
return grad_; }
139 Scalar GradientNorm()
const {
return grad_.norm(); }
140 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 gd.h:106
auto GetAccFunc(const AccFunc &acc) const
Ugly function to make sure the returned type is always a pair<scalar, scalar>...
Definition gd.h:81
std::optional< Vector< Scalar, Dims > > Solve() const override
Solve the linear system dx = -lr * grad, returns nullopt on failure.
Definition gd.h:130
bool Build(const X_t &x, const AccFunc &acc, bool resize_and_clear=true)
Definition gd.h:116
void reset()
Reset the solver state and clear gradient & hessian.
Definition gd.h:34
Scalar Evaluate(const X_t &x, const AccFunc &acc, bool save)
Accumulate residuals and return the final error.
Definition gd.h:95
bool resize(int dims=Dims)
Resize H and grad if needed, return true if they were resized.
Definition gd.h:52
bool ResizeIfNeeded(const X_t &x)
Check whether we need to resize the system (gradient), return true if it did.
Definition gd.h:68
void InitWith(const Grad_t &g)
Initialize solver with specific gradient and hessian.
Definition gd.h:31
void clear()
Definition gd.h:64
bool resize(int dims)
Resize H and grad if needed, return true if they were resized.
Definition gd.h:38