wedge.H 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // (c) Daniel Llorens - 2008-2011, 2014-2015
  2. // This library is free software; you can redistribute it and/or modify it under
  3. // the terms of the GNU Lesser General Public License as published by the Free
  4. // Software Foundation; either version 3 of the License, or (at your option) any
  5. // later version.
  6. /// @file wedge.H
  7. /// @brief Wedge product and cross product.
  8. #pragma once
  9. #include "ra/tuple-list.H"
  10. #include "ra/type.H" // only for is_scalar<>
  11. namespace mp {
  12. template <class P>
  13. struct MatchPermutationP
  14. {
  15. template <class A> using type = int_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 = typename Complement<C, D>::type;
  40. static_assert((len<EC>)>=2, "can't correct this complement");
  41. using type = typename PermutationFlipSign
  42. <PermutationSign<Append_<C, EC>, Iota_<D>>::value,
  43. EC>::type;
  44. };
  45. template <class C, int D> struct MapAntiCombination;
  46. template <int D, class ... C>
  47. struct MapAntiCombination<std::tuple<C ...>, D>
  48. {
  49. using type = std::tuple<typename AntiCombination<C, D>::type ...>;
  50. };
  51. template <int D, int O, class Enable=void>
  52. struct ChooseComponents
  53. {
  54. static_assert(D>=O, "bad dimension or form order");
  55. using type = Combinations_<Iota_<D>, O>;
  56. };
  57. template <int D, int O>
  58. struct ChooseComponents<D, O, std::enable_if_t<(D>1) && (2*O>D)>>
  59. {
  60. static_assert(D>=O, "bad dimension or form order");
  61. using C = typename ChooseComponents<D, D-O>::type;
  62. using type = typename MapAntiCombination<C, D>::type;
  63. };
  64. template <int D, int O> using ChooseComponents_ = typename ChooseComponents<D, O>::type;
  65. } // namespace mp
  66. namespace fun {
  67. // 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.
  68. template <int D, int Oa, int Ob>
  69. struct Wedge
  70. {
  71. constexpr static int Or = Oa+Ob;
  72. static_assert(Oa<=D && Ob<=D && Or<=D, "bad orders");
  73. constexpr static int Na = mp::n_over_p(D, Oa);
  74. constexpr static int Nb = mp::n_over_p(D, Ob);
  75. constexpr static int Nr = mp::n_over_p(D, Or);
  76. // in lexicographic order. Can be used to sort Ca below with FindPermutation.
  77. using LexOrCa = mp::Combinations_<mp::Iota_<D>, Oa>;
  78. // the actual components used, which are in lex. order only in some cases.
  79. using Ca = mp::ChooseComponents_<D, Oa>;
  80. using Cb = mp::ChooseComponents_<D, Ob>;
  81. using Cr = mp::ChooseComponents_<D, Or>;
  82. // optimizations.
  83. constexpr static bool yields_expr = (Na>1) != (Nb>1);
  84. constexpr static bool yields_expr_a1 = yields_expr && Na==1;
  85. constexpr static bool yields_expr_b1 = yields_expr && Nb==1;
  86. constexpr static bool both_scalars = (Na==1 && Nb==1);
  87. constexpr static bool dot_plus = Na>1 && Nb>1 && Or==D && (Oa<Ob || (Oa>Ob && !odd(Oa*Ob)));
  88. constexpr static bool dot_minus = Na>1 && Nb>1 && Or==D && (Oa>Ob && odd(Oa*Ob));
  89. constexpr static bool general_case = (Na>1 && Nb>1) && ((Oa+Ob!=D) || (Oa==Ob));
  90. template <class Va, class Vb>
  91. using valtype = std::decay_t<decltype(std::declval<Va>()[0] * std::declval<Vb>()[0])>;
  92. template <class Xr, class Fa, class Va, class Vb>
  93. static std::enable_if_t<mp::nilp<Fa>, valtype<Va, Vb>>
  94. term(Va const & a, Vb const & b)
  95. {
  96. return 0.;
  97. }
  98. template <class Xr, class Fa, class Va, class Vb>
  99. static std::enable_if_t<!mp::nilp<Fa>, valtype<Va, Vb>>
  100. term(Va const & a, Vb const & b)
  101. {
  102. using Fa0 = mp::First_<Fa>;
  103. using Fb = mp::ComplementList_<Fa0, Xr>;
  104. using Sa = mp::FindCombination<Fa0, Ca>;
  105. using Sb = mp::FindCombination<Fb, Cb>;
  106. constexpr int sign = Sa::sign * Sb::sign * mp::PermutationSign<mp::Append_<Fa0, Fb>, Xr>::value;
  107. static_assert(sign==+1 || sign==-1, "bad sign in wedge term");
  108. return valtype<Va, Vb>(sign)*a[Sa::where]*b[Sb::where] + term<Xr, mp::Drop1_<Fa>>(a, b);
  109. }
  110. template <class Va, class Vb, class Vr, int wr>
  111. static std::enable_if_t<(wr<Nr)>
  112. coeff(Va const & a, Vb const & b, Vr & r)
  113. {
  114. using Xr = mp::Ref_<Cr, wr>;
  115. using Fa = mp::Combinations_<Xr, Oa>;
  116. r[wr] = term<Xr, Fa>(a, b);
  117. coeff<Va, Vb, Vr, wr+1>(a, b, r);
  118. }
  119. template <class Va, class Vb, class Vr, int wr>
  120. static std::enable_if_t<(wr==Nr)>
  121. coeff(Va const & a, Vb const & b, Vr & r)
  122. {
  123. }
  124. template <class Va, class Vb, class Vr>
  125. static void product(Va const & a, Vb const & b, Vr & r)
  126. {
  127. static_assert(int(Va::size())==Na, "bad Va dim"); // gcc accepts a.size(), etc.
  128. static_assert(int(Vb::size())==Nb, "bad Vb dim");
  129. static_assert(int(Vr::size())==Nr, "bad Vr dim");
  130. coeff<Va, Vb, Vr, 0>(a, b, r);
  131. }
  132. };
  133. // This is for Euclidean space, it only does component shuffling.
  134. template <int D, int O>
  135. struct Hodge
  136. {
  137. using W = Wedge<D, O, D-O>;
  138. using Ca = typename W::Ca;
  139. using Cb = typename W::Cb;
  140. using Cr = typename W::Cr;
  141. using LexOrCa = typename W::LexOrCa;
  142. constexpr static int Na = W::Na;
  143. constexpr static int Nb = W::Nb;
  144. template <int i, class Va, class Vb>
  145. static std::enable_if_t<(i==W::Na)>
  146. hodge_aux(Va const & a, Vb & b)
  147. {
  148. }
  149. template <int i, class Va, class Vb>
  150. static std::enable_if_t<(i<W::Na)>
  151. hodge_aux(Va const & a, Vb & b)
  152. {
  153. using Cai = mp::Ref_<Ca, i>;
  154. static_assert(mp::len<Cai> == O, "bad");
  155. // sort Cai, because Complement only accepts sorted combinations.
  156. // Ref<Cb, i> should be complementary to Cai, but I don't want to rely on that.
  157. using SCai = mp::Ref_<LexOrCa, mp::FindCombination<Cai, LexOrCa>::where>;
  158. using CompCai = typename mp::Complement<SCai, D>::type;
  159. static_assert(mp::len<CompCai> == D-O, "bad");
  160. using fpw = mp::FindCombination<CompCai, Cb>;
  161. // for the sign see e.g. DoCarmo1991 I.Ex 10.
  162. using fps = mp::FindCombination<mp::Append_<Cai, mp::Ref_<Cb, fpw::where>>, Cr>;
  163. static_assert(fps::sign!=0, "bad");
  164. b[fpw::where] = decltype(a[i])(fps::sign)*a[i];
  165. hodge_aux<i+1>(a, b);
  166. }
  167. };
  168. // The order of components is taken from Wedge<D, O, D-O>; this works for whatever order is defined there.
  169. // With lexicographic order, component order is reversed, but signs vary.
  170. // 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.
  171. template <int D, int O, class Va, class Vb>
  172. void hodgex(Va const & a, Vb & b)
  173. {
  174. static_assert(O<=D, "bad orders");
  175. static_assert(Va::size()==Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc.
  176. static_assert(Vb::size()==Hodge<D, O>::Nb, "error");
  177. Hodge<D, O>::template hodge_aux<0>(a, b);
  178. }
  179. // 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.
  180. // However if 2*O=D, it is not possible to differentiate the bases by order and hodgex() must be used.
  181. // 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!
  182. #define TRIVIAL(D, O) (2*O!=D && ((2*O<D) || !odd(O*(D-O))))
  183. template <int D, int O, class Va, class Vb>
  184. std::enable_if_t<TRIVIAL(D, O)> hodge(Va const & a, Vb & b)
  185. {
  186. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc
  187. static_assert(Vb::size()==fun::Hodge<D, O>::Nb, "error");
  188. b = a;
  189. }
  190. template <int D, int O, class Va>
  191. std::enable_if_t<TRIVIAL(D, O), Va const &> hodge(Va const & a)
  192. {
  193. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size()
  194. return a;
  195. }
  196. template <int D, int O, class Va, class Vb>
  197. std::enable_if_t<!TRIVIAL(D, O)> hodge(Va const & a, Vb & b)
  198. {
  199. fun::hodgex<D, O>(a, b);
  200. }
  201. template <int D, int O, class Va>
  202. std::enable_if_t<!TRIVIAL(D, O), Va &> hodge(Va & a)
  203. {
  204. Va b(a);
  205. fun::hodgex<D, O>(b, a);
  206. return a;
  207. }
  208. #undef TRIVIAL
  209. } // namespace fun