* \file test_eigen Dual number Eigen integration tests
*
* (c)2019 Michael Tesch. tesch1@gmail.com
*/
#include "type_name.hpp"
#include <duals/dual_eigen>
#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <Eigen/StdVector>
#include <unsupported/Eigen/MatrixFunctions>
#include "eexpokit/padm.hpp"
#include "gtest/gtest.h"
using duals::rpart;
using duals::dpart;
using duals::dualf;
using duals::duald;
using duals::dualld;
using duals::hyperdualf;
using duals::hyperduald;
using duals::hyperdualld;
using duals::is_dual;
using duals::is_complex;
using duals::dual_traits;
using namespace duals::literals;
typedef std::complex<double> complexd;
typedef std::complex<float> complexf;
typedef std::complex<duald> cduald;
typedef std::complex<dualf> cdualf;
template <class eT, int N=Eigen::Dynamic, int K = N> using emtx = Eigen::Matrix<eT, N, K>;
template <class eT> using smtx = Eigen::SparseMatrix<eT>;
template <int N=2, int K = N> using ecf = Eigen::Matrix<complexf, N, K> ;
template <int N=2, int K = N> using edf = Eigen::Matrix<dualf, N, K> ;
template <int N=2, int K = N> using ecdf = Eigen::Matrix<cdualf, N, K> ;
#define _EXPECT_TRUE(...) {typedef __VA_ARGS__ tru; EXPECT_TRUE(tru::value); static_assert(tru::value, "sa"); }
#define _EXPECT_FALSE(...) {typedef __VA_ARGS__ fal; EXPECT_FALSE(fal::value); static_assert(!fal::value, "sa"); }
#define EXPECT_DEQ(A,B) EXPECT_EQ(rpart(A), rpart(B)); EXPECT_EQ(dpart(A), dpart(B))
#define ASSERT_DEQ(A,B) ASSERT_EQ(rpart(A), rpart(B)); ASSERT_EQ(dpart(A), dpart(B))
#define EXPECT_DNE(A,B) EXPECT_NE(rpart(A), rpart(B)); EXPECT_NE(dpart(A), dpart(B))
#define EXPECT_DNEAR(A,B,tol) \
EXPECT_NEAR(rpart(A), rpart(B),tol); \
EXPECT_NEAR(dpart(A), dpart(B),tol)
template <class DerivedA, typename ReturnT = typename DerivedA::PlainObject>
ReturnT
expm4(const Eigen::EigenBase<DerivedA> & A_,
typename DerivedA::RealScalar mn = std::numeric_limits<typename DerivedA::RealScalar>::epsilon() * 1000)
{
typedef typename DerivedA::RealScalar Real;
using std::isfinite;
const DerivedA & A = A_.derived();
int maxt = std::numeric_limits<Real>::digits;
int s = log2(rpart(A.derived().norm())) + 1;
s = std::max(0, s);
auto B = A * pow(Real(2), -s);
ReturnT R(A.rows(), A.cols());
R.setIdentity();
R += B;
ReturnT S = B;
for (int ii = 2; ii < maxt; ii++) {
S = S * B * Real(1.0/ii);
R += S;
auto Sn = S.norm();
if (!isfinite(Sn)) {
std::cout << "expm() non-finite norm:" << Sn << " at " << ii << "\n";
std::cout << " |R| = " << R.norm() << " s=" << s << "\n"
<< " |A| = " << rpart(A.real().norm()) << "\n"
<< " |A/2^s|=" << rpart(A.real().norm()/pow(2,s)) << "\n";
break;
}
if (Sn < mn)
break;
if (ii == maxt - 1) {
std::cout << "expm() didn't converge in " << maxt << " |S| = " << Sn << "\n";
throw std::invalid_argument("no converge");
}
}
for (; s; s--)
R = R * R;
return R;
}
TEST(Eigen, NumTraits) {
_EXPECT_TRUE(std::is_same<typename Eigen::NumTraits<duals::dual<float>>::Real, dualf>);
_EXPECT_TRUE(std::is_same<typename Eigen::NumTraits<cdualf>::Real, dualf>);
EXPECT_EQ(Eigen::NumTraits<duals::dual<float>>::dummy_precision(),
Eigen::NumTraits<float>::dummy_precision());
EXPECT_EQ(Eigen::NumTraits<duals::dual<float>>::digits10(),
Eigen::NumTraits<float>::digits10());
EXPECT_EQ(Eigen::NumTraits<cdualf>::dummy_precision(),
Eigen::NumTraits<float>::dummy_precision());
}
TEST(Eigen, construction)
{
emtx<double> ed;
emtx<float> ef;
emtx<complexd> ecd;
emtx<complexf> ecf;
emtx<duald> edd;
emtx<dualf> edf_;
emtx<cduald> ecdd;
emtx<cdualf> ecdf_;
}
TEST(Eigen, sparse)
{
smtx<double> ed;
smtx<float> ef;
smtx<complexd> ecd;
smtx<complexf> ecf;
smtx<duald> edd;
smtx<dualf> edf_;
smtx<cduald> ecdd;
smtx<cdualf> ecdf_;
}
TEST(Eigen, expr_type_dense) {
emtx<double> ed;
emtx<float> ef;
emtx<complexd> ecd;
emtx<complexf> ecf;
emtx<duald> edd;
emtx<dualf> edf_;
emtx<cduald> ecdd;
emtx<cdualf> ecdf_;
ecf = ecf * 1;
#define PO_EXPR_TYPE(...) typename std::decay<decltype( __VA_ARGS__ )>::type::PlainObject
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecf*1), emtx<complexf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecf + ecf), emtx<complexf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecf * cdualf(1,2)), emtx<cdualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(edf_ * 2), emtx<dualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(edf_ * cdualf(2,2)), emtx<cdualf>>);
static_assert(!duals::can_promote<dualf, emtx<dualf>>::value, "nope");
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(edf_ * 2_ef), emtx<dualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecf * complexf(1,2)), emtx<complexf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecdf_ * 1), emtx<cdualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecdf_ * 2_ef), emtx<cdualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecdf_ * complexf(1,2)), emtx<cdualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecdf_ * cdualf(1_ef,2_ef)), emtx<cdualf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(edd * 1_e), emtx<duald>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(edd * 1_ef), emtx<duald>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ed*1), emtx<double>>);
auto a1 = ecf * 1;
auto a2 = ecf * ecf;
auto a5 = ecf * cdualf(1,2);
auto a6 = edf_ * 2;
auto a7 = edf_ * 2_ef;
auto a8 = ecf * complexf(1,2);
auto a9 = ecdf_ * 1;
ecdf_ = ecdf_ * complexf(1,2);
ecdf_ = ecdf_ * cdualf(1_ef,2_ef);
auto a12 = edd * 1_e;
auto a13 = ed * 1;
auto A1 = a1.eval(); A1 += A1;
auto A2 = a2.eval(); A2 += A2;
auto A5 = a5.eval(); A5 += A5;
auto A6 = a6.eval(); A6 += A6;
auto A7 = a7.eval(); A7 += A7;
auto A8 = a8.eval(); A8 += A8;
auto A9 = a9.eval(); A9 += A9;
auto A12 = a12.eval(); A12 += A12;
auto A13 = a13.eval(); A13 += A13;
}
TEST(init, Constant) {
edf<3> a = edf<3>::Constant(3,3, 5 + 8_ef);
ecdf<3> b = ecdf<3>::Constant(3,3, cdualf(4,2 + 3_ef));
EXPECT_EQ(a(1,1), 5 + 8_ef);
EXPECT_EQ(b(0,1), cdualf(4,2 + 3_ef));
EXPECT_TRUE((a.array() < 6).all());
EXPECT_FALSE((a.array() != 5 + 8_ef).any());
EXPECT_EQ((a.array() == 5 + 8_ef).count(), 9);
}
TEST(init, setIdentity) {
edf<3> a = edf<3>::Constant(3,3, 5 + 8_ef);
ecdf<3> b = ecdf<3>::Constant(3,3, cdualf(4,2 + 3_ef));
a.setIdentity();
b.setIdentity();
EXPECT_EQ(a(0,0), 1);
EXPECT_EQ(a(0,1), 0);
EXPECT_EQ(a(0,2), 0);
EXPECT_EQ(a(1,0), 0);
EXPECT_EQ(a(1,1), 1);
EXPECT_EQ(a(1,2), 0);
EXPECT_EQ(a(2,0), 0);
EXPECT_EQ(a(2,1), 0);
EXPECT_EQ(a(2,2), 1);
EXPECT_EQ(b(0,0).real(), 1);
EXPECT_EQ(b(0,1).real(), 0);
EXPECT_EQ(b(0,2).real(), 0);
EXPECT_EQ(b(1,0).real(), 0);
EXPECT_EQ(b(1,1).real(), 1);
EXPECT_EQ(b(1,2).real(), 0);
EXPECT_EQ(b(2,0).real(), 0);
EXPECT_EQ(b(2,1).real(), 0);
EXPECT_EQ(b(2,2).real(), 1);
}
TEST(init, setZero) {
edf<3> a = edf<3>::Constant(3,3, 5 + 8_ef);
ecdf<3> b = ecdf<3>::Constant(3,3, cdualf(4,2 + 3_ef));
EXPECT_EQ(a(1,1), 5 + 8_ef);
a.setZero();
b.setZero();
EXPECT_EQ(a(0,0), 0);
EXPECT_EQ(a(0,1), 0);
EXPECT_EQ(a(0,2), 0);
EXPECT_EQ(a(1,0), 0);
EXPECT_EQ(a(1,1), 0);
EXPECT_EQ(a(1,2), 0);
EXPECT_EQ(a(2,0), 0);
EXPECT_EQ(a(2,1), 0);
EXPECT_EQ(a(2,2), 0);
EXPECT_EQ(b(0,0).real(), 0);
EXPECT_EQ(b(0,1).real(), 0);
EXPECT_EQ(b(0,2).real(), 0);
EXPECT_EQ(b(1,0).real(), 0);
EXPECT_EQ(b(1,1).real(), 0);
EXPECT_EQ(b(1,2).real(), 0);
EXPECT_EQ(b(2,0).real(), 0);
EXPECT_EQ(b(2,1).real(), 0);
EXPECT_EQ(b(2,2).real(), 0);
EXPECT_EQ(b(0,0).imag(), 0);
EXPECT_EQ(b(0,1).imag(), 0);
EXPECT_EQ(b(0,2).imag(), 0);
EXPECT_EQ(b(1,0).imag(), 0);
EXPECT_EQ(b(1,1).imag(), 0);
EXPECT_EQ(b(1,2).imag(), 0);
EXPECT_EQ(b(2,0).imag(), 0);
EXPECT_EQ(b(2,1).imag(), 0);
EXPECT_EQ(b(2,2).imag(), 0);
}
TEST(init, LinSpaced) {
ecf<3,1> a = ecf<3,1>::LinSpaced(3, 4, complexf(6,7));
EXPECT_EQ(a(0).real(), 4);
EXPECT_EQ(a(1).real(), 5);
EXPECT_EQ(a(2).real(), 6);
edf<3,1> b = edf<3,1>::LinSpaced(3, 4, 6 + 6_ef);
EXPECT_EQ(b(0), 4);
EXPECT_EQ(b(1), 5 + 3_ef);
EXPECT_EQ(b(2), 6 + 6_ef);
}
TEST(init, Random) {
complexf c = emtx<complexf,1>::Random()(0,0);
EXPECT_NE(c.real(), 0);
EXPECT_NE(c.imag(), 0);
typedef dualf Rt;
using duals::random;
Rt b = emtx<Rt,1>::Random()(0,0);
EXPECT_NE(b.rpart(), 0);
edf<3> j = edf<3>::Random(3,3);
EXPECT_NE(j(1,2).rpart(), 0);
ecdf<3> k = ecdf<3>::Random(3,3);
EXPECT_NE(k(0,1).real().rpart(), 0);
using duals::randos::random2;
dualf x = random2<dualf>();
EXPECT_NE(x.rpart(), 0);
EXPECT_NE(x.dpart(), 0);
duald y = random2<duald>();
EXPECT_NE(y.rpart(), 0);
EXPECT_NE(y.dpart(), 0);
}
TEST(assign, ops) {
emtx<double> ed;
emtx<float> ef;
emtx<complexd> ecd;
emtx<complexf> ecf;
emtx<duald> edd;
emtx<dualf> edf_;
emtx<cduald> ecdd;
emtx<cdualf> ecdf_;
ed *= 2;
ef *= 2;
edd *= 2;
edf_ *= 2;
edd *= 2_e;
edf_ *= 2_ef;
}
TEST(Array, math) {
int N = 5;
emtx<float> x(N,1);
emtx<float> y(N,1);
emtx<float> z(N,1);
x.array() += 1;
x += y + z;
x *= 2;
emtx<duald> a(N,1);
emtx<duald> A(N,1);
emtx<duald> c(N,1);
a.array() = 1 + 1_e;
a.array() += 3_e;
a.array() = 0;
a.array() += 1;
A.array() += 2;
c = a + A;
c = a * 2;
c = a * 2_e;
c = 2 * a;
c = (3 + 2_e) * a;
c *= 3;
c *= 3_e;
EXPECT_EQ(c(N-1), 27_e);
}
TEST(access, CwiseRpartOp) {
emtx<float,3> a, b = emtx<float,3>::Random();
a = b.unaryExpr(duals::CwiseRpartOp<float>());
EXPECT_NE(a.norm(), 0);
emtx<dualf,3> d;
a = d.unaryExpr(duals::CwiseRpartOp<dualf>());
a = d.unaryExpr(duals::CwiseDpartOp<dualf>());
a = rpart(d);
a = dpart(d);
emtx<complexf,3> g, f = emtx<complexf,3>::Random();
emtx<cdualf,3> e;
e.array() = f.array().cast<cdualf>() - 1_ef * f.array().cast<cdualf>();
g = e.unaryExpr(duals::CwiseRpartOp<cdualf>());
EXPECT_EQ((f-g).norm(), 0);
g = f.unaryExpr(duals::CwiseRpartOp<complexf>());
EXPECT_EQ((f-g).norm(), 0);
g = rpart(f);
EXPECT_EQ((f-g).norm(), 0);
g = e.unaryExpr(duals::CwiseDpartOp<cdualf>());
EXPECT_EQ((f+g).norm(), 0);
g = f.unaryExpr(duals::CwiseDpartOp<complexf>());
EXPECT_EQ((g).norm(), 0);
g = dpart(f);
EXPECT_EQ((g).norm(), 0);
}
TEST(access, CwiseDpartOp) {
emtx<float,3> a, b = emtx<float,3>::Random();
a = b.unaryExpr(duals::CwiseDpartOp<float>());
EXPECT_EQ(a.norm(), 0);
emtx<dualf,3> d = b - 1_ef * b;
a = d.unaryExpr(duals::CwiseDpartOp<dualf>());
EXPECT_NE(a.norm(), 0);
EXPECT_EQ((a+b).norm(), 0);
emtx<complexf,3> g, f = emtx<complexf,3>::Random();
emtx<cdualf,3> e;
e.array() = f.array().cast<cdualf>() - 1_ef * f.array().cast<cdualf>();
g = e.unaryExpr(duals::CwiseDpartOp<cdualf>());
EXPECT_EQ((g+f).norm(), 0);
d = e.real();
EXPECT_NE(d.norm(), 0);
}
TEST(measure, norm) {
typedef emtx<complexf, 3> MatrixC;
MatrixC c = (MatrixC() << 1,2,3, 4,5,6, 7,8,9).finished();
EXPECT_EQ(c.cwiseAbs().colwise().sum().maxCoeff(), complexf(18));
typedef dualf Rt;
typedef emtx<Rt, 3> MatrixD;
Rt b = 1+0_ef;
b = 3;
Rt d(1);
MatrixD x;
x << d;
MatrixD a = (MatrixD() << 1,2,3, 4,5+5_ef,6, 7,8,9).finished();
EXPECT_EQ(a.sum(), 45 + 5_ef);
EXPECT_NEAR(rpart(a.norm()), 16.8819430161341337282, 1e-5);
EXPECT_NEAR(rpart(a.mean()), 5, 1e-5);
EXPECT_NEAR(dpart(a.mean()), 0.555555555555555, 1e-5);
EXPECT_EQ(a.minCoeff(), 1);
EXPECT_EQ(a.maxCoeff(), 9);
EXPECT_EQ(a.trace(), 15 + 5_ef);
EXPECT_EQ(a.squaredNorm(), 285+0_e);
EXPECT_EQ(a.lpNorm<1>(), 45 + 5_ef);
EXPECT_EQ(a.cwiseAbs().colwise().sum().maxCoeff(), 18);
}
TEST(dual_decay, stat) {
emtx<float> ef;
emtx<complexd> ecd;
emtx<complexf> ecf;
emtx<duald> edd;
emtx<dualf> edf_;
emtx<cduald> ecdd;
emtx<cdualf> ecdf_;
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(rpart(ef)), emtx<float>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(dpart(ef)), emtx<float>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(rpart(edf_)), emtx<float>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(dpart(edf_)), emtx<float>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(rpart(ecdf_)), emtx<complexf>>);
_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(dpart(ecdf_)), emtx<complexf>>);
}
TEST(dpart, matrix) {
emtx<float,3> a = emtx<float,3>::Random(3,3);
emtx<float,3> b = emtx<float,3>::Random(3,3);
emtx<float,3> c = emtx<float,3>::Random(3,3);
emtx<float,3> d = emtx<float,3>::Random(3,3);
emtx<dualf,3> A = a + 1_ef * b;
emtx<dualf,3> B = c + 1_ef * d;
emtx<cdualf,3> AA;
emtx<complexf,3> BB,CC;
AA.real() = A;
AA.imag() = B;
BB.real() = a;
BB.imag() = c;
CC.real() = b;
CC.imag() = d;
EXPECT_EQ((rpart(A) - a).norm(),0);
EXPECT_EQ((dpart(A) - b).norm(),0);
EXPECT_EQ((rpart(AA) - BB).norm(),0);
EXPECT_EQ((dpart(AA) - CC).norm(),0);
}
TEST(func, expm) {
typedef float T;
typedef dual<T> dualt;
typedef std::complex<dual<T>> cdualt;
#define NN 3
#define N2 6
emtx<dualt, NN> a,b;
a = emtx<dualt, NN>::Random();
a.array() += 1.1 + 2.2_ef;
a.setZero();
a = eexpokit::padm(a);
EXPECT_LT((a - emtx<dualt, NN>::Identity()).norm(), 1e-6) << "a=" << a << "\n";
a *= 1+2_e;
EXPECT_LT((a - emtx<dualt, NN>::Identity()).norm(), 1e-6) << "a=" << a << "\n";
emtx<cdualt, NN> c;
c.setZero();
c = c.exp();
EXPECT_LT((c - emtx<cdualf, NN>::Identity()).norm(), 1e-6) << "b=" << b << "\n";
#undef NN
#undef N2
}
#define QUOTE(...) STRFY(__VA_ARGS__)
#define STRFY(...) #__VA_ARGS__
int main(int argc, char **argv)
{
std::cout << "OPT_FLAGS=" << QUOTE(OPT_FLAGS) << "\n";
std::cout << "INSTRUCTIONSET=" << Eigen::SimdInstructionSetsInUse() << "\n";
::testing::InitGoogleTest(&argc, argv);
std::cout.precision(20);
std::cerr.precision(20);
return RUN_ALL_TESTS();
}