#pragma once #include #include "autoopt/dual.hpp" namespace autoopt { template T derivative(Func&& f, const T& x) { dual a(x, T(1)); dual b = f(a); return b._dx; } template std::array gradient(Func&& f, const std::array& x) { std::array grad{}; std::array, N> dual_x{}; for (std::size_t i = 0; i < N; ++i) { dual_x[i] = dual(x[i], T(0)); } for (std::size_t i = 0; i < N; ++i) { dual_x[i]._dx = T(1); dual dual_y = f(dual_x); grad[i] = dual_y._dx; dual_x[i]._dx = T(0); } return grad; } template using matrix_t = std::array, N>; template matrix_t jacobian(Func&& f, const std::array& x) { matrix_t jacob{}; std::array, N> dual_x{}; for (std::size_t i = 0; i < N; ++i) { dual_x[i] = dual(x[i], T(0)); } for (std::size_t i = 0; i < N; ++i) { dual_x[i]._dx = T(1); std::array, M> dual_y = f(dual_x); for (std::size_t j = 0; j < M; ++j) { jacob[j][i] = dual_y[j]._dx; } dual_x[i]._dx = T(0); } return jacob; } template matrix_t hessian(Func&& f, const std::array& x) { auto helper_func = [&f](const std::array& y) { return gradient(f, y); }; return jacobian(helper_func, x); } } // namespace autoopt