9#include <tinyopt/diff/jet_traits.h>
10#include <tinyopt/math.h>
11#include <tinyopt/traits.h>
13#include <tinyopt/losses/norms.h>
23template <
typename Res
idualT,
typename LossTag>
24struct RobustResidual {
25 using StorageT = std::decay_t<ResidualT>;
27 explicit RobustResidual(
const ResidualT &residual,
double delta)
28 : residual(StorageT(residual)), delta(delta), delta2(delta * delta) {}
36const T &UnwrapResidual(
const T &residual) {
40template <
typename Res
idualT,
typename LossTag>
41const ResidualT &UnwrapResidual(
const RobustResidual<ResidualT, LossTag> &residual) {
42 return residual.residual;
45inline double HuberWeight(
double n2,
double delta) {
46 const double delta2 = delta * delta;
47 if (n2 <= delta2)
return 1.0;
48 return delta / std::sqrt(std::max(n2, std::numeric_limits<double>::min()));
51inline double HuberLossValue(
double n2,
double delta) {
52 const double delta2 = delta * delta;
53 if (n2 <= delta2)
return n2;
54 return 2.0 * delta * std::sqrt(std::max(n2, std::numeric_limits<double>::min())) - delta2;
57template <
typename T,
typename =
void>
61struct has_rows<T, std::void_t<decltype(std::declval<const T &>().rows())>> : std::true_type {};
64auto Huber(
const T &residual,
double delta)
65 -> std::enable_if_t<!std::is_arithmetic_v<std::decay_t<T>> && !traits::is_pair_v<T> &&
66 !traits::is_jet_type_v<std::decay_t<T>> &&
67 (traits::is_matrix_or_array_v<std::decay_t<T>> ||
68 traits::is_robust_residual_v<std::decay_t<T>>),
74auto Cauchy(
const T &residual,
double delta)
75 -> std::enable_if_t<!std::is_arithmetic_v<std::decay_t<T>> && !traits::is_pair_v<T> &&
76 !traits::is_jet_type_v<std::decay_t<T>> &&
77 (traits::is_matrix_or_array_v<std::decay_t<T>> ||
78 traits::is_robust_residual_v<std::decay_t<T>>),
79 RobustResidual<std::decay_t<T>, CauchyTag>> {
80 return RobustResidual<std::decay_t<T>, CauchyTag>(residual, delta);
84auto Tukey(
const T &residual,
double delta)
85 -> std::enable_if_t<!std::is_arithmetic_v<std::decay_t<T>> && !traits::is_pair_v<T> &&
86 !traits::is_jet_type_v<std::decay_t<T>> &&
87 (traits::is_matrix_or_array_v<std::decay_t<T>> ||
88 traits::is_robust_residual_v<std::decay_t<T>>),
89 RobustResidual<std::decay_t<T>, TukeyTag>> {
90 return RobustResidual<std::decay_t<T>, TukeyTag>(residual, delta);
111template <
typename T,
typename ExportJ = std::
nullptr_t>
112auto Truncated(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
113 const ExportJ &Jx_or_bool =
nullptr) {
114 if constexpr (traits::is_pair_v<T>) {
115 return Truncated(n2.first, th2, n2.second);
117 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
118 const auto l = n2 <= th2 ? n2 : T(th2);
119 if constexpr (std::is_null_pointer_v<ExportJ>) {
121 }
else if constexpr (traits::is_bool_v<ExportJ>) {
122 return std::make_pair(l, n2 <= th2 ? T(1) : T(0));
124 assert(Jx_or_bool.rows() == 1);
126 return std::make_pair(l, Jx_or_bool);
128 return std::make_pair(l, ExportJ::Zero(1, Jx_or_bool.cols()).eval());
136template <
typename T,
typename ExportJ = std::
nullptr_t>
138 const ExportJ &Jx_or_bool =
nullptr) {
147 typename T,
typename ExportJ = std::nullptr_t,
148 std::enable_if_t<!traits::is_matrix_or_array_v<T> && !traits::is_robust_residual_v<T>,
int> = 0>
149auto Huber(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
150 const ExportJ &Jx_or_bool =
nullptr) {
151 if constexpr (traits::is_pair_v<T>) {
152 return Huber(n2.first, th2, n2.second);
156 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
160 if constexpr (std::is_null_pointer_v<ExportJ>) {
163 if constexpr (traits::is_bool_v<ExportJ>)
164 return std::make_pair(l, T(1));
166 return std::make_pair(l, Jx_or_bool);
169 const auto th = T(sqrt(th2)), n = sqrt(n2);
170 const auto l = T(2.0) * th * n - T(th2);
171 if constexpr (std::is_null_pointer_v<ExportJ>) {
174 const T J_scale = max<T>(std::numeric_limits<T>::min(), th / n);
175 if constexpr (traits::is_bool_v<ExportJ>)
176 return std::make_pair(l, J_scale);
178 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
189template <
typename T,
typename ExportJ = std::
nullptr_t>
190auto HuberLoss(
const T &x,
typename traits::params_trait<T>::Scalar th2,
191 const ExportJ &Jx_or_bool =
nullptr) {
192 return Huber(
SquaredL2(x, Jx_or_bool), th2);
201 typename T,
typename ExportJ = std::nullptr_t,
202 std::enable_if_t<!traits::is_matrix_or_array_v<T> && !traits::is_robust_residual_v<T>,
int> = 0>
203auto Tukey(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
204 const ExportJ &Jx_or_bool =
nullptr) {
205 if constexpr (traits::is_pair_v<T>) {
206 return Tukey(n2.first, th2, n2.second);
208 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
210 const auto s = T(1.0) - n2 / T(th2), s2 = s * s;
211 const auto l = T(th2) * (T(1.0) - s2 * s);
212 if constexpr (std::is_null_pointer_v<ExportJ>) {
215 const auto J_scale = T(3.0) * (th2 - n2) * (th2 - n2) / (th2 * th2);
216 if constexpr (traits::is_bool_v<ExportJ>)
217 return std::make_pair(l, J_scale);
219 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
223 if constexpr (std::is_null_pointer_v<ExportJ>) {
226 if constexpr (traits::is_bool_v<ExportJ>)
227 return std::make_pair(l, T(0));
229 return std::make_pair(l, ExportJ::Zero(1, Jx_or_bool.cols()).eval());
238template <
typename T,
typename ExportJ = std::
nullptr_t>
239auto TukeyLoss(
const T &x,
typename traits::params_trait<T>::Scalar th2,
240 const ExportJ &Jx_or_bool =
nullptr) {
241 return Tukey(
SquaredL2(x, Jx_or_bool), th2);
249template <
typename T,
typename ExportJ = std::
nullptr_t>
250auto Arctan(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
251 const ExportJ &Jx_or_bool =
nullptr) {
252 if constexpr (traits::is_pair_v<T>) {
253 return Arctan(n2.first, th2, n2.second);
258 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
259 const T th = T(sqrt(th2));
260 const auto l = th * atan2(n2, th);
261 if constexpr (std::is_null_pointer_v<ExportJ>) {
264 const auto tmp = n2 * n2 / th2;
265 const T J_scale = max<T>(std::numeric_limits<T>::min(), T(1.0) / (tmp + T(1.0)));
266 if constexpr (traits::is_bool_v<ExportJ>)
267 return std::make_pair(l, J_scale);
269 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
277template <
typename T,
typename ExportJ = std::
nullptr_t>
278auto ArctanLoss(
const T &x,
typename traits::params_trait<T>::Scalar th2,
279 const ExportJ &Jx_or_bool =
nullptr) {
289 typename T,
typename ExportJ = std::nullptr_t,
290 std::enable_if_t<!traits::is_matrix_or_array_v<T> && !traits::is_robust_residual_v<T>,
int> = 0>
291auto Cauchy(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
292 const ExportJ &Jx_or_bool =
nullptr) {
293 if constexpr (traits::is_pair_v<T>) {
294 return Cauchy(n2.first, th2, n2.second);
298 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
299 const auto s = T(1.0) + n2 / T(th2);
300 const auto l = T(th2) * log(s);
301 if constexpr (std::is_null_pointer_v<ExportJ>) {
304 const T J_scale = std::max<T>(std::numeric_limits<T>::min(), T(1.0) / s);
305 if constexpr (traits::is_bool_v<ExportJ>)
306 return std::make_pair(l, J_scale);
308 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
316template <
typename T,
typename ExportJ = std::
nullptr_t>
317auto CauchyLoss(
const T &x,
typename traits::params_trait<T>::Scalar th2,
318 const ExportJ &Jx_or_bool =
nullptr) {
319 return Cauchy(
SquaredL2(x, Jx_or_bool), th2);
327template <
typename T,
typename ExportJ = std::
nullptr_t>
328auto GemanMcClure(
const T &n2,
typename traits::params_trait<T>::Scalar th2,
329 const ExportJ &Jx_or_bool =
nullptr) {
330 if constexpr (traits::is_pair_v<T>) {
335 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
336 const T e2_th2 = n2 + T(th2);
337 const auto l = n2 / (e2_th2);
338 if constexpr (std::is_null_pointer_v<ExportJ>) {
341 const auto J_scale = th2 / (e2_th2 * e2_th2);
342 if constexpr (traits::is_bool_v<ExportJ>)
343 return std::make_pair(l, J_scale);
345 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
353template <
typename T,
typename ExportJ = std::
nullptr_t>
355 const ExportJ &Jx_or_bool =
nullptr) {
364template <
typename T,
typename ExportJ = std::
nullptr_t>
366 const ExportJ &Jx_or_bool =
nullptr) {
367 if constexpr (traits::is_pair_v<T>) {
373 static_assert(!traits::is_matrix_or_array_v<T>,
"`n` must be a scalar");
374 const auto epsilon = T(exp(-th2));
375 const auto l = -log(exp(-n2) + epsilon);
376 if constexpr (std::is_null_pointer_v<ExportJ>) {
379 const auto J_scale = T(1.0) / (epsilon * exp(n2) + T(1.0));
380 if constexpr (traits::is_bool_v<ExportJ>)
381 return std::make_pair(l, J_scale);
383 return std::make_pair(l, (J_scale * Jx_or_bool).eval());
391template <
typename T,
typename ExportJ = std::
nullptr_t>
393 const ExportJ &Jx_or_bool =
nullptr) {
403template <
typename... Args>
404auto Residuals(Args &&...args) {
405 return std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...);
Definition activations.h:8
auto TruncatedLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Hard Truncated L2 Loss: Clip the L2 loss of x to a max of th, also return the scale {0,...
Definition robust_norms.h:137
auto ArctanLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Arctan: Return a scaled loss of 'x' such that , also return the scale or scaled jacobian if given as ...
Definition robust_norms.h:278
auto TukeyLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Tukey: Return a scaled loss of 'x' such that , also return the scale or scaled jacobian if given as i...
Definition robust_norms.h:239
auto SquaredL2(const T &x)
Return the squared L2 norm of a vector or scalar (a.k.a Sum of Squares).
Definition norms.h:19
auto BlakeZisserman(const T &n2, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
BlakeZisserman: Return a scaled loss n' such that , also return the scale or scaled jacobian if given...
Definition robust_norms.h:365
auto Arctan(const T &n2, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Arctan: Return a scaled loss , also return the scale or scaled jacobian if given as input Jx_or_bool.
Definition robust_norms.h:250
auto GemanMcClureLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
GemanMcClure: Return a scaled loss of 'x' such that , also return the scale or scaled jacobian if giv...
Definition robust_norms.h:354
auto HuberLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Huber: Return a scaled loss of 'x' such that , also return the scale {1, th/n} or scaled jacobian if ...
Definition robust_norms.h:190
auto Truncated(const T &n2, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Hard Truncation: Clip the given loss n to a max of th, also return the scale {0 1}...
Definition robust_norms.h:112
auto CauchyLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
Cauchy: Return a scaled loss of 'x' such that , also return the scale or scaled jacobian if given as ...
Definition robust_norms.h:317
auto GemanMcClure(const T &n2, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
GemanMcClure: Return a scaled loss , also return the scale or scaled jacobian if given as input Jx_or...
Definition robust_norms.h:328
auto BlakeZissermanLoss(const T &x, typename traits::params_trait< T >::Scalar th2, const ExportJ &Jx_or_bool=nullptr)
BlakeZisserman: Return a scaled loss of 'x' such that , also return the scale or scaled jacobian if g...
Definition robust_norms.h:392
Definition robust_norms.h:20
Definition robust_norms.h:19
Definition robust_norms.h:24
Definition robust_norms.h:21
Definition robust_norms.h:58