norms.hpp 17 KB

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