test-wedge-product.C 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // (c) Daniel Llorens - 2008-2010, 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 test-wedge-product.C
  7. /// @brief Test generic wedge product with compile-time dimensions.
  8. #include <iostream>
  9. #include "ra/operators.H"
  10. #include "ra/io.H"
  11. #include "ra/test.H"
  12. // TODO using w/list after gcc 7.2 (P0028R4)
  13. using std::cout; using std::endl; using std::flush;
  14. using fun::Wedge; using fun::hodge; using fun::hodgex;
  15. using namespace mp;
  16. using real = double;
  17. using complex = std::complex<double>;
  18. real const GARBAGE(99);
  19. template <class T, ra::dim_t N> using vec = ra::Small<T, N>;
  20. using real1 = vec<real, 1>;
  21. using real2 = vec<real, 2>;
  22. using real3 = vec<real, 3>;
  23. using real4 = vec<real, 4>;
  24. using real6 = vec<real, 6>;
  25. using complex1 = vec<complex, 1>;
  26. using complex2 = vec<complex, 2>;
  27. using complex3 = vec<complex, 3>;
  28. template <class P, class Plist, int w, int s>
  29. struct FindCombinationTester
  30. {
  31. using finder = FindCombination<P, Plist>;
  32. static_assert(finder::where==w && finder::sign==s, "bad");
  33. static void check() {};
  34. };
  35. template <int N, int O>
  36. std::enable_if_t<(O>N)> test_optimized_hodge_aux(TestRecorder & tr) {}
  37. template <int N, int O>
  38. std::enable_if_t<(O<=N)> test_optimized_hodge_aux(TestRecorder & tr)
  39. {
  40. tr.section(format("hodge() vs hodgex() with N=", N, " O=", O));
  41. static_assert(N>=O, "bad_N_or_bad_O");
  42. using Va = vec<real, fun::Wedge<N, O, N-O>::Na>;
  43. using Vb = vec<real, fun::Wedge<N, O, N-O>::Nb>;
  44. Va u = ra::iota(u.size(), 1);
  45. Vb w(GARBAGE);
  46. hodge<N, O>(u, w);
  47. cout << "-> " << u << " hodge " << w << endl;
  48. // this is the property that u^(*u) = dot(u, u)*vol form.
  49. if (O==1) {
  50. real S = sum(sqr(u));
  51. // since the volume form and the 1-forms are always ordered lexicographically (0 1 2...) vs (0) (1) (2) ...
  52. tr.info("with O=1, S: ", S, " vs wedge(u, w): ", wedge<N, O, N-O>(u, w))
  53. .test_eq(S, wedge<N, O, N-O>(u, w));
  54. } else if (O+1==N) {
  55. real S = sum(sqr(w));
  56. // compare with the case above, this is the sign of the (anti)commutativity of the exterior product.
  57. S *= odd(O*(N-O)) ? -1 : +1;
  58. tr.info("with O=N-1, S: ", S, " vs wedge(u, w): ", wedge<N, N-O, O>(u, w))
  59. .test_eq(S, wedge<N, N-O, O>(u, w));
  60. }
  61. // test that it does the same as hodgex().
  62. Vb x(GARBAGE);
  63. hodgex<N, O>(u, x);
  64. if (2*O==N) {
  65. tr.info("-> ", u, " hodgex ", x).test_eq(wedge<N, O, N-O>(u, w), wedge<N, O, N-O>(u, x));
  66. }
  67. // test basic duality property, **w = (-1)^{o(n-o)} w.
  68. {
  69. Va b(GARBAGE);
  70. hodgex<N, N-O>(x, b);
  71. tr.info("duality test with hodgex() (N ", N, " O ", O, ") -> ", u, " hodge ", x, " hodge(hodge) ", b)
  72. .test_eq((odd(O*(N-O)) ? -1 : +1)*u, b);
  73. }
  74. {
  75. Va a(GARBAGE);
  76. hodge<N, N-O>(w, a);
  77. tr.info("duality test with hodge() (N ", N, " O ", O, ") -> ", u, " hodge ", w, " hodge(hodge) ", a)
  78. .test_eq((odd(O*(N-O)) ? -1 : +1)*u, a);
  79. }
  80. test_optimized_hodge_aux<N, O+1>(tr);
  81. }
  82. template <int N>
  83. void test_optimized_hodge(TestRecorder & tr)
  84. {
  85. static_assert(N>=0, "bad_N");
  86. test_optimized_hodge_aux<N, 0>(tr);
  87. test_optimized_hodge<N-1>(tr);
  88. }
  89. template <>
  90. void test_optimized_hodge<-1>(TestRecorder & tr)
  91. {
  92. }
  93. template <int D, class R, class A, class B>
  94. R test_scalar_case(A const & a, B const & b)
  95. {
  96. R r = wedge<D, 0, 0>(a, b);
  97. cout << "[" << D << "/0/0] " << a << " ^ " << b << " -> " << r << endl;
  98. return r;
  99. }
  100. template <int D, int OA, int OB, class R, class A, class B>
  101. R test_one_one_case(TestRecorder & tr, A const & a, B const & b)
  102. {
  103. R r1(GARBAGE);
  104. Wedge<D, OA, OB>::product(a, b, r1);
  105. cout << "[" << D << "/" << OA << "/" << OB << "] " << a << " ^ " << b << " -> " << r1 << endl;
  106. R r2(wedge<D, OA, OB>(a, b));
  107. cout << "[" << D << "/" << OA << "/" << OB << "] " << a << " ^ " << b << " -> " << r2 << endl;
  108. tr.test_eq(r1, r2);
  109. return r1;
  110. }
  111. template <int D, int OA, int OB, class R, class A, class B>
  112. R test_one_scalar_case(A const & a, B const & b)
  113. {
  114. R r2(wedge<D, OA, OB>(a, b));
  115. cout << "[" << D << "/" << OA << "/" << OB << "] " << a << " ^ " << b << " -> " << r2 << endl;
  116. return r2;
  117. }
  118. int main()
  119. {
  120. TestRecorder tr(std::cout);
  121. // gcc let a bug pass by https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57891. Cf http://stackoverflow.com/a/24346350.
  122. // TODO We weren't getting the proper diagnostic from clang, probably due to disable_if doing !(integer).
  123. tr.section("MatchPermutationP");
  124. {
  125. using thematch = MatchPermutationP< int_list<1, 0> >::type<int_list<0, 1> >;
  126. cout << "A... " << (thematch::value) << endl;
  127. using index_if = IndexIf<std::tuple<int_list<1, 0> >, MatchPermutationP< int_list<0, 1> >::template type>;
  128. cout << "B... " << index_if::value << endl;
  129. static_assert(index_if::value==0, "bad MatchPermutationP");
  130. }
  131. tr.section("Testing FindCombination");
  132. {
  133. using la = Iota<3>::type;
  134. using ca = Combinations<la, 2>::type;
  135. FindCombinationTester<int_list<0, 1>, ca, 0, +1>::check();
  136. FindCombinationTester<int_list<1, 0>, ca, 0, -1>::check();
  137. FindCombinationTester<int_list<0, 2>, ca, 1, +1>::check();
  138. FindCombinationTester<int_list<2, 0>, ca, 1, -1>::check();
  139. FindCombinationTester<int_list<1, 2>, ca, 2, +1>::check();
  140. FindCombinationTester<int_list<2, 1>, ca, 2, -1>::check();
  141. FindCombinationTester<int_list<0, 0>, ca, -1, 0>::check();
  142. FindCombinationTester<int_list<1, 1>, ca, -1, 0>::check();
  143. FindCombinationTester<int_list<2, 2>, ca, -1, 0>::check();
  144. FindCombinationTester<int_list<3, 0>, ca, -1, 0>::check();
  145. }
  146. tr.section("Testing AntiCombination");
  147. {
  148. using la = Iota<3>::type;
  149. using ca = Combinations<la, 1>::type;
  150. using cc0 = AntiCombination<Ref<ca, 0>::type, 3>::type;
  151. static_assert(check_idx<cc0, 1, 2>::value, "bad");
  152. using cc1 = AntiCombination<Ref<ca, 1>::type, 3>::type;
  153. static_assert(check_idx<cc1, 2, 0>::value, "bad");
  154. using cc2 = AntiCombination<Ref<ca, 2>::type, 3>::type;
  155. static_assert(check_idx<cc2, 0, 1>::value, "bad");
  156. }
  157. tr.section("Testing ChooseComponents");
  158. {
  159. using c1 = ChooseComponents<3, 1>::type;
  160. static_assert(len<c1> == 3, "bad");
  161. static_assert(check_idx<Ref<c1, 0>::type, 0>::value, "bad");
  162. static_assert(check_idx<Ref<c1, 1>::type, 1>::value, "bad");
  163. static_assert(check_idx<Ref<c1, 2>::type, 2>::value, "bad");
  164. using c2 = ChooseComponents<3, 2>::type;
  165. static_assert(len<c2> == 3, "bad");
  166. static_assert(check_idx<Ref<c2, 0>::type, 1, 2>::value, "bad");
  167. static_assert(check_idx<Ref<c2, 1>::type, 2, 0>::value, "bad");
  168. static_assert(check_idx<Ref<c2, 2>::type, 0, 1>::value, "bad");
  169. using c3 = ChooseComponents<3, 3>::type;
  170. static_assert(len<c3> == 1, "bad");
  171. static_assert(check_idx<Ref<c3, 0>::type, 0, 1, 2>::value, "bad");
  172. }
  173. {
  174. using c0 = ChooseComponents<1, 0>::type;
  175. static_assert(len<c0> == 1, "bad");
  176. static_assert(check_idx<Ref<c0, 0>::type>::value, "bad");
  177. using c1 = ChooseComponents<1, 1>::type;
  178. static_assert(len<c1> == 1, "bad");
  179. static_assert(check_idx<Ref<c1, 0>::type, 0>::value, "bad");
  180. }
  181. tr.section("Testing Wedge<>::product()");
  182. {
  183. real1 a(1);
  184. real1 b(3);
  185. real1 r(GARBAGE);
  186. Wedge<1, 0, 0>::product(a, b, r);
  187. tr.info("[1/0/0] ", a, " ^ ", b, " -> ", r).test_eq(3, r[0]);
  188. real1 h(GARBAGE);
  189. hodgex<1, 0>(r, h);
  190. tr.info("thodge-star: ", h).test_eq(3, h[0]);
  191. }
  192. tr.section("change order changes sign");
  193. {
  194. real3 a{1, 0, 0};
  195. real3 b{0, 1, 0};
  196. real3 r(GARBAGE);
  197. Wedge<3, 1, 1>::product(a, b, r);
  198. tr.info("[3/1/1] ", a, " ^ ", b, " -> ", r).test_eq(real3{0, 0, +1}, r); // +1, 0, 0 in lex. order.
  199. real3 h(GARBAGE);
  200. hodgex<3, 2>(r, h);
  201. tr.info("hodge-star: ", h).test_eq(real3{0, 0, 1}, h);
  202. }
  203. {
  204. real3 a{0, 1, 0};
  205. real3 b{1, 0, 0};
  206. real3 r(GARBAGE);
  207. Wedge<3, 1, 1>::product(a, b, r);
  208. tr.info("[3/1/1] ", a, " ^ ", b, " -> ", r).test_eq(real3{0, 0, -1}, r); // -1, 0, 0 in lex order.
  209. real3 h(GARBAGE);
  210. hodgex<3, 2>(r, h);
  211. tr.info("hodge-star: ", h).test_eq(real3{0, 0, -1}, h);
  212. }
  213. tr.section("check type promotion");
  214. {
  215. complex3 a{complex(0, 1), 0, 0};
  216. real3 b{0, 1, 0};
  217. complex3 r(GARBAGE);
  218. Wedge<3, 1, 1>::product(a, b, r);
  219. tr.info("[3/1/1] ", a, " ^ ", b, " -> ", r).test_eq(complex3{0, 0, complex(0, 1)}, r); // +j, 0, 0 in lex. o.
  220. complex3 h(GARBAGE);
  221. hodgex<3, 2>(r, h);
  222. tr.info("hodge-star: ", h).test_eq(complex3{0, 0, complex(0, 1)}, h);
  223. }
  224. tr.section("sign change in going from lexicographic -> our peculiar order");
  225. {
  226. real3 a{1, 0, 0};
  227. real3 b{0, 0, 2};
  228. real3 r(GARBAGE);
  229. Wedge<3, 1, 1>::product(a, b, r);
  230. tr.info("[3/1/1] ", a, " ^ ", b, " -> ", r).test_eq(real3{0, -2, 0}, r); // 0, 2, 0 in lex order.
  231. real3 h(GARBAGE);
  232. hodgex<3, 2>(r, h);
  233. tr.info("hodge-star: ", h).test_eq(real3{0, -2, 0}, h);
  234. }
  235. {
  236. real3 a{1, 0, 2};
  237. real3 b{1, 0, 2};
  238. real3 r(GARBAGE);
  239. Wedge<3, 1, 1>::product(a, b, r);
  240. tr.info("[3/1/1] ", a, " ^ ", b, " -> ", r).test_eq(0., r);
  241. real3 h(GARBAGE);
  242. hodgex<3, 2>(r, h);
  243. tr.info("hodge-star: ", h).test_eq(0., h);
  244. }
  245. {
  246. real3 a{0, 1, 0};
  247. real3 b{0, -1, 0}; // 0, 1, 0 in lex order.
  248. real1 r(GARBAGE);
  249. Wedge<3, 1, 2>::product(a, b, r);
  250. tr.info("[3/1/2] ", a, " ^ ", b, " -> ", r).test_eq(-1, r[0]);
  251. real1 h(GARBAGE);
  252. hodgex<3, 3>(r, h);
  253. tr.info("\thodge-star: ", h).test_eq(-1, h[0]);
  254. // this is not forced for hodgex (depends on vec::ChooseComponents<> as used in fun::Wedge<>) so if you change that, change this too.
  255. real3 c;
  256. hodgex<3, 1>(b, c);
  257. tr.info("hodge<3, 1>(", b, "): ", c).test_eq(real3{0, -1, 0}, b);
  258. hodgex<3, 2>(b, c);
  259. tr.info("hodge<3, 2>(", b, "): ", c).test_eq(real3{0, -1, 0}, b);
  260. }
  261. {
  262. real4 a{1, 0, 0, 0};
  263. real4 b{0, 0, 1, 0};
  264. real6 r(GARBAGE);
  265. Wedge<4, 1, 1>::product(a, b, r);
  266. tr.info("[4/1/1] ", a, " ^ ", b, " -> ", r).test_eq(real6{0, 1, 0, 0, 0, 0}, r);
  267. real6 h(GARBAGE);
  268. hodgex<4, 2>(r, h);
  269. tr.info("hodge-star: ", h).test_eq(real6{0, 0, 0, 0, -1, 0}, h);
  270. r = GARBAGE;
  271. hodgex<4, 2>(h, r);
  272. tr.info("hodge-star(hodge-star()): ", r).test_eq(real6{0, 1, 0, 0, 0, 0}, r);
  273. }
  274. {
  275. real4 a{0, 0, 1, 0};
  276. real4 b{1, 0, 0, 0};
  277. real6 r(GARBAGE);
  278. Wedge<4, 1, 1>::product(a, b, r);
  279. tr.info("[4/1/1] ", a, " ^ ", b, " -> ", r).test_eq(real6{0, -1, 0, 0, 0, 0}, r);
  280. }
  281. {
  282. real6 r{1, 0, 0, 0, 0, 0};
  283. real6 h(GARBAGE);
  284. hodgex<4, 2>(r, h);
  285. tr.info("r: ", r, " -> hodge-star: ", h).test_eq(real6{0, 0, 0, 0, 0, 1}, h);
  286. }
  287. tr.section("important as a case where a^b==b^a");
  288. {
  289. real6 a{1, 0, 0, 0, 0, 0};
  290. real6 b{0, 0, 0, 0, 0, 1};
  291. real1 r(GARBAGE);
  292. Wedge<4, 2, 2>::product(a, b, r);
  293. tr.info("[4/2/2] ", a, " ^ ", b, " -> ", r).test_eq(1, r[0]);
  294. Wedge<4, 2, 2>::product(b, a, r);
  295. tr.info("[4/2/2] ", a, " ^ ", b, " -> ", r).test_eq(1, r[0]);
  296. }
  297. tr.section("important as a case where a^a!=0, see DoCarmo1994, Ch. 1 after Prop. 2.");
  298. {
  299. real6 a{1, 0, 0, 0, 0, 1};
  300. real6 b{1, 0, 0, 0, 0, 1};
  301. real1 r(GARBAGE);
  302. Wedge<4, 2, 2>::product(a, b, r);
  303. tr.info("[4/2/2] ", a, " ^ ", b, " -> ", r).test_eq(2, r[0]);
  304. }
  305. tr.section("important as a case where a^b is not dot(a, b) even though O(a)=D-O(b). This happens when O(a)==O(b), i.e. they have the same components");
  306. {
  307. real2 a{1, 0};
  308. real2 b{0, 1};
  309. real1 r(GARBAGE);
  310. Wedge<2, 1, 1>::product(a, b, r);
  311. tr.info("[2/1/1] ", a, " ^ ", b, " -> ", r).test_eq(1, r[0]);
  312. real2 p{1, 2};
  313. real2 q(GARBAGE);
  314. hodgex<2, 1>(p, q);
  315. tr.info("p: ", p, " -> hodge-star: ", q).test_eq(real2{-2, 1}, q);
  316. }
  317. tr.section("test the specializations in cross(), wedge<>()");
  318. {
  319. real2 a{1, 0};
  320. real2 b{0, 1};
  321. real c(cross(a, b));
  322. tr.info("a cross b: ", c).test_eq(1, c);
  323. c = cross(b, a);
  324. tr.test_eq(-1, c);
  325. // accepts expr arguments.
  326. c = cross(a, b+1.);
  327. tr.test_eq(2, c);
  328. }
  329. tr.section("test the cross product some more. This was moved from test_small_vec.C");
  330. {
  331. real3 x3{1., 0. ,0.};
  332. real3 y3{0., 1., 0.};
  333. real3 z3{0., 0., 1.};
  334. tr.test_eq(z3, cross(x3, y3));
  335. tr.test_eq(x3, cross(y3, z3));
  336. tr.test_eq(y3, cross(z3, x3));
  337. tr.test_eq(-z3, cross(y3, x3));
  338. tr.test_eq(-x3, cross(z3, y3));
  339. tr.test_eq(-y3, cross(x3, z3));
  340. real2 x2{1., 0.};
  341. real2 y2{0., 1.};
  342. tr.test_eq(1., cross(x2, y2));
  343. tr.test_eq(-1., cross(y2, x2));
  344. complex2 cy2{0., 1.};
  345. tr.test_eq(complex(1., 0.), cross(x2, cy2));
  346. }
  347. tr.section("verify that wedge<>() returns an expression where appropriate. This was moved from test_small_vec.C");
  348. {
  349. real3 u{1., 2., 3.};
  350. real3 v{3., 2., 1.};
  351. tr.test_eq(10., wedge<3, 1, 2>(u, v));
  352. tr.test_eq(cross(u, v), wedge<3, 1, 1>(u, v));
  353. tr.test_eq(10., wedge<3, 1, 2>(u, v));
  354. // #if VEC2RA==0
  355. // cout << "this should be an expression: " << flush;
  356. // assert(is_it_expr(wedge<3, 0, 1>(9., u)));
  357. // cout << resolve(wedge<3, 0, 1>(9., u)) << endl;
  358. // assert(every(wedge<3, 0, 1>(9., u)==real3{9., 18., 27.}));
  359. // #else
  360. // #endif
  361. }
  362. tr.section("verify that we are allowed to choose our return type to wedge<>(a, b, r)");
  363. {
  364. real a(GARBAGE);
  365. real1 b(GARBAGE);
  366. wedge<2, 1, 1>(real2{1, 0}, real2{0, 1}, a);
  367. wedge<2, 1, 1>(real2{1, 0}, real2{0, 1}, b);
  368. tr.test_eq(1, a);
  369. tr.test_eq(1, b[0]);
  370. }
  371. tr.section("check the optimization of hodgex() that relies on a complementary order of bases in the 2*O>D forms");
  372. {
  373. test_optimized_hodge<6>(tr);
  374. }
  375. tr.section("Test scalar arg cases");
  376. {
  377. tr.test_eq(6, test_scalar_case<0, real>(real1(2), real(3)));
  378. tr.test_eq(6, test_scalar_case<1, real>(real1(2), real(3)));
  379. tr.test_eq(6, test_scalar_case<0, real>(real(2), real(3)));
  380. tr.test_eq(6, test_scalar_case<1, real>(real(2), real(3)));
  381. tr.test_eq(6, test_scalar_case<0, real>(real(2), real1(3)));
  382. tr.test_eq(6, test_scalar_case<1, real>(real(2), real1(3)));
  383. tr.test_eq(6, test_scalar_case<0, real>(real1(2), real1(3)));
  384. tr.test_eq(6, test_scalar_case<1, real>(real1(2), real1(3)));
  385. tr.test_eq(6, test_scalar_case<0, real1>(real(2), real(3)));
  386. tr.test_eq(6, test_scalar_case<1, real1>(real(2), real(3)));
  387. tr.test_eq(6, test_scalar_case<0, real1>(real1(2), real(3)));
  388. tr.test_eq(6, test_scalar_case<1, real1>(real1(2), real(3)));
  389. tr.test_eq(6, test_scalar_case<0, real1>(real(2), real1(3)));
  390. tr.test_eq(6, test_scalar_case<1, real1>(real(2), real1(3)));
  391. tr.test_eq(6, test_scalar_case<0, real1>(real1(2), real1(3)));
  392. tr.test_eq(6, test_scalar_case<1, real1>(real1(2), real1(3)));
  393. }
  394. tr.section("Test scalar x nonscalar arg cases.");
  395. {
  396. tr.test_eq(real2{6, 10}, test_one_one_case<2, 0, 1, real2>(tr, real1(2), real2{3, 5}));
  397. tr.test_eq(real2{6, 10}, test_one_one_case<2, 1, 0, real2>(tr, real2{3, 5}, real1(2)));
  398. tr.test_eq(real3{2, 6, 10}, test_one_one_case<3, 0, 1, real3>(tr, real1(2), real3{1, 3, 5}));
  399. tr.test_eq(real3{2, 6, 10}, test_one_one_case<3, 1, 0, real3>(tr, real3{1, 3, 5}, real1(2)));
  400. }
  401. {
  402. tr.test_eq(real2{6, 10}, test_one_scalar_case<2, 0, 1, real2>(real1(2), real2{3, 5}));
  403. tr.test_eq(real2{6, 10}, test_one_scalar_case<2, 1, 0, real2>(real2{3, 5}, real1(2)));
  404. tr.test_eq(real3{2, 6, 10}, test_one_scalar_case<3, 0, 1, real3>(real1(2), real3{1, 3, 5}));
  405. tr.test_eq(real3{2, 6, 10}, test_one_scalar_case<3, 1, 0, real3>(real3{1, 3, 5}, real1(2)));
  406. }
  407. tr.section("Test scalar x ~scalar arg cases.");
  408. {
  409. tr.test_eq(6., wedge<1, 0, 1>(3., complex1(2.)));
  410. }
  411. return tr.summary();
  412. }