This commit is contained in:
2026-01-21 15:27:17 +01:00
parent 650e5cc6b8
commit 16334e4834
7 changed files with 195 additions and 143 deletions
+42 -36
View File
@@ -50,58 +50,63 @@ TEST(DualTest, DerivativeFunction) {
}
TEST(DualTest, GradientFunction) {
auto func = []<typename T>(const std::array<T, 2>& x) {
return x[0] * x[0] + std::sin(x[1]);
auto func = []<typename T>(const Eigen::VectorX<T>& x) {
return x(0) * x(0) + std::sin(x(1));
};
std::array<double, 2> point = {1.0, 0.0};
std::array<double, 2> grad = gradient(func, point);
Eigen::VectorX<double> point(2);
point << 1.0, 0.0;
Eigen::VectorX<double> grad = gradient(func, point);
EXPECT_DOUBLE_EQ(grad[0], 2.0 * point[0]); // d/dx1
EXPECT_DOUBLE_EQ(grad[1], std::cos(point[1])); // d/dx2
EXPECT_DOUBLE_EQ(grad(0), 2.0 * point(0)); // d/dx1
EXPECT_DOUBLE_EQ(grad(1), std::cos(point(1))); // d/dx2
}
TEST(DualTest, JacobianFunction) {
auto func = []<typename T>(const std::array<T, 2>& x) {
return std::array<T, 2>{x[0] * x[0], std::sin(x[1])};
auto func = []<typename T>(const Eigen::VectorX<T>& x) {
Eigen::VectorX<T> y(2);
y << x(0) * x(0), std::sin(x(1));
return y;
};
std::array<double, 2> point = {1.0, 0.0};
auto jacob = jacobian<double, 2, 2>(func, point);
Eigen::VectorX<double> point(2);
point << 1.0, 0.0;
auto jacob = jacobian<double>(func, point);
EXPECT_DOUBLE_EQ(jacob[0][0], 2.0 * point[0]); // d(f1)/d(x1)
EXPECT_DOUBLE_EQ(jacob[0][1], 0.0); // d(f1)/d(x2)
EXPECT_DOUBLE_EQ(jacob[1][0], 0.0); // d(f2)/d(x1)
EXPECT_DOUBLE_EQ(jacob[1][1], std::cos(point[1])); // d(f2)/d(x2)
EXPECT_DOUBLE_EQ(jacob(0, 0), 2.0 * point(0)); // d(f1)/d(x1)
EXPECT_DOUBLE_EQ(jacob(0, 1), 0.0); // d(f1)/d(x2)
EXPECT_DOUBLE_EQ(jacob(1, 0), 0.0); // d(f2)/d(x1)
EXPECT_DOUBLE_EQ(jacob(1, 1), std::cos(point(1))); // d(f2)/d(x2)
}
TEST(DualTest, HessianFunction) {
auto func = []<typename T>(const std::array<T, 2>& x) {
return x[0] * x[0] + x[1] * x[1];
auto func = []<typename T>(const Eigen::VectorX<T>& x) {
return x(0) * x(0) + x(1) * x(1);
};
std::array<double, 2> point = {1.0, 2.0};
auto hess = hessian<double, 2>(func, point);
Eigen::VectorX<double> point(2);
point << 1.0, 2.0;
auto hess = hessian<double>(func, point);
EXPECT_DOUBLE_EQ(hess[0][0], 2.0); // d²f/dx1²
EXPECT_DOUBLE_EQ(hess[0][1], 0.0); // d²f/dx1dx2
EXPECT_DOUBLE_EQ(hess[1][0], 0.0); // d²f/dx2dx1
EXPECT_DOUBLE_EQ(hess[1][1], 2.0); // d²f/dx2²
EXPECT_DOUBLE_EQ(hess(0, 0), 2.0); // d²f/dx1²
EXPECT_DOUBLE_EQ(hess(0, 1), 0.0); // d²f/dx1dx2
EXPECT_DOUBLE_EQ(hess(1, 0), 0.0); // d²f/dx2dx1
EXPECT_DOUBLE_EQ(hess(1, 1), 2.0); // d²f/dx2²
}
struct opti_func {
std::vector<std::array<double, 2>> test_data;
std::vector<Eigen::Vector2<double>> test_data;
// loss function
template <typename T>
T operator()(const std::array<T, 3>& params) const {
T operator()(const Eigen::VectorX<T>& params) const {
T sum = T(0);
for (const auto& data_point : test_data) {
T x = T{data_point[0]};
T y_true = T{data_point[1]};
T a = params[0];
T b = params[1];
T c = params[2];
T x = T{data_point(0)};
T y_true = T{data_point(1)};
T a = params(0);
T b = params(1);
T c = params(2);
T y_pred = a * x * x + b * x + c;
T error = y_pred - y_true;
sum = sum + error * error;
@@ -120,18 +125,19 @@ TEST(DualTest, OptimizationFunction) {
{4.0, 4.0},
};
std::array<double, 3> params = {1.0, -4.0, 4.0};
Eigen::VectorX<double> params(3);
params << 1.0, -4.0, 4.0;
auto grad = gradient<double, 3>(f, params);
auto grad = gradient<double>(f, params);
EXPECT_DOUBLE_EQ(grad[0], 0.0); // dL/da
EXPECT_DOUBLE_EQ(grad[1], 0.0); // dL/db
EXPECT_DOUBLE_EQ(grad[2], 0.0); // dL/dc
EXPECT_DOUBLE_EQ(grad(0), 0.0); // dL/da
EXPECT_DOUBLE_EQ(grad(1), 0.0); // dL/db
EXPECT_DOUBLE_EQ(grad(2), 0.0); // dL/dc
auto hess = hessian<double, 3>(f, params);
auto hess = hessian<double>(f, params);
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
EXPECT_GE(hess[i][j], 0.0); // Hessian should be positive semi-definite
EXPECT_GE(hess(i, j), 0.0); // Hessian should be positive semi-definite
}
}
}
+24 -30
View File
@@ -3,6 +3,7 @@
#include <autoopt/ellipse.hpp>
#include <autoopt/optimization_problem.hpp>
#include <autoopt/util.hpp>
#include <autoopt/btls.hpp>
#include <iomanip>
#include <iostream>
@@ -21,11 +22,12 @@ TEST(Ellipse, ParamGradient) {
std::vector<std::pair<double, double>> data_points = {
{-10.0, -0.001}, {0.0, 0.0}, {10, 0.0009}};
std::array<double, 4> params = {100, 1000, deg2rad(1.0), 0.0};
Eigen::VectorX<double> params(4);
params << 100, 1000, deg2rad(1.0), 0.0;
auto loss_func = [&data_points]<typename T>(const std::array<T, 4>& p) {
ellipse<T> e{T{p[0]}, T{p[1]}, T{p[2]}};
quadric<T> q = e.to_quadric().rotated_by(T{p[3]});
auto loss_func = [&data_points]<typename T>(const Eigen::VectorX<T>& p) {
ellipse<T> e{T{p(0)}, T{p(1)}, T{p(2)}};
quadric<T> q = e.to_quadric().rotated_by(T{p(3)});
T loss = T{0};
for (const auto& [x, y_true] : data_points) {
T y_pred = q.slope_at(T{x});
@@ -35,44 +37,36 @@ TEST(Ellipse, ParamGradient) {
return loss / T(data_points.size());
};
auto_diff_optimization_problem<double, 4, decltype(loss_func)> problem(loss_func, params);
auto_diff_optimization_problem<double, decltype(loss_func)> problem(loss_func, params);
auto grad = problem.gradient(params);
EXPECT_NEAR(grad[0], -2.0789313126683308e-10, 1e-15); // d/d(left_arm)
EXPECT_NEAR(grad[1], -1.7464984353858657e-12, 1e-15); // d/d(right_arm)
EXPECT_NEAR(grad[2], 1.2013025455499119e-06, 1e-15); // d/d(entrance_angle)
EXPECT_NEAR(grad[3], -2.0332702665822054e-05, 1e-15); // d/d(rotation_angle)
std::cout << "Gradient:\n";
for (size_t i = 0; i < 4; ++i) {
std::cout << grad[i] << "\n";
}
std::cout << grad << std::endl;
EXPECT_NEAR(grad(0), -2.0789313126683308e-10, 1e-15); // d/d(left_arm)
EXPECT_NEAR(grad(1), -1.7464984353858657e-12, 1e-15); // d/d(right_arm)
EXPECT_NEAR(grad(2), 1.2013025455499119e-06, 1e-15); // d/d(entrance_angle)
EXPECT_NEAR(grad(3), -2.0332702665822054e-05, 1e-15); // d/d(rotation_angle)
auto hess = problem.hessian(params);
// set formatting for easier reading
std::cout << std::scientific;
// set field width for alignment
std::cout << "Hessian matrix:\n";
std::cout << hess << std::endl;
for (size_t i = 0; i < 4; ++i) {
;
for (size_t j = 0; j < 4; ++j) {
std::cout << std::setprecision(5) << std::setw(15) << hess[i][j];
}
std::cout << "\n";
}
Eigen::VectorX<double> params_delta(4);
params_delta << 1.0, 1.0, deg2rad(0.1), deg2rad(0.1);
params(0) += 0.9; // left_arm
// log barrier
log_barrier_optimization_problem<double, 4> log_barrier_problem(
log_barrier_optimization_problem<double> log_barrier_problem(
problem,
{1.0, 1.0, deg2rad(0.1), deg2rad(0.1)},
params_delta,
1e-3);
auto log_barrier_grad = log_barrier_problem.gradient(params);
std::cout << "Log Barrier Gradient:\n";
for (size_t i = 0; i < 4; ++i) {
std::cout << log_barrier_grad[i] << "\n";
}
std::cout << "Log barrier gradient:" << std::endl;
std::cout << log_barrier_grad << std::endl;
btls(problem);
}