init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
file(GLOB_RECURSE TEST_SOURCES *.cpp)
|
||||
|
||||
add_executable(autoopt-test ${TEST_SOURCES})
|
||||
target_link_libraries(autoopt-test autoopt gtest gtest_main)
|
||||
|
||||
install(TARGETS autoopt-test DESTINATION bin)
|
||||
|
||||
include(GoogleTest)
|
||||
|
||||
gtest_discover_tests(autoopt-test)
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
#include "autoopt/dual.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "autoopt/derivative.hpp"
|
||||
|
||||
using namespace autoopt;
|
||||
|
||||
TEST(DualTest, BasicOperations) {
|
||||
dual<double> a(2.0, 1.0); // a = 2.0, da/dx = 1.0
|
||||
dual<double> b(3.0, 0.0); // b = 3.0, db/dx = 0.0
|
||||
|
||||
dual<double> c = a + b;
|
||||
EXPECT_DOUBLE_EQ(c._x, 5.0);
|
||||
EXPECT_DOUBLE_EQ(c._dx, 1.0);
|
||||
|
||||
dual<double> d = a * b;
|
||||
EXPECT_DOUBLE_EQ(d._x, 6.0);
|
||||
EXPECT_DOUBLE_EQ(d._dx, 3.0);
|
||||
|
||||
dual<double> e = a / b;
|
||||
EXPECT_DOUBLE_EQ(e._x, 2.0 / 3.0);
|
||||
EXPECT_DOUBLE_EQ(e._dx, 1.0 / 3.0);
|
||||
}
|
||||
|
||||
TEST(DualTest, StandardFunctions) {
|
||||
dual<double> a(0.5, 1.0); // a = 0.5, da/dx = 1.0
|
||||
|
||||
dual<double> b = std::sin(a);
|
||||
EXPECT_DOUBLE_EQ(b._x, std::sin(0.5));
|
||||
EXPECT_DOUBLE_EQ(b._dx, std::cos(0.5));
|
||||
|
||||
dual<double> c = std::exp(a);
|
||||
EXPECT_DOUBLE_EQ(c._x, std::exp(0.5));
|
||||
EXPECT_DOUBLE_EQ(c._dx, std::exp(0.5));
|
||||
|
||||
dual<double> d = std::log(a);
|
||||
EXPECT_DOUBLE_EQ(d._x, std::log(0.5));
|
||||
EXPECT_DOUBLE_EQ(d._dx, 1.0 / 0.5);
|
||||
}
|
||||
|
||||
TEST(DualTest, DerivativeFunction) {
|
||||
auto func = []<typename T>(const T& x) { return std::sin(x) * std::exp(x); };
|
||||
|
||||
for (double val : {0.0, 0.5, 1.0, 2.0}) {
|
||||
double deriv = derivative(func, val);
|
||||
double expected = (std::cos(val) + std::sin(val)) * std::exp(val);
|
||||
EXPECT_DOUBLE_EQ(deriv, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DualTest, GradientFunction) {
|
||||
auto func = []<typename T>(const std::array<T, 2>& 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);
|
||||
|
||||
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])};
|
||||
};
|
||||
|
||||
std::array<double, 2> point = {1.0, 0.0};
|
||||
auto jacob = jacobian<double, 2, 2>(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)
|
||||
}
|
||||
|
||||
TEST(DualTest, HessianFunction) {
|
||||
auto func = []<typename T>(const std::array<T, 2>& 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);
|
||||
|
||||
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;
|
||||
|
||||
// loss function
|
||||
template <typename T>
|
||||
T operator()(const std::array<T, 3>& 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 y_pred = a * x * x + b * x + c;
|
||||
T error = y_pred - y_true;
|
||||
sum = sum + error * error;
|
||||
}
|
||||
return sum / T(test_data.size());
|
||||
}
|
||||
};
|
||||
|
||||
TEST(DualTest, OptimizationFunction) {
|
||||
opti_func f;
|
||||
f.test_data = {
|
||||
{0.0, 4.0},
|
||||
{1.0, 1.0},
|
||||
{2.0, 0.0},
|
||||
{3.0, 1.0},
|
||||
{4.0, 4.0},
|
||||
};
|
||||
|
||||
std::array<double, 3> params = {1.0, -4.0, 4.0};
|
||||
|
||||
auto grad = gradient<double, 3>(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
|
||||
|
||||
auto hess = hessian<double, 3>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <autoopt/ellipse.hpp>
|
||||
#include <autoopt/optimization_problem.hpp>
|
||||
#include <autoopt/util.hpp>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
using namespace autoopt;
|
||||
|
||||
TEST(Ellipse, Slope) {
|
||||
ellipse<double> e{100, 1000, deg2rad(1.0)}; // entrance angle 1 degree
|
||||
quadric<double> q = e.to_quadric();
|
||||
|
||||
EXPECT_NEAR(q.slope_at(-10), -0.0010305116165301856, 1e-9);
|
||||
EXPECT_NEAR(q.slope_at(0), 0.0, 1e-9);
|
||||
EXPECT_NEAR(q.slope_at(10), 0.00090001261192696272, 1e-9);
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
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]});
|
||||
T loss = T{0};
|
||||
for (const auto& [x, y_true] : data_points) {
|
||||
T y_pred = q.slope_at(T{x});
|
||||
T error = y_pred - T{y_true};
|
||||
loss = loss + error * error;
|
||||
}
|
||||
return loss / T(data_points.size());
|
||||
};
|
||||
|
||||
auto_diff_optimization_problem<double, 4, 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";
|
||||
}
|
||||
|
||||
auto hess = problem.hessian(params);
|
||||
|
||||
// set formatting for easier reading
|
||||
std::cout << std::scientific;
|
||||
// set field width for alignment
|
||||
std::cout << "Hessian matrix:\n";
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
// log barrier
|
||||
log_barrier_optimization_problem<double, 4> log_barrier_problem(
|
||||
problem,
|
||||
{1.0, 1.0, deg2rad(0.1), deg2rad(0.1)},
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "autoopt/quadric.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace autoopt;
|
||||
|
||||
TEST(QuadricTest, ParabolaRotation) {
|
||||
quadric<double> q(1.0, 0.0, 0.0, 0.0, -1.0, 0.0); // y = x^2
|
||||
|
||||
EXPECT_DOUBLE_EQ(q.at(-1.0), 1.0); // At x=1, y=1
|
||||
EXPECT_DOUBLE_EQ(q.at(0.0), 0.0); // At x=0, y=0
|
||||
EXPECT_DOUBLE_EQ(q.at(1.0), 1.0); // At x=1, y=1
|
||||
|
||||
double angle = M_PI / 4; // 45 degrees
|
||||
quadric<double> q_rotated = q.rotated_by(angle);
|
||||
|
||||
EXPECT_NEAR(q_rotated.at(-1.0), -0.11729096183611623, 1e-9);
|
||||
EXPECT_NEAR(q_rotated.at(0.0), 0.0, 1e-9);
|
||||
EXPECT_TRUE(std::isnan(q_rotated.at(1.0))); // Expect NaN due to no real solution
|
||||
}
|
||||
Reference in New Issue
Block a user