6#include <tinyopt/cost.h>
7#include <tinyopt/math.h>
8#include <tinyopt/traits.h>
11namespace tinyopt::diff {
56template <
typename X_t,
typename Func>
57auto NumEval(
const X_t &x,
const Func &f,
const diff::Method &method = diff::Method::kCentral,
58 typename traits::params_trait<X_t>::Scalar h =
59 FloatEpsilon<
typename traits::params_trait<X_t>::Scalar>()) {
60 using ptrait = traits::params_trait<X_t>;
61 using Scalar =
typename ptrait::Scalar;
63 constexpr Index Dims = ptrait::Dims;
64 const Index dims = traits::DynDims(x);
67 auto fg = [&](
const auto &x) {
69 if constexpr (std::is_invocable_v<Func, const X_t &>)
71 else if constexpr (std::is_invocable_v<Func, const X_t &, std::nullptr_t &>)
73 else if constexpr (std::is_invocable_v<Func, const X_t &, std::nullptr_t &, std::nullptr_t &>)
74 return f(x, nul, nul);
76 SparseMatrix<Scalar> H;
83 const auto res = fg(x);
85 using ResType =
typename std::decay_t<
decltype(res)>;
86 constexpr int ResDims = traits::params_trait<ResType>::Dims;
87 const Index res_dims = traits::DynDims(res);
89 using J_t = Matrix<Scalar, ResDims, Dims>;
90 J_t J(res_dims, dims);
93 Vector<Scalar, Dims> dx = Vector<Scalar, Dims>::Zero(dims);
94 for (Index r = 0; r < dims; ++r) {
96 if (r > 0) dx[r - 1] = 0;
98 ptrait::PlusEq(y, dx);
99 const auto res_plus = fg(y);
100 using ResType2 =
typename std::decay_t<
decltype(res_plus)>;
101 if (method == Method::kCentral) {
104 ptrait::PlusEq(y, dx);
105 const auto res_minus = fg(y);
106 if constexpr (std::is_scalar_v<ResType2>)
107 J[r] = (res_plus - res_minus) / (2 * h);
109 J.col(r) = (res_plus.reshaped() - res_minus.reshaped()) / (2 * h);
110 }
else if (method == Method::kFastCentral) {
112 ptrait::PlusEq(y, dx);
113 const auto res_minus = fg(y);
114 if constexpr (std::is_scalar_v<ResType2>)
115 J[r] = (res_plus - res_minus) / (2 * h);
117 J.col(r) = (res_plus.reshaped() - res_minus.reshaped()) / (2 * h);
119 if constexpr (std::is_scalar_v<ResType2>)
120 J[r] = (res_plus - res) / h;
122 J.col(r) = (res_plus.reshaped() - res) / h;
125 return std::make_pair(res, J);
130template <
typename X_t,
typename Func>
131auto EstimateNumJac(
const X_t &x,
const Func &f,
132 const diff::Method &method = diff::Method::kCentral,
133 typename traits::params_trait<X_t>::Scalar h =
134 FloatEpsilon<
typename traits::params_trait<X_t>::Scalar>()) {
135 const auto &[res, J] = NumEval(x, f, method, h);
197template <
typename X_t,
typename Res
idualsFunc>
198auto CreateNumDiffFunc1(X_t &,
const ResidualsFunc &residuals,
199 const Method &method = Method::kCentral,
200 typename traits::params_trait<X_t>::Scalar h =
201 FloatEpsilon<
typename traits::params_trait<X_t>::Scalar>()) {
202 auto loss = [&residuals, method, h](
const auto &x,
auto &grad) {
203 constexpr bool HasGrad = !traits::is_nullptr_v<
decltype(grad)>;
205 const auto res = residuals(x);
207 if constexpr (HasGrad) {
208 const auto J = EstimateNumJac(x, residuals, method, h);
209 grad = J.transpose() * res;
212 using ResType =
typename std::decay_t<
decltype(res)>;
213 if constexpr (std::is_scalar_v<ResType>) {
214 return std::abs(res);
217 return Cost(res.matrix().norm(), res.size());
283template <
typename X_t,
typename Res
idualsFunc>
284auto CreateNumDiffFunc2(X_t &,
const ResidualsFunc &residuals,
285 const Method &method = Method::kCentral,
286 typename traits::params_trait<X_t>::Scalar h =
287 FloatEpsilon<
typename traits::params_trait<X_t>::Scalar>()) {
288 auto loss = [&residuals, method, h](
const auto &x,
auto &grad,
auto &H) {
289 constexpr bool HasGrad = !traits::is_nullptr_v<
decltype(grad)>;
290 constexpr bool HasH = !traits::is_nullptr_v<
decltype(H)>;
292 const auto res = residuals(x);
294 if constexpr (HasGrad) {
295 const auto J = EstimateNumJac(x, residuals, method, h);
296 grad = J.transpose() * res;
297 if constexpr (HasH) H = J.transpose() * J;
300 using ResType =
typename std::decay_t<
decltype(res)>;
301 if constexpr (std::is_scalar_v<ResType>) {
302 return std::abs(res);
305 return Cost(res.matrix().norm(), res.size());