test_autodiff_1.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. // Copyright Matthew Pulver 2018 - 2019.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. #include "test_autodiff.hpp"
  6. BOOST_AUTO_TEST_SUITE(test_autodiff_1)
  7. BOOST_AUTO_TEST_CASE_TEMPLATE(constructors, T, all_float_types) {
  8. constexpr std::size_t m = 3;
  9. constexpr std::size_t n = 4;
  10. // Verify value-initialized instance has all 0 entries.
  11. const autodiff_fvar<T, m> empty1 = autodiff_fvar<T, m>();
  12. for (auto i : boost::irange(m + 1)) {
  13. BOOST_CHECK_EQUAL(empty1.derivative(i), 0);
  14. }
  15. const auto empty2 = autodiff_fvar<T, m, n>();
  16. for (auto i : boost::irange(m + 1)) {
  17. for (auto j : boost::irange(n + 1)) {
  18. BOOST_CHECK_EQUAL(empty2.derivative(i, j), 0);
  19. }
  20. }
  21. // Single variable
  22. const T cx = 10.0;
  23. const auto x = make_fvar<T, m>(cx);
  24. for (auto i : boost::irange(m + 1)) {
  25. if (i == 0u) {
  26. BOOST_CHECK_EQUAL(x.derivative(i), cx);
  27. } else if (i == 1) {
  28. BOOST_CHECK_EQUAL(x.derivative(i), 1);
  29. } else {
  30. BOOST_CHECK_EQUAL(x.derivative(i), 0);
  31. }
  32. }
  33. const autodiff_fvar<T, n> xn = x;
  34. for (auto i : boost::irange(n + 1)) {
  35. if (i == 0) {
  36. BOOST_CHECK_EQUAL(xn.derivative(i), cx);
  37. } else if (i == 1) {
  38. BOOST_CHECK_EQUAL(xn.derivative(i), 1);
  39. } else {
  40. BOOST_CHECK_EQUAL(xn.derivative(i), 0);
  41. }
  42. }
  43. // Second independent variable
  44. const T cy = 100.0;
  45. const auto y = make_fvar<T, m, n>(cy);
  46. for (auto i : boost::irange(m + 1)) {
  47. for (auto j : boost::irange(n + 1)) {
  48. if (i == 0 && j == 0) {
  49. BOOST_CHECK_EQUAL(y.derivative(i, j), cy);
  50. } else if (i == 0 && j == 1) {
  51. BOOST_CHECK_EQUAL(y.derivative(i, j), 1.0);
  52. } else {
  53. BOOST_CHECK_EQUAL(y.derivative(i, j), 0.0);
  54. }
  55. }
  56. }
  57. }
  58. BOOST_AUTO_TEST_CASE_TEMPLATE(implicit_constructors, T, all_float_types) {
  59. constexpr std::size_t m = 3;
  60. const autodiff_fvar<T, m> x = 3;
  61. const autodiff_fvar<T, m> one = uncast_return(x);
  62. const autodiff_fvar<T, m> two_and_a_half = 2.5;
  63. BOOST_CHECK_EQUAL(static_cast<T>(x), 3.0);
  64. BOOST_CHECK_EQUAL(static_cast<T>(one), 1.0);
  65. BOOST_CHECK_EQUAL(static_cast<T>(two_and_a_half), 2.5);
  66. }
  67. BOOST_AUTO_TEST_CASE_TEMPLATE(assignment, T, all_float_types) {
  68. constexpr std::size_t m = 3;
  69. constexpr std::size_t n = 4;
  70. const T cx = 10.0;
  71. const T cy = 10.0;
  72. autodiff_fvar<T, m, n>
  73. empty; // Uninitialized variable<> may have non-zero values.
  74. // Single variable
  75. auto x = make_fvar<T, m>(cx);
  76. empty = static_cast<decltype(empty)>(
  77. x); // Test static_cast of single-variable to double-variable type.
  78. for (auto i : boost::irange(m + 1)) {
  79. for (auto j : boost::irange(n + 1)) {
  80. if (i == 0 && j == 0) {
  81. BOOST_CHECK_EQUAL(empty.derivative(i, j), cx);
  82. } else if (i == 1 && j == 0) {
  83. BOOST_CHECK_EQUAL(empty.derivative(i, j), 1.0);
  84. } else {
  85. BOOST_CHECK_EQUAL(empty.derivative(i, j), 0.0);
  86. }
  87. }
  88. }
  89. auto y = make_fvar<T, m, n>(cy);
  90. empty = y; // default assignment operator
  91. for (auto i : boost::irange(m + 1)) {
  92. for (auto j : boost::irange(n + 1)) {
  93. if (i == 0 && j == 0) {
  94. BOOST_CHECK_EQUAL(empty.derivative(i, j), cy);
  95. } else if (i == 0 && j == 1) {
  96. BOOST_CHECK_EQUAL(empty.derivative(i, j), 1.0);
  97. } else {
  98. BOOST_CHECK_EQUAL(empty.derivative(i, j), 0.0);
  99. }
  100. }
  101. }
  102. empty = cx; // set a constant
  103. for (auto i : boost::irange(m + 1)) {
  104. for (auto j : boost::irange(n + 1)) {
  105. if (i == 0 && j == 0) {
  106. BOOST_CHECK_EQUAL(empty.derivative(i, j), cx);
  107. } else {
  108. BOOST_CHECK_EQUAL(empty.derivative(i, j), 0.0);
  109. }
  110. }
  111. }
  112. }
  113. BOOST_AUTO_TEST_CASE_TEMPLATE(ostream, T, all_float_types) {
  114. constexpr std::size_t m = 3;
  115. const T cx = 10;
  116. const auto x = make_fvar<T, m>(cx);
  117. std::ostringstream ss;
  118. ss << "x = " << x;
  119. BOOST_CHECK_EQUAL(ss.str(), "x = depth(1)(10,1,0,0)");
  120. ss.str(std::string());
  121. const auto scalar = make_fvar<T,0>(cx);
  122. ss << "scalar = " << scalar;
  123. BOOST_CHECK_EQUAL(ss.str(), "scalar = depth(1)(10)");
  124. }
  125. BOOST_AUTO_TEST_CASE_TEMPLATE(addition_assignment, T, all_float_types) {
  126. constexpr std::size_t m = 3;
  127. constexpr std::size_t n = 4;
  128. const T cx = 10.0;
  129. auto sum = autodiff_fvar<T, m, n>(); // zero-initialized
  130. // Single variable
  131. const auto x = make_fvar<T, m>(cx);
  132. sum += x;
  133. for (auto i : boost::irange(m + 1)) {
  134. for (auto j : boost::irange(n + 1)) {
  135. if (i == 0 && j == 0) {
  136. BOOST_CHECK_EQUAL(sum.derivative(i, j), cx);
  137. } else if (i == 1 && j == 0) {
  138. BOOST_CHECK_EQUAL(sum.derivative(i, j), 1.0);
  139. } else {
  140. BOOST_CHECK_EQUAL(sum.derivative(i, j), 0.0);
  141. }
  142. }
  143. }
  144. // Arithmetic constant
  145. const T cy = 11.0;
  146. sum = 0;
  147. sum += cy;
  148. for (auto i : boost::irange(m + 1)) {
  149. for (auto j : boost::irange(n + 1)) {
  150. if (i == 0 && j == 0) {
  151. BOOST_CHECK_EQUAL(sum.derivative(i, j), cy);
  152. } else {
  153. BOOST_CHECK_EQUAL(sum.derivative(i, j), 0.0);
  154. }
  155. }
  156. }
  157. }
  158. BOOST_AUTO_TEST_CASE_TEMPLATE(subtraction_assignment, T, all_float_types) {
  159. constexpr std::size_t m = 3;
  160. constexpr std::size_t n = 4;
  161. const T cx = 10.0;
  162. auto sum = autodiff_fvar<T, m, n>(); // zero-initialized
  163. // Single variable
  164. const auto x = make_fvar<T, m>(cx);
  165. sum -= x;
  166. for (auto i : boost::irange(m + 1)) {
  167. for (auto j : boost::irange(n + 1)) {
  168. if (i == 0 && j == 0) {
  169. BOOST_CHECK_EQUAL(sum.derivative(i, j), -cx);
  170. } else if (i == 1 && j == 0) {
  171. BOOST_CHECK_EQUAL(sum.derivative(i, j), -1.0);
  172. } else {
  173. BOOST_CHECK_EQUAL(sum.derivative(i, j), 0.0);
  174. }
  175. }
  176. }
  177. // Arithmetic constant
  178. const T cy = 11.0;
  179. sum = 0;
  180. sum -= cy;
  181. for (auto i : boost::irange(m + 1)) {
  182. for (auto j : boost::irange(n + 1)) {
  183. if (i == 0 && j == 0) {
  184. BOOST_CHECK_EQUAL(sum.derivative(i, j), -cy);
  185. } else {
  186. BOOST_CHECK_EQUAL(sum.derivative(i, j), 0.0);
  187. }
  188. }
  189. }
  190. }
  191. BOOST_AUTO_TEST_CASE_TEMPLATE(multiplication_assignment, T, all_float_types) {
  192. // Try explicit bracing based on feedback. Doesn't add very much except 26
  193. // extra lines.
  194. constexpr std::size_t m = 3;
  195. constexpr std::size_t n = 4;
  196. const T cx = 10.0;
  197. auto product = autodiff_fvar<T, m, n>(1); // unit constant
  198. // Single variable
  199. auto x = make_fvar<T, m>(cx);
  200. product *= x;
  201. for (auto i : boost::irange(m + 1)) {
  202. for (auto j : boost::irange(n + 1)) {
  203. if (i == 0 && j == 0) {
  204. BOOST_CHECK_EQUAL(product.derivative(i, j), cx);
  205. } else if (i == 1 && j == 0) {
  206. BOOST_CHECK_EQUAL(product.derivative(i, j), 1.0);
  207. } else {
  208. BOOST_CHECK_EQUAL(product.derivative(i, j), 0.0);
  209. }
  210. }
  211. }
  212. // Arithmetic constant
  213. const T cy = 11.0;
  214. product = 1;
  215. product *= cy;
  216. for (auto i : boost::irange(m + 1)) {
  217. for (auto j : boost::irange(n + 1)) {
  218. if (i == 0 && j == 0) {
  219. BOOST_CHECK_EQUAL(product.derivative(i, j), cy);
  220. } else {
  221. BOOST_CHECK_EQUAL(product.derivative(i, j), 0.0);
  222. }
  223. }
  224. }
  225. // 0 * inf = nan
  226. x = make_fvar<T, m>(0.0);
  227. x *= std::numeric_limits<T>::infinity();
  228. // std::cout << "x = " << x << std::endl;
  229. for (auto i : boost::irange(m + 1)) {
  230. if (i == 0) {
  231. BOOST_CHECK(boost::math::isnan(static_cast<T>(x))); // Correct
  232. // BOOST_CHECK_EQUAL(x.derivative(i) == 0.0); // Wrong. See
  233. // multiply_assign_by_root_type().
  234. } else if (i == 1) {
  235. BOOST_CHECK(boost::math::isinf(x.derivative(i)));
  236. } else {
  237. BOOST_CHECK_EQUAL(x.derivative(i), 0.0);
  238. }
  239. }
  240. }
  241. BOOST_AUTO_TEST_CASE_TEMPLATE(division_assignment, T, all_float_types) {
  242. constexpr std::size_t m = 3;
  243. constexpr std::size_t n = 4;
  244. const T cx = 16.0;
  245. auto quotient = autodiff_fvar<T, m, n>(1); // unit constant
  246. // Single variable
  247. const auto x = make_fvar<T, m>(cx);
  248. quotient /= x;
  249. BOOST_CHECK_EQUAL(quotient.derivative(0, 0), 1 / cx);
  250. BOOST_CHECK_EQUAL(quotient.derivative(1, 0), -1 / pow(cx, 2));
  251. BOOST_CHECK_EQUAL(quotient.derivative(2, 0), 2 / pow(cx, 3));
  252. BOOST_CHECK_EQUAL(quotient.derivative(3, 0), -6 / pow(cx, 4));
  253. for (auto i : boost::irange(m + 1)) {
  254. for (auto j : boost::irange(std::size_t(1), n + 1)) {
  255. BOOST_CHECK_EQUAL(quotient.derivative(i, j), 0.0);
  256. }
  257. }
  258. // Arithmetic constant
  259. const T cy = 32.0;
  260. quotient = 1;
  261. quotient /= cy;
  262. for (auto i : boost::irange(m + 1)) {
  263. for (auto j : boost::irange(n + 1)) {
  264. if (i == 0 && j == 0) {
  265. BOOST_CHECK_EQUAL(quotient.derivative(i, j), 1 / cy);
  266. } else {
  267. BOOST_CHECK_EQUAL(quotient.derivative(i, j), 0.0);
  268. }
  269. }
  270. }
  271. }
  272. BOOST_AUTO_TEST_CASE_TEMPLATE(unary_signs, T, all_float_types) {
  273. constexpr std::size_t m = 3;
  274. constexpr std::size_t n = 4;
  275. const T cx = 16.0;
  276. autodiff_fvar<T, m, n> lhs;
  277. // Single variable
  278. const auto x = make_fvar<T, m>(cx);
  279. lhs = static_cast<decltype(lhs)>(-x);
  280. for (auto i : boost::irange(m + 1)) {
  281. for (auto j : boost::irange(n + 1)) {
  282. if (i == 0 && j == 0) {
  283. BOOST_CHECK_EQUAL(lhs.derivative(i, j), -cx);
  284. } else if (i == 1 && j == 0) {
  285. BOOST_CHECK_EQUAL(lhs.derivative(i, j), -1.0);
  286. } else {
  287. BOOST_CHECK_EQUAL(lhs.derivative(i, j), 0.0);
  288. }
  289. }
  290. }
  291. lhs = static_cast<decltype(lhs)>(+x);
  292. for (auto i : boost::irange(m + 1)) {
  293. for (auto j : boost::irange(n + 1)) {
  294. if (i == 0 && j == 0) {
  295. BOOST_CHECK_EQUAL(lhs.derivative(i, j), cx);
  296. } else if (i == 1 && j == 0) {
  297. BOOST_CHECK_EQUAL(lhs.derivative(i, j), 1.0);
  298. } else {
  299. BOOST_CHECK_EQUAL(lhs.derivative(i, j), 0.0);
  300. }
  301. }
  302. }
  303. }
  304. // TODO 3 tests for 3 operator+() definitions.
  305. BOOST_AUTO_TEST_CASE_TEMPLATE(cast_double, T, all_float_types) {
  306. const T ca(13);
  307. const T i(12);
  308. constexpr std::size_t m = 3;
  309. const auto x = make_fvar<T, m>(ca);
  310. BOOST_CHECK_LT(i, x);
  311. BOOST_CHECK_EQUAL(i * x, i * ca);
  312. }
  313. BOOST_AUTO_TEST_CASE_TEMPLATE(int_double_casting, T, all_float_types) {
  314. const T ca = 3.0;
  315. const auto x0 = make_fvar<T, 0>(ca);
  316. BOOST_CHECK_EQUAL(static_cast<T>(x0), ca);
  317. const auto x1 = make_fvar<T, 1>(ca);
  318. BOOST_CHECK_EQUAL(static_cast<T>(x1), ca);
  319. const auto x2 = make_fvar<T, 2>(ca);
  320. BOOST_CHECK_EQUAL(static_cast<T>(x2), ca);
  321. }
  322. BOOST_AUTO_TEST_CASE_TEMPLATE(scalar_addition, T, all_float_types) {
  323. const T ca = 3.0;
  324. const T cb = 4.0;
  325. const auto sum0 = autodiff_fvar<T, 0>(ca) + autodiff_fvar<T, 0>(cb);
  326. BOOST_CHECK_EQUAL(ca + cb, static_cast<T>(sum0));
  327. const auto sum1 = autodiff_fvar<T, 0>(ca) + cb;
  328. BOOST_CHECK_EQUAL(ca + cb, static_cast<T>(sum1));
  329. const auto sum2 = ca + autodiff_fvar<T, 0>(cb);
  330. BOOST_CHECK_EQUAL(ca + cb, static_cast<T>(sum2));
  331. }
  332. BOOST_AUTO_TEST_CASE_TEMPLATE(power8, T, all_float_types) {
  333. constexpr std::size_t n = 8u;
  334. const T ca = 3.0;
  335. auto x = make_fvar<T, n>(ca);
  336. // Test operator*=()
  337. x *= x;
  338. x *= x;
  339. x *= x;
  340. const T power_factorial = boost::math::factorial<T>(n);
  341. for (auto i : boost::irange(n + 1)) {
  342. BOOST_CHECK_CLOSE(
  343. static_cast<T>(x.derivative(i)),
  344. static_cast<T>(power_factorial /
  345. boost::math::factorial<T>(static_cast<unsigned>(n - i)) *
  346. pow(ca, n - i)),
  347. std::numeric_limits<T>::epsilon());
  348. }
  349. x = make_fvar<T, n>(ca);
  350. // Test operator*()
  351. x = x * x * x * x * x * x * x * x;
  352. for (auto i : boost::irange(n + 1)) {
  353. BOOST_CHECK_CLOSE(
  354. x.derivative(i),
  355. power_factorial /
  356. boost::math::factorial<T>(static_cast<unsigned>(n - i)) *
  357. pow(ca, n - i),
  358. std::numeric_limits<T>::epsilon());
  359. }
  360. }
  361. BOOST_AUTO_TEST_CASE_TEMPLATE(dim1_multiplication, T, all_float_types) {
  362. constexpr std::size_t m = 2;
  363. constexpr std::size_t n = 3;
  364. const T cy = 4.0;
  365. auto y0 = make_fvar<T, m>(cy);
  366. auto y = make_fvar<T, n>(cy);
  367. y *= y0;
  368. BOOST_CHECK_EQUAL(y.derivative(0), cy * cy);
  369. BOOST_CHECK_EQUAL(y.derivative(1), 2 * cy);
  370. BOOST_CHECK_EQUAL(y.derivative(2), 2.0);
  371. BOOST_CHECK_EQUAL(y.derivative(3), 0.0);
  372. y = y * cy;
  373. BOOST_CHECK_EQUAL(y.derivative(0), cy * cy * cy);
  374. BOOST_CHECK_EQUAL(y.derivative(1), 2 * cy * cy);
  375. BOOST_CHECK_EQUAL(y.derivative(2), 2.0 * cy);
  376. BOOST_CHECK_EQUAL(y.derivative(3), 0.0);
  377. }
  378. BOOST_AUTO_TEST_CASE_TEMPLATE(dim1and2_multiplication, T, all_float_types) {
  379. constexpr std::size_t m = 2;
  380. constexpr std::size_t n = 3;
  381. const T cx = 3.0;
  382. const T cy = 4.0;
  383. auto x = make_fvar<T, m>(cx);
  384. auto y = make_fvar<T, m, n>(cy);
  385. y *= x;
  386. BOOST_CHECK_EQUAL(y.derivative(0, 0), cx * cy);
  387. BOOST_CHECK_EQUAL(y.derivative(0, 1), cx);
  388. BOOST_CHECK_EQUAL(y.derivative(1, 0), cy);
  389. BOOST_CHECK_EQUAL(y.derivative(1, 1), 1.0);
  390. for (auto i : boost::irange(std::size_t(1), m)) {
  391. for (auto j : boost::irange(std::size_t(1), n)) {
  392. if (i == 1 && j == 1) {
  393. BOOST_CHECK_EQUAL(y.derivative(i, j), 1.0);
  394. } else {
  395. BOOST_CHECK_EQUAL(y.derivative(i, j), 0.0);
  396. }
  397. }
  398. }
  399. }
  400. BOOST_AUTO_TEST_CASE_TEMPLATE(dim2_addition, T, all_float_types) {
  401. constexpr std::size_t m = 2;
  402. constexpr std::size_t n = 3;
  403. const T cx = 3.0;
  404. const auto x = make_fvar<T, m>(cx);
  405. BOOST_CHECK_EQUAL(x.derivative(0), cx);
  406. BOOST_CHECK_EQUAL(x.derivative(1), 1.0);
  407. BOOST_CHECK_EQUAL(x.derivative(2), 0.0);
  408. const T cy = 4.0;
  409. const auto y = make_fvar<T, m, n>(cy);
  410. BOOST_CHECK_EQUAL(static_cast<T>(y.derivative(0)), cy);
  411. BOOST_CHECK_EQUAL(static_cast<T>(y.derivative(1)),
  412. 0.0); // partial of y w.r.t. x.
  413. BOOST_CHECK_EQUAL(y.derivative(0, 0), cy);
  414. BOOST_CHECK_EQUAL(y.derivative(0, 1), 1.0);
  415. BOOST_CHECK_EQUAL(y.derivative(1, 0), 0.0);
  416. BOOST_CHECK_EQUAL(y.derivative(1, 1), 0.0);
  417. const auto z = x + y;
  418. BOOST_CHECK_EQUAL(z.derivative(0, 0), cx + cy);
  419. BOOST_CHECK_EQUAL(z.derivative(0, 1), 1.0);
  420. BOOST_CHECK_EQUAL(z.derivative(1, 0), 1.0);
  421. BOOST_CHECK_EQUAL(z.derivative(1, 1), 0.0);
  422. // The following 4 are unnecessarily more expensive than the previous 4.
  423. BOOST_CHECK_EQUAL(z.derivative(0).derivative(0), cx + cy);
  424. BOOST_CHECK_EQUAL(z.derivative(0).derivative(1), 1.0);
  425. BOOST_CHECK_EQUAL(z.derivative(1).derivative(0), 1.0);
  426. BOOST_CHECK_EQUAL(z.derivative(1).derivative(1), 0.0);
  427. }
  428. BOOST_AUTO_TEST_CASE_TEMPLATE(dim2_multiplication, T, all_float_types) {
  429. constexpr std::size_t m = 3;
  430. constexpr std::size_t n = 4;
  431. const T cx = 6.0;
  432. const auto x = make_fvar<T, m>(cx);
  433. const T cy = 5.0;
  434. const auto y = make_fvar<T, 0, n>(cy);
  435. const auto z = x * x * y * y * y;
  436. BOOST_CHECK_EQUAL(z.derivative(0, 0), cx * cx * cy * cy * cy); // x^2 * y^3
  437. BOOST_CHECK_EQUAL(z.derivative(0, 1), cx * cx * 3 * cy * cy); // x^2 * 3y^2
  438. BOOST_CHECK_EQUAL(z.derivative(0, 2), cx * cx * 6 * cy); // x^2 * 6y
  439. BOOST_CHECK_EQUAL(z.derivative(0, 3), cx * cx * 6); // x^2 * 6
  440. BOOST_CHECK_EQUAL(z.derivative(0, 4), 0.0); // x^2 * 0
  441. BOOST_CHECK_EQUAL(z.derivative(1, 0), 2 * cx * cy * cy * cy); // 2x * y^3
  442. BOOST_CHECK_EQUAL(z.derivative(1, 1), 2 * cx * 3 * cy * cy); // 2x * 3y^2
  443. BOOST_CHECK_EQUAL(z.derivative(1, 2), 2 * cx * 6 * cy); // 2x * 6y
  444. BOOST_CHECK_EQUAL(z.derivative(1, 3), 2 * cx * 6); // 2x * 6
  445. BOOST_CHECK_EQUAL(z.derivative(1, 4), 0.0); // 2x * 0
  446. BOOST_CHECK_EQUAL(z.derivative(2, 0), 2 * cy * cy * cy); // 2 * y^3
  447. BOOST_CHECK_EQUAL(z.derivative(2, 1), 2 * 3 * cy * cy); // 2 * 3y^2
  448. BOOST_CHECK_EQUAL(z.derivative(2, 2), 2 * 6 * cy); // 2 * 6y
  449. BOOST_CHECK_EQUAL(z.derivative(2, 3), 2 * 6); // 2 * 6
  450. BOOST_CHECK_EQUAL(z.derivative(2, 4), 0.0); // 2 * 0
  451. BOOST_CHECK_EQUAL(z.derivative(3, 0), 0.0); // 0 * y^3
  452. BOOST_CHECK_EQUAL(z.derivative(3, 1), 0.0); // 0 * 3y^2
  453. BOOST_CHECK_EQUAL(z.derivative(3, 2), 0.0); // 0 * 6y
  454. BOOST_CHECK_EQUAL(z.derivative(3, 3), 0.0); // 0 * 6
  455. BOOST_CHECK_EQUAL(z.derivative(3, 4), 0.0); // 0 * 0
  456. }
  457. BOOST_AUTO_TEST_CASE_TEMPLATE(dim2_multiplication_and_subtraction, T,
  458. all_float_types) {
  459. constexpr std::size_t m = 3;
  460. constexpr std::size_t n = 4;
  461. const T cx = 6.0;
  462. const auto x = make_fvar<T, m>(cx);
  463. const T cy = 5.0;
  464. const auto y = make_fvar<T, 0, n>(cy);
  465. const auto z = x * x - y * y;
  466. BOOST_CHECK_EQUAL(z.derivative(0, 0), cx * cx - cy * cy);
  467. BOOST_CHECK_EQUAL(z.derivative(0, 1), -2 * cy);
  468. BOOST_CHECK_EQUAL(z.derivative(0, 2), -2.0);
  469. BOOST_CHECK_EQUAL(z.derivative(0, 3), 0.0);
  470. BOOST_CHECK_EQUAL(z.derivative(0, 4), 0.0);
  471. BOOST_CHECK_EQUAL(z.derivative(1, 0), 2 * cx);
  472. BOOST_CHECK_EQUAL(z.derivative(2, 0), 2.0);
  473. for (auto i : boost::irange(std::size_t(1), m + 1)) {
  474. for (auto j : boost::irange(std::size_t(1), n + 1)) {
  475. BOOST_CHECK_EQUAL(z.derivative(i, j), 0.0);
  476. }
  477. }
  478. }
  479. BOOST_AUTO_TEST_CASE_TEMPLATE(inverse, T, all_float_types) {
  480. constexpr std::size_t m = 3;
  481. const T cx = 4.0;
  482. const auto x = make_fvar<T, m>(cx);
  483. const auto xinv = x.inverse();
  484. BOOST_CHECK_EQUAL(xinv.derivative(0), 1 / cx);
  485. BOOST_CHECK_EQUAL(xinv.derivative(1), -1 / pow(cx, 2));
  486. BOOST_CHECK_EQUAL(xinv.derivative(2), 2 / pow(cx, 3));
  487. BOOST_CHECK_EQUAL(xinv.derivative(3), -6 / pow(cx, 4));
  488. const auto zero = make_fvar<T, m>(0);
  489. const auto inf = zero.inverse();
  490. for (auto i : boost::irange(m + 1)) {
  491. BOOST_CHECK_EQUAL(inf.derivative(i),
  492. (i % 2 == 1 ? -1 : 1) *
  493. std::numeric_limits<T>::infinity());
  494. }
  495. }
  496. BOOST_AUTO_TEST_CASE_TEMPLATE(division, T, all_float_types) {
  497. constexpr std::size_t m = 3;
  498. constexpr std::size_t n = 4;
  499. const T cx = 16.0;
  500. auto x = make_fvar<T, m>(cx);
  501. const T cy = 4.0;
  502. auto y = make_fvar<T, 1, n>(cy);
  503. auto z = x * x / (y * y);
  504. BOOST_CHECK_EQUAL(z.derivative(0, 0), cx * cx / (cy * cy)); // x^2 * y^-2
  505. BOOST_CHECK_EQUAL(z.derivative(0, 1), cx * cx * (-2) * pow(cy, -3));
  506. BOOST_CHECK_EQUAL(z.derivative(0, 2), cx * cx * (6) * pow(cy, -4));
  507. BOOST_CHECK_EQUAL(z.derivative(0, 3), cx * cx * (-24) * pow(cy, -5));
  508. BOOST_CHECK_EQUAL(z.derivative(0, 4), cx * cx * (120) * pow(cy, -6));
  509. BOOST_CHECK_EQUAL(z.derivative(1, 0), 2 * cx / (cy * cy));
  510. BOOST_CHECK_EQUAL(z.derivative(1, 1), 2 * cx * (-2) * pow(cy, -3));
  511. BOOST_CHECK_EQUAL(z.derivative(1, 2), 2 * cx * (6) * pow(cy, -4));
  512. BOOST_CHECK_EQUAL(z.derivative(1, 3), 2 * cx * (-24) * pow(cy, -5));
  513. BOOST_CHECK_EQUAL(z.derivative(1, 4), 2 * cx * (120) * pow(cy, -6));
  514. BOOST_CHECK_EQUAL(z.derivative(2, 0), 2 / (cy * cy));
  515. BOOST_CHECK_EQUAL(z.derivative(2, 1), 2 * (-2) * pow(cy, -3));
  516. BOOST_CHECK_EQUAL(z.derivative(2, 2), 2 * (6) * pow(cy, -4));
  517. BOOST_CHECK_EQUAL(z.derivative(2, 3), 2 * (-24) * pow(cy, -5));
  518. BOOST_CHECK_EQUAL(z.derivative(2, 4), 2 * (120) * pow(cy, -6));
  519. for (auto j : boost::irange(n + 1)) {
  520. BOOST_CHECK_EQUAL(z.derivative(3, j), 0.0);
  521. }
  522. auto x1 = make_fvar<T, m>(cx);
  523. auto z1 = x1 / cy;
  524. BOOST_CHECK_EQUAL(z1.derivative(0), cx / cy);
  525. BOOST_CHECK_EQUAL(z1.derivative(1), 1 / cy);
  526. BOOST_CHECK_EQUAL(z1.derivative(2), 0.0);
  527. BOOST_CHECK_EQUAL(z1.derivative(3), 0.0);
  528. auto y2 = make_fvar<T, m, n>(cy);
  529. auto z2 = cx / y2;
  530. BOOST_CHECK_EQUAL(z2.derivative(0, 0), cx / cy);
  531. BOOST_CHECK_EQUAL(z2.derivative(0, 1), -cx / pow(cy, 2));
  532. BOOST_CHECK_EQUAL(z2.derivative(0, 2), 2 * cx / pow(cy, 3));
  533. BOOST_CHECK_EQUAL(z2.derivative(0, 3), -6 * cx / pow(cy, 4));
  534. BOOST_CHECK_EQUAL(z2.derivative(0, 4), 24 * cx / pow(cy, 5));
  535. for (auto i : boost::irange(std::size_t(1), m + 1)) {
  536. for (auto j : boost::irange(n + 1)) {
  537. BOOST_CHECK_EQUAL(z2.derivative(i, j), 0.0);
  538. }
  539. }
  540. const auto z3 = y / x;
  541. BOOST_CHECK_EQUAL(z3.derivative(0, 0), cy / cx);
  542. BOOST_CHECK_EQUAL(z3.derivative(0, 1), 1 / cx);
  543. BOOST_CHECK_EQUAL(z3.derivative(1, 0), -cy / pow(cx, 2));
  544. BOOST_CHECK_EQUAL(z3.derivative(1, 1), -1 / pow(cx, 2));
  545. BOOST_CHECK_EQUAL(z3.derivative(2, 0), 2 * cy / pow(cx, 3));
  546. BOOST_CHECK_EQUAL(z3.derivative(2, 1), 2 / pow(cx, 3));
  547. BOOST_CHECK_EQUAL(z3.derivative(3, 0), -6 * cy / pow(cx, 4));
  548. BOOST_CHECK_EQUAL(z3.derivative(3, 1), -6 / pow(cx, 4));
  549. for (auto i : boost::irange(m + 1)) {
  550. for (auto j : boost::irange(std::size_t(2), n + 1)) {
  551. BOOST_CHECK_EQUAL(z3.derivative(i, j), 0.0);
  552. }
  553. }
  554. }
  555. BOOST_AUTO_TEST_CASE_TEMPLATE(equality, T, all_float_types) {
  556. constexpr std::size_t m = 3;
  557. constexpr std::size_t n = 4;
  558. const T cx = 10.0;
  559. const T cy = 10.0;
  560. const auto x = make_fvar<T, m>(cx);
  561. const auto y = make_fvar<T, 0, n>(cy);
  562. BOOST_CHECK_EQUAL(x, y);
  563. BOOST_CHECK_EQUAL(x, cy);
  564. BOOST_CHECK_EQUAL(cx, y);
  565. BOOST_CHECK_EQUAL(cy, x);
  566. BOOST_CHECK_EQUAL(y, cx);
  567. }
  568. BOOST_AUTO_TEST_CASE_TEMPLATE(inequality, T, all_float_types) {
  569. constexpr std::size_t m = 3;
  570. constexpr std::size_t n = 4;
  571. const T cx = 10.0;
  572. const T cy = 11.0;
  573. const auto x = make_fvar<T, m>(cx);
  574. const auto y = make_fvar<T, 0, n>(cy);
  575. BOOST_CHECK_NE(x, y);
  576. BOOST_CHECK_NE(x, cy);
  577. BOOST_CHECK_NE(cx, y);
  578. BOOST_CHECK_NE(cy, x);
  579. BOOST_CHECK_NE(y, cx);
  580. }
  581. BOOST_AUTO_TEST_CASE_TEMPLATE(less_than_or_equal_to, T, all_float_types) {
  582. constexpr std::size_t m = 3;
  583. constexpr std::size_t n = 4;
  584. const T cx = 10.0;
  585. const T cy = 11.0;
  586. const auto x = make_fvar<T, m>(cx);
  587. const auto y = make_fvar<T, 0, n>(cy);
  588. BOOST_CHECK_LE(x, y);
  589. BOOST_CHECK_LE(x, y - 1);
  590. BOOST_CHECK_LT(x, y);
  591. BOOST_CHECK_LE(x, cy);
  592. BOOST_CHECK_LE(x, cy - 1);
  593. BOOST_CHECK_LT(x, cy);
  594. BOOST_CHECK_LE(cx, y);
  595. BOOST_CHECK_LE(cx, y - 1);
  596. BOOST_CHECK_LT(cx, y);
  597. }
  598. BOOST_AUTO_TEST_CASE_TEMPLATE(greater_than_or_equal_to, T, all_float_types) {
  599. constexpr std::size_t m = 3;
  600. constexpr std::size_t n = 4;
  601. const T cx = 11.0;
  602. const T cy = 10.0;
  603. const auto x = make_fvar<T, m>(cx);
  604. const auto y = make_fvar<T, 0, n>(cy);
  605. BOOST_CHECK_GE(x, y);
  606. BOOST_CHECK_GE(x, y + 1);
  607. BOOST_CHECK_GT(x, y);
  608. BOOST_CHECK_GE(x, cy);
  609. BOOST_CHECK_GE(x, cy + 1);
  610. BOOST_CHECK_GT(x, cy);
  611. BOOST_CHECK_GE(cx, y);
  612. BOOST_CHECK_GE(cx, y + 1);
  613. BOOST_CHECK_GT(cx, y);
  614. }
  615. BOOST_AUTO_TEST_CASE_TEMPLATE(fabs_test, T, all_float_types) {
  616. using bmp::fabs;
  617. using detail::fabs;
  618. using std::fabs;
  619. constexpr std::size_t m = 3;
  620. const T cx = 11.0;
  621. const auto x = make_fvar<T, m>(cx);
  622. auto a = fabs(x);
  623. BOOST_CHECK_EQUAL(a.derivative(0), fabs(cx));
  624. BOOST_CHECK_EQUAL(a.derivative(1), 1.0);
  625. BOOST_CHECK_EQUAL(a.derivative(2), 0.0);
  626. BOOST_CHECK_EQUAL(a.derivative(3), 0.0);
  627. a = fabs(-x);
  628. BOOST_CHECK_EQUAL(a.derivative(0), fabs(cx));
  629. BOOST_CHECK_EQUAL(a.derivative(1), 1.0); // fabs(-x) = fabs(x)
  630. BOOST_CHECK_EQUAL(a.derivative(2), 0.0);
  631. BOOST_CHECK_EQUAL(a.derivative(3), 0.0);
  632. const auto xneg = make_fvar<T, m>(-cx);
  633. a = fabs(xneg);
  634. BOOST_CHECK_EQUAL(a.derivative(0), fabs(cx));
  635. BOOST_CHECK_EQUAL(a.derivative(1), -1.0);
  636. BOOST_CHECK_EQUAL(a.derivative(2), 0.0);
  637. BOOST_CHECK_EQUAL(a.derivative(3), 0.0);
  638. const auto zero = make_fvar<T, m>(0);
  639. a = fabs(zero);
  640. for (auto i : boost::irange(m + 1)) {
  641. BOOST_CHECK_EQUAL(a.derivative(i), 0.0);
  642. }
  643. }
  644. BOOST_AUTO_TEST_CASE_TEMPLATE(ceil_and_floor, T, all_float_types) {
  645. using bmp::ceil;
  646. using bmp::floor;
  647. using std::ceil;
  648. using std::floor;
  649. constexpr std::size_t m = 3;
  650. T tests[]{-1.5, 0.0, 1.5};
  651. for (T &test : tests) {
  652. const auto x = make_fvar<T, m>(test);
  653. auto c = ceil(x);
  654. auto f = floor(x);
  655. BOOST_CHECK_EQUAL(c.derivative(0), ceil(test));
  656. BOOST_CHECK_EQUAL(f.derivative(0), floor(test));
  657. for (auto i : boost::irange(std::size_t(1), m + 1)) {
  658. BOOST_CHECK_EQUAL(c.derivative(i), 0.0);
  659. BOOST_CHECK_EQUAL(f.derivative(i), 0.0);
  660. }
  661. }
  662. }
  663. BOOST_AUTO_TEST_SUITE_END()