norms.hpp 16 KB

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