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