wedge.cc 16 KB

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