Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:40:40

0001 //  (C) Copyright Nick Thompson 2018.
0002 //  Use, modification and distribution are subject to the
0003 //  Boost Software License, Version 1.0. (See accompanying file
0004 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 // Mallat, "A Wavelet Tour of Signal Processing", equation 2.60:
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         // Higham, Accuracy and Stability of Numerical Algorithms,
0202         // Problem 27.5 presents a different algorithm to deal with overflow.
0203         // The algorithm used here takes 3 passes *if* there is overflow.
0204         // Higham's algorithm is 1 pass, but more requires operations than the no overflow case.
0205         // I'm operating under the assumption that overflow is rare since the dynamic range of floating point numbers is huge.
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             // Naively you'd expect the integer subtraction to be faster,
0407             // but this can overflow or wraparound:
0408             //double tmp = *it1++ - *it2++;
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 // integral values:
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