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