A simple example on how to use Auto-Diff Codegeneration to compute the Jacobian (derivative) of a general function .
#pragma once
template <typename SCALAR>
Eigen::Matrix<SCALAR, outDim, 1>
testFunction(
const Eigen::Matrix<SCALAR, inDim, 1>&
x)
{
Eigen::Matrix<SCALAR, outDim, 1> y;
y(0) = 3 * x(0) + 2 * x(0) * x(0) - x(1) * x(2);
y(1) = x(2) + x(1) + 3;
return y;
}
template <typename SCALAR>
Eigen::Matrix<SCALAR, outDim, inDim>
jacobianCheck(
const Eigen::Matrix<SCALAR, inDim, 1>&
x)
{
Eigen::Matrix<SCALAR, outDim, inDim> jac;
jac << 3 + 4 * x(0), -x(2), -x(1), 0, 1, 1;
return jac;
}
template <typename SCALAR>
Eigen::Matrix<SCALAR, inDim, inDim>
hessianCheck(
const Eigen::Matrix<SCALAR, inDim, 1>&
x,
const Eigen::Matrix<SCALAR, outDim, 1>& w)
{
Eigen::Matrix<SCALAR, inDim, inDim> hes;
hes << 4, 0, 0, 0, 0, -1, 0, -1, 0;
return w(0) * hes;
}
{
typename derivativesCppadJIT::FUN_TYPE_CG f_cg = testFunction<derivativesCppadJIT::CG_SCALAR>;
typename derivativesCppad::FUN_TYPE_AD f_ad = testFunction<derivativesCppad::AD_SCALAR>;
DerivativesCppadSettings settings;
settings.createForwardZero_ = true;
settings.createJacobian_ = true;
settings.useDynamicLibrary_ = useDynamicLib;
Eigen::VectorXd someVec(
inDim);
someVec.setRandom();
Eigen::VectorXd vecOut = jacAd.forwardZero(someVec);
jacCG.compileJIT(settings,
"forwardZeroTestLib",
verbose);
Eigen::VectorXd vecOut2 = jacCG.forwardZero(someVec);
ASSERT_LT((vecOut - vecOut2).array().abs().maxCoeff(), 1e-10);
}
{
typename derivativesCppadJIT::FUN_TYPE_CG f = testFunction<derivativesCppadJIT::CG_SCALAR>;
typename derivativesCppad::FUN_TYPE_AD f_ad = testFunction<derivativesCppad::AD_SCALAR>;
DerivativesCppadSettings settings;
settings.createJacobian_ = true;
settings.useDynamicLibrary_ = useDynamicLib;
jacCG.compileJIT(settings,
"jacobianCGLib",
verbose);
Eigen::Matrix<double, inDim, 1>
x;
for (
size_t i = 0;
i < 1000;
i++)
{
x.setRandom();
ASSERT_LT((jacCG.jacobian(x) -
jacobianCheck(x)).array().abs().maxCoeff(), 1e-10);
ASSERT_LT((jacAd.jacobian(x) -
jacobianCheck(x)).array().abs().maxCoeff(), 1e-10);
ASSERT_LT((jacCG.jacobian(x) - jacAd.jacobian(x)).array().abs().maxCoeff(), 1e-10);
}
}
{
typename derivativesCppadJIT::FUN_TYPE_CG f = testFunction<derivativesCppadJIT::CG_SCALAR>;
typename derivativesCppad::FUN_TYPE_AD f_ad = testFunction<derivativesCppad::AD_SCALAR>;
DerivativesCppadSettings settings;
settings.createHessian_ = true;
settings.useDynamicLibrary_ = useDynamicLib;
hessianCg.compileJIT(settings,
"hessianCGLib",
verbose);
Eigen::Matrix<double, inDim, 1>
x;
Eigen::Matrix<double, outDim, 1> w;
for (
size_t i = 0;
i < 1000; ++
i)
{
x.setRandom();
w.setRandom();
ASSERT_LT((hessianCg.hessian(x, w) -
hessianCheck(x, w)).array().abs().maxCoeff(), 1e-10);
ASSERT_LT((hessianAd.hessian(x, w) -
hessianCheck(x, w)).array().abs().maxCoeff(), 1e-10);
ASSERT_LT((hessianCg.hessian(x, w) - hessianAd.hessian(x, w)).array().abs().maxCoeff(), 1e-10);
}
}
{
typename derivativesCppadJIT::FUN_TYPE_CG f = testFunction<derivativesCppadJIT::CG_SCALAR>;
typename derivativesCppad::FUN_TYPE_AD f_ad = testFunction<derivativesCppad::AD_SCALAR>;
DerivativesCppadSettings settings;
settings.createJacobian_ = true;
settings.useDynamicLibrary_ = useDynamicLib;
jacCG->compileJIT(settings,
"jacobianCGLib",
verbose);
Eigen::Matrix<double, inDim, 1>
x;
std::shared_ptr<derivativesCppadJIT> jacCG_cloned(jacCG->clone());
if (useDynamicLib && (jacCG_cloned->getDynamicLib() == jacCG->getDynamicLib()))
{
std::cout << "FATAL ERROR: dynamic library not cloned correctly in JIT." << std::endl;
ASSERT_TRUE(false);
}
#ifdef LLVM
if (!useDynamicLib && (jacCG_cloned->getLlvmLib() == jacCG->getLlvmLib()))
{
std::cout << "FATAL ERROR: Llvm library not cloned correctly in JIT." << std::endl;
ASSERT_TRUE(false);
}
#endif
for (
size_t i = 0;
i < 100;
i++)
{
x.setRandom();
ASSERT_LT((jacCG_cloned->jacobian(x) -
jacobianCheck(x)).array().abs().maxCoeff(), 1e-10);
ASSERT_LT((jacCG_cloned->jacobian(x) - jacAd->jacobian(x)).array().abs().maxCoeff(), 1e-10);
}
}
TEST(JacobianCGTest, ForwardZeroTest)
{
try
{
#ifdef LLVM
#endif
} catch (std::exception& e)
{
std::cout << "Exception thrown: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
TEST(JacobianCGTest, JITCompilationTest)
{
try
{
#ifdef LLVM
#endif
} catch (std::exception& e)
{
std::cout << "Exception thrown: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
TEST(HessianCGTest, JITHessianTest)
{
try
{
#ifdef LLVM
#endif
} catch (std::exception& e)
{
std::cout << "Exception thrown: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
TEST(JacobianCGTest, DISABLED_LlvmCloneTest)
{
try
{
} catch (std::exception& e)
{
std::cout << "Exception thrown: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
TEST(JacobianCGTest, JitCloneTest)
{
try
{
} catch (std::exception& e)
{
std::cout << "Exception thrown: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
TEST(JacobianCGTest, CodegenTest)
{
typename derivativesCppadCG::FUN_TYPE_CG f = testFunction<derivativesCppadCG::CG_SCALAR>;
jacCG.generateJacobianSource("TestJacobian");
jacCG.generateForwardZeroSource("TestForwardZero");
jacCG.generateHessianSource("TestHessian");
}