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