tinyopt 700
Tinyopt, a lightweight, header only optimization library
Loading...
Searching...
No Matches
cg.h
1// Copyright 2026 Julien Michot.
2// SPDX-License-Identifier: Apache-2.0
3
4#pragma once
5
6#include <algorithm>
7#include <limits>
8
9#include <tinyopt/solvers/gd.h>
10
11namespace tinyopt::solvers {
12
13template <typename Gradient_t = VecX>
14class SolverCG : public SolverGD<Gradient_t> {
15 public:
16 using Base = SolverGD<Gradient_t>;
17 using Scalar = typename Base::Scalar;
18 using Grad_t = typename Base::Grad_t;
19 static constexpr Index Dims = Base::Dims;
20 static constexpr bool IsNLLS = false;
21 static constexpr bool FirstOrder = true;
22
23 explicit SolverCG(const Options &options = {}) : Base(options), step_size_(options.cg.step_size) {
24 // `Grad_t::Zero()` is invalid for dynamic sizes, so zero-initialize here instead
25 ResetDirection();
26 direction_.setZero();
27 }
28
29 void reset() {
31 ResetDirection();
32 step_size_ = this->options_.cg.step_size;
33 }
34
35 template <typename X_t, typename AccFunc>
36 bool Build(const X_t &x, const AccFunc &acc, bool resize_and_clear = true) {
37 if (!Base::Build(x, acc, resize_and_clear)) return false;
38 ResizeWorkspace();
39
40 Scalar beta = 0;
41 if (has_previous_) {
42 const Scalar denominator = previous_gradient_.squaredNorm();
43 if (denominator > std::numeric_limits<Scalar>::epsilon()) {
44 beta = std::max<Scalar>(0, this->grad_.dot(this->grad_ - previous_gradient_) / denominator);
45 }
46 }
47
48 direction_ = -this->grad_ + beta * previous_direction_;
49 if (this->grad_.dot(direction_) >= 0) direction_ = -this->grad_;
50 previous_gradient_ = this->grad_;
51 previous_direction_ = direction_;
52 has_previous_ = true;
53 return true;
54 }
55
56 std::optional<Vector<Scalar, Dims>> Solve() const override {
57 if (!this->cost().isValid()) return std::nullopt;
58 return (step_size_ * direction_).eval();
59 }
60
61 void BadStep(Scalar /*quality*/ = 0) override {
62 step_size_ *= this->options_.cg.step_reduction;
63 ResetDirection();
64 }
65
66 private:
67 void ResizeWorkspace() {
68 if (previous_gradient_.size() == this->grad_.size()) return;
69 previous_gradient_.resize(this->grad_.size());
70 previous_direction_.resize(this->grad_.size());
71 previous_gradient_.setZero();
72 previous_direction_.setZero();
73 has_previous_ = false;
74 }
75
76 void ResetDirection() {
77 previous_gradient_.setZero();
78 previous_direction_.setZero();
79 has_previous_ = false;
80 }
81
82 Grad_t previous_gradient_;
83 Grad_t previous_direction_;
84 Grad_t direction_;
85 Scalar step_size_;
86 bool has_previous_ = false;
87};
88
89} // namespace tinyopt::solvers
std::optional< Vector< Scalar, Dims > > Solve() const override
Solve the linear system dx = -H^-1 * grad, returns nullopt on failure.
Definition cg.h:56
bool Build(const X_t &x, const AccFunc &acc, bool resize_and_clear=true)
Definition gd.h:116
Definition options.h:18