wedge.H 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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. #ifndef RA_WEDGE_H
  7. #define RA_WEDGE_H
  8. /// @file wedge.H
  9. /// @brief Wedge product and cross product.
  10. #include "ra/tuple-list.H"
  11. #include "ra/ra-type.H" // only for is_scalar<>
  12. // This global version must be available so that ra::wedge<> may be searched by ADL even when giving explicit template args. See http://stackoverflow.com/questions/9838862 .
  13. template <int D, int Oa, int Ob, class A, class B,
  14. enableif_<mp::And<ra::is_scalar<A>, ra::is_scalar<B> >, int> =0>
  15. auto wedge(A const & a, B const & b) { return a*b; }
  16. namespace mp {
  17. template <class P>
  18. struct MatchPermutationP
  19. {
  20. template <class A> using type = int_t<(PermutationSign<P, A>::value!=0)>;
  21. };
  22. template <class P, class Plist> struct FindCombination
  23. {
  24. using type = IndexIf<Plist, mp::MatchPermutationP<P>::template type>;
  25. static int const where = type::value;
  26. static int const sign = (where>=0) ? PermutationSign<P, typename type::type>::value :0;
  27. };
  28. // Produce a permutation of opposite sign if sign = -1.
  29. template <int sign, class C> struct PermutationFlipSign;
  30. template <class C0, class C1, class ... C>
  31. struct PermutationFlipSign<-1, std::tuple<C0, C1, C ...>>
  32. {
  33. using type = std::tuple<C1, C0, C ...>;
  34. };
  35. template <class C0, class C1, class ... C>
  36. struct PermutationFlipSign<+1, std::tuple<C0, C1, C ...>>
  37. {
  38. using type = std::tuple<C0, C1, C ...>;
  39. };
  40. // 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].
  41. template <class C, int D>
  42. struct AntiCombination
  43. {
  44. using EC = typename Complement<C, D>::type;
  45. static_assert(Len<EC>::value>=2, "can't correct this complement");
  46. using type = typename PermutationFlipSign
  47. <PermutationSign<Append_<C, EC>, Iota_<D>>::value,
  48. EC>::type;
  49. };
  50. template <class C, int D> struct MapAntiCombination;
  51. template <int D, class ... C>
  52. struct MapAntiCombination<std::tuple<C ...>, D>
  53. {
  54. using type = std::tuple<typename AntiCombination<C, D>::type ...>;
  55. };
  56. template <int D, int O, class Enable=void>
  57. struct ChooseComponents
  58. {
  59. static_assert(D>=O, "bad dimension or form order");
  60. using type = Combinations_<Iota_<D>, O>;
  61. };
  62. template <int D, int O>
  63. struct ChooseComponents<D, O, std::enable_if_t<(D>1) && (2*O>D)>>
  64. {
  65. static_assert(D>=O, "bad dimension or form order");
  66. using C = typename ChooseComponents<D, D-O>::type;
  67. using type = typename MapAntiCombination<C, D>::type;
  68. };
  69. template <int D, int O> using ChooseComponents_ = typename ChooseComponents<D, O>::type;
  70. } // namespace mp
  71. namespace fun {
  72. // 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.
  73. template <int D, int Oa, int Ob>
  74. struct Wedge
  75. {
  76. static int const Or = Oa+Ob;
  77. static_assert(Oa<=D && Ob<=D && Or<=D, "bad orders");
  78. static int const Na = mp::n_over_p<D, Oa>::value;
  79. static int const Nb = mp::n_over_p<D, Ob>::value;
  80. static int const Nr = mp::n_over_p<D, Or>::value;
  81. // in lexicographic order. Can be used to sort Ca below with FindPermutation.
  82. using LexOrCa = mp::Combinations_<mp::Iota_<D>, Oa>;
  83. // the actual components used, which are in lex. order only in some cases.
  84. using Ca = mp::ChooseComponents_<D, Oa>;
  85. using Cb = mp::ChooseComponents_<D, Ob>;
  86. using Cr = mp::ChooseComponents_<D, Or>;
  87. // optimizations.
  88. static bool const yields_expr = (Na>1) != (Nb>1);
  89. static bool const yields_expr_a1 = yields_expr && Na==1;
  90. static bool const yields_expr_b1 = yields_expr && Nb==1;
  91. static bool const both_scalars = (Na==1 && Nb==1);
  92. static bool const dot_plus = Na>1 && Nb>1 && Or==D && (Oa<Ob || (Oa>Ob && !odd(Oa*Ob)));
  93. static bool const dot_minus = Na>1 && Nb>1 && Or==D && (Oa>Ob && odd(Oa*Ob));
  94. static bool const general_case = (Na>1 && Nb>1) && ((Oa+Ob!=D) || (Oa==Ob));
  95. template <class Va, class Vb>
  96. using valtype = std::decay_t<decltype(std::declval<Va>()[0] * std::declval<Vb>()[0])>;
  97. template <class Xr, class Fa, class Va, class Vb>
  98. static enableif_<mp::NilP<Fa>, valtype<Va, Vb>>
  99. term(Va const & a, Vb const & b)
  100. {
  101. return 0.;
  102. }
  103. template <class Xr, class Fa, class Va, class Vb>
  104. static enableif_<mp::Not<mp::NilP<Fa>>, valtype<Va, Vb>>
  105. term(Va const & a, Vb const & b)
  106. {
  107. using Fa0 = mp::First_<Fa>;
  108. using Fb = mp::ComplementList_<Fa0, Xr>;
  109. using Sa = mp::FindCombination<Fa0, Ca>;
  110. using Sb = mp::FindCombination<Fb, Cb>;
  111. int const sign = Sa::sign * Sb::sign * mp::PermutationSign<mp::Append_<Fa0, Fb>, Xr>::value;
  112. static_assert(sign==+1 || sign==-1, "bad sign in wedge term");
  113. return valtype<Va, Vb>(sign)*a[Sa::where]*b[Sb::where] + term<Xr, mp::Drop1_<Fa>>(a, b);
  114. }
  115. template <class Va, class Vb, class Vr, int wr>
  116. static std::enable_if_t<(wr<Nr)>
  117. coeff(Va const & a, Vb const & b, Vr & r)
  118. {
  119. using Xr = mp::Ref_<Cr, wr>;
  120. using Fa = mp::Combinations_<Xr, Oa>;
  121. r[wr] = term<Xr, Fa>(a, b);
  122. coeff<Va, Vb, Vr, wr+1>(a, b, r);
  123. }
  124. template <class Va, class Vb, class Vr, int wr>
  125. static std::enable_if_t<(wr==Nr)>
  126. coeff(Va const & a, Vb const & b, Vr & r)
  127. {
  128. }
  129. template <class Va, class Vb, class Vr>
  130. static void product(Va const & a, Vb const & b, Vr & r)
  131. {
  132. static_assert(int(Va::size())==Na, "bad Va dim"); // gcc accepts a.size(), etc.
  133. static_assert(int(Vb::size())==Nb, "bad Vb dim");
  134. static_assert(int(Vr::size())==Nr, "bad Vr dim");
  135. coeff<Va, Vb, Vr, 0>(a, b, r);
  136. }
  137. };
  138. // This is for Euclidean space, it only does component shuffling.
  139. template <int D, int O>
  140. struct Hodge
  141. {
  142. using W = Wedge<D, O, D-O>;
  143. using Ca = typename W::Ca;
  144. using Cb = typename W::Cb;
  145. using Cr = typename W::Cr;
  146. using LexOrCa = typename W::LexOrCa;
  147. static int const Na = W::Na;
  148. static int const Nb = W::Nb;
  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. }
  154. template <int i, class Va, class Vb>
  155. static std::enable_if_t<(i<W::Na)>
  156. hodge_aux(Va const & a, Vb & b)
  157. {
  158. using Cai = mp::Ref_<Ca, i>;
  159. static_assert(mp::Len<Cai>::value==O, "bad");
  160. // sort Cai, because Complement only accepts sorted combinations.
  161. // Ref<Cb, i> should be complementary to Cai, but I don't want to rely on that.
  162. using SCai = mp::Ref_<LexOrCa, mp::FindCombination<Cai, LexOrCa>::where>;
  163. using CompCai = typename mp::Complement<SCai, D>::type;
  164. static_assert(mp::Len<CompCai>::value==D-O, "bad");
  165. using fpw = mp::FindCombination<CompCai, Cb>;
  166. // for the sign see e.g. DoCarmo1991 I.Ex 10.
  167. using fps = mp::FindCombination<mp::Append_<Cai, mp::Ref_<Cb, fpw::where>>, Cr>;
  168. static_assert(fps::sign!=0, "bad");
  169. b[fpw::where] = decltype(a[i])(fps::sign)*a[i];
  170. hodge_aux<i+1>(a, b);
  171. }
  172. };
  173. // The order of components is taken from Wedge<D, O, D-O>; this works for whatever order is defined there.
  174. // With lexicographic order, component order is reversed, but signs vary.
  175. // 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.
  176. template <int D, int O, class Va, class Vb>
  177. void hodgex(Va const & a, Vb & b)
  178. {
  179. static_assert(O<=D, "bad orders");
  180. static_assert(Va::size()==Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc.
  181. static_assert(Vb::size()==Hodge<D, O>::Nb, "error");
  182. Hodge<D, O>::template hodge_aux<0>(a, b);
  183. }
  184. // 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.
  185. // However if 2*O=D, it is not possible to differentiate the bases by order and hodgex() must be used.
  186. // 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!
  187. #define TRIVIAL(D, O) (2*O!=D && ((2*O<D) || !odd(O*(D-O))))
  188. template <int D, int O, class Va, class Vb>
  189. std::enable_if_t<TRIVIAL(D, O)> hodge(Va const & a, Vb & b)
  190. {
  191. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size(), etc
  192. static_assert(Vb::size()==fun::Hodge<D, O>::Nb, "error");
  193. b = a;
  194. }
  195. template <int D, int O, class Va>
  196. std::enable_if_t<TRIVIAL(D, O), Va const &> hodge(Va const & a)
  197. {
  198. static_assert(Va::size()==fun::Hodge<D, O>::Na, "error"); // gcc accepts a.size()
  199. return a;
  200. }
  201. template <int D, int O, class Va, class Vb>
  202. std::enable_if_t<!TRIVIAL(D, O)> hodge(Va const & a, Vb & b)
  203. {
  204. fun::hodgex<D, O>(a, b);
  205. }
  206. template <int D, int O, class Va>
  207. std::enable_if_t<!TRIVIAL(D, O), Va &> hodge(Va & a)
  208. {
  209. Va b(a);
  210. fun::hodgex<D, O>(b, a);
  211. return a;
  212. }
  213. #undef TRIVIAL
  214. } // namespace fun
  215. #endif // RA_WEDGE