ngspice/src/include/cppduals/tests/test_eigen.cpp

530 lines
15 KiB
C++

//===-- test_funcs.cpp - test duals/dual ------------------------*- C++ -*-===//
//
// Part of the cppduals project.
// https://gitlab.com/tesch1/cppduals
//
// See https://gitlab.com/tesch1/cppduals/blob/master/LICENSE.txt for
// license information.
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// (c)2019 Michael Tesch. tesch1@gmail.com
//
/**
* \file test_eigen Dual number Eigen integration tests
*
* (c)2019 Michael Tesch. tesch1@gmail.com
*/
#include <duals/dual_eigen>
#include "type_name.hpp"
#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <Eigen/StdVector>
#include <unsupported/Eigen/MatrixFunctions>
//#include <unsupported/Eigen/AutoDiff>
#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 long double ldouble;
typedef std::complex<float> complexf;
typedef std::complex<double> complexd;
typedef std::complex<long double> complexld;
typedef std::complex<dualf> cdualf;
typedef std::complex<duald> cduald;
typedef std::complex<dualld> cdualld;
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)
TEST(Eigen, NumTraits) {
//::testing::StaticAssertTypeEq<Eigen::NumTraits<duals::dual<float>>::Real, float>();
_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_;
//_EXPECT_TRUE(std::is_base_of<Eigen::EigenBase<U>,U >::value);
//_EXPECT_TRUE(std::is_same<std::common_type<emtx<dualf>,dualf>::type, emtx<dualf>>);
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 * edf_), emtx<cdualf>>); [1]
//_EXPECT_TRUE(std::is_same<PO_EXPR_TYPE(ecf * 1_ef), emtx<cdualf>>); [1]
_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(edf_ * 2_e), emtx<duald>>);
_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 a3 = ecf * edf_; [1]
//auto a4 = ecf * 1_ef; [1]
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 A3 = a3.eval(); A3 += A3; [1]
//auto A4 = a4.eval(); A4 += A4; [1]
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);
//EXPECT_NE(b.dpart(), 0); //
edf<3> j = edf<3>::Random(3,3);
EXPECT_NE(j(1,2).rpart(), 0);
//EXPECT_NE(j(2,0).dpart(), 0); //
ecdf<3> k = ecdf<3>::Random(3,3);
EXPECT_NE(k(0,1).real().rpart(), 0);
//EXPECT_NE(k(1,1).imag().dpart(), 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);
//EXPECT_NE(k(1,1).imag().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;
//ecd *= 2;
//ecf *= 2;
//ecd *= 2_e;
//ecf *= 2_ef;
edd *= 2;
edf_ *= 2;
edd *= 2_e;
edf_ *= 2_ef;
//ecdd *= 2;
// ecdf_ *= 2;
}
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) {
// on float matrices (pass-through)
emtx<float,3> a, b = emtx<float,3>::Random();
a = b.unaryExpr(duals::CwiseRpartOp<float>());
EXPECT_NE(a.norm(), 0);
// on dual<float> matrices
emtx<dualf,3> d;
a = d.unaryExpr(duals::CwiseRpartOp<dualf>());
a = d.unaryExpr(duals::CwiseDpartOp<dualf>());
a = rpart(d);
a = dpart(d);
// on complex<dual<float>> matrices
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) {
// on float matrices (pass-through)
emtx<float,3> a, b = emtx<float,3>::Random();
a = b.unaryExpr(duals::CwiseDpartOp<float>());
EXPECT_EQ(a.norm(), 0);
// on dual<float> matrices
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);
// on complex<dual<float>> matrices
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));
//EXPECT_EQ(c.maxCoeff(), complexf(9));
//typedef duald Rt;
typedef dualf Rt;
//typedef cdualf Rt;
typedef emtx<Rt, 3> MatrixD;
Rt b = 1+0_ef;
b = 3;
Rt d(1);
MatrixD x;
x.array() = d;
MatrixD a = (MatrixD() << 1,2,3, 4,5+5_ef,6, 7,8,9).finished();
//typename MatrixD::Index index;
EXPECT_EQ(a.sum(), 45 + 5_ef);
EXPECT_EQ(x.sum(), 9);
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.lpNorm<Eigen::Infinity>(), 9);
// 1-norm
//EXPECT_EQ(a.colwise().lpNorm<1>().maxCoeff(), 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(eigen, exp_typechecks) {
typedef emtx<dualf,3> Mat;
typedef emtx<duald,3> Matd;
EXPECT_FALSE(Eigen::internal::is_exp_known_type<typename Mat::Scalar>::value);
EXPECT_FALSE(Eigen::internal::is_exp_known_type<typename Matd::Scalar>::value);
typedef emtx<cdualf,3> Matc;
typedef emtx<cduald,3> Matcd;
EXPECT_FALSE(Eigen::internal::is_exp_known_type<typename Matc::Scalar>::value);
EXPECT_FALSE(Eigen::internal::is_exp_known_type<typename Matcd::Scalar>::value);
}
const bool _exp = true;
const bool _padm = false;
#define TEST_EXP(SCALAR_T, SIDE, EXP_OR_PADM) \
TEST(func, exp_##SCALAR_T##_##SIDE##EXP_OR_PADM) { \
emtx<SCALAR_T, SIDE> a,b; \
a = emtx<SCALAR_T, SIDE>::Random(); \
a.setZero(); \
if (EXP_OR_PADM == _exp) a = a.exp(); \
else a = eexpokit::padm(a); \
EXPECT_LT((a - emtx<SCALAR_T, SIDE>::Identity()).norm(), 1e-6) << "a=" << a << "\n"; \
}
#define TEST_EXP_SIZES(SCALAR_T, EXP_OR_PADM) \
TEST_EXP(SCALAR_T, 3, EXP_OR_PADM) \
TEST_EXP(SCALAR_T, 4, EXP_OR_PADM) \
TEST_EXP(SCALAR_T, 17, EXP_OR_PADM)
#define TEST_EXP_SIZES_EOP(SCALAR_T) \
TEST_EXP_SIZES(SCALAR_T, _padm) \
TEST_EXP_SIZES(SCALAR_T, _exp)
// just make sure padm is working
TEST_EXP_SIZES_EOP(float)
TEST_EXP_SIZES_EOP(double)
TEST_EXP_SIZES_EOP(ldouble)
TEST_EXP_SIZES_EOP(complexf)
TEST_EXP_SIZES_EOP(complexd)
TEST_EXP_SIZES_EOP(complexld)
/* testing engine */
#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();
}