1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
// SPDX-FileCopyrightText: Bradley M. Bell <bradbell@seanet.com>
// SPDX-FileContributor: 2003-22 Bradley M. Bell
// ----------------------------------------------------------------------------
# include <cppad/cppad.hpp>
# include <cppad/example/atomic_two/eigen_mat_inv.hpp>
namespace { // BEGIN_EMPTY_NAMESPACE
typedef double scalar;
typedef CppAD::AD<scalar> ad_scalar;
typedef atomic_eigen_mat_inv<scalar>::ad_matrix ad_matrix;
scalar eps = 10. * std::numeric_limits<scalar>::epsilon();
using CppAD::NearEqual;
// --------------------------------------------------------------------------
/*
Test atomic_eigen_mat_inv using a non-symmetric matrix
f(x) = [ x[0] -1 ]^{-1}
[ 2 x[1] ]
= [ x[1] 1 ] / (x[0] * x[1] + 2)
[ -2 x[0] ]
y[0] = x[1] / (x[0] * x[1] + 2)
y[1] = 1.0 / (x[0] * x[1] + 2)
y[2] = -2.0 / (x[0] * x[1] + 2)
y[3] = x[0] / (x[0] * x[1] + 2)
*/
bool non_symmetric(void)
{
bool ok = true;
using Eigen::Index;
// -------------------------------------------------------------------
// object that computes inverse of a 2x2 matrix
atomic_eigen_mat_inv<scalar> mat_inv;
// -------------------------------------------------------------------
// declare independent variable vector x
size_t n = 2;
CPPAD_TESTVECTOR(ad_scalar) ad_x(n);
for(size_t j = 0; j < n; j++)
ad_x[j] = ad_scalar(j);
CppAD::Independent(ad_x);
// -------------------------------------------------------------------
// arg = [ x[0] -1 ]
// [ 2 x[1] ]
size_t nr = 2;
ad_matrix ad_arg(nr, nr);
ad_arg(0, 0) = ad_x[0];
ad_arg(0, 1) = ad_scalar(-1.0);
ad_arg(1, 0) = ad_scalar(2.0);
ad_arg(1, 1) = ad_x[1];
// -------------------------------------------------------------------
// use atomic operation to compute arg^{-1}
ad_matrix ad_result = mat_inv.op(ad_arg);
// -------------------------------------------------------------------
// declare the dependent variable vector y
size_t m = 4;
CPPAD_TESTVECTOR(ad_scalar) ad_y(4);
for(size_t i = 0; i < nr; i++)
for(size_t j = 0; j < nr; j++)
ad_y[ i * nr + j ] = ad_result( Index(i), Index(j) );
/* Used to test hand calculated derivaives
CppAD::AD<scalar> ad_dinv = 1.0 / (ad_x[0] * ad_x[1] + 2.0);
ad_y[0] = ad_x[1] * ad_dinv;
ad_y[1] = 1.0 * ad_dinv;
ad_y[2] = -2.0 * ad_dinv;
ad_y[3] = ad_x[0] * ad_dinv;
*/
CppAD::ADFun<scalar> f(ad_x, ad_y);
// -------------------------------------------------------------------
// check zero order forward mode
CPPAD_TESTVECTOR(scalar) x(n), y(m);
for(size_t i = 0; i < n; i++)
x[i] = scalar(i + 2);
scalar dinv = 1.0 / (x[0] * x[1] + 2.0);
y = f.Forward(0, x);
ok &= NearEqual(y[0], x[1] * dinv, eps, eps);
ok &= NearEqual(y[1], 1.0 * dinv, eps, eps);
ok &= NearEqual(y[2], -2.0 * dinv, eps, eps);
ok &= NearEqual(y[3], x[0] * dinv, eps, eps);
// -------------------------------------------------------------------
// check first order forward mode
CPPAD_TESTVECTOR(scalar) x1(n), y1(m);
scalar dinv_x0 = - x[1] * dinv * dinv;
x1[0] = 1.0;
x1[1] = 0.0;
y1 = f.Forward(1, x1);
ok &= NearEqual(y1[0], x[1] * dinv_x0, eps, eps);
ok &= NearEqual(y1[1], 1.0 * dinv_x0, eps, eps);
ok &= NearEqual(y1[2], -2.0 * dinv_x0, eps, eps);
ok &= NearEqual(y1[3], dinv + x[0] * dinv_x0, eps, eps);
//
scalar dinv_x1 = - x[0] * dinv * dinv;
x1[0] = 0.0;
x1[1] = 1.0;
y1 = f.Forward(1, x1);
ok &= NearEqual(y1[0], dinv + x[1] * dinv_x1, eps, eps);
ok &= NearEqual(y1[1], 1.0 * dinv_x1, eps, eps);
ok &= NearEqual(y1[2], -2.0 * dinv_x1, eps, eps);
ok &= NearEqual(y1[3], x[0] * dinv_x1, eps, eps);
// -------------------------------------------------------------------
// check second order forward mode
CPPAD_TESTVECTOR(scalar) x2(n), y2(m);
scalar dinv_x1_x1 = 2.0 * x[0] * x[0] * dinv * dinv * dinv;
x2[0] = 0.0;
x2[1] = 0.0;
y2 = f.Forward(2, x2);
ok &= NearEqual(2.0*y2[0], 2.0*dinv_x1 + x[1]*dinv_x1_x1, eps, eps);
ok &= NearEqual(2.0*y2[1], 1.0 * dinv_x1_x1, eps, eps);
ok &= NearEqual(2.0*y2[2], -2.0 * dinv_x1_x1, eps, eps);
ok &= NearEqual(2.0*y2[3], x[0] * dinv_x1_x1, eps, eps);
// -------------------------------------------------------------------
// check first order reverse
CPPAD_TESTVECTOR(scalar) w(m), d1w(n);
for(size_t i = 0; i < m; i++)
w[i] = 0.0;
w[0] = 1.0;
d1w = f.Reverse(1, w);
ok &= NearEqual(d1w[0], x[1] * dinv_x0, eps, eps);
ok &= NearEqual(d1w[1], dinv + x[1] * dinv_x1, eps, eps);
w[0] = 0.0;
w[1] = 1.0;
d1w = f.Reverse(1, w);
ok &= NearEqual(d1w[0], 1.0 * dinv_x0, eps, eps);
ok &= NearEqual(d1w[1], 1.0 * dinv_x1, eps, eps);
w[1] = 0.0;
w[2] = 1.0;
d1w = f.Reverse(1, w);
ok &= NearEqual(d1w[0], -2.0 * dinv_x0, eps, eps);
ok &= NearEqual(d1w[1], -2.0 * dinv_x1, eps, eps);
w[2] = 0.0;
w[3] = 1.0;
d1w = f.Reverse(1, w);
ok &= NearEqual(d1w[0], dinv + x[0] * dinv_x0, eps, eps);
ok &= NearEqual(d1w[1], x[0] * dinv_x1, eps, eps);
// -------------------------------------------------------------------
// check second order reverse
CPPAD_TESTVECTOR(scalar) d2w(2 * n);
// dinv_x1 = - x[0] * dinv * dinv;
scalar dinv_x1_x0 = 2.0 * x[0] * x[1] * dinv * dinv * dinv - dinv * dinv;
d2w = f.Reverse(2, w);
// partial f_3 w.r.t x_0
ok &= NearEqual(d2w[0 * 2 + 0], dinv + x[0] * dinv_x0, eps, eps);
// partial f_3 w.r.t x_1
ok &= NearEqual(d2w[1 * 2 + 0], x[0] * dinv_x1, eps, eps);
// partial f_3 w.r.t. x_1, x_0
ok &= NearEqual(d2w[0 * 2 + 1], dinv_x1 + x[0] * dinv_x1_x0, eps, eps);
// partial f_3 w.r.t. x_1, x_1
ok &= NearEqual(d2w[1 * 2 + 1], x[0] * dinv_x1_x1, eps, eps);
// -------------------------------------------------------------------
return ok;
}
} // END_EMPTY_NAMESPACE
bool eigen_mat_inv(void)
{ bool ok = true;
ok &= non_symmetric();
return ok;
}
|