$darkmode
vstat
vstat.hpp
1 // SPDX-License-Identifier: MIT
2 // SPDX-FileCopyrightText: Copyright 2020-2024 Heal Research
3 
4 #ifndef VSTAT_HPP
5 #define VSTAT_HPP
6 
7 #include "bivariate.hpp"
8 #include "univariate.hpp"
9 
10 #include <algorithm>
11 #include <functional>
12 #include <iterator>
13 #include <limits>
14 
15 #include <eve/module/math.hpp>
16 #include <eve/module/special.hpp>
17 
18 namespace VSTAT_NAMESPACE {
19 
20 namespace detail {
21  // utility method to load data into a wide type
22  template<eve::simd_value T, std::input_iterator I, typename F>
23  requires std::is_invocable_v<F, std::iter_value_t<I>>
24  auto inline load(I iter, F&& func) {
25  return [&]<std::size_t ...Idx>(std::index_sequence<Idx...>){
26  return T{ std::forward<F>(func)(*(iter + Idx))... };
27  }(std::make_index_sequence<T::size()>{});
28  }
29 
30  // utility method to advance a set of iterators
31  template<typename Distance, typename... Iters>
32  auto inline advance(Distance d, Iters&... iters) -> void {
33  (std::advance(iters, d), ...);
34  }
35 } // namespace detail
36 
37 namespace concepts {
38  template<typename T>
39  concept arithmetic = std::is_arithmetic_v<T>;
40 
41  template<typename F, typename... Args>
42  concept arithmetic_projection = requires(F&&) {
43  { std::is_invocable_v<F, Args...> };
44  { arithmetic<std::remove_reference_t<std::invoke_result_t<F, Args...>>> };
45  };
46 } // namespace concepts
47 
48 
54 namespace univariate {
66 template<std::floating_point T, std::input_iterator I, typename F = std::identity>
67 requires concepts::arithmetic_projection<F, std::iter_value_t<I>>
68 inline auto accumulate(I first, std::sized_sentinel_for<I> auto last, F&& f = F{}) noexcept -> univariate_statistics
69 {
70  using wide = eve::wide<T>;
71  auto constexpr s{ wide::size() };
72  auto const n{ std::distance(first, last) };
73  auto const m = n - n % s;
74 
75  if (n < s) {
76  univariate_accumulator<T> scalar_acc;
77  for (; first < last; ++first) {
78  scalar_acc(std::invoke(std::forward<F>(f), *first));
79  }
80  return univariate_statistics(scalar_acc);
81  }
82 
83  univariate_accumulator<wide> acc;
84  for (size_t i = 0; i < m; i += s) {
85  acc(detail::load<wide>(first, std::forward<F>(f)));
86  detail::advance(s, first);
87  }
88 
89  // gather the remaining values with a scalar accumulator
90  if (m < n) {
91  auto [sw, sx, sxx] = acc.stats();
92  auto scalar_acc = univariate_accumulator<T>::load_state(sw, sx, sxx);
93  for (; first < last; ++first) {
94  scalar_acc(std::invoke(std::forward<F>(f), *first));
95  }
96  return univariate_statistics(scalar_acc);
97  }
98  return univariate_statistics(acc);
99 }
100 
113 template<std::floating_point T, std::input_iterator I, std::input_iterator J, typename F = std::identity>
114 requires concepts::arithmetic_projection<F, std::iter_value_t<I>> and std::is_arithmetic_v<std::iter_value_t<J>>
115 inline auto accumulate(I first1, std::sized_sentinel_for<I> auto last1, J first2, F&& f = F{}) noexcept -> univariate_statistics
116 {
117  using wide = eve::wide<T>;
118  auto constexpr s{ wide::size() };
119  auto const n { std::distance(first1, last1) };
120  const size_t m = n - n % s;
121 
122  if (n < s) {
123  univariate_accumulator<T> scalar_acc;
124  for (; first1 < last1; ++first1, ++first2) {
125  scalar_acc(std::invoke(std::forward<F>(f), *first1), *first2);
126  }
127  return univariate_statistics(scalar_acc);
128  }
129 
130  univariate_accumulator<wide> acc;
131  for (size_t i = 0; i < m; i += s) {
132  acc(detail::load<wide>(first1, std::forward<F>(f)), wide(first2, first2 + s));
133  detail::advance(s, first1, first2);
134  }
135 
136  // use a scalar accumulator to gather the remaining values
137  if (m < n) {
138  auto [sw, sx, sxx] = acc.stats();
139  auto scalar_acc = univariate_accumulator<T>::load_state(sw, sx, sxx);
140  for (; first1 < last1; ++first1, ++first2) {
141  scalar_acc(std::invoke(std::forward<F>(f), *first1), *first2);
142  }
143  return univariate_statistics(scalar_acc);
144  }
145  return univariate_statistics(acc);
146 }
147 
165 template<std::floating_point T, std::input_iterator I, std::input_iterator J, typename BinaryOp, typename F1 = std::identity, typename F2 = std::identity>
166 requires std::is_invocable_v<F1, std::iter_value_t<I>> and
167  std::is_invocable_v<F2, std::iter_value_t<J>> and
168  std::is_invocable_v<
169  BinaryOp,
170  std::invoke_result_t<F1, std::iter_value_t<I>>,
171  std::invoke_result_t<F2, std::iter_value_t<J>>
172  > and
173  concepts::arithmetic_projection<
174  BinaryOp,
175  std::invoke_result_t<F1, std::iter_value_t<I>>,
176  std::invoke_result_t<F2, std::iter_value_t<J>>
177  >
178 inline auto accumulate(I first1, std::sized_sentinel_for<I> auto last1, J first2, BinaryOp&& op = BinaryOp{}, F1&& f1 = F1{}, F2&& f2 = F2{}) noexcept -> univariate_statistics
179 {
180  using wide = eve::wide<T>;
181  auto constexpr s{ wide::size() };
182  auto const n{ std::distance(first1, last1) };
183  auto const m = n - n % s;
184 
185  auto f = [&](auto a, auto b){
186  return std::invoke(std::forward<BinaryOp>(op), std::invoke(std::forward<F1>(f1), a), std::invoke(std::forward<F2>(f2), b));
187  };
188 
189  if (n < s) {
190  univariate_accumulator<T> scalar_acc;
191  for (; first1 < last1; ++first1, ++first2) {
192  scalar_acc(f(*first1, *first2));
193  }
194  return univariate_statistics(scalar_acc);
195  }
196 
197  std::array<T, s> x;
198  univariate_accumulator<wide> acc;
199  for (size_t i = 0; i < m; i += s) {
200  std::transform(first1, first1 + s, first2, x.begin(), f);
201  acc(wide{x.data()});
202  detail::advance(s, first1, first2);
203  }
204 
205  // use a scalar accumulator to gather the remaining values
206  if (m < n) {
207  auto [sw, sx, sxx] = acc.stats();
208  auto scalar_acc = univariate_accumulator<T>::load_state(sw, sx, sxx);
209  for (; first1 < last1; ++first1, ++first2) {
210  scalar_acc(f(*first1, *first2));
211  }
212  return univariate_statistics(scalar_acc);
213  }
214  return univariate_statistics(acc);
215 }
216 
235 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K, typename BinaryOp, typename F1 = std::identity, typename F2 = std::identity>
236 requires std::is_arithmetic_v<std::iter_value_t<K>> &&
237  std::is_invocable_v<F1, std::iter_value_t<I>> &&
238  std::is_invocable_v<F2, std::iter_value_t<J>> &&
239  std::is_invocable_v<
240  BinaryOp,
241  std::invoke_result_t<F1, std::iter_value_t<I>>,
242  std::invoke_result_t<F2, std::iter_value_t<J>>
243  > &&
244  concepts::arithmetic_projection<
245  BinaryOp,
246  std::invoke_result_t<F1, std::iter_value_t<I>>,
247  std::invoke_result_t<F2, std::iter_value_t<J>>
248  >
249 inline auto accumulate(I first1, std::sized_sentinel_for<I> auto last1, J first2, K first3, BinaryOp&& op = BinaryOp{}, F1&& f1 = F1{}, F2&& f2 = F2{}) noexcept -> univariate_statistics
250 {
251  using wide = eve::wide<T>;
252  auto constexpr s{ wide::size() };
253  auto const n{ std::distance(first1, last1) };
254  auto const m = n - n % s;
255 
256  auto f = [&](auto a, auto b){
257  return std::invoke(std::forward<BinaryOp>(op), std::invoke(std::forward<F1>(f1), a), std::invoke(std::forward<F2>(f2), b));
258  };
259 
260  if (n < s) {
261  univariate_accumulator<T> scalar_acc;
262  for (; first1 < last1; ++first1, ++first2, ++first3) {
263  scalar_acc(f(*first1, *first2), *first3);
264  }
265  return univariate_statistics(scalar_acc);
266  }
267 
268  std::array<T, s> x;
269  univariate_accumulator<wide> acc;
270  for (size_t i = 0; i < m; i += s) {
271  std::transform(first1, first1 + s, first2, x.begin(), f);
272  acc(wide(x), wide(first3, first3 + s));
273  detail::advance(s, first1, first2, first3);
274  }
275 
276  // use a scalar accumulator to gather the remaining values
277  if (m < n) {
278  auto [sw, sx, sxx] = acc.stats();
279  auto scalar_acc = univariate_accumulator<T>::load_state(sw, sx, sxx);
280  for (; first1 < last1; ++first1, ++first2, ++first3) {
281  scalar_acc(f(*first1, *first2), *first3);
282  }
283  return univariate_statistics(scalar_acc);
284  }
285  return univariate_statistics(acc);
286 }
287 } // namespace univariate
288 
289 namespace bivariate {
333 template<std::floating_point T, std::input_iterator I, std::input_iterator J, typename F1 = std::identity, typename F2 = std::identity>
334 requires concepts::arithmetic_projection<F1, std::iter_value_t<I>> and
335  concepts::arithmetic_projection<F2, std::iter_value_t<J>>
336 inline auto accumulate(I first1, std::sized_sentinel_for<I> auto last1, J first2, F1&& f1 = F1{}, F2&& f2 = F2{})
337 {
338  using wide = eve::wide<T>;
339  auto constexpr s { wide::size() };
340  auto const n { std::distance(first1, last1) };
341  auto const m = n - n % s;
342 
343  if (n < s) {
344  bivariate_accumulator<T> scalar_acc;
345  for (; first1 < last1; ++first1, ++first2) {
346  scalar_acc(std::invoke(std::forward<F1>(f1), *first1), std::invoke(std::forward<F2>(f2), *first2));
347  }
348  return bivariate_statistics(scalar_acc);
349  }
350 
351  bivariate_accumulator<wide> acc;
352  for (size_t i = 0; i < m; i += s) {
353  acc(detail::load<wide>(first1, std::forward<F1>(f1)), detail::load<wide>(first2, std::forward<F2>(f2)));
354  detail::advance(s, first1, first2);
355  }
356 
357  if (m < n) {
358  auto [sw, sx, sy, sxx, syy, sxy] = acc.stats();
359  auto scalar_acc = bivariate_accumulator<T>::load_state(sx, sy, sw, sxx, syy, sxy);
360  for (; first1 < last1; ++first1, ++first2) {
361  scalar_acc(std::invoke(std::forward<F1>(f1), *first1), std::invoke(std::forward<F2>(f2), *first2));
362  }
363  return bivariate_statistics(scalar_acc);
364  }
365 
366  return bivariate_statistics(acc);
367 }
368 
369 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K, typename F1 = std::identity, typename F2 = std::identity>
370 requires concepts::arithmetic_projection<F1, std::iter_value_t<I>> and
371  concepts::arithmetic_projection<F2, std::iter_value_t<J>> and
372  std::is_arithmetic_v<std::iter_value_t<K>>
373 inline auto accumulate(I first1, std::sized_sentinel_for<I> auto last1, J first2, K first3, F1&& f1 = F1{}, F2&& f2 = F2{}) noexcept -> bivariate_statistics
374 {
375  using wide = eve::wide<T>;
376  auto constexpr s { wide::size() };
377  auto const n = std::distance(first1, last1);
378  auto const m = n - n % s;
379 
380  if (n < s) {
381  bivariate_accumulator<T> scalar_acc;
382  for (; first1 < last1; ++first1, ++first2, ++first3) {
383  scalar_acc(std::invoke(std::forward<F1>(f1), *first1++), std::invoke(std::forward<F2>(f2), *first2++), *first3++);
384  }
385  return bivariate_statistics(scalar_acc);
386  }
387 
388  bivariate_accumulator<wide> acc;
389  for (size_t i = 0; i < m; i += s) {
390  acc(
391  detail::load<wide>(first1, std::forward<F1>(f1)),
392  detail::load<wide>(first2, std::forward<F2>(f2)),
393  wide(first3, first3 + s)
394  );
395  detail::advance(s, first1, first2, first3);
396  }
397 
398  if (m < n) {
399  auto [sw, sx, sy, sxx, syy, sxy] = acc.stats();
400  auto scalar_acc = bivariate_accumulator<T>::load_state(sx, sy, sw, sxx, syy, sxy);
401  for (; first1 < last1; ++first1, ++first2, ++first3) {
402  scalar_acc(std::invoke(std::forward<F1>(f1), *first1), std::invoke(std::forward<F2>(f2), *first2), *first3);
403  }
404  return bivariate_statistics(scalar_acc);
405  }
406  return bivariate_statistics(acc);
407 }
408 } // namespace bivariate
409 
410 namespace metrics {
430 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
431 inline auto r2_score(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
432  using wide = eve::wide<T>;
433  auto constexpr s{ wide::size() };
434  auto const n{ std::distance(first1, last1) };
435  auto const m{ n - n % s };
436 
439  for (auto i = 0; i < m; i += s) {
440  wide y_true{first1, first1+s};
441  wide y_pred{first2, first2+s};
442  wx(eve::sqr(y_true-y_pred));
443  wy(y_true);
444  detail::advance(s, first1, first2);
445  }
446 
447  // use scalar accumulators for the remaining values
448  auto sx = univariate_accumulator<T>::load_state(wx.stats());
449  auto sy = univariate_accumulator<T>::load_state(wy.stats());
450 
451  for(; first1 < last1; ++first1, ++first2) {
452  sx(eve::sqr(*first1 - *first2));
453  sy(*first1);
454  }
455 
456  auto const rss = univariate_statistics(sx).sum;
457  auto const tss = univariate_statistics(sy).ssr;
458 
459  return tss < std::numeric_limits<double>::epsilon()
460  ? std::numeric_limits<double>::lowest()
461  : 1.0 - rss / tss;
462 }
463 
475 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
476 inline auto r2_score(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
477  using wide = eve::wide<T>;
478  auto constexpr s{ wide::size() };
479  auto const n{ std::distance(first1, last1) };
480  auto const m{ n - n % s };
481 
484  for (auto i = 0; i < m; i += s) {
485  wide y_true{first1, first1+s};
486  wide y_pred{first2, first2+s};
487  wide weight{first3, first3+s};
488  wx(eve::sqr(y_true-y_pred), weight);
489  wy(y_true, weight);
490  detail::advance(s, first1, first2, first3);
491  }
492 
493  // use scalar accumulators for the remaining values
494  auto sx = univariate_accumulator<T>::load_state(wx.stats());
495  auto sy = univariate_accumulator<T>::load_state(wy.stats());
496 
497  for(; first1 < last1; ++first1, ++first2, ++first3) {
498  sx(eve::sqr(*first1 - *first2), *first3);
499  sy(*first1, *first3);
500  }
501 
502  auto const rss = univariate_statistics(sx).sum;
503  auto const tss = univariate_statistics(sy).ssr;
504 
505  return tss < std::numeric_limits<double>::epsilon()
506  ? std::numeric_limits<double>::lowest()
507  : 1.0 - rss / tss;
508 }
509 
519 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
520 inline auto mean_squared_error(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
521  using wide = eve::wide<T>;
522  auto constexpr s{ wide::size() };
523  auto const n{ std::distance(first1, last1) };
524  auto const m{ n - n % s };
525 
527  for (auto i = 0; i < m; i += s) {
528  wide y_true{first1, first1+s};
529  wide y_pred{first2, first2+s};
530  we(eve::sqr(y_true-y_pred));
531  detail::advance(s, first1, first2);
532  }
533 
534  // use scalar accumulators for the remaining values
535  auto se = univariate_accumulator<T>::load_state(we.stats());
536  for(; first1 < last1; ++first1, ++first2) {
537  se(eve::sqr(*first1 - *first2));
538  }
539  return univariate_statistics(se).mean;
540 }
541 
551 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
552 inline auto mean_squared_error(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
553  using wide = eve::wide<T>;
554  auto constexpr s{ wide::size() };
555  auto const n{ std::distance(first1, last1) };
556  auto const m{ n - n % s };
557 
559  for (auto i = 0; i < m; i += s) {
560  wide y_true{first1, first1+s};
561  wide y_pred{first2, first2+s};
562  wide weight{first3, first3+s};
563  we(eve::sqr(y_true-y_pred), weight);
564  detail::advance(s, first1, first2, first3);
565  }
566 
567  // use scalar accumulators for the remaining values
568  auto se = univariate_accumulator<T>::load_state(we.stats());
569  for(; first1 < last1; ++first1, ++first2, ++first3) {
570  se(eve::sqr(*first1 - *first2), *first3);
571  }
572  return univariate_statistics(se).mean;
573 }
574 
584 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
585 inline auto mean_squared_log_error(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
586  using wide = eve::wide<T>;
587  auto constexpr s{ wide::size() };
588  auto const n{ std::distance(first1, last1) };
589  auto const m{ n - n % s };
590 
592  for (auto i = 0; i < m; i += s) {
593  wide y_true{first1, first1+s};
594  wide y_pred{first2, first2+s};
595  we(eve::sqr(eve::log1p(y_true)-eve::log1p(y_pred)));
596  detail::advance(s, first1, first2);
597  }
598 
599  // use scalar accumulators for the remaining values
600  auto se = univariate_accumulator<T>::load_state(we.stats());
601  for(; first1 < last1; ++first1, ++first2) {
602  se(eve::sqr(eve::log1p(*first1) - eve::log1p(*first2)));
603  }
604  return univariate_statistics(se).mean;
605 }
606 
616 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
617 inline auto mean_squared_log_error(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
618  using wide = eve::wide<T>;
619  auto constexpr s{ wide::size() };
620  auto const n{ std::distance(first1, last1) };
621  auto const m{ n - n % s };
622 
624  for (auto i = 0; i < m; i += s) {
625  wide y_true{first1, first1+s};
626  wide y_pred{first2, first2+s};
627  wide weight{first3, first3+s};
628  we(eve::sqr(eve::log1p(y_true)-eve::log1p(y_pred)), weight);
629  detail::advance(s, first1, first2, first3);
630  }
631 
632  // use scalar accumulators for the remaining values
633  auto se = univariate_accumulator<T>::load_state(we.stats());
634  for(; first1 < last1; ++first1, ++first2, ++first3) {
635  se(eve::sqr(eve::log1p(*first1) - eve::log1p(*first2)), *first3);
636  }
637  return univariate_statistics(se).mean;
638 }
639 
649 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
650 inline auto mean_absolute_error(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
651  using wide = eve::wide<T>;
652  auto constexpr s{ wide::size() };
653  auto const n{ std::distance(first1, last1) };
654  auto const m{ n - n % s };
655 
657  for (auto i = 0; i < m; i += s) {
658  wide y_true{first1, first1+s};
659  wide y_pred{first2, first2+s};
660  we(eve::abs(y_true-y_pred));
661  detail::advance(s, first1, first2);
662  }
663 
664  // use scalar accumulators for the remaining values
665  auto se = univariate_accumulator<T>::load_state(we.stats());
666  for(; first1 < last1; ++first1, ++first2) {
667  se(eve::abs(*first1 - *first2));
668  }
669  return univariate_statistics(se).mean;
670 }
671 
681 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
682 inline auto mean_absolute_error(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
683  using wide = eve::wide<T>;
684  auto constexpr s{ wide::size() };
685  auto const n{ std::distance(first1, last1) };
686  auto const m{ n - n % s };
687 
689  for (auto i = 0; i < m; i += s) {
690  wide y_true{first1, first1+s};
691  wide y_pred{first2, first2+s};
692  wide weight{first3, first3+s};
693  we(eve::abs(y_true-y_pred), weight);
694  detail::advance(s, first1, first2, first3);
695  }
696 
697  // use scalar accumulators for the remaining values
698  auto se = univariate_accumulator<T>::load_state(we.stats());
699  for(; first1 < last1; ++first1, ++first2, ++first3) {
700  se(eve::abs(*first1 - *first2), *first3);
701  }
702  return univariate_statistics(se).mean;
703 }
704 
717 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
718 inline auto mean_absolute_percentage_error(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
719  using wide = eve::wide<T>;
720  auto constexpr s{ wide::size() };
721  auto const n{ std::distance(first1, last1) };
722  auto const m{ n - n % s };
723 
724  auto constexpr eps{ std::numeric_limits<T>::epsilon() };
725 
727  for (auto i = 0; i < m; i += s) {
728  wide y_true{first1, first1+s};
729  wide y_pred{first2, first2+s};
730  we(eve::abs(y_true-y_pred) / eve::max(eps, eve::abs(y_true)));
731  detail::advance(s, first1, first2);
732  }
733 
734  // use scalar accumulators for the remaining values
735  auto se = univariate_accumulator<T>::load_state(we.stats());
736  for(; first1 < last1; ++first1, ++first2) {
737  se(eve::abs(*first1 - *first2) / eve::max(eps, eve::abs(*first1)));
738  }
739  return univariate_statistics(se).mean;
740 }
741 
751 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
752 inline auto mean_absolute_percentage_error(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
753  using wide = eve::wide<T>;
754  auto constexpr s{ wide::size() };
755  auto const n{ std::distance(first1, last1) };
756  auto const m{ n - n % s };
757 
759  for (auto i = 0; i < m; i += s) {
760  wide y_true{first1, first1+s};
761  wide y_pred{first2, first2+s};
762  wide weight{first3, first3+s};
763  we(eve::abs(y_true-y_pred), weight);
764  detail::advance(s, first1, first2, first3);
765  }
766 
767  // use scalar accumulators for the remaining values
768  auto se = univariate_accumulator<T>::load_state(we.stats());
769  for(; first1 < last1; ++first1, ++first2, ++first3) {
770  se(eve::abs(*first1 - *first2), *first3);
771  }
772  return univariate_statistics(se).mean;
773 }
774 
784 template<std::floating_point T, std::input_iterator I, std::input_iterator J>
785 inline auto poisson_neg_likelihood_loss(I first1, std::sentinel_for<I> auto last1, J first2) noexcept -> double {
786  using wide = eve::wide<T>;
787  auto constexpr s{ wide::size() };
788  auto const n{ std::distance(first1, last1) };
789  auto const m{ n - n % s };
790 
791  auto constexpr eps{ std::numeric_limits<T>::epsilon() };
792 
794  for (auto i = 0; i < m; i += s) {
795  wide y_true{first1, first1+s};
796  wide y_pred{first2, first2+s};
797  we(y_pred - y_true * eve::log(y_pred) + eve::log_abs_gamma(T{1} + y_true));
798  detail::advance(s, first1, first2);
799  }
800 
801  // use scalar accumulators for the remaining values
802  auto se = univariate_accumulator<T>::load_state(we.stats());
803  for(; first1 < last1; ++first1, ++first2) {
804  se(*first2 - *first1 * eve::log(*first2) + eve::log_abs_gamma(T{1} + *first1));
805  }
806  return univariate_statistics(se).sum;
807 }
808 
818 template<std::floating_point T, std::input_iterator I, std::input_iterator J, std::input_iterator K>
819 inline auto poisson_neg_likelihood_loss(I first1, std::sentinel_for<I> auto last1, J first2, K first3) noexcept -> double {
820  using wide = eve::wide<T>;
821  auto constexpr s{ wide::size() };
822  auto const n{ std::distance(first1, last1) };
823  auto const m{ n - n % s };
824 
825  auto constexpr eps{ std::numeric_limits<T>::epsilon() };
826 
828  for (auto i = 0; i < m; i += s) {
829  wide y_true{first1, first1+s};
830  wide y_pred = eve::mul(wide{first2, first2+s}, wide{first3, first3+s});
831  we(y_pred - y_true * eve::log(y_pred) + eve::log_abs_gamma(T{1} + y_true));
832  detail::advance(s, first1, first2, first3);
833  }
834 
835  // use scalar accumulators for the remaining values
836  auto se = univariate_accumulator<T>::load_state(we.stats());
837  for(; first1 < last1; ++first1, ++first2, ++first3) {
838  se(*first2 * *first3 - *first1 * eve::log(*first2 * *first3) + eve::log_abs_gamma(T{1} + *first1));
839  }
840  return univariate_statistics(se).sum;
841 }
842 } // namespace metrics
843 
844 } // namespace VSTAT_NAMESPACE
845 
846 #endif
requires concepts::arithmetic_projection< F1, std::iter_value_t< I > > and concepts::arithmetic_projection< F2, std::iter_value_t< J > > auto accumulate(I first1, std::sized_sentinel_for< I > auto last1, J first2, F1 &&f1=F1{}, F2 &&f2=F2{})
Compute bivariate statistics from two sequences of values. The values can be provided directly or via...
Definition: vstat.hpp:336
auto poisson_neg_likelihood_loss(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Negative log likelihood loss with Poisson distribution of target. The mean in each bin is multiplied ...
Definition: vstat.hpp:819
auto mean_absolute_percentage_error(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Weighted mean absolute percentage error.
Definition: vstat.hpp:752
auto mean_squared_log_error(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Computes the weighted mean squared logarithmic error.
Definition: vstat.hpp:617
auto mean_squared_error(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Computes the weighted mean squared error.
Definition: vstat.hpp:552
auto r2_score(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Computes the weighted coefficient of determination .
Definition: vstat.hpp:476
auto mean_absolute_error(I first1, std::sentinel_for< I > auto last1, J first2, K first3) noexcept -> double
Weighted mean absolute error.
Definition: vstat.hpp:682
Univariate accumulator object.
Definition: univariate.hpp:14
Univariate statistics.
Definition: univariate.hpp:84