• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  (C) Copyright Nick Thompson 2018.
2 //  Use, modification and distribution are subject to the
3 //  Boost Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef BOOST_MATH_TOOLS_NORMS_HPP
7 #define BOOST_MATH_TOOLS_NORMS_HPP
8 #include <algorithm>
9 #include <iterator>
10 #include <boost/assert.hpp>
11 #include <boost/math/tools/complex.hpp>
12 
13 
14 namespace boost::math::tools {
15 
16 // Mallat, "A Wavelet Tour of Signal Processing", equation 2.60:
17 template<class ForwardIterator>
total_variation(ForwardIterator first,ForwardIterator last)18 auto total_variation(ForwardIterator first, ForwardIterator last)
19 {
20     using T = typename std::iterator_traits<ForwardIterator>::value_type;
21     using std::abs;
22     BOOST_ASSERT_MSG(first != last && std::next(first) != last, "At least two samples are required to compute the total variation.");
23     auto it = first;
24     if constexpr (std::is_unsigned<T>::value)
25     {
26         T tmp = *it;
27         double tv = 0;
28         while (++it != last)
29         {
30             if (*it > tmp)
31             {
32                 tv += *it - tmp;
33             }
34             else
35             {
36                 tv += tmp - *it;
37             }
38             tmp = *it;
39         }
40         return tv;
41     }
42     else if constexpr (std::is_integral<T>::value)
43     {
44         double tv = 0;
45         double tmp = *it;
46         while(++it != last)
47         {
48             double tmp2 = *it;
49             tv += abs(tmp2 - tmp);
50             tmp = *it;
51         }
52         return tv;
53     }
54     else
55     {
56         T tmp = *it;
57         T tv = 0;
58         while (++it != last)
59         {
60             tv += abs(*it - tmp);
61             tmp = *it;
62         }
63         return tv;
64     }
65 }
66 
67 template<class Container>
total_variation(Container const & v)68 inline auto total_variation(Container const & v)
69 {
70     return total_variation(v.cbegin(), v.cend());
71 }
72 
73 
74 template<class ForwardIterator>
sup_norm(ForwardIterator first,ForwardIterator last)75 auto sup_norm(ForwardIterator first, ForwardIterator last)
76 {
77     BOOST_ASSERT_MSG(first != last, "At least one value is required to compute the sup norm.");
78     using T = typename std::iterator_traits<ForwardIterator>::value_type;
79     using std::abs;
80     if constexpr (boost::math::tools::is_complex_type<T>::value)
81     {
82         auto it = std::max_element(first, last, [](T a, T b) { return abs(b) > abs(a); });
83         return abs(*it);
84     }
85     else if constexpr (std::is_unsigned<T>::value)
86     {
87         return *std::max_element(first, last);
88     }
89     else
90     {
91         auto pair = std::minmax_element(first, last);
92         if (abs(*pair.first) > abs(*pair.second))
93         {
94             return abs(*pair.first);
95         }
96         else
97         {
98             return abs(*pair.second);
99         }
100     }
101 }
102 
103 template<class Container>
sup_norm(Container const & v)104 inline auto sup_norm(Container const & v)
105 {
106     return sup_norm(v.cbegin(), v.cend());
107 }
108 
109 template<class ForwardIterator>
l1_norm(ForwardIterator first,ForwardIterator last)110 auto l1_norm(ForwardIterator first, ForwardIterator last)
111 {
112     using T = typename std::iterator_traits<ForwardIterator>::value_type;
113     using std::abs;
114     if constexpr (std::is_unsigned<T>::value)
115     {
116         double l1 = 0;
117         for (auto it = first; it != last; ++it)
118         {
119             l1 += *it;
120         }
121         return l1;
122     }
123     else if constexpr (std::is_integral<T>::value)
124     {
125         double l1 = 0;
126         for (auto it = first; it != last; ++it)
127         {
128             double tmp = *it;
129             l1 += abs(tmp);
130         }
131         return l1;
132     }
133     else
134     {
135         decltype(abs(*first)) l1 = 0;
136         for (auto it = first; it != last; ++it)
137         {
138             l1 += abs(*it);
139         }
140         return l1;
141     }
142 
143 }
144 
145 template<class Container>
l1_norm(Container const & v)146 inline auto l1_norm(Container const & v)
147 {
148     return l1_norm(v.cbegin(), v.cend());
149 }
150 
151 
152 template<class ForwardIterator>
l2_norm(ForwardIterator first,ForwardIterator last)153 auto l2_norm(ForwardIterator first, ForwardIterator last)
154 {
155     using T = typename std::iterator_traits<ForwardIterator>::value_type;
156     using std::abs;
157     using std::norm;
158     using std::sqrt;
159     using std::is_floating_point;
160     if constexpr (boost::math::tools::is_complex_type<T>::value)
161     {
162         typedef typename T::value_type Real;
163         Real l2 = 0;
164         for (auto it = first; it != last; ++it)
165         {
166             l2 += norm(*it);
167         }
168         Real result = sqrt(l2);
169         if (!isfinite(result))
170         {
171             Real a = sup_norm(first, last);
172             l2 = 0;
173             for (auto it = first; it != last; ++it)
174             {
175                 l2 += norm(*it/a);
176             }
177             return a*sqrt(l2);
178         }
179         return result;
180     }
181     else if constexpr (is_floating_point<T>::value ||
182                        std::numeric_limits<T>::max_exponent)
183     {
184         T l2 = 0;
185         for (auto it = first; it != last; ++it)
186         {
187             l2 += (*it)*(*it);
188         }
189         T result = sqrt(l2);
190         // Higham, Accuracy and Stability of Numerical Algorithms,
191         // Problem 27.5 presents a different algorithm to deal with overflow.
192         // The algorithm used here takes 3 passes *if* there is overflow.
193         // Higham's algorithm is 1 pass, but more requires operations than the no overflow case.
194         // I'm operating under the assumption that overflow is rare since the dynamic range of floating point numbers is huge.
195         if (!isfinite(result))
196         {
197             T a = sup_norm(first, last);
198             l2 = 0;
199             for (auto it = first; it != last; ++it)
200             {
201                 T tmp = *it/a;
202                 l2 += tmp*tmp;
203             }
204             return a*sqrt(l2);
205         }
206         return result;
207     }
208     else
209     {
210         double l2 = 0;
211         for (auto it = first; it != last; ++it)
212         {
213             double tmp = *it;
214             l2 += tmp*tmp;
215         }
216         return sqrt(l2);
217     }
218 }
219 
220 template<class Container>
l2_norm(Container const & v)221 inline auto l2_norm(Container const & v)
222 {
223     return l2_norm(v.cbegin(), v.cend());
224 }
225 
226 template<class ForwardIterator>
l0_pseudo_norm(ForwardIterator first,ForwardIterator last)227 size_t l0_pseudo_norm(ForwardIterator first, ForwardIterator last)
228 {
229     using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
230     size_t count = 0;
231     for (auto it = first; it != last; ++it)
232     {
233         if (*it != RealOrComplex(0))
234         {
235             ++count;
236         }
237     }
238     return count;
239 }
240 
241 template<class Container>
l0_pseudo_norm(Container const & v)242 inline size_t l0_pseudo_norm(Container const & v)
243 {
244     return l0_pseudo_norm(v.cbegin(), v.cend());
245 }
246 
247 template<class ForwardIterator>
hamming_distance(ForwardIterator first1,ForwardIterator last1,ForwardIterator first2)248 size_t hamming_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
249 {
250     size_t count = 0;
251     auto it1 = first1;
252     auto it2 = first2;
253     while (it1 != last1)
254     {
255         if (*it1++ != *it2++)
256         {
257             ++count;
258         }
259     }
260     return count;
261 }
262 
263 template<class Container>
hamming_distance(Container const & v,Container const & w)264 inline size_t hamming_distance(Container const & v, Container const & w)
265 {
266     return hamming_distance(v.cbegin(), v.cend(), w.cbegin());
267 }
268 
269 template<class ForwardIterator>
lp_norm(ForwardIterator first,ForwardIterator last,unsigned p)270 auto lp_norm(ForwardIterator first, ForwardIterator last, unsigned p)
271 {
272     using std::abs;
273     using std::pow;
274     using std::is_floating_point;
275     using std::isfinite;
276     using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
277     if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
278     {
279         using std::norm;
280         using Real = typename RealOrComplex::value_type;
281         Real lp = 0;
282         for (auto it = first; it != last; ++it)
283         {
284             lp += pow(abs(*it), p);
285         }
286 
287         auto result = pow(lp, Real(1)/Real(p));
288         if (!isfinite(result))
289         {
290             auto a = boost::math::tools::sup_norm(first, last);
291             Real lp = 0;
292             for (auto it = first; it != last; ++it)
293             {
294                 lp += pow(abs(*it)/a, p);
295             }
296             result = a*pow(lp, Real(1)/Real(p));
297         }
298         return result;
299     }
300     else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
301     {
302         BOOST_ASSERT_MSG(p >= 0, "For p < 0, the lp norm is not a norm");
303         RealOrComplex lp = 0;
304 
305         for (auto it = first; it != last; ++it)
306         {
307             lp += pow(abs(*it), p);
308         }
309 
310         RealOrComplex result = pow(lp, RealOrComplex(1)/RealOrComplex(p));
311         if (!isfinite(result))
312         {
313             RealOrComplex a = boost::math::tools::sup_norm(first, last);
314             lp = 0;
315             for (auto it = first; it != last; ++it)
316             {
317                 lp += pow(abs(*it)/a, p);
318             }
319             result = a*pow(lp, RealOrComplex(1)/RealOrComplex(p));
320         }
321         return result;
322     }
323     else
324     {
325         double lp = 0;
326 
327         for (auto it = first; it != last; ++it)
328         {
329             double tmp = *it;
330             lp += pow(abs(tmp), p);
331         }
332         double result = pow(lp, 1.0/double(p));
333         if (!isfinite(result))
334         {
335             double a = boost::math::tools::sup_norm(first, last);
336             lp = 0;
337             for (auto it = first; it != last; ++it)
338             {
339                 double tmp = *it;
340                 lp += pow(abs(tmp)/a, p);
341             }
342             result = a*pow(lp, double(1)/double(p));
343         }
344         return result;
345     }
346 }
347 
348 template<class Container>
lp_norm(Container const & v,unsigned p)349 inline auto lp_norm(Container const & v, unsigned p)
350 {
351     return lp_norm(v.cbegin(), v.cend(), p);
352 }
353 
354 
355 template<class ForwardIterator>
lp_distance(ForwardIterator first1,ForwardIterator last1,ForwardIterator first2,unsigned p)356 auto lp_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2, unsigned p)
357 {
358     using std::pow;
359     using std::abs;
360     using std::is_floating_point;
361     using std::isfinite;
362     using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
363     auto it1 = first1;
364     auto it2 = first2;
365 
366     if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
367     {
368         using Real = typename RealOrComplex::value_type;
369         using std::norm;
370         Real dist = 0;
371         while(it1 != last1)
372         {
373             auto tmp = *it1++ - *it2++;
374             dist += pow(abs(tmp), p);
375         }
376         return pow(dist, Real(1)/Real(p));
377     }
378     else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
379     {
380         RealOrComplex dist = 0;
381         while(it1 != last1)
382         {
383             auto tmp = *it1++ - *it2++;
384             dist += pow(abs(tmp), p);
385         }
386         return pow(dist, RealOrComplex(1)/RealOrComplex(p));
387     }
388     else
389     {
390         double dist = 0;
391         while(it1 != last1)
392         {
393             double tmp1 = *it1++;
394             double tmp2 = *it2++;
395             // Naively you'd expect the integer subtraction to be faster,
396             // but this can overflow or wraparound:
397             //double tmp = *it1++ - *it2++;
398             dist += pow(abs(tmp1 - tmp2), p);
399         }
400         return pow(dist, 1.0/double(p));
401     }
402 }
403 
404 template<class Container>
lp_distance(Container const & v,Container const & w,unsigned p)405 inline auto lp_distance(Container const & v, Container const & w, unsigned p)
406 {
407     return lp_distance(v.cbegin(), v.cend(), w.cbegin(), p);
408 }
409 
410 
411 template<class ForwardIterator>
l1_distance(ForwardIterator first1,ForwardIterator last1,ForwardIterator first2)412 auto l1_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
413 {
414     using std::abs;
415     using std::is_floating_point;
416     using std::isfinite;
417     using T = typename std::iterator_traits<ForwardIterator>::value_type;
418     auto it1 = first1;
419     auto it2 = first2;
420     if constexpr (boost::math::tools::is_complex_type<T>::value)
421     {
422         using Real = typename T::value_type;
423         Real sum = 0;
424         while (it1 != last1) {
425             sum += abs(*it1++ - *it2++);
426         }
427         return sum;
428     }
429     else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
430     {
431         T sum = 0;
432         while (it1 != last1)
433         {
434             sum += abs(*it1++ - *it2++);
435         }
436         return sum;
437     }
438     else if constexpr (std::is_unsigned<T>::value)
439     {
440         double sum = 0;
441         while(it1 != last1)
442         {
443             T x1 = *it1++;
444             T x2 = *it2++;
445             if (x1 > x2)
446             {
447                 sum += (x1 - x2);
448             }
449             else
450             {
451                 sum += (x2 - x1);
452             }
453         }
454         return sum;
455     }
456     else if constexpr (std::is_integral<T>::value)
457     {
458         double sum = 0;
459         while(it1 != last1)
460         {
461             double x1 = *it1++;
462             double x2 = *it2++;
463             sum += abs(x1-x2);
464         }
465         return sum;
466     }
467     else
468     {
469         BOOST_ASSERT_MSG(false, "Could not recognize type.");
470     }
471 
472 }
473 
474 template<class Container>
l1_distance(Container const & v,Container const & w)475 auto l1_distance(Container const & v, Container const & w)
476 {
477     using std::size;
478     BOOST_ASSERT_MSG(size(v) == size(w),
479                      "L1 distance requires both containers to have the same number of elements");
480     return l1_distance(v.cbegin(), v.cend(), w.begin());
481 }
482 
483 template<class ForwardIterator>
l2_distance(ForwardIterator first1,ForwardIterator last1,ForwardIterator first2)484 auto l2_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
485 {
486     using std::abs;
487     using std::norm;
488     using std::sqrt;
489     using std::is_floating_point;
490     using std::isfinite;
491     using T = typename std::iterator_traits<ForwardIterator>::value_type;
492     auto it1 = first1;
493     auto it2 = first2;
494     if constexpr (boost::math::tools::is_complex_type<T>::value)
495     {
496         using Real = typename T::value_type;
497         Real sum = 0;
498         while (it1 != last1) {
499             sum += norm(*it1++ - *it2++);
500         }
501         return sqrt(sum);
502     }
503     else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
504     {
505         T sum = 0;
506         while (it1 != last1)
507         {
508             T tmp = *it1++ - *it2++;
509             sum += tmp*tmp;
510         }
511         return sqrt(sum);
512     }
513     else if constexpr (std::is_unsigned<T>::value)
514     {
515         double sum = 0;
516         while(it1 != last1)
517         {
518             T x1 = *it1++;
519             T x2 = *it2++;
520             if (x1 > x2)
521             {
522                 double tmp = x1-x2;
523                 sum += tmp*tmp;
524             }
525             else
526             {
527                 double tmp = x2 - x1;
528                 sum += tmp*tmp;
529             }
530         }
531         return sqrt(sum);
532     }
533     else
534     {
535         double sum = 0;
536         while(it1 != last1)
537         {
538             double x1 = *it1++;
539             double x2 = *it2++;
540             double tmp = x1-x2;
541             sum += tmp*tmp;
542         }
543         return sqrt(sum);
544     }
545 }
546 
547 template<class Container>
l2_distance(Container const & v,Container const & w)548 auto l2_distance(Container const & v, Container const & w)
549 {
550     using std::size;
551     BOOST_ASSERT_MSG(size(v) == size(w),
552                      "L2 distance requires both containers to have the same number of elements");
553     return l2_distance(v.cbegin(), v.cend(), w.begin());
554 }
555 
556 template<class ForwardIterator>
sup_distance(ForwardIterator first1,ForwardIterator last1,ForwardIterator first2)557 auto sup_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
558 {
559     using std::abs;
560     using std::norm;
561     using std::sqrt;
562     using std::is_floating_point;
563     using std::isfinite;
564     using T = typename std::iterator_traits<ForwardIterator>::value_type;
565     auto it1 = first1;
566     auto it2 = first2;
567     if constexpr (boost::math::tools::is_complex_type<T>::value)
568     {
569         using Real = typename T::value_type;
570         Real sup_sq = 0;
571         while (it1 != last1) {
572             Real tmp = norm(*it1++ - *it2++);
573             if (tmp > sup_sq) {
574                 sup_sq = tmp;
575             }
576         }
577         return sqrt(sup_sq);
578     }
579     else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
580     {
581         T sup = 0;
582         while (it1 != last1)
583         {
584             T tmp = *it1++ - *it2++;
585             if (sup < abs(tmp))
586             {
587                 sup = abs(tmp);
588             }
589         }
590         return sup;
591     }
592     else // integral values:
593     {
594         double sup = 0;
595         while(it1 != last1)
596         {
597             T x1 = *it1++;
598             T x2 = *it2++;
599             double tmp;
600             if (x1 > x2)
601             {
602                 tmp = x1-x2;
603             }
604             else
605             {
606                 tmp = x2 - x1;
607             }
608             if (sup < tmp) {
609                 sup = tmp;
610             }
611         }
612         return sup;
613     }
614 }
615 
616 template<class Container>
sup_distance(Container const & v,Container const & w)617 auto sup_distance(Container const & v, Container const & w)
618 {
619     using std::size;
620     BOOST_ASSERT_MSG(size(v) == size(w),
621                      "sup distance requires both containers to have the same number of elements");
622     return sup_distance(v.cbegin(), v.cend(), w.begin());
623 }
624 
625 
626 }
627 #endif
628