add blts
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "autoopt/optimization_problem.hpp"
|
||||
|
||||
namespace autoopt {
|
||||
|
||||
template <typename T>
|
||||
struct btls_parameters {
|
||||
T step_decrease = T{0.5};
|
||||
T step_increase = T{1.5};
|
||||
T sufficient_decrease = T{1e-2};
|
||||
T tolerance = T{1e-9};
|
||||
size_t max_iters = 1000;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void btls(optimization_problem<T>& problem,
|
||||
const btls_parameters<T>& params = btls_parameters<T>()) {
|
||||
Eigen::VectorX<T>& x = problem.x();
|
||||
T step_size = T{1.0};
|
||||
|
||||
for (size_t iter = 0; iter < params.max_iters; ++iter) {
|
||||
T obj_value = problem.objective(x);
|
||||
|
||||
std::cout << "Iter " << iter << ": obj = " << obj_value
|
||||
<< ", x = " << x.transpose() << ", step_size = " << step_size
|
||||
<< std::endl;
|
||||
|
||||
Eigen::VectorX<T> grad = -problem.gradient(x);
|
||||
|
||||
Eigen::VectorX<T> step_dir = grad.normalized();
|
||||
while (problem.objective(x + step_size * step_dir) >
|
||||
obj_value +
|
||||
params.sufficient_decrease * step_size * grad.dot(step_dir)) {
|
||||
step_size *= params.step_decrease;
|
||||
}
|
||||
x += step_size * step_dir;
|
||||
|
||||
if (step_size < params.tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
problem.x() = x;
|
||||
}
|
||||
|
||||
} // namespace autoopt
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
#include "autoopt/dual.hpp"
|
||||
|
||||
@@ -13,49 +13,46 @@ T derivative(Func&& f, const T& x) {
|
||||
return b._dx;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, class Func>
|
||||
std::array<T, N> gradient(Func&& f, const std::array<T, N>& x) {
|
||||
std::array<T, N> grad{};
|
||||
std::array<dual<T>, N> dual_x{};
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
dual_x[i] = dual<T>(x[i], T(0));
|
||||
template <typename T, class Func>
|
||||
Eigen::VectorX<T> gradient(Func&& f, const Eigen::VectorX<T>& x) {
|
||||
Eigen::VectorX<T> grad{x.size()};
|
||||
Eigen::VectorX<dual<T>> dual_x{x.size()};
|
||||
for (int i = 0; i < x.size(); ++i) {
|
||||
dual_x(i) = dual<T>(x(i), T(0));
|
||||
}
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
dual_x[i]._dx = T(1);
|
||||
for (int i = 0; i < x.size(); ++i) {
|
||||
dual_x(i)._dx = T(1);
|
||||
dual<T> dual_y = f(dual_x);
|
||||
grad[i] = dual_y._dx;
|
||||
dual_x[i]._dx = T(0);
|
||||
grad(i) = dual_y._dx;
|
||||
dual_x(i)._dx = T(0);
|
||||
}
|
||||
return grad;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
using matrix_t = std::array<std::array<T, M>, N>;
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M, class Func>
|
||||
matrix_t<T, M, N> jacobian(Func&& f, const std::array<T, N>& x) {
|
||||
matrix_t<T, M, N> jacob{};
|
||||
std::array<dual<T>, N> dual_x{};
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
dual_x[i] = dual<T>(x[i], T(0));
|
||||
template <typename T, class Func>
|
||||
Eigen::MatrixX<T> jacobian(Func&& f, const Eigen::VectorX<T>& x) {
|
||||
Eigen::MatrixX<T> jacob(f(x).size(), x.size());
|
||||
Eigen::VectorX<dual<T>> dual_x(x.size());
|
||||
for (int i = 0; i < x.size(); ++i) {
|
||||
dual_x(i) = dual<T>(x(i), T(0));
|
||||
}
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
dual_x[i]._dx = T(1);
|
||||
std::array<dual<T>, M> dual_y = f(dual_x);
|
||||
for (std::size_t j = 0; j < M; ++j) {
|
||||
jacob[j][i] = dual_y[j]._dx;
|
||||
for (int i = 0; i < x.size(); ++i) {
|
||||
dual_x(i)._dx = T(1);
|
||||
Eigen::VectorX<dual<T>> dual_y = f(dual_x);
|
||||
for (int j = 0; j < dual_y.size(); ++j) {
|
||||
jacob(j, i) = dual_y(j)._dx;
|
||||
}
|
||||
dual_x[i]._dx = T(0);
|
||||
dual_x(i)._dx = T(0);
|
||||
}
|
||||
return jacob;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, class Func>
|
||||
matrix_t<T, N, N> hessian(Func&& f, const std::array<T, N>& x) {
|
||||
auto helper_func = [&f]<typename U>(const std::array<U, N>& y) {
|
||||
return gradient<U, N>(f, y);
|
||||
template <typename T, class Func>
|
||||
Eigen::MatrixX<T> hessian(Func&& f, const Eigen::VectorX<T>& x) {
|
||||
auto helper_func = [&f]<typename U>(const Eigen::VectorX<U>& y) {
|
||||
return gradient<U>(f, y);
|
||||
};
|
||||
return jacobian<T, N, N>(helper_func, x);
|
||||
return jacobian<T>(helper_func, x);
|
||||
}
|
||||
|
||||
} // namespace autoopt
|
||||
@@ -1,108 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "autoopt/derivative.hpp"
|
||||
|
||||
namespace autoopt {
|
||||
|
||||
template <typename T, size_t N>
|
||||
template <typename T>
|
||||
struct optimization_problem {
|
||||
virtual std::array<T, N> initial_guess() = 0;
|
||||
virtual Eigen::VectorX<T>& initial_guess() = 0;
|
||||
virtual Eigen::VectorX<T>& x() = 0;
|
||||
|
||||
virtual T objective(const std::array<T, N>& params) = 0;
|
||||
virtual std::array<T, N> gradient(const std::array<T, N>& params) = 0;
|
||||
virtual matrix_t<T, N, N> hessian(const std::array<T, N>& params) = 0;
|
||||
virtual T objective(const Eigen::VectorX<T>& params) = 0;
|
||||
virtual Eigen::VectorX<T> gradient(const Eigen::VectorX<T>& params) = 0;
|
||||
virtual Eigen::MatrixX<T> hessian(const Eigen::VectorX<T>& params) = 0;
|
||||
};
|
||||
|
||||
template <typename T, size_t N, class Func>
|
||||
struct auto_diff_optimization_problem : public optimization_problem<T, N> {
|
||||
template <typename T, class Func>
|
||||
struct auto_diff_optimization_problem : public optimization_problem<T> {
|
||||
Func _objective_func;
|
||||
std::array<T, N> _initial_guess;
|
||||
Eigen::VectorX<T> _initial_guess;
|
||||
Eigen::VectorX<T> _x;
|
||||
|
||||
auto_diff_optimization_problem(
|
||||
Func objective_func, std::array<T, N> initial_guess = std::array<T, N>{})
|
||||
: _objective_func(objective_func), _initial_guess(initial_guess) {}
|
||||
Func objective_func, Eigen::VectorX<T> initial_guess = Eigen::VectorX<T>{})
|
||||
: _objective_func(objective_func), _initial_guess(initial_guess), _x(initial_guess) {}
|
||||
|
||||
std::array<T, N> initial_guess() override { return _initial_guess; }
|
||||
Eigen::VectorX<T>& initial_guess() override { return _initial_guess; }
|
||||
|
||||
T objective(const std::array<T, N>& params) override {
|
||||
Eigen::VectorX<T>& x() override { return _x; }
|
||||
|
||||
T objective(const Eigen::VectorX<T>& params) override {
|
||||
return _objective_func(params);
|
||||
}
|
||||
|
||||
std::array<T, N> gradient(const std::array<T, N>& params) override {
|
||||
return autoopt::gradient<T, N>(_objective_func, params);
|
||||
Eigen::VectorX<T> gradient(const Eigen::VectorX<T>& params) override {
|
||||
return autoopt::gradient<T>(_objective_func, params);
|
||||
}
|
||||
|
||||
matrix_t<T, N, N> hessian(const std::array<T, N>& params) override {
|
||||
return autoopt::hessian<T, N>(_objective_func, params);
|
||||
Eigen::MatrixX<T> hessian(const Eigen::VectorX<T>& params) override {
|
||||
return autoopt::hessian<T>(_objective_func, params);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
template <typename T>
|
||||
struct log_barrier_optimization_problem
|
||||
: public optimization_problem<T, N> {
|
||||
optimization_problem<T, N>& _base_problem;
|
||||
std::array<T, N> _delta;
|
||||
: public optimization_problem<T> {
|
||||
optimization_problem<T>& _base_problem;
|
||||
Eigen::VectorX<T> _delta;
|
||||
T _barrier_strength;
|
||||
|
||||
log_barrier_optimization_problem(
|
||||
optimization_problem<T, N>& base_problem,
|
||||
std::array<T, N> delta,
|
||||
optimization_problem<T>& base_problem,
|
||||
Eigen::VectorX<T> delta,
|
||||
T barrier_strength = T{1e-3})
|
||||
: _base_problem(base_problem),
|
||||
_delta(delta),
|
||||
_barrier_strength(barrier_strength) {}
|
||||
|
||||
std::array<T, N> initial_guess() override {
|
||||
Eigen::VectorX<T>& initial_guess() override {
|
||||
return _base_problem.initial_guess();
|
||||
}
|
||||
|
||||
T objective(const std::array<T, N>& params) override {
|
||||
Eigen::VectorX<T>& x() override {
|
||||
return _base_problem.x();
|
||||
}
|
||||
|
||||
T objective(const Eigen::VectorX<T>& params) override {
|
||||
T base_obj = _base_problem.objective(params);
|
||||
T barrier = barrier_term(params);
|
||||
return base_obj + barrier;
|
||||
}
|
||||
|
||||
std::array<T, N> gradient(const std::array<T, N>& params) override {
|
||||
Eigen::VectorX<T> gradient(const Eigen::VectorX<T>& params) override {
|
||||
auto base_grad = _base_problem.gradient(params);
|
||||
std::array<T, N> barrier_grad = autoopt::gradient<T, N>(
|
||||
[this]<typename U>(const std::array<U, N>& p) {
|
||||
Eigen::VectorX<T> barrier_grad = autoopt::gradient<T>(
|
||||
[this]<typename U>(const Eigen::VectorX<U>& p) {
|
||||
return barrier_term<U>(p);
|
||||
},
|
||||
params);
|
||||
std::array<T, N> total_grad;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
total_grad[i] = base_grad[i] + barrier_grad[i];
|
||||
}
|
||||
Eigen::VectorX<T> total_grad(params.size());
|
||||
total_grad = base_grad + barrier_grad;
|
||||
return total_grad;
|
||||
}
|
||||
|
||||
matrix_t<T, N, N> hessian(const std::array<T, N>& params) override {
|
||||
Eigen::MatrixX<T> hessian(const Eigen::VectorX<T>& params) override {
|
||||
auto base_hess = _base_problem.hessian(params);
|
||||
matrix_t<T, N, N> barrier_hess = autoopt::hessian<T, N>(
|
||||
[this]<typename U>(const std::array<U, N>& p) {
|
||||
Eigen::MatrixX<T> barrier_hess = autoopt::hessian<T>(
|
||||
[this]<typename U>(const Eigen::VectorX<U>& p) {
|
||||
return barrier_term<U>(p);
|
||||
},
|
||||
params);
|
||||
matrix_t<T, N, N> total_hess;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
for (size_t j = 0; j < N; ++j) {
|
||||
total_hess[i][j] = base_hess[i][j] + barrier_hess[i][j];
|
||||
}
|
||||
}
|
||||
Eigen::MatrixX<T> total_hess(params.size(), params.size());
|
||||
total_hess = base_hess + barrier_hess;
|
||||
return total_hess;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
U barrier_term(const std::array<U, N>& params) {
|
||||
U barrier_term(const Eigen::VectorX<U>& params) {
|
||||
U barrier = U{0};
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
U lb = _base_problem.initial_guess()[i] - _delta[i];
|
||||
U ub = _base_problem.initial_guess()[i] + _delta[i];
|
||||
barrier = barrier + std::log(params[i] - lb) + std::log(ub - params[i]);
|
||||
for (int i = 0; i < params.size(); ++i) {
|
||||
U lb = U{_base_problem.initial_guess()(i) - _delta(i)};
|
||||
U ub = U{_base_problem.initial_guess()(i) + _delta(i)};
|
||||
barrier = barrier + std::log(params(i) - lb) + std::log(ub - params(i));
|
||||
}
|
||||
return -U{_barrier_strength} * barrier;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user