File indexing completed on 2025-01-18 09:40:40
0001
0002
0003
0004
0005
0006 #ifndef BOOST_MATH_TOOLS_NORMS_HPP
0007 #define BOOST_MATH_TOOLS_NORMS_HPP
0008 #include <algorithm>
0009 #include <iterator>
0010 #include <complex>
0011 #include <cmath>
0012 #include <boost/math/tools/assert.hpp>
0013 #include <boost/math/tools/complex.hpp>
0014
0015 #include <boost/math/tools/is_standalone.hpp>
0016 #ifndef BOOST_MATH_STANDALONE
0017 #include <boost/config.hpp>
0018 #ifdef BOOST_NO_CXX17_IF_CONSTEXPR
0019 #error "The header <boost/math/norms.hpp> can only be used in C++17 and later."
0020 #endif
0021 #endif
0022
0023
0024 namespace boost::math::tools {
0025
0026
0027 template<class ForwardIterator>
0028 auto total_variation(ForwardIterator first, ForwardIterator last)
0029 {
0030 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0031 using std::abs;
0032 BOOST_MATH_ASSERT_MSG(first != last && std::next(first) != last, "At least two samples are required to compute the total variation.");
0033 auto it = first;
0034 if constexpr (std::is_unsigned<T>::value)
0035 {
0036 T tmp = *it;
0037 double tv = 0;
0038 while (++it != last)
0039 {
0040 if (*it > tmp)
0041 {
0042 tv += *it - tmp;
0043 }
0044 else
0045 {
0046 tv += tmp - *it;
0047 }
0048 tmp = *it;
0049 }
0050 return tv;
0051 }
0052 else if constexpr (std::is_integral<T>::value)
0053 {
0054 double tv = 0;
0055 double tmp = *it;
0056 while(++it != last)
0057 {
0058 double tmp2 = *it;
0059 tv += abs(tmp2 - tmp);
0060 tmp = *it;
0061 }
0062 return tv;
0063 }
0064 else
0065 {
0066 T tmp = *it;
0067 T tv = 0;
0068 while (++it != last)
0069 {
0070 tv += abs(*it - tmp);
0071 tmp = *it;
0072 }
0073 return tv;
0074 }
0075 }
0076
0077 template<class Container>
0078 inline auto total_variation(Container const & v)
0079 {
0080 return total_variation(v.cbegin(), v.cend());
0081 }
0082
0083
0084 template<class ForwardIterator>
0085 auto sup_norm(ForwardIterator first, ForwardIterator last)
0086 {
0087 BOOST_MATH_ASSERT_MSG(first != last, "At least one value is required to compute the sup norm.");
0088 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0089 using std::abs;
0090 if constexpr (boost::math::tools::is_complex_type<T>::value)
0091 {
0092 auto it = std::max_element(first, last, [](T a, T b) { return abs(b) > abs(a); });
0093 return abs(*it);
0094 }
0095 else if constexpr (std::is_unsigned<T>::value)
0096 {
0097 return *std::max_element(first, last);
0098 }
0099 else
0100 {
0101 auto pair = std::minmax_element(first, last);
0102 if (abs(*pair.first) > abs(*pair.second))
0103 {
0104 return abs(*pair.first);
0105 }
0106 else
0107 {
0108 return abs(*pair.second);
0109 }
0110 }
0111 }
0112
0113 template<class Container>
0114 inline auto sup_norm(Container const & v)
0115 {
0116 return sup_norm(v.cbegin(), v.cend());
0117 }
0118
0119 template<class ForwardIterator>
0120 auto l1_norm(ForwardIterator first, ForwardIterator last)
0121 {
0122 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0123 using std::abs;
0124 if constexpr (std::is_unsigned<T>::value)
0125 {
0126 double l1 = 0;
0127 for (auto it = first; it != last; ++it)
0128 {
0129 l1 += *it;
0130 }
0131 return l1;
0132 }
0133 else if constexpr (std::is_integral<T>::value)
0134 {
0135 double l1 = 0;
0136 for (auto it = first; it != last; ++it)
0137 {
0138 double tmp = *it;
0139 l1 += abs(tmp);
0140 }
0141 return l1;
0142 }
0143 else
0144 {
0145 decltype(abs(*first)) l1 = 0;
0146 for (auto it = first; it != last; ++it)
0147 {
0148 l1 += abs(*it);
0149 }
0150 return l1;
0151 }
0152
0153 }
0154
0155 template<class Container>
0156 inline auto l1_norm(Container const & v)
0157 {
0158 return l1_norm(v.cbegin(), v.cend());
0159 }
0160
0161
0162 template<class ForwardIterator>
0163 auto l2_norm(ForwardIterator first, ForwardIterator last)
0164 {
0165 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0166 using std::abs;
0167 using std::norm;
0168 using std::sqrt;
0169 using std::is_floating_point;
0170 using std::isfinite;
0171 if constexpr (boost::math::tools::is_complex_type<T>::value)
0172 {
0173 typedef typename T::value_type Real;
0174 Real l2 = 0;
0175 for (auto it = first; it != last; ++it)
0176 {
0177 l2 += norm(*it);
0178 }
0179 Real result = sqrt(l2);
0180 if (!isfinite(result))
0181 {
0182 Real a = sup_norm(first, last);
0183 l2 = 0;
0184 for (auto it = first; it != last; ++it)
0185 {
0186 l2 += norm(*it/a);
0187 }
0188 return a*sqrt(l2);
0189 }
0190 return result;
0191 }
0192 else if constexpr (is_floating_point<T>::value ||
0193 std::numeric_limits<T>::max_exponent)
0194 {
0195 T l2 = 0;
0196 for (auto it = first; it != last; ++it)
0197 {
0198 l2 += (*it)*(*it);
0199 }
0200 T result = sqrt(l2);
0201
0202
0203
0204
0205
0206 if (!isfinite(result))
0207 {
0208 T a = sup_norm(first, last);
0209 l2 = 0;
0210 for (auto it = first; it != last; ++it)
0211 {
0212 T tmp = *it/a;
0213 l2 += tmp*tmp;
0214 }
0215 return a*sqrt(l2);
0216 }
0217 return result;
0218 }
0219 else
0220 {
0221 double l2 = 0;
0222 for (auto it = first; it != last; ++it)
0223 {
0224 double tmp = *it;
0225 l2 += tmp*tmp;
0226 }
0227 return sqrt(l2);
0228 }
0229 }
0230
0231 template<class Container>
0232 inline auto l2_norm(Container const & v)
0233 {
0234 return l2_norm(v.cbegin(), v.cend());
0235 }
0236
0237 template<class ForwardIterator>
0238 size_t l0_pseudo_norm(ForwardIterator first, ForwardIterator last)
0239 {
0240 using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
0241 size_t count = 0;
0242 for (auto it = first; it != last; ++it)
0243 {
0244 if (*it != RealOrComplex(0))
0245 {
0246 ++count;
0247 }
0248 }
0249 return count;
0250 }
0251
0252 template<class Container>
0253 inline size_t l0_pseudo_norm(Container const & v)
0254 {
0255 return l0_pseudo_norm(v.cbegin(), v.cend());
0256 }
0257
0258 template<class ForwardIterator>
0259 size_t hamming_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
0260 {
0261 size_t count = 0;
0262 auto it1 = first1;
0263 auto it2 = first2;
0264 while (it1 != last1)
0265 {
0266 if (*it1++ != *it2++)
0267 {
0268 ++count;
0269 }
0270 }
0271 return count;
0272 }
0273
0274 template<class Container>
0275 inline size_t hamming_distance(Container const & v, Container const & w)
0276 {
0277 return hamming_distance(v.cbegin(), v.cend(), w.cbegin());
0278 }
0279
0280 template<class ForwardIterator>
0281 auto lp_norm(ForwardIterator first, ForwardIterator last, unsigned p)
0282 {
0283 using std::abs;
0284 using std::pow;
0285 using std::is_floating_point;
0286 using std::isfinite;
0287 using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
0288 if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
0289 {
0290 using std::norm;
0291 using Real = typename RealOrComplex::value_type;
0292 Real lp = 0;
0293 for (auto it = first; it != last; ++it)
0294 {
0295 lp += pow(abs(*it), p);
0296 }
0297
0298 auto result = pow(lp, Real(1)/Real(p));
0299 if (!isfinite(result))
0300 {
0301 auto a = boost::math::tools::sup_norm(first, last);
0302 Real lp = 0;
0303 for (auto it = first; it != last; ++it)
0304 {
0305 lp += pow(abs(*it)/a, p);
0306 }
0307 result = a*pow(lp, Real(1)/Real(p));
0308 }
0309 return result;
0310 }
0311 else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
0312 {
0313 BOOST_MATH_ASSERT_MSG(p >= 0, "For p < 0, the lp norm is not a norm");
0314 RealOrComplex lp = 0;
0315
0316 for (auto it = first; it != last; ++it)
0317 {
0318 lp += pow(abs(*it), p);
0319 }
0320
0321 RealOrComplex result = pow(lp, RealOrComplex(1)/RealOrComplex(p));
0322 if (!isfinite(result))
0323 {
0324 RealOrComplex a = boost::math::tools::sup_norm(first, last);
0325 lp = 0;
0326 for (auto it = first; it != last; ++it)
0327 {
0328 lp += pow(abs(*it)/a, p);
0329 }
0330 result = a*pow(lp, RealOrComplex(1)/RealOrComplex(p));
0331 }
0332 return result;
0333 }
0334 else
0335 {
0336 double lp = 0;
0337
0338 for (auto it = first; it != last; ++it)
0339 {
0340 double tmp = *it;
0341 lp += pow(abs(tmp), p);
0342 }
0343 double result = pow(lp, 1.0/static_cast<double>(p));
0344 if (!isfinite(result))
0345 {
0346 double a = boost::math::tools::sup_norm(first, last);
0347 lp = 0;
0348 for (auto it = first; it != last; ++it)
0349 {
0350 double tmp = *it;
0351 lp += pow(abs(tmp)/a, p);
0352 }
0353 result = a*pow(lp, static_cast<double>(1)/static_cast<double>(p));
0354 }
0355 return result;
0356 }
0357 }
0358
0359 template<class Container>
0360 inline auto lp_norm(Container const & v, unsigned p)
0361 {
0362 return lp_norm(v.cbegin(), v.cend(), p);
0363 }
0364
0365
0366 template<class ForwardIterator>
0367 auto lp_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2, unsigned p)
0368 {
0369 using std::pow;
0370 using std::abs;
0371 using std::is_floating_point;
0372 using std::isfinite;
0373 using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
0374 auto it1 = first1;
0375 auto it2 = first2;
0376
0377 if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
0378 {
0379 using Real = typename RealOrComplex::value_type;
0380 using std::norm;
0381 Real dist = 0;
0382 while(it1 != last1)
0383 {
0384 auto tmp = *it1++ - *it2++;
0385 dist += pow(abs(tmp), p);
0386 }
0387 return pow(dist, Real(1)/Real(p));
0388 }
0389 else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
0390 {
0391 RealOrComplex dist = 0;
0392 while(it1 != last1)
0393 {
0394 auto tmp = *it1++ - *it2++;
0395 dist += pow(abs(tmp), p);
0396 }
0397 return pow(dist, RealOrComplex(1)/RealOrComplex(p));
0398 }
0399 else
0400 {
0401 double dist = 0;
0402 while(it1 != last1)
0403 {
0404 double tmp1 = *it1++;
0405 double tmp2 = *it2++;
0406
0407
0408
0409 dist += pow(abs(tmp1 - tmp2), p);
0410 }
0411 return pow(dist, 1.0/static_cast<double>(p));
0412 }
0413 }
0414
0415 template<class Container>
0416 inline auto lp_distance(Container const & v, Container const & w, unsigned p)
0417 {
0418 return lp_distance(v.cbegin(), v.cend(), w.cbegin(), p);
0419 }
0420
0421
0422 template<class ForwardIterator>
0423 auto l1_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
0424 {
0425 using std::abs;
0426 using std::is_floating_point;
0427 using std::isfinite;
0428 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0429 auto it1 = first1;
0430 auto it2 = first2;
0431 if constexpr (boost::math::tools::is_complex_type<T>::value)
0432 {
0433 using Real = typename T::value_type;
0434 Real sum = 0;
0435 while (it1 != last1) {
0436 sum += abs(*it1++ - *it2++);
0437 }
0438 return sum;
0439 }
0440 else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
0441 {
0442 T sum = 0;
0443 while (it1 != last1)
0444 {
0445 sum += abs(*it1++ - *it2++);
0446 }
0447 return sum;
0448 }
0449 else if constexpr (std::is_unsigned<T>::value)
0450 {
0451 double sum = 0;
0452 while(it1 != last1)
0453 {
0454 T x1 = *it1++;
0455 T x2 = *it2++;
0456 if (x1 > x2)
0457 {
0458 sum += (x1 - x2);
0459 }
0460 else
0461 {
0462 sum += (x2 - x1);
0463 }
0464 }
0465 return sum;
0466 }
0467 else if constexpr (std::is_integral<T>::value)
0468 {
0469 double sum = 0;
0470 while(it1 != last1)
0471 {
0472 double x1 = *it1++;
0473 double x2 = *it2++;
0474 sum += abs(x1-x2);
0475 }
0476 return sum;
0477 }
0478 else
0479 {
0480 BOOST_MATH_ASSERT_MSG(false, "Could not recognize type.");
0481 }
0482
0483 }
0484
0485 template<class Container>
0486 auto l1_distance(Container const & v, Container const & w)
0487 {
0488 using std::size;
0489 BOOST_MATH_ASSERT_MSG(size(v) == size(w),
0490 "L1 distance requires both containers to have the same number of elements");
0491 return l1_distance(v.cbegin(), v.cend(), w.begin());
0492 }
0493
0494 template<class ForwardIterator>
0495 auto l2_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
0496 {
0497 using std::abs;
0498 using std::norm;
0499 using std::sqrt;
0500 using std::is_floating_point;
0501 using std::isfinite;
0502 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0503 auto it1 = first1;
0504 auto it2 = first2;
0505 if constexpr (boost::math::tools::is_complex_type<T>::value)
0506 {
0507 using Real = typename T::value_type;
0508 Real sum = 0;
0509 while (it1 != last1) {
0510 sum += norm(*it1++ - *it2++);
0511 }
0512 return sqrt(sum);
0513 }
0514 else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
0515 {
0516 T sum = 0;
0517 while (it1 != last1)
0518 {
0519 T tmp = *it1++ - *it2++;
0520 sum += tmp*tmp;
0521 }
0522 return sqrt(sum);
0523 }
0524 else if constexpr (std::is_unsigned<T>::value)
0525 {
0526 double sum = 0;
0527 while(it1 != last1)
0528 {
0529 T x1 = *it1++;
0530 T x2 = *it2++;
0531 if (x1 > x2)
0532 {
0533 double tmp = x1-x2;
0534 sum += tmp*tmp;
0535 }
0536 else
0537 {
0538 double tmp = x2 - x1;
0539 sum += tmp*tmp;
0540 }
0541 }
0542 return sqrt(sum);
0543 }
0544 else
0545 {
0546 double sum = 0;
0547 while(it1 != last1)
0548 {
0549 double x1 = *it1++;
0550 double x2 = *it2++;
0551 double tmp = x1-x2;
0552 sum += tmp*tmp;
0553 }
0554 return sqrt(sum);
0555 }
0556 }
0557
0558 template<class Container>
0559 auto l2_distance(Container const & v, Container const & w)
0560 {
0561 using std::size;
0562 BOOST_MATH_ASSERT_MSG(size(v) == size(w),
0563 "L2 distance requires both containers to have the same number of elements");
0564 return l2_distance(v.cbegin(), v.cend(), w.begin());
0565 }
0566
0567 template<class ForwardIterator>
0568 auto sup_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
0569 {
0570 using std::abs;
0571 using std::norm;
0572 using std::sqrt;
0573 using std::is_floating_point;
0574 using std::isfinite;
0575 using T = typename std::iterator_traits<ForwardIterator>::value_type;
0576 auto it1 = first1;
0577 auto it2 = first2;
0578 if constexpr (boost::math::tools::is_complex_type<T>::value)
0579 {
0580 using Real = typename T::value_type;
0581 Real sup_sq = 0;
0582 while (it1 != last1) {
0583 Real tmp = norm(*it1++ - *it2++);
0584 if (tmp > sup_sq) {
0585 sup_sq = tmp;
0586 }
0587 }
0588 return sqrt(sup_sq);
0589 }
0590 else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
0591 {
0592 T sup = 0;
0593 while (it1 != last1)
0594 {
0595 T tmp = *it1++ - *it2++;
0596 if (sup < abs(tmp))
0597 {
0598 sup = abs(tmp);
0599 }
0600 }
0601 return sup;
0602 }
0603 else
0604 {
0605 double sup = 0;
0606 while(it1 != last1)
0607 {
0608 T x1 = *it1++;
0609 T x2 = *it2++;
0610 double tmp;
0611 if (x1 > x2)
0612 {
0613 tmp = x1-x2;
0614 }
0615 else
0616 {
0617 tmp = x2 - x1;
0618 }
0619 if (sup < tmp) {
0620 sup = tmp;
0621 }
0622 }
0623 return sup;
0624 }
0625 }
0626
0627 template<class Container>
0628 auto sup_distance(Container const & v, Container const & w)
0629 {
0630 using std::size;
0631 BOOST_MATH_ASSERT_MSG(size(v) == size(w),
0632 "sup distance requires both containers to have the same number of elements");
0633 return sup_distance(v.cbegin(), v.cend(), w.begin());
0634 }
0635
0636
0637 }
0638 #endif