norms.hpp 16 KB

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