xnorm.hpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /***************************************************************************
  2. * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
  3. * Copyright (c) Ullrich Koethe
  4. * Copyright (c) QuantStack *
  5. * *
  6. * Distributed under the terms of the BSD 3-Clause License. *
  7. * *
  8. * The full license is in the file LICENSE, distributed with this software. *
  9. ****************************************************************************/
  10. #ifndef XTENSOR_NORM_HPP
  11. #define XTENSOR_NORM_HPP
  12. #include <cmath>
  13. // std::abs(int) prior to C++ 17
  14. #include <complex>
  15. #include <cstdlib>
  16. #include <xtl/xtype_traits.hpp>
  17. #include "xmath.hpp"
  18. #include "xoperation.hpp"
  19. #include "xutils.hpp"
  20. namespace xt
  21. {
  22. /********************************************
  23. * type inference for norm and squared norm *
  24. ********************************************/
  25. template <class T>
  26. struct norm_type;
  27. template <class T>
  28. struct squared_norm_type;
  29. namespace traits_detail
  30. {
  31. template <class T, bool scalar = xtl::is_arithmetic<T>::value>
  32. struct norm_of_scalar_impl;
  33. template <class T>
  34. struct norm_of_scalar_impl<T, false>
  35. {
  36. static const bool value = false;
  37. using norm_type = void*;
  38. using squared_norm_type = void*;
  39. };
  40. template <class T>
  41. struct norm_of_scalar_impl<T, true>
  42. {
  43. static const bool value = true;
  44. using norm_type = xtl::promote_type_t<T>;
  45. using squared_norm_type = xtl::promote_type_t<T>;
  46. };
  47. template <class T, bool integral = xtl::is_integral<T>::value, bool floating = std::is_floating_point<T>::value>
  48. struct norm_of_array_elements_impl;
  49. template <>
  50. struct norm_of_array_elements_impl<void*, false, false>
  51. {
  52. using norm_type = void*;
  53. using squared_norm_type = void*;
  54. };
  55. template <class T>
  56. struct norm_of_array_elements_impl<T, false, false>
  57. {
  58. using norm_type = typename norm_type<T>::type;
  59. using squared_norm_type = typename squared_norm_type<T>::type;
  60. };
  61. template <class T>
  62. struct norm_of_array_elements_impl<T, true, false>
  63. {
  64. static_assert(
  65. !std::is_same<T, char>::value,
  66. "'char' is not a numeric type, use 'signed char' or 'unsigned char'."
  67. );
  68. using norm_type = double;
  69. using squared_norm_type = uint64_t;
  70. };
  71. template <class T>
  72. struct norm_of_array_elements_impl<T, false, true>
  73. {
  74. using norm_type = double;
  75. using squared_norm_type = double;
  76. };
  77. template <>
  78. struct norm_of_array_elements_impl<long double, false, true>
  79. {
  80. using norm_type = long double;
  81. using squared_norm_type = long double;
  82. };
  83. template <class ARRAY>
  84. struct norm_of_vector_impl
  85. {
  86. static void* test(...);
  87. template <class U>
  88. static typename U::value_type test(U*, typename U::value_type* = 0);
  89. using T = decltype(test(std::declval<ARRAY*>()));
  90. static const bool value = !std::is_same<T, void*>::value;
  91. using norm_type = typename norm_of_array_elements_impl<T>::norm_type;
  92. using squared_norm_type = typename norm_of_array_elements_impl<T>::squared_norm_type;
  93. };
  94. template <class U>
  95. struct norm_type_base
  96. {
  97. using T = std::decay_t<U>;
  98. static_assert(
  99. !std::is_same<T, char>::value,
  100. "'char' is not a numeric type, use 'signed char' or 'unsigned char'."
  101. );
  102. using norm_of_scalar = norm_of_scalar_impl<T>;
  103. using norm_of_vector = norm_of_vector_impl<T>;
  104. static const bool value = norm_of_scalar::value || norm_of_vector::value;
  105. static_assert(value, "norm_type<T> are undefined for type U.");
  106. };
  107. } // namespace traits_detail
  108. /**
  109. * @brief Traits class for the result type of the <tt>norm_l2()</tt> function.
  110. *
  111. * Member 'type' defines the result of <tt>norm_l2(t)</tt>, where <tt>t</tt>
  112. * is of type @tparam T. It implements the following rules designed to
  113. * minimize the potential for overflow:
  114. * - @tparam T is an arithmetic type: 'type' is the result type of <tt>abs(t)</tt>.
  115. * - @tparam T is a container of 'long double' elements: 'type' is <tt>long double</tt>.
  116. * - @tparam T is a container of another arithmetic type: 'type' is <tt>double</tt>.
  117. * - @tparam T is a container of some other type: 'type' is the element's norm type,
  118. *
  119. * Containers are recognized by having an embedded typedef 'value_type'.
  120. * To change the behavior for a case not covered here, specialize the
  121. * <tt>traits_detail::norm_type_base</tt> template.
  122. */
  123. template <class T>
  124. struct norm_type : traits_detail::norm_type_base<T>
  125. {
  126. using base_type = traits_detail::norm_type_base<T>;
  127. using type = typename std::conditional<
  128. base_type::norm_of_vector::value,
  129. typename base_type::norm_of_vector::norm_type,
  130. typename base_type::norm_of_scalar::norm_type>::type;
  131. };
  132. /**
  133. * Abbreviation of 'typename norm_type<T>::type'.
  134. */
  135. template <class T>
  136. using norm_type_t = typename norm_type<T>::type;
  137. /**
  138. * @brief Traits class for the result type of the <tt>norm_sq()</tt> function.
  139. *
  140. * Member 'type' defines the result of <tt>norm_sq(t)</tt>, where <tt>t</tt>
  141. * is of type @tparam T. It implements the following rules designed to
  142. * minimize the potential for overflow:
  143. * - @tparam T is an arithmetic type: 'type' is the result type of <tt>t*t</tt>.
  144. * - @tparam T is a container of 'long double' elements: 'type' is <tt>long double</tt>.
  145. * - @tparam T is a container of another floating-point type: 'type' is <tt>double</tt>.
  146. * - @tparam T is a container of integer elements: 'type' is <tt>uint64_t</tt>.
  147. * - @tparam T is a container of some other type: 'type' is the element's squared norm type,
  148. *
  149. * Containers are recognized by having an embedded typedef 'value_type'.
  150. * To change the behavior for a case not covered here, specialize the
  151. * <tt>traits_detail::norm_type_base</tt> template.
  152. */
  153. template <class T>
  154. struct squared_norm_type : traits_detail::norm_type_base<T>
  155. {
  156. using base_type = traits_detail::norm_type_base<T>;
  157. using type = typename std::conditional<
  158. base_type::norm_of_vector::value,
  159. typename base_type::norm_of_vector::squared_norm_type,
  160. typename base_type::norm_of_scalar::squared_norm_type>::type;
  161. };
  162. /**
  163. * Abbreviation of 'typename squared_norm_type<T>::type'.
  164. */
  165. template <class T>
  166. using squared_norm_type_t = typename squared_norm_type<T>::type;
  167. /*************************************
  168. * norm functions for built-in types *
  169. *************************************/
  170. ///@cond DOXYGEN_INCLUDE_SFINAE
  171. #define XTENSOR_DEFINE_SIGNED_NORMS(T) \
  172. inline auto norm_lp(T t, double p) noexcept \
  173. { \
  174. using rt = decltype(std::abs(t)); \
  175. return p == 0.0 ? static_cast<rt>(t != 0) : std::abs(t); \
  176. } \
  177. inline auto norm_lp_to_p(T t, double p) noexcept \
  178. { \
  179. using rt = xtl::real_promote_type_t<T>; \
  180. return p == 0.0 ? static_cast<rt>(t != 0) \
  181. : std::pow(static_cast<rt>(std::abs(t)), static_cast<rt>(p)); \
  182. } \
  183. inline std::size_t norm_l0(T t) noexcept \
  184. { \
  185. return (t != 0); \
  186. } \
  187. inline auto norm_l1(T t) noexcept \
  188. { \
  189. return std::abs(t); \
  190. } \
  191. inline auto norm_l2(T t) noexcept \
  192. { \
  193. return std::abs(t); \
  194. } \
  195. inline auto norm_linf(T t) noexcept \
  196. { \
  197. return std::abs(t); \
  198. } \
  199. inline auto norm_sq(T t) noexcept \
  200. { \
  201. return t * t; \
  202. }
  203. XTENSOR_DEFINE_SIGNED_NORMS(signed char)
  204. XTENSOR_DEFINE_SIGNED_NORMS(short)
  205. XTENSOR_DEFINE_SIGNED_NORMS(int)
  206. XTENSOR_DEFINE_SIGNED_NORMS(long)
  207. XTENSOR_DEFINE_SIGNED_NORMS(long long)
  208. XTENSOR_DEFINE_SIGNED_NORMS(float)
  209. XTENSOR_DEFINE_SIGNED_NORMS(double)
  210. XTENSOR_DEFINE_SIGNED_NORMS(long double)
  211. #undef XTENSOR_DEFINE_SIGNED_NORMS
  212. #define XTENSOR_DEFINE_UNSIGNED_NORMS(T) \
  213. inline T norm_lp(T t, double p) noexcept \
  214. { \
  215. return p == 0.0 ? (t != 0) : t; \
  216. } \
  217. inline auto norm_lp_to_p(T t, double p) noexcept \
  218. { \
  219. using rt = xtl::real_promote_type_t<T>; \
  220. return p == 0.0 ? static_cast<rt>(t != 0) : std::pow(static_cast<rt>(t), static_cast<rt>(p)); \
  221. } \
  222. inline T norm_l0(T t) noexcept \
  223. { \
  224. return t != 0 ? 1 : 0; \
  225. } \
  226. inline T norm_l1(T t) noexcept \
  227. { \
  228. return t; \
  229. } \
  230. inline T norm_l2(T t) noexcept \
  231. { \
  232. return t; \
  233. } \
  234. inline T norm_linf(T t) noexcept \
  235. { \
  236. return t; \
  237. } \
  238. inline auto norm_sq(T t) noexcept \
  239. { \
  240. return t * t; \
  241. }
  242. XTENSOR_DEFINE_UNSIGNED_NORMS(unsigned char)
  243. XTENSOR_DEFINE_UNSIGNED_NORMS(unsigned short)
  244. XTENSOR_DEFINE_UNSIGNED_NORMS(unsigned int)
  245. XTENSOR_DEFINE_UNSIGNED_NORMS(unsigned long)
  246. XTENSOR_DEFINE_UNSIGNED_NORMS(unsigned long long)
  247. #undef XTENSOR_DEFINE_UNSIGNED_NORMS
  248. /***********************************
  249. * norm functions for std::complex *
  250. ***********************************/
  251. /**
  252. * @brief L0 pseudo-norm of a complex number.
  253. * Equivalent to <tt>t != 0</tt>.
  254. */
  255. template <class T>
  256. inline uint64_t norm_l0(const std::complex<T>& t) noexcept
  257. {
  258. return t.real() != 0 || t.imag() != 0;
  259. }
  260. /**
  261. * @brief L1 norm of a complex number.
  262. */
  263. template <class T>
  264. inline auto norm_l1(const std::complex<T>& t) noexcept
  265. {
  266. return std::abs(t.real()) + std::abs(t.imag());
  267. }
  268. /**
  269. * @brief L2 norm of a complex number.
  270. * Equivalent to <tt>std::abs(t)</tt>.
  271. */
  272. template <class T>
  273. inline auto norm_l2(const std::complex<T>& t) noexcept
  274. {
  275. return std::abs(t);
  276. }
  277. /**
  278. * @brief Squared norm of a complex number.
  279. * Equivalent to <tt>std::norm(t)</tt> (yes, the C++ standard really defines
  280. * <tt>norm()</tt> to compute the squared norm).
  281. */
  282. template <class T>
  283. inline auto norm_sq(const std::complex<T>& t) noexcept
  284. {
  285. // Does not use std::norm since it returns a std::complex on OSX
  286. return t.real() * t.real() + t.imag() * t.imag();
  287. }
  288. /**
  289. * @brief L-infinity norm of a complex number.
  290. */
  291. template <class T>
  292. inline auto norm_linf(const std::complex<T>& t) noexcept
  293. {
  294. return (std::max)(std::abs(t.real()), std::abs(t.imag()));
  295. }
  296. /**
  297. * @brief p-th power of the Lp norm of a complex number.
  298. */
  299. template <class T>
  300. inline auto norm_lp_to_p(const std::complex<T>& t, double p) noexcept
  301. {
  302. using rt = decltype(std::pow(std::abs(t.real()), static_cast<T>(p)));
  303. return p == 0 ? static_cast<rt>(t.real() != 0 || t.imag() != 0)
  304. : std::pow(std::abs(t.real()), static_cast<T>(p))
  305. + std::pow(std::abs(t.imag()), static_cast<T>(p));
  306. }
  307. /**
  308. * @brief Lp norm of a complex number.
  309. */
  310. template <class T>
  311. inline auto norm_lp(const std::complex<T>& t, double p) noexcept
  312. {
  313. return p == 0 ? norm_lp_to_p(t, p) : std::pow(norm_lp_to_p(t, p), 1.0 / p);
  314. }
  315. /***********************************
  316. * norm functions for xexpressions *
  317. ***********************************/
  318. #define XTENSOR_NORM_FUNCTION_AXES(NAME) \
  319. template <class E, class I, std::size_t N, class EVS = DEFAULT_STRATEGY_REDUCERS> \
  320. inline auto NAME(E&& e, const I(&axes)[N], EVS es = EVS()) noexcept \
  321. { \
  322. using axes_type = std::array<typename std::decay_t<E>::size_type, N>; \
  323. return NAME(std::forward<E>(e), xtl::forward_sequence<axes_type, decltype(axes)>(axes), es); \
  324. }
  325. namespace detail
  326. {
  327. template <class T>
  328. struct norm_value_type
  329. {
  330. using type = T;
  331. };
  332. template <class T>
  333. struct norm_value_type<std::complex<T>>
  334. {
  335. using type = T;
  336. };
  337. template <class T>
  338. using norm_value_type_t = typename norm_value_type<T>::type;
  339. }
  340. #define XTENSOR_EMPTY
  341. #define XTENSOR_COMMA ,
  342. #define XTENSOR_NORM_FUNCTION(NAME, RESULT_TYPE, REDUCE_EXPR, REDUCE_OP, MERGE_FUNC) \
  343. template <class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation<is_reducer_options<X>>)> \
  344. inline auto NAME(E&& e, X&& axes, EVS es = EVS()) noexcept \
  345. { \
  346. using value_type = typename std::decay_t<E>::value_type; \
  347. using result_type = detail::norm_value_type_t<RESULT_TYPE>; \
  348. \
  349. auto reduce_func = [](result_type const& r, value_type const& v) \
  350. { \
  351. return REDUCE_EXPR(r REDUCE_OP NAME(v)); \
  352. }; \
  353. \
  354. return xt::reduce( \
  355. make_xreducer_functor(std::move(reduce_func), const_value<result_type>(0), MERGE_FUNC<result_type>()), \
  356. std::forward<E>(e), \
  357. std::forward<X>(axes), \
  358. es \
  359. ); \
  360. } \
  361. \
  362. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)> \
  363. inline auto NAME(E&& e, EVS es = EVS()) noexcept \
  364. { \
  365. return NAME(std::forward<E>(e), arange(e.dimension()), es); \
  366. } \
  367. XTENSOR_NORM_FUNCTION_AXES(NAME)
  368. XTENSOR_NORM_FUNCTION(norm_l0, unsigned long long, XTENSOR_EMPTY, +, std::plus)
  369. XTENSOR_NORM_FUNCTION(norm_l1, xtl::big_promote_type_t<value_type>, XTENSOR_EMPTY, +, std::plus)
  370. XTENSOR_NORM_FUNCTION(norm_sq, xtl::big_promote_type_t<value_type>, XTENSOR_EMPTY, +, std::plus)
  371. XTENSOR_NORM_FUNCTION(
  372. norm_linf,
  373. decltype(norm_linf(std::declval<value_type>())),
  374. (std::max<result_type>),
  375. XTENSOR_COMMA,
  376. math::maximum
  377. )
  378. #undef XTENSOR_EMPTY
  379. #undef XTENSOR_COMMA
  380. #undef XTENSOR_NORM_FUNCTION
  381. #undef XTENSOR_NORM_FUNCTION_AXES
  382. /// @endcond
  383. /**
  384. * @ingroup red_functions
  385. * @brief L0 (count) pseudo-norm of an array-like argument over given axes.
  386. *
  387. * Returns an \ref xreducer for the L0 pseudo-norm of the elements across given \em axes.
  388. * @param e an \ref xexpression
  389. * @param axes the axes along which the norm is computed (optional)
  390. * @param es evaluation strategy to use (lazy (default), or immediate)
  391. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  392. * When no axes are provided, the norm is calculated over the entire array. In this case,
  393. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  394. */
  395. template <class E, class X, class EVS, class>
  396. auto norm_l0(E&& e, X&& axes, EVS es) noexcept;
  397. /**
  398. * @ingroup red_functions
  399. * @brief L1 norm of an array-like argument over given axes.
  400. *
  401. * Returns an \ref xreducer for the L1 norm of the elements across given \em axes.
  402. * @param e an \ref xexpression
  403. * @param axes the axes along which the norm is computed (optional)
  404. * @param es evaluation strategy to use (lazy (default), or immediate)
  405. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  406. * When no axes are provided, the norm is calculated over the entire array. In this case,
  407. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  408. */
  409. template <class E, class X, class EVS, class>
  410. auto norm_l1(E&& e, X&& axes, EVS es) noexcept;
  411. /**
  412. * @ingroup red_functions
  413. * @brief Squared L2 norm of an array-like argument over given axes.
  414. *
  415. * Returns an \ref xreducer for the squared L2 norm of the elements across given \em axes.
  416. * @param e an \ref xexpression
  417. * @param axes the axes along which the norm is computed (optional)
  418. * @param es evaluation strategy to use (lazy (default), or immediate)
  419. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  420. * When no axes are provided, the norm is calculated over the entire array. In this case,
  421. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  422. */
  423. template <class E, class X, class EVS, class>
  424. auto norm_sq(E&& e, X&& axes, EVS es) noexcept;
  425. /**
  426. * @ingroup red_functions
  427. * @brief L2 norm of a scalar or array-like argument.
  428. * @param e an xexpression
  429. * @param es evaluation strategy to use (lazy (default), or immediate)
  430. * For scalar types: implemented as <tt>abs(t)</tt><br>
  431. * otherwise: implemented as <tt>sqrt(norm_sq(t))</tt>.
  432. */
  433. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)>
  434. inline auto norm_l2(E&& e, EVS es = EVS()) noexcept
  435. {
  436. using std::sqrt;
  437. return sqrt(norm_sq(std::forward<E>(e), es));
  438. }
  439. /**
  440. * @ingroup red_functions
  441. * @brief L2 norm of an array-like argument over given axes.
  442. *
  443. * Returns an \ref xreducer for the L2 norm of the elements across given \em axes.
  444. * @param e an \ref xexpression
  445. * @param es evaluation strategy to use (lazy (default), or immediate)
  446. * @param axes the axes along which the norm is computed
  447. * @return an \ref xreducer (specifically: <tt>sqrt(norm_sq(e, axes))</tt>) (or xcontainer, depending on
  448. * evaluation strategy)
  449. */
  450. template <
  451. class E,
  452. class X,
  453. class EVS = DEFAULT_STRATEGY_REDUCERS,
  454. XTL_REQUIRES(is_xexpression<E>, xtl::negation<is_reducer_options<X>>)>
  455. inline auto norm_l2(E&& e, X&& axes, EVS es = EVS()) noexcept
  456. {
  457. return sqrt(norm_sq(std::forward<E>(e), std::forward<X>(axes), es));
  458. }
  459. template <class E, class I, std::size_t N, class EVS = DEFAULT_STRATEGY_REDUCERS>
  460. inline auto norm_l2(E&& e, const I (&axes)[N], EVS es = EVS()) noexcept
  461. {
  462. using axes_type = std::array<typename std::decay_t<E>::size_type, N>;
  463. return sqrt(norm_sq(std::forward<E>(e), xtl::forward_sequence<axes_type, decltype(axes)>(axes), es));
  464. }
  465. /**
  466. * @ingroup red_functions
  467. * @brief Infinity (maximum) norm of an array-like argument over given axes.
  468. *
  469. * Returns an \ref xreducer for the infinity norm of the elements across given \em axes.
  470. * @param e an \ref xexpression
  471. * @param axes the axes along which the norm is computed (optional)
  472. * @param es evaluation strategy to use (lazy (default), or immediate)
  473. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  474. * When no axes are provided, the norm is calculated over the entire array. In this case,
  475. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  476. */
  477. template <class E, class X, class EVS, class>
  478. auto norm_linf(E&& e, X&& axes, EVS es) noexcept;
  479. /**
  480. * @ingroup red_functions
  481. * @brief p-th power of the Lp norm of an array-like argument over given axes.
  482. *
  483. * Returns an \ref xreducer for the p-th power of the Lp norm of the elements across given \em axes.
  484. * @param e an \ref xexpression
  485. * @param p
  486. * @param axes the axes along which the norm is computed (optional)
  487. * @param es evaluation strategy to use (lazy (default), or immediate)
  488. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  489. * When no axes are provided, the norm is calculated over the entire array. In this case,
  490. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  491. */
  492. template <class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation<is_reducer_options<X>>)>
  493. inline auto norm_lp_to_p(E&& e, double p, X&& axes, EVS es = EVS()) noexcept
  494. {
  495. using value_type = typename std::decay_t<E>::value_type;
  496. using result_type = norm_type_t<std::decay_t<E>>;
  497. auto reduce_func = [p](const result_type& r, const value_type& v)
  498. {
  499. return r + norm_lp_to_p(v, p);
  500. };
  501. return xt::reduce(
  502. make_xreducer_functor(std::move(reduce_func), xt::const_value<result_type>(0), std::plus<result_type>()),
  503. std::forward<E>(e),
  504. std::forward<X>(axes),
  505. es
  506. );
  507. }
  508. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)>
  509. inline auto norm_lp_to_p(E&& e, double p, EVS es = EVS()) noexcept
  510. {
  511. return norm_lp_to_p(std::forward<E>(e), p, arange(e.dimension()), es);
  512. }
  513. template <class E, class I, std::size_t N, class EVS = DEFAULT_STRATEGY_REDUCERS>
  514. inline auto norm_lp_to_p(E&& e, double p, const I (&axes)[N], EVS es = EVS()) noexcept
  515. {
  516. using axes_type = std::array<typename std::decay_t<E>::size_type, N>;
  517. return norm_lp_to_p(std::forward<E>(e), p, xtl::forward_sequence<axes_type, decltype(axes)>(axes), es);
  518. }
  519. /**
  520. * @ingroup red_functions
  521. * @brief Lp norm of an array-like argument over given axes.
  522. *
  523. * Returns an \ref xreducer for the Lp norm (p != 0) of the elements across given \em axes.
  524. * @param e an \ref xexpression
  525. * @param p
  526. * @param axes the axes along which the norm is computed (optional)
  527. * @param es evaluation strategy to use (lazy (default), or immediate)
  528. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  529. * When no axes are provided, the norm is calculated over the entire array. In this case,
  530. * the reducer represents a scalar result, otherwise an array of appropriate dimension.
  531. */
  532. template <class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation<is_reducer_options<X>>)>
  533. inline auto norm_lp(E&& e, double p, X&& axes, EVS es = EVS())
  534. {
  535. XTENSOR_PRECONDITION(p != 0, "norm_lp(): p must be nonzero, use norm_l0() instead.");
  536. return pow(norm_lp_to_p(std::forward<E>(e), p, std::forward<X>(axes), es), 1.0 / p);
  537. }
  538. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)>
  539. inline auto norm_lp(E&& e, double p, EVS es = EVS())
  540. {
  541. return norm_lp(std::forward<E>(e), p, arange(e.dimension()), es);
  542. }
  543. template <class E, class I, std::size_t N, class EVS = DEFAULT_STRATEGY_REDUCERS>
  544. inline auto norm_lp(E&& e, double p, const I (&axes)[N], EVS es = EVS())
  545. {
  546. using axes_type = std::array<typename std::decay_t<E>::size_type, N>;
  547. return norm_lp(std::forward<E>(e), p, xtl::forward_sequence<axes_type, decltype(axes)>(axes), es);
  548. }
  549. /**
  550. * @ingroup red_functions
  551. * @brief Induced L1 norm of a matrix.
  552. *
  553. * Returns an \ref xreducer for the induced L1 norm (i.e. the maximum of the L1 norms of e's columns).
  554. * @param e a 2D \ref xexpression
  555. * @param es evaluation strategy to use (lazy (default), or immediate)
  556. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  557. */
  558. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)>
  559. inline auto norm_induced_l1(E&& e, EVS es = EVS())
  560. {
  561. XTENSOR_PRECONDITION(
  562. e.dimension() == 2,
  563. "norm_induced_l1(): only applicable to matrices (e.dimension() must be 2)."
  564. );
  565. return norm_linf(norm_l1(std::forward<E>(e), {0}, es), es);
  566. }
  567. /**
  568. * @ingroup red_functions
  569. * @brief Induced L-infinity norm of a matrix.
  570. *
  571. * Returns an \ref xreducer for the induced L-infinity norm (i.e. the maximum of the L1 norms of e's
  572. * rows).
  573. * @param e a 2D \ref xexpression
  574. * @param es evaluation strategy to use (lazy (default), or immediate)
  575. * @return an \ref xreducer (or xcontainer, depending on evaluation strategy)
  576. */
  577. template <class E, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_xexpression<E>)>
  578. inline auto norm_induced_linf(E&& e, EVS es = EVS())
  579. {
  580. XTENSOR_PRECONDITION(
  581. e.dimension() == 2,
  582. "norm_induced_linf(): only applicable to matrices (e.dimension() must be 2)."
  583. );
  584. return norm_linf(norm_l1(std::forward<E>(e), {1}, es), es);
  585. }
  586. } // namespace xt
  587. #endif