wedge.hh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // -*- mode: c++; coding: utf-8 -*-
  2. /// @file wedge.hh
  3. /// @brief Wedge product and cross product.
  4. // (c) Daniel Llorens - 2008-2011, 2014-2015
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU Lesser General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. #pragma once
  10. #include "ra/bootstrap.hh"
  11. namespace mp {
  12. template <class P>
  13. struct MatchPermutationP
  14. {
  15. template <class A> using type = bool_t<(PermutationSign<P, A>::value!=0)>;
  16. };
  17. template <class P, class Plist> struct FindCombination
  18. {
  19. using type = IndexIf<Plist, mp::MatchPermutationP<P>::template type>;
  20. static int const where = type::value;
  21. static int const sign = (where>=0) ? PermutationSign<P, typename type::type>::value :0;
  22. };
  23. // Produce a permutation of opposite sign if sign = -1.
  24. template <int sign, class C> struct PermutationFlipSign;
  25. template <class C0, class C1, class ... C>
  26. struct PermutationFlipSign<-1, std::tuple<C0, C1, C ...>>
  27. {
  28. using type = std::tuple<C1, C0, C ...>;
  29. };
  30. template <class C0, class C1, class ... C>
  31. struct PermutationFlipSign<+1, std::tuple<C0, C1, C ...>>
  32. {
  33. using type = std::tuple<C0, C1, C ...>;
  34. };
  35. // A combination antiC complementary to C wrt [0, 1, ... Dim-1], but permuted to make the permutation [C, antiC] positive with respect to [0, 1, ... Dim-1].
  36. template <class C, int D>
  37. struct AntiCombination
  38. {
  39. using EC = complement<C, D>;
  40. static_assert((len<EC>)>=2, "can't correct this complement");
  41. using type = typename PermutationFlipSign<PermutationSign<append<C, EC>, iota<D>>::value, EC>::type;
  42. };
  43. template <class C, int D> struct MapAntiCombination;
  44. template <int D, class ... C>
  45. struct MapAntiCombination<std::tuple<C ...>, D>
  46. {
  47. using type = std::tuple<typename AntiCombination<C, D>::type ...>;
  48. };
  49. template <int D, int O>
  50. struct ChooseComponents
  51. {
  52. static_assert(D>=O, "bad dimension or form order");
  53. using type = mp::combinations<iota<D>, O>;
  54. };
  55. template <int D, int O>
  56. requires ((D>1) && (2*O>D))
  57. struct ChooseComponents<D, O>
  58. {
  59. static_assert(D>=O, "bad dimension or form order");
  60. using C = typename ChooseComponents<D, D-O>::type;
  61. using type = typename MapAntiCombination<C, D>::type;
  62. };
  63. template <int D, int O> using ChooseComponents_ = typename ChooseComponents<D, O>::type;
  64. } // namespace mp
  65. namespace fun {
  66. // Works *almost* to the range of size_t.
  67. constexpr size_t n_over_p(size_t const n, size_t p)
  68. {
  69. if (p>n) {
  70. return 0;
  71. } else if (p>(n-p)) {
  72. p = n-p;
  73. }
  74. size_t v = 1;
  75. for (size_t i=0; i!=p; ++i) {
  76. v = v*(n-i)/(i+1);
  77. }
  78. return v;
  79. }
  80. // We form the basis for the result (Cr) and split it in pieces for Oa and Ob; there are (D over Oa) ways. Then we see where and with which signs these pieces are in the bases for Oa (Ca) and Ob (Cb), and form the product.
  81. template <int D, int Oa, int Ob>
  82. struct Wedge
  83. {
  84. constexpr static int Or = Oa+Ob;
  85. static_assert(Oa<=D && Ob<=D && Or<=D, "bad orders");
  86. constexpr static int Na = n_over_p(D, Oa);
  87. constexpr static int Nb = n_over_p(D, Ob);
  88. constexpr static int Nr = n_over_p(D, Or);
  89. // in lexicographic order. Can be used to sort Ca below with FindPermutation.
  90. using LexOrCa = mp::combinations<mp::iota<D>, Oa>;
  91. // the actual components used, which are in lex. order only in some cases.
  92. using Ca = mp::ChooseComponents_<D, Oa>;
  93. using Cb = mp::ChooseComponents_<D, Ob>;
  94. using Cr = mp::ChooseComponents_<D, Or>;
  95. // optimizations.
  96. constexpr static bool yields_expr = (Na>1) != (Nb>1);
  97. constexpr static bool yields_expr_a1 = yields_expr && Na==1;
  98. constexpr static bool yields_expr_b1 = yields_expr && Nb==1;
  99. constexpr static bool both_scalars = (Na==1 && Nb==1);
  100. constexpr static bool dot_plus = Na>1 && Nb>1 && Or==D && (Oa<Ob || (Oa>Ob && !ra::odd(Oa*Ob)));
  101. constexpr static bool dot_minus = Na>1 && Nb>1 && Or==D && (Oa>Ob && ra::odd(Oa*Ob));
  102. constexpr static bool general_case = (Na>1 && Nb>1) && ((Oa+Ob!=D) || (Oa==Ob));
  103. template <class Va, class Vb>
  104. using valtype = std::decay_t<decltype(std::declval<Va>()[0] * std::declval<Vb>()[0])>;
  105. template <class Xr, class Fa, class Va, class Vb>
  106. requires (mp::nilp<Fa>)
  107. static valtype<Va, Vb>
  108. term(Va const & a, Vb const & b)
  109. {
  110. return 0.;
  111. }
  112. template <class Xr, class Fa, class Va, class Vb>
  113. requires (!mp::nilp<Fa>)
  114. static valtype<Va, Vb>
  115. term(Va const & a, Vb const & b)
  116. {
  117. using Fa0 = mp::first<Fa>;
  118. using Fb = mp::complement_list<Fa0, Xr>;
  119. using Sa = mp::FindCombination<Fa0, Ca>;
  120. using Sb = mp::FindCombination<Fb, Cb>;
  121. constexpr int sign = Sa::sign * Sb::sign * mp::PermutationSign<mp::append<Fa0, Fb>, Xr>::value;
  122. static_assert(sign==+1 || sign==-1, "bad sign in wedge term");
  123. return valtype<Va, Vb>(sign)*a[Sa::where]*b[Sb::where] + term<Xr, mp::drop1<Fa>>(a, b);
  124. }
  125. template <class Va, class Vb, class Vr, int wr>
  126. requires (wr<Nr)
  127. static void
  128. coeff(Va const & a, Vb const & b, Vr & r)
  129. {
  130. using Xr = mp::ref<Cr, wr>;
  131. using Fa = mp::combinations<Xr, Oa>;
  132. r[wr] = term<Xr, Fa>(a, b);
  133. coeff<Va, Vb, Vr, wr+1>(a, b, r);
  134. }
  135. template <class Va, class Vb, class Vr, int wr>
  136. requires (wr==Nr)
  137. static void
  138. coeff(Va const & a, Vb const & b, Vr & r)
  139. {
  140. }
  141. template <class Va, class Vb, class Vr>
  142. static void
  143. product(Va const & a, Vb const & b, Vr & r)
  144. {
  145. static_assert(int(Va::size())==Na, "bad Va dim"); // gcc accepts a.size(), etc.
  146. static_assert(int(Vb::size())==Nb, "bad Vb dim");
  147. static_assert(int(Vr::size())==Nr, "bad Vr dim");
  148. coeff<Va, Vb, Vr, 0>(a, b, r);
  149. }
  150. };
  151. // This is for Euclidean space, it only does component shuffling.
  152. template <int D, int O>
  153. struct Hodge
  154. {
  155. using W = Wedge<D, O, D-O>;
  156. using Ca = typename W::Ca;
  157. using Cb = typename W::Cb;
  158. using Cr = typename W::Cr;
  159. using LexOrCa = typename W::LexOrCa;
  160. constexpr static int Na = W::Na;
  161. constexpr static int Nb = W::Nb;
  162. template <int i, class Va, class Vb>
  163. static void
  164. hodge_aux(Va const & a, Vb & b)
  165. {
  166. static_assert(i<=W::Na, "Bad argument to hodge_aux");
  167. if constexpr (i<W::Na) {
  168. using Cai = mp::ref<Ca, i>;
  169. static_assert(mp::len<Cai> == O, "bad");
  170. // sort Cai, because mp::complement only accepts sorted combinations.
  171. // ref<Cb, i> should be complementary to Cai, but I don't want to rely on that.
  172. using SCai = mp::ref<LexOrCa, mp::FindCombination<Cai, LexOrCa>::where>;
  173. using CompCai = mp::complement<SCai, D>;
  174. static_assert(mp::len<CompCai> == D-O, "bad");
  175. using fpw = mp::FindCombination<CompCai, Cb>;
  176. // for the sign see e.g. DoCarmo1991 I.Ex 10.
  177. using fps = mp::FindCombination<mp::append<Cai, mp::ref<Cb, fpw::where>>, Cr>;
  178. static_assert(fps::sign!=0, "bad");
  179. b[fpw::where] = decltype(a[i])(fps::sign)*a[i];
  180. hodge_aux<i+1>(a, b);
  181. }
  182. }
  183. };
  184. // The order of components is taken from Wedge<D, O, D-O>; this works for whatever order is defined there.
  185. // With lexicographic order, component order is reversed, but signs vary.
  186. // With the order given by ChooseComponents<>, fpw::where==i and fps::sign==+1 in hodge_aux(), always. Then hodge() becomes a free operation, (with one exception) and the next function hodge() can be used.
  187. template <int D, int O, class Va, class Vb>
  188. void hodgex(Va const & a, Vb & b)
  189. {
  190. static_assert(O<=D, "bad orders");
  191. static_assert(Va::size()==Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc.
  192. static_assert(Vb::size()==Hodge<D, O>::Nb, "error");
  193. Hodge<D, O>::template hodge_aux<0>(a, b);
  194. }
  195. // This depends on Wedge<>::Ca, Cb, Cr coming from ChooseCombinations, as enforced in the tests in test_wedge_product. hodgex() should always work, but this is cheaper.
  196. // However if 2*O=D, it is not possible to differentiate the bases by order and hodgex() must be used.
  197. // Likewise, when O(N-O) is odd, Hodge from (2*O>D) to (2*O<D) change sign, since **w= -w in that case, and the basis in the (2*O>D) case is selected to make Hodge(<)->Hodge(>) trivial; but can't do both!
  198. #define TRIVIAL(D, O) (2*O!=D && ((2*O<D) || !ra::odd(O*(D-O))))
  199. template <int D, int O, class Va, class Vb>
  200. requires (TRIVIAL(D, O))
  201. inline void
  202. hodge(Va const & a, Vb & b)
  203. {
  204. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc
  205. static_assert(Vb::size()==fun::Hodge<D, O>::Nb, "error");
  206. b = a;
  207. }
  208. template <int D, int O, class Va>
  209. requires (TRIVIAL(D, O))
  210. inline Va const &
  211. hodge(Va const & a)
  212. {
  213. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size()
  214. return a;
  215. }
  216. template <int D, int O, class Va, class Vb>
  217. requires (!TRIVIAL(D, O))
  218. inline void
  219. hodge(Va const & a, Vb & b)
  220. {
  221. fun::hodgex<D, O>(a, b);
  222. }
  223. template <int D, int O, class Va>
  224. requires (!TRIVIAL(D, O))
  225. inline Va &
  226. hodge(Va & a)
  227. {
  228. Va b(a);
  229. fun::hodgex<D, O>(b, a);
  230. return a;
  231. }
  232. #undef TRIVIAL
  233. } // namespace fun