16class SolverBFGS :
public SolverGD<Gradient_t> {
18 using Base = SolverGD<Gradient_t>;
19 using Scalar =
typename Base::Scalar;
20 using Grad_t =
typename Base::Grad_t;
21 using Matrix_t = Matrix<Scalar, Base::Dims, Base::Dims>;
22 static constexpr Index Dims = Base::Dims;
23 static constexpr bool IsNLLS =
false;
24 static constexpr bool FirstOrder =
true;
26 explicit SolverBFGS(
const Options &options = {}) : Base(options), step_size_(options.bfgs.step_size) {}
30 step_size_ = this->options_.bfgs.step_size;
31 matrix_initialized_ =
false;
32 has_gradient_ =
false;
34 update_available_ =
false;
37 template <
typename X_t,
typename AccFunc>
38 bool Build(
const X_t &x,
const AccFunc &acc,
bool resize_and_clear =
true) {
40 InitializeWorkspace();
42 const bool can_update = has_gradient_ && has_step_;
44 update_step_ = last_step_;
45 update_previous_gradient_ = this->grad_;
49 has_gradient_ =
false;
50 update_available_ =
false;
54 update_available_ = can_update;
55 if (can_update) update_gradient_delta_ = this->grad_ - update_previous_gradient_;
61 std::optional<Vector<Scalar, Dims>>
Solve()
const override {
62 if (!this->cost().isValid())
return std::nullopt;
64 Vector<Scalar, Dims> direction = -(inverse_hessian_ * this->grad_).eval();
65 if (!direction.allFinite() || this->grad_.dot(direction) >= 0) {
66 inverse_hessian_.setIdentity();
67 direction = -this->grad_;
70 last_step_ = step_size_ * direction;
75 void GoodStep(Scalar = 0)
override {
76 if (update_available_) {
77 const Scalar curvature = update_step_.dot(update_gradient_delta_);
78 const Scalar minimum_curvature = this->options_.bfgs.curvature_threshold *
79 update_step_.norm() * update_gradient_delta_.norm();
80 if (std::isfinite(curvature) && curvature > minimum_curvature) {
81 const Scalar rho = Scalar(1) / curvature;
82 Matrix_t transform = Matrix_t::Identity(inverse_hessian_.rows(), inverse_hessian_.cols());
83 transform.noalias() -= rho * update_step_ * update_gradient_delta_.transpose();
84 Matrix_t updated = (transform * inverse_hessian_ * transform.transpose()).eval();
85 updated.noalias() += rho * update_step_ * update_step_.transpose();
86 inverse_hessian_.swap(updated);
89 update_available_ =
false;
90 step_size_ = std::min<Scalar>(this->options_.bfgs.max_step_size,
91 step_size_ * this->options_.bfgs.step_growth);
94 void BadStep(Scalar = 0)
override {
95 step_size_ *= this->options_.bfgs.step_reduction;
96 has_gradient_ =
false;
98 update_available_ =
false;
102 void InitializeWorkspace() {
103 const Index dims = this->grad_.size();
104 if (!matrix_initialized_ || inverse_hessian_.rows() != dims) {
105 inverse_hessian_.resize(dims, dims);
106 inverse_hessian_.setIdentity();
107 update_previous_gradient_.resize(dims);
108 update_step_.resize(dims);
109 update_gradient_delta_.resize(dims);
110 last_step_.resize(dims);
111 matrix_initialized_ =
true;
112 has_gradient_ =
false;
117 mutable Matrix_t inverse_hessian_;
118 Vector<Scalar, Dims> update_previous_gradient_;
119 Vector<Scalar, Dims> update_step_;
120 Vector<Scalar, Dims> update_gradient_delta_;
121 mutable Vector<Scalar, Dims> last_step_;
123 bool matrix_initialized_ =
false;
124 bool has_gradient_ =
false;
125 mutable bool has_step_ =
false;
126 bool update_available_ =
false;
130class SolverLBFGS :
public SolverGD<Gradient_t> {
132 using Base = SolverGD<Gradient_t>;
133 using Scalar =
typename Base::Scalar;
134 using Grad_t =
typename Base::Grad_t;
135 static constexpr Index Dims = Base::Dims;
136 static constexpr bool IsNLLS =
false;
137 static constexpr bool FirstOrder =
true;
139 explicit SolverLBFGS(
const Options &options = {})
141 step_size_(options.lbfgs.step_size),
142 history_limit_(std::clamp<std::size_t>(options.lbfgs.history_size, 1, HistoryCapacity)) {}
146 step_size_ = this->options_.lbfgs.step_size;
149 has_gradient_ =
false;
151 update_available_ =
false;
154 template <
typename X_t,
typename AccFunc>
155 bool Build(
const X_t &x,
const AccFunc &acc,
bool resize_and_clear =
true) {
159 const bool can_update = has_gradient_ && has_step_;
161 update_step_ = last_step_;
162 update_previous_gradient_ = this->grad_;
166 has_gradient_ =
false;
167 update_available_ =
false;
171 update_available_ = can_update;
172 if (can_update) update_gradient_delta_ = this->grad_ - update_previous_gradient_;
173 has_gradient_ =
true;
178 std::optional<Vector<Scalar, Dims>>
Solve()
const override {
179 if (!this->cost().isValid())
return std::nullopt;
181 Vector<Scalar, Dims> q = this->grad_;
182 for (std::size_t offset = 0; offset < history_count_; ++offset) {
183 const std::size_t index = HistoryIndex(history_count_ - offset - 1);
184 alpha_[index] = rho_[index] * step_history_[index].dot(q);
185 q.noalias() -= alpha_[index] * gradient_history_[index];
189 if (history_count_ > 0) {
190 const std::size_t newest = HistoryIndex(history_count_ - 1);
191 const Scalar yy = gradient_history_[newest].squaredNorm();
192 if (yy > std::numeric_limits<Scalar>::epsilon())
193 scale = step_history_[newest].dot(gradient_history_[newest]) / yy;
195 Vector<Scalar, Dims> direction = -scale * q;
197 for (std::size_t offset = 0; offset < history_count_; ++offset) {
198 const std::size_t index = HistoryIndex(offset);
199 const Scalar beta = rho_[index] * gradient_history_[index].dot(direction);
200 direction.noalias() += (alpha_[index] - beta) * step_history_[index];
203 if (!direction.allFinite() || this->grad_.dot(direction) >= 0) {
206 direction = -this->grad_;
208 last_step_ = step_size_ * direction;
213 void GoodStep(Scalar = 0)
override {
214 if (update_available_) {
215 const Scalar curvature = update_step_.dot(update_gradient_delta_);
216 const Scalar minimum_curvature = this->options_.lbfgs.curvature_threshold *
217 update_step_.norm() * update_gradient_delta_.norm();
218 if (std::isfinite(curvature) && curvature > minimum_curvature) StorePair(curvature);
220 update_available_ =
false;
221 step_size_ = std::min<Scalar>(this->options_.lbfgs.max_step_size,
222 step_size_ * this->options_.lbfgs.step_growth);
225 void BadStep(Scalar = 0)
override {
226 step_size_ *= this->options_.lbfgs.step_reduction;
227 has_gradient_ =
false;
229 update_available_ =
false;
233 std::size_t HistoryIndex(std::size_t offset)
const {
234 return (history_start_ + offset) % HistoryCapacity;
237 void ResizeWorkspace() {
238 const Index dims = this->grad_.size();
239 if (workspace_dims_ == dims)
return;
240 for (
auto &step : step_history_) step.resize(dims);
241 for (
auto &gradient : gradient_history_) gradient.resize(dims);
242 update_previous_gradient_.resize(dims);
243 update_step_.resize(dims);
244 update_gradient_delta_.resize(dims);
245 last_step_.resize(dims);
246 workspace_dims_ = dims;
249 has_gradient_ =
false;
253 void StorePair(Scalar curvature) {
255 if (history_count_ < history_limit_) {
256 index = HistoryIndex(history_count_);
259 index = history_start_;
260 history_start_ = (history_start_ + 1) % HistoryCapacity;
262 step_history_[index] = update_step_;
263 gradient_history_[index] = update_gradient_delta_;
264 rho_[index] = Scalar(1) / curvature;
267 std::array<Vector<Scalar, Dims>, HistoryCapacity> step_history_;
268 std::array<Vector<Scalar, Dims>, HistoryCapacity> gradient_history_;
269 mutable std::array<Scalar, HistoryCapacity> alpha_{};
270 std::array<Scalar, HistoryCapacity> rho_{};
271 Vector<Scalar, Dims> update_previous_gradient_;
272 Vector<Scalar, Dims> update_step_;
273 Vector<Scalar, Dims> update_gradient_delta_;
274 mutable Vector<Scalar, Dims> last_step_;
276 std::size_t history_limit_;
277 mutable std::size_t history_count_ = 0;
278 mutable std::size_t history_start_ = 0;
279 Index workspace_dims_ = 0;
280 bool has_gradient_ =
false;
281 mutable bool has_step_ =
false;
282 bool update_available_ =
false;