6#include <tinyopt/cost.h>
7#include <tinyopt/log.h>
8#include <tinyopt/losses/robust_norms.h>
9#include <tinyopt/math.h>
11#include <tinyopt/diff/jet.h>
13namespace tinyopt::diff {
17template <
typename X_t,
typename CostOrResFunc>
18auto Eval(
const X_t &x,
const CostOrResFunc &cost_or_res_func,
bool check_residuals =
true) {
19 using ptrait = traits::params_trait<X_t>;
20 using Scalar =
typename ptrait::Scalar;
21 constexpr Index Dims = ptrait::Dims;
22 constexpr bool is_userdef_type =
23 !std::is_floating_point_v<X_t> && !traits::is_matrix_or_array_v<X_t>;
24 const Index dims = traits::DynDims(x);
27 using Jet = diff::Jet<Scalar, Dims>;
30 auto x_jet = ptrait::template cast<Jet>(x);
32 if constexpr (is_userdef_type) {
33 Vector<Jet, Dims> dx_jet = Vector<Jet, Dims>::Zero(dims);
34 for (Index i = 0; i < dims; ++i) {
36 if constexpr (Dims == Dynamic) dx_jet[i].v = Vector<Scalar, Dynamic>::Zero(dims);
39 using ptrait_jet = traits::params_trait<std::decay_t<
decltype(x_jet)>>;
40 ptrait_jet::PlusEq(x_jet, dx_jet);
41 }
else if constexpr (std::is_floating_point_v<X_t>) {
45 for (
int c = 0; c < x.cols(); ++c) {
46 for (
int r = 0; r < x.rows(); ++r) {
47 const auto i = r + c * x.rows();
48 if constexpr (Dims == Dynamic) x_jet(r, c).v = Vector<Scalar, Dims>::Zero(dims);
55 auto fg = [&](
const auto &x) {
57 if constexpr (std::is_invocable_v<CostOrResFunc, const X_t &>)
58 return cost_or_res_func(x);
59 else if constexpr (std::is_invocable_v<CostOrResFunc, const X_t &, std::nullptr_t &>)
60 return cost_or_res_func(x, nul);
61 else if constexpr (std::is_invocable_v<CostOrResFunc,
const X_t &, std::nullptr_t &,
63 return cost_or_res_func(x, nul, nul);
65 SparseMatrix<Scalar> H;
67 return cost_or_res_func(x, nul, H);
72 const auto res = fg(x_jet);
73 using ResType =
typename std::decay_t<
decltype(res)>;
75 if constexpr (traits::is_robust_residual_v<ResType>) {
76 const auto &raw = losses::UnwrapResidual(res);
77 using RawType = std::decay_t<
decltype(raw)>;
79 traits::is_jet_type_v<RawType> || (traits::is_matrix_or_array_v<RawType> &&
80 traits::is_jet_type_v<typename RawType::Scalar>),
81 "Robust residuals must wrap Jet-valued residuals");
83 if constexpr (traits::is_jet_type_v<RawType>) {
84 return std::make_pair(raw.a, raw.v.transpose().eval());
86 constexpr int ResDims = traits::params_trait<RawType>::Dims;
87 const Index res_dims = traits::DynDims(raw);
88 Matrix<Scalar, ResDims, Dims> J(res_dims, dims);
89 Vector<Scalar, ResDims> res_f(res.size());
90 if constexpr (RawType::ColsAtCompileTime != 1) {
91 for (
int c = 0; c < raw.cols(); ++c)
92 for (
int r = 0; r < raw.rows(); ++r) {
93 const Index i = r + c * raw.rows();
94 res_f[i] = raw(r, c).a;
95 if constexpr (Dims == Dynamic) {
96 if (check_residuals && raw(r, c).v.size() != dims) {
97 TINYOPT_LOG(
"⚠️ Residual ({},{}) is not connected to the parameters", r, c);
102 J.row(i) = raw(r, c).v;
105 for (Index i = 0; i < res_dims; ++i) {
107 if constexpr (Dims == Dynamic) {
108 if (check_residuals && raw[i].v.size() != dims) {
109 TINYOPT_LOG(
"⚠️ Residual #{} is not connected to the parameters", i);
117 return std::make_pair(res_f, J);
122 traits::is_jet_type_v<ResType> ||
123 (traits::is_matrix_or_array_v<ResType> && traits::is_jet_type_v<typename ResType::Scalar>));
125 if constexpr (!traits::is_matrix_or_array_v<ResType>) {
126 return std::make_pair(res.a, res.v.transpose().eval());
128 constexpr int ResDims = traits::params_trait<ResType>::Dims;
129 const Index res_dims = traits::DynDims(res);
131 Matrix<Scalar, ResDims, Dims> J(res_dims, dims);
132 Vector<Scalar, ResDims> res_f(res.size());
133 if constexpr (traits::is_matrix_or_array_v<ResType>) {
134 if constexpr (ResType::ColsAtCompileTime != 1) {
135 for (
int c = 0; c < res.cols(); ++c)
136 for (
int r = 0; r < res.rows(); ++r) {
137 const Index i = r + c * res.rows();
138 res_f[i] = res(r, c).a;
139 if constexpr (Dims == Dynamic) {
140 if (check_residuals && res(r, c).v.size() != dims) {
141 TINYOPT_LOG(
"⚠️ Residual ({},{}) is not connected to the parameters", r, c);
146 J.row(i) = res(r, c).v;
149 for (Index i = 0; i < res_dims; ++i) {
151 if constexpr (Dims == Dynamic) {
152 if (check_residuals && res[i].v.size() != dims) {
153 TINYOPT_LOG(
"⚠️ Residual #{} is not connected to the parameters", i);
162 for (Index i = 0; i < res_dims; ++i) {
164 if constexpr (Dims == Dynamic) {
165 if (check_residuals && res.v.size() != dims) {
166 TINYOPT_LOG(
"⚠️ Residual is not connected to the parameters");
174 return std::make_pair(res_f, J);
181template <
typename X_t,
typename CostOrResFunc>
182auto CalculateJac(
const X_t &x,
const CostOrResFunc &cost_func,
bool check_residuals =
true) {
183 const auto &[res, J] = Eval(x, cost_func, check_residuals);