diff --git a/CMakeLists.txt b/CMakeLists.txt index de73d496..7d45ff77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,7 @@ ENDIF() # ---------------------------------------------------------------------------- # Check if it is possible to use LLVM/Clang for JIT # ---------------------------------------------------------------------------- -SET(CPPADCG_LLVM_LINK_LIB "3.2|3.6|3.8|4.0|5.0|6.0|7.0|8.0") +SET(CPPADCG_LLVM_LINK_LIB "3.2|3.6|3.8|4.0|5.0|6.0|7.0|8.0|9.0") IF((("${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}" MATCHES "^(${CPPADCG_LLVM_LINK_LIB})$") AND CLANG_FOUND) OR diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake index 785e6bcf..87b85305 100644 --- a/cmake/FindLLVM.cmake +++ b/cmake/FindLLVM.cmake @@ -41,7 +41,7 @@ UNSET(LLVM_MODULE_LIBS CACHE) MACRO(find_llvm_iteratively) IF(NOT LLVM_CONFIG AND NOT LLVM_FIND_VERSION_EXACT) - SET(_LLVM_KNOWN_VERSIONS ${LLVM_ADDITIONAL_VERSIONS} "8" "7" "6.0" "5.0" "4.0" "3.8" "3.7" "3.6" "3.5" "3.4" "3.3" "3.2") + SET(_LLVM_KNOWN_VERSIONS ${LLVM_ADDITIONAL_VERSIONS} "9" "8" "7" "6.0" "5.0" "4.0" "3.8" "3.7" "3.6" "3.5" "3.4" "3.3" "3.2") # Select acceptable versions. FOREACH(version ${_LLVM_KNOWN_VERSIONS}) diff --git a/include/cppad/cg/lang/mathml/language_mathml.hpp b/include/cppad/cg/lang/mathml/language_mathml.hpp index 0a629d30..023fa81e 100644 --- a/include/cppad/cg/lang/mathml/language_mathml.hpp +++ b/include/cppad/cg/lang/mathml/language_mathml.hpp @@ -601,7 +601,7 @@ class LanguageMathML : public Language { const std::vector& indArg = _nameGen->getIndependent(); const std::vector& depArg = _nameGen->getDependent(); const std::vector& tmpArg = _nameGen->getTemporary(); - CPPADCG_ASSERT_KNOWN(!indArg.empty() && depArg.size() > 0, + CPPADCG_ASSERT_KNOWN(!indArg.empty() && !depArg.empty(), "There must be at least one dependent and one independent argument") CPPADCG_ASSERT_KNOWN(tmpArg.size() == 3, "There must be three temporary variables") @@ -1906,7 +1906,7 @@ class LanguageMathML : public Language { CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexAssign, "Invalid node type") CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for an index assignment operation") - IndexAssignOperationNode& inode = static_cast&> (node); + auto& inode = static_cast&> (node); const IndexPattern& ip = inode.getIndexPattern(); _code << _startEq diff --git a/include/cppad/cg/model/llvm/llvm.hpp b/include/cppad/cg/model/llvm/llvm.hpp index 23d6914a..360b9be1 100644 --- a/include/cppad/cg/model/llvm/llvm.hpp +++ b/include/cppad/cg/model/llvm/llvm.hpp @@ -36,6 +36,8 @@ #include #elif LLVM_VERSION_MAJOR==8 && LLVM_VERSION_MINOR==0 #include +#elif LLVM_VERSION_MAJOR==9 && LLVM_VERSION_MINOR==0 +#include #endif #endif diff --git a/include/cppad/cg/model/llvm/v9_0/llvm9_0.hpp b/include/cppad/cg/model/llvm/v9_0/llvm9_0.hpp new file mode 100644 index 00000000..c5719a2c --- /dev/null +++ b/include/cppad/cg/model/llvm/v9_0/llvm9_0.hpp @@ -0,0 +1,92 @@ +#ifndef CPPAD_CG_LLVM9_0_INCLUDED +#define CPPAD_CG_LLVM9_0_INCLUDED +/* -------------------------------------------------------------------------- + * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation: + * Copyright (C) 2019 Joao Leal + * + * CppADCodeGen is distributed under multiple licenses: + * + * - Eclipse Public License Version 1.0 (EPL1), and + * - GNU General Public License Version 3 (GPL3). + * + * EPL1 terms and conditions can be found in the file "epl-v10.txt", while + * terms and conditions for the GPL3 can be found in the file "gpl3.txt". + * ---------------------------------------------------------------------------- + * Author: Joao Leal + */ + +/** + * LLVM requires the use of it own flags which can make it difficult to compile + * libraries not using NDEBUG often required by LLVM. + * The define LLVM_CPPFLAG_NDEBUG can be used to apply NDEBUG only to LLVM + * headers. + */ +#ifdef LLVM_WITH_NDEBUG + +// save the original NDEBUG definition +#ifdef NDEBUG +#define _OUTER_NDEBUG_DEFINED +#endif + +#if LLVM_WITH_NDEBUG == 1 +#define NDEBUG +#else +#undef NDEBUG +#endif + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include + +#ifdef LLVM_WITH_NDEBUG + +// recover the original NDEBUG +#ifdef _OUTER_NDEBUG_DEFINED +#define NDEBUG +#else +#undef NDEBUG +#endif + +// no need for this anymore +#undef _OUTER_NDEBUG_DEFINED + +#endif + +#include +#include +#include +#include // yes, this is from version 5.0 +#include + +#endif diff --git a/include/cppad/cg/model/llvm/v9_0/llvm_model_library_processor.hpp b/include/cppad/cg/model/llvm/v9_0/llvm_model_library_processor.hpp new file mode 100644 index 00000000..d5176654 --- /dev/null +++ b/include/cppad/cg/model/llvm/v9_0/llvm_model_library_processor.hpp @@ -0,0 +1,54 @@ +#ifndef CPPAD_CG_LLVM_MODEL_LIBRARY_PROCESSOR_INCLUDED +#define CPPAD_CG_LLVM_MODEL_LIBRARY_PROCESSOR_INCLUDED +/* -------------------------------------------------------------------------- + * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation: + * Copyright (C) 2020 Joao Leal + * + * CppADCodeGen is distributed under multiple licenses: + * + * - Eclipse Public License Version 1.0 (EPL1), and + * - GNU General Public License Version 3 (GPL3). + * + * EPL1 terms and conditions can be found in the file "epl-v10.txt", while + * terms and conditions for the GPL3 can be found in the file "gpl3.txt". + * ---------------------------------------------------------------------------- + * Author: Joao Leal + */ + +#include + +namespace CppAD { +namespace cg { + +/** + * Useful class for generating a JIT evaluated model library (LLVM 9.0). + * + * @author Joao Leal + */ +template +class LlvmModelLibraryProcessor : public LlvmBaseModelLibraryProcessorImpl { +public: + + /** + * Creates a LLVM model library processor. + * + * @param librarySourceGen + */ + LlvmModelLibraryProcessor(ModelLibraryCSourceGen& librarySourceGen) : + LlvmBaseModelLibraryProcessorImpl(librarySourceGen, "9") { + } + + virtual ~LlvmModelLibraryProcessor() = default; + + using LlvmBaseModelLibraryProcessorImpl::create; + + static inline std::unique_ptr> create(ModelLibraryCSourceGen& modelLibraryHelper) { + LlvmModelLibraryProcessor p(modelLibraryHelper); + return p.create(); + } +}; + +} // END cg namespace +} // END CppAD namespace + +#endif diff --git a/include/cppad/cg/util.hpp b/include/cppad/cg/util.hpp index ec630632..ac6a00c1 100644 --- a/include/cppad/cg/util.hpp +++ b/include/cppad/cg/util.hpp @@ -139,7 +139,7 @@ inline void addMatrixSparsity(const VectorSet& a, template inline void addMatrixSparsity(const VectorSet& a, VectorSet2& result) { - CPPADCG_ASSERT_UNKNOWN(result.size() == a.size()); + CPPADCG_ASSERT_UNKNOWN(result.size() == a.size()) addMatrixSparsity(a, a.size(), result); } @@ -197,7 +197,7 @@ inline void multMatrixMatrixSparsity(const VectorSet& a, for (size_t jj = 0; jj < q; jj++) { //loop columns of b const std::set& colB = bt[jj]; - if (colB.size() > 0) { + if (!colB.empty()) { for (size_t i = 0; i < m; i++) { const std::set& rowA = a[i]; for (size_t rowb : colB) { @@ -264,7 +264,7 @@ inline void multMatrixTransMatrixSparsity(const VectorSet& a, for (size_t jj = 0; jj < q; jj++) { //loop columns of b const std::set& colB = bt[jj]; - if (colB.size() > 0) { + if (!colB.empty()) { for (size_t i = 0; i < n; i++) { const std::set& rowAt = at[i]; if (!rowAt.empty()) { @@ -805,6 +805,16 @@ inline std::string implode(const std::vector& text, } } +std::string readStringFromFile(const std::string& path) { + std::ifstream iStream; + iStream.open(path); + + std::stringstream strStream; + strStream << iStream.rdbuf(); + + return strStream.str(); +} + } // END cg namespace } // END CppAD namespace diff --git a/test/CppADCGTest.hpp b/test/CppADCGTest.hpp index 671e421e..ce2b68b3 100644 --- a/test/CppADCGTest.hpp +++ b/test/CppADCGTest.hpp @@ -53,6 +53,19 @@ class CppADCGTest : public ::testing::Test { protected: + static inline ::testing::AssertionResult compareValues(const std::vector& depCGen, + const std::vector >& dep, + double epsilonR = 1e-14, double epsilonA = 1e-14) { + + std::vector depd(dep.size()); + + for (size_t i = 0; i < depd.size(); i++) { + depd[i] = dep[i].getValue(); + } + + return compareValues(depCGen, depd, epsilonR, epsilonA); + } + template static inline ::testing::AssertionResult compareValues(const std::vector >& depCGen, const std::vector >& dep, diff --git a/test/cppad/cg/CppADCGDynamicAtomicTest.hpp b/test/cppad/cg/CppADCGDynamicAtomicTest.hpp index 9d259e55..067d3115 100644 --- a/test/cppad/cg/CppADCGDynamicAtomicTest.hpp +++ b/test/cppad/cg/CppADCGDynamicAtomicTest.hpp @@ -16,13 +16,13 @@ * Author: Joao Leal */ -#include "CppADCGTest.hpp" +#include "CppADCGModelTest.hpp" #include "gccCompilerFlags.hpp" namespace CppAD { namespace cg { -class CppADCGDynamicAtomicTest : public CppADCGTest { +class CppADCGDynamicAtomicTest : public CppADCGModelTest { public: using Super = CppADCGTest; using Base = Super::Base; @@ -43,7 +43,7 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { explicit CppADCGDynamicAtomicTest(std::string modelName, bool verbose = false, bool printValues = false) : - CppADCGTest(verbose, printValues), + CppADCGModelTest(verbose, printValues), _modelName(std::move(modelName)) { //this->verbose_ = true; } @@ -51,14 +51,14 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { virtual std::vector model(const std::vector& x) = 0; virtual std::vector modelOuter(const std::vector& y) { - std::vector Z(y.size() - 1); + std::vector z(y.size() - 1); for (size_t i = 0; i < y.size() - 1; i++) { - Z[i] = 2 * y[i]; + z[i] = 2 * y[i]; } - Z[Z.size() - 1] += y[y.size() - 1]; + z[z.size() - 1] += y[y.size() - 1]; - return Z; + return z; } void TearDown() override { @@ -120,196 +120,56 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { atomicfun(ax, ay); - // create f2: x -> y and stop tape recording - ADFun f2(ax, ay); + // create fWrapAtom: x -> y and stop tape recording + ADFun fWrapAtom(ax, ay); /** * Test zero order */ - vector xOrig(n); - for (size_t j = 0; j < n; j++) - xOrig[j] = x[j]; - - vector yOrig = _funInner->Forward(0, xOrig); - vector yInner = modelLib->ForwardZero(x); - vector yOuter = f2.Forward(0, x); + std::vector xOrig(x.data(), x.data() + x.size()); - ASSERT_TRUE(compareValues(yInner, yOrig, epsilonR, epsilonA)); - ASSERT_TRUE(compareValues(yOuter, yOrig, epsilonR, epsilonA)); + testForwardZeroResults(*modelLib, *_funInner, &fWrapAtom, xOrig, epsilonR, epsilonA); /** * Test first order forward mode */ - size_t k = 1; - size_t k1 = k + 1; - - vector x_p(n); - for (size_t j = 0; j < n; j++) - x_p[j] = 0; - - vector x_pOrig(n); - vector tx(k1 * n); - for (size_t j = 0; j < n; j++) - tx[j * k1] = x[j]; // zero order - for (size_t j = 0; j < n; j++) - tx[j * k1 + 1] = 0; // first order - - for (size_t j = 0; j < n; j++) { - x_p[j] = 1; - x_pOrig[j] = 1; - tx[j * k1 + 1] = 1; - - vector y_pOrig = _funInner->Forward(1, x_pOrig); - vector y_pInner = modelLib->ForwardOne(tx); - vector y_pOuter = f2.Forward(1, x_p); - - x_p[j] = 0; - x_pOrig[j] = 0; - tx[j * k1 + 1] = 0; - - ASSERT_TRUE(compareValues(y_pInner, y_pOrig, epsilonR, epsilonA)); - ASSERT_TRUE(compareValues(y_pOuter, y_pOrig, epsilonR, epsilonA)); - } + testForwardOneResults(*modelLib, *_funInner, &fWrapAtom, xOrig, epsilonR, epsilonA); /** * Test first order reverse mode */ - k = 0; - k1 = k + 1; - - vector w(m); - for (size_t i = 0; i < m; i++) - w[i] = 0; - vector wOrig(m); - tx.resize(k1 * n); - for (size_t j = 0; j < n; j++) - tx[j * k1] = x[j]; // zero order - vector ty(k1 * m); - for (size_t i = 0; i < m; i++) - ty[i * k1] = yInner[i]; // zero order - - for (size_t i = 0; i < m; i++) { - w[i] = 1; - wOrig[i] = 1; - - vector dwOrig = _funInner->Reverse(1, wOrig); - vector dwInner = modelLib->ReverseOne(tx, ty, w); - vector dwOuter = f2.Reverse(1, w); - - w[i] = 0; - wOrig[i] = 0; - - ASSERT_TRUE(compareValues(dwInner, dwOrig, epsilonR, epsilonA)); - ASSERT_TRUE(compareValues(dwOuter, dwOrig, epsilonR, epsilonA)); - } + testReverseOneResults(*modelLib, *_funInner, &fWrapAtom, xOrig, epsilonR, epsilonA); /** * Test second order reverse mode */ - k = 1; - k1 = k + 1; - tx.resize(k1 * n); - ty.resize(k1 * m); - vector py(k1 * m); - vector pyOrig(k1 * m); - //wOrig.resize(k1 * m); - for (size_t j = 0; j < n; j++) { - tx[j * k1] = x[j]; // zero order - tx[j * k1 + 1] = 0; // first order - } - for (size_t i = 0; i < m; i++) { - ty[i * k1] = yInner[i]; // zero order - py[i * k1] = 0.0; - py[i * k1 + 1] = 1.0; // first order - pyOrig[i * k1] = 0.0; - pyOrig[i * k1 + 1] = 1.0; // first order - } - - for (size_t j = 0; j < n; j++) { - x_p[j] = 1; - x_pOrig[j] = 1; - tx[j * k1 + 1] = 1; - - _funInner->Forward(1, x_pOrig); - vector dwOrig = _funInner->Reverse(2, pyOrig); - vector dwInner = modelLib->ReverseTwo(tx, ty, py); - f2.Forward(1, x_p); - vector dwOuter = f2.Reverse(2, py); - - x_p[j] = 0; - x_pOrig[j] = 0; - tx[j * k1 + 1] = 0; - - // only compare second order information - // (location of the elements is different then if py.size() == m) - ASSERT_EQ(dwOrig.size(), n * k1); - ASSERT_EQ(dwOrig.size(), dwInner.size()); - ASSERT_EQ(dwOrig.size(), dwOuter.size()); - for (size_t j2 = 0; j2 < n; j2++) { - ASSERT_TRUE(nearEqual(dwInner[j2 * k1], dwOrig[j2 * k1].getValue())); - ASSERT_TRUE(nearEqual(dwOuter[j2 * k1], dwOrig[j2 * k1].getValue())); - } - } + testReverseTwoResults(*modelLib, *_funInner, &fWrapAtom, xOrig, epsilonR, epsilonA); /** - * Jacobian + * Dense Jacobian */ - vector jacOrig = _funInner->Jacobian(xOrig); - vector jacOuter = f2.Jacobian(x); - ASSERT_TRUE(compareValues(jacOuter, jacOrig, epsilonR, epsilonA)); + testDenseJacResults(*modelLib, *_funInner, xOrig, epsilonR, epsilonA); /** * Jacobian sparsity */ - const std::vector jacSparsityOrig = jacobianForwardSparsity, CGD>(*_funInner); - const std::vector jacSparsityOuter = jacobianForwardSparsity, double>(f2); - - compareBoolValues(jacSparsityOrig, jacSparsityOuter); - - const std::vector jacSparsityOrigRev = jacobianReverseSparsity, CGD>(*_funInner); - const std::vector jacSparsityOuterRev = jacobianReverseSparsity, double>(f2); - - compareBoolValues(jacSparsityOrigRev, jacSparsityOrig); - compareBoolValues(jacSparsityOrigRev, jacSparsityOuterRev); + testJacobianSparsity(*_funInner, fWrapAtom); /** - * Sparse jacobian + * Sparse Jacobian */ - jacOrig = _funInner->SparseJacobian(xOrig); - jacOuter = f2.SparseJacobian(x); - ASSERT_TRUE(compareValues(jacOuter, jacOrig, epsilonR, epsilonA)); - - // sparse reverse - std::vector row, col; - generateSparsityIndexes(jacSparsityOuter, m, n, row, col); - - sparse_jacobian_work workOrig; - jacOrig.resize(row.size()); - _funInner->SparseJacobianReverse(xOrig, jacSparsityOrig, row, col, jacOrig, workOrig); - - sparse_jacobian_work work2; - jacOuter.resize(row.size()); - f2.SparseJacobianReverse(x, jacSparsityOuter, row, col, jacOuter, work2); - - ASSERT_TRUE(compareValues(jacOuter, jacOrig, epsilonR, epsilonA)); + size_t n_tests = _dynamicLib->getThreadNumber() > 1 ? 2 : 1; + testSparseJacobianResults(n_tests, *modelLib, *_funInner, &fWrapAtom, xOrig, false, epsilonR, epsilonA); /** - * Hessian + * Dense Hessian */ - for (size_t i = 0; i < m; i++) { - w[i] = 1; - wOrig[i] = 1; - } - vector hessOrig = _funInner->Hessian(xOrig, wOrig); - vector hessOuter = f2.Hessian(x, w); - ASSERT_TRUE(compareValues(hessOuter, hessOrig, epsilonR, epsilonA)); + testDenseHessianResults(*modelLib, *_funInner, xOrig, epsilonR, epsilonA); /** * Sparse Hessian */ - hessOrig = _funInner->SparseHessian(xOrig, wOrig); - hessOuter = f2.SparseHessian(x, w); - ASSERT_TRUE(compareValues(hessOuter, hessOrig, epsilonR, epsilonA)); + testSparseHessianResults(n_tests, *modelLib, *_funInner, &fWrapAtom, xOrig, false, epsilonR, epsilonA); } /** @@ -345,8 +205,8 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { Base epsilonR = 1e-14, Base epsilonA = 1e-14) { CppAD::vector xNorm(x.size()); - for (size_t i = 0; i < xNorm.size(); i++) - xNorm[i] = 1.0; + for (double & i : xNorm) + i = 1.0; CppAD::vector eqNorm; testADFunAtomicLib(x, xNorm, eqNorm, epsilonR, epsilonA); @@ -359,8 +219,8 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { Base epsilonR = 1e-14, Base epsilonA = 1e-14) { CppAD::vector xNorm(x.size()); - for (size_t i = 0; i < xNorm.size(); i++) - xNorm[i] = 1.0; + for (double & i : xNorm) + i = 1.0; CppAD::vector eqNorm; testAtomicLibAtomicLib(x, xNorm, eqNorm, epsilonR, epsilonA); @@ -651,10 +511,10 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { ASSERT_TRUE(compareValues(hessOuter, hessOrig, epsilonR, epsilonA)); } - void testAtomicLibModelInCppAD(ADFun& funOuter, - ADFun& funOuterAtom, // with an atomic function - const CppAD::vector& xx, - Base epsilonR = 1e-14, Base epsilonA = 1e-14) { + static void testAtomicLibModelInCppAD(ADFun& funOuter, + ADFun& funOuterAtom, // with an atomic function + const CppAD::vector& xx, + Base epsilonR = 1e-14, Base epsilonA = 1e-14) { using namespace CppAD; using namespace std; using CppAD::vector; @@ -679,8 +539,6 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { /** * Test first order forward mode */ - size_t k = 1; - size_t k1 = k + 1; vector x_p(n); @@ -713,8 +571,8 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { /** * Test second order reverse mode */ - k = 1; - k1 = k + 1; + size_t k = 1; + size_t k1 = k + 1; vector py(m); // not (k1 * m) for (size_t i = 0; i < m; i++) { //py[i * k1] = 0.0; @@ -843,7 +701,7 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { generateSparsityIndexes(jacOuterSpar, jacOuterRows, jacOuterCols); vector jacOrig(jacOuterCols.size()); _funOuter->SparseJacobianReverse(xOrig, jacSparsityOrig, - jacOuterRows, jacOuterCols, jacOrig, work); + jacOuterRows, jacOuterCols, jacOrig, work); std::vector jacOuter(jacOuterRows.size()); size_t const* rows, *cols; @@ -1180,6 +1038,8 @@ class CppADCGDynamicAtomicTest : public CppADCGTest { cSourceInner->setCreateForwardOne(true); cSourceInner->setCreateReverseOne(true); cSourceInner->setCreateReverseTwo(true); + cSourceInner->setCreateJacobian(true); + cSourceInner->setCreateHessian(true); cSourceInner->setCreateSparseJacobian(true); cSourceInner->setCreateSparseHessian(true); diff --git a/test/cppad/cg/CppADCGDynamicTest.hpp b/test/cppad/cg/CppADCGDynamicTest.hpp index d0d25f5c..21b5f64c 100644 --- a/test/cppad/cg/CppADCGDynamicTest.hpp +++ b/test/cppad/cg/CppADCGDynamicTest.hpp @@ -82,7 +82,10 @@ class CppADCGDynamicTest : public CppADCGModelTest { std::vector xTape(_xTape.size()); for (size_t i = 0; i < xTape.size(); ++i) xTape[i] = _xTape[i]; - CppAD::Independent(xTape); + size_t abort_op_index = 0; + bool record_compare = false; + CppAD::Independent(xTape, abort_op_index, record_compare); + if (!_xNorm.empty()) { for (size_t i = 0; i < xTape.size(); i++) @@ -169,7 +172,7 @@ class CppADCGDynamicTest : public CppADCGModelTest { } void testForwardZero() { - this->testForwardZeroResults(*_model, *_fun, _xRun, epsilonR, epsilonA); + this->testForwardZeroResults(*_model, *_fun, nullptr, _xRun, epsilonR, epsilonA); } // Jacobian @@ -183,12 +186,20 @@ class CppADCGDynamicTest : public CppADCGModelTest { // sparse Jacobian void testJacobian() { - this->testJacobianResults(*_dynamicLib, *_model, *_fun, _xRun, !_jacRow.empty(),epsilonR, epsilonA); + // sparse Jacobian again (make sure the second run is also OK) + size_t n_tests = _dynamicLib->getThreadNumber() > 1 ? 2 : 1; + + this->testSparseJacobianResults(n_tests, *_model, *_fun, nullptr, _xRun, !_jacRow.empty(), epsilonR, + epsilonA); } // sparse Hessian void testHessian() { - this->testHessianResults(*_dynamicLib, *_model, *_fun, _xRun, !_hessRow.empty(), epsilonR, epsilonA); + // sparse Hessian again (make sure the second run is also OK) + size_t n_tests = _dynamicLib->getThreadNumber() > 1 ? 2 : 1; + + this->testSparseHessianResults(n_tests, *_model, *_fun, nullptr, _xRun, !_hessRow.empty(), epsilonR, + epsilonA); } }; diff --git a/test/cppad/cg/CppADCGModelTest.hpp b/test/cppad/cg/CppADCGModelTest.hpp index c1c6920c..4a7ef65f 100644 --- a/test/cppad/cg/CppADCGModelTest.hpp +++ b/test/cppad/cg/CppADCGModelTest.hpp @@ -48,11 +48,12 @@ class CppADCGModelTest : public CppADCGTest { * @param epsilonR relative error * @param epsilonA absolute error */ - void testForwardZeroResults(GenericModel& model, - ADFun& fun, - const std::vector& x, - double epsilonR = 1e-14, - double epsilonA = 1e-14) { + static void testForwardZeroResults(GenericModel& model, + ADFun& fun, + ADFun* fun2, + const std::vector& x, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { ASSERT_EQ(model.Domain(), fun.Domain()); ASSERT_EQ(model.Range(), fun.Range()); @@ -62,6 +63,171 @@ class CppADCGModelTest : public CppADCGTest { std::vector depCGen = model.ForwardZero(x); ASSERT_TRUE(compareValues(depCGen, dep, epsilonR, epsilonA)); + + if (fun2 != nullptr) { + std::vector yOuter = fun2->Forward(0, x); + ASSERT_TRUE(compareValues(yOuter, depCGen, epsilonR, epsilonA)); + } + } + + static void testForwardOneResults(GenericModel& model, + ADFun& fun, + ADFun* funWrapModel, + const std::vector& x, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { + ASSERT_EQ(model.Domain(), fun.Domain()); + ASSERT_EQ(model.Range(), fun.Range()); + + size_t n = model.Domain(); + + /** + * Test first order forward mode + */ + size_t k = 1; + size_t k1 = k + 1; + + std::vector x_p(n, 0.0); + std::vector x_pOrig(n); + std::vector tx(k1 * n); + for (size_t j = 0; j < n; j++) + tx[j * k1] = x[j]; // zero order + for (size_t j = 0; j < n; j++) + tx[j * k1 + 1] = 0; // first order + + for (size_t j = 0; j < n; j++) { + x_p[j] = 1; + x_pOrig[j] = 1; + tx[j * k1 + 1] = 1; + + std::vector y_pOrig = fun.Forward(1, x_pOrig); + std::vector y_pInner = model.ForwardOne(tx); + + ASSERT_TRUE(compareValues(y_pInner, y_pOrig, epsilonR, epsilonA)); + + if (funWrapModel != nullptr) { + std::vector y_pOuter = funWrapModel->Forward(1, x_p); + ASSERT_TRUE(compareValues(y_pOuter, y_pOrig, epsilonR, epsilonA)); + } + + x_p[j] = 0; + x_pOrig[j] = 0; + tx[j * k1 + 1] = 0; + } + } + + static void testReverseOneResults(GenericModel& model, + ADFun& fun, + ADFun* funWrapModel, + const std::vector& x, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { + ASSERT_EQ(model.Domain(), fun.Domain()); + ASSERT_EQ(model.Range(), fun.Range()); + + size_t m = model.Range(); + size_t n = model.Domain(); + + size_t k = 0; + size_t k1 = k + 1; + + std::vector yInner = model.ForwardZero(x); + + std::vector w(m, 0.0); + std::vector wOrig(m); + std::vector tx(k1 * n); + for (size_t j = 0; j < n; j++) + tx[j * k1] = x[j]; // zero order + std::vector ty(k1 * m); + for (size_t i = 0; i < m; i++) + ty[i * k1] = yInner[i]; // zero order + + for (size_t i = 0; i < m; i++) { + w[i] = 1; + wOrig[i] = 1; + + std::vector dwOrig = fun.Reverse(1, wOrig); + std::vector dwInner = model.ReverseOne(tx, ty, w); + + ASSERT_TRUE(compareValues(dwInner, dwOrig, epsilonR, epsilonA)); + + if (funWrapModel != nullptr) { + std::vector dwOuter = funWrapModel->Reverse(1, w); + ASSERT_TRUE(compareValues(dwOuter, dwOrig, epsilonR, epsilonA)); + } + + w[i] = 0; + wOrig[i] = 0; + } + } + + static void testReverseTwoResults(GenericModel& model, + ADFun& fun, + ADFun* funWrapModel, + const std::vector& x, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { + ASSERT_EQ(model.Domain(), fun.Domain()); + ASSERT_EQ(model.Range(), fun.Range()); + + size_t m = model.Range(); + size_t n = model.Domain(); + + std::vector yInner = model.ForwardZero(x); + + size_t k = 1; + size_t k1 = k + 1; + std::vector tx(k1 * n); + std::vector ty(k1 * m); + std::vector py(k1 * m); + std::vector pyOrig(k1 * m); + //wOrig.resize(k1 * m); + for (size_t j = 0; j < n; j++) { + tx[j * k1] = x[j]; // zero order + tx[j * k1 + 1] = 0; // first order + } + for (size_t i = 0; i < m; i++) { + ty[i * k1] = yInner[i]; // zero order + py[i * k1] = 0.0; + py[i * k1 + 1] = 1.0; // first order + pyOrig[i * k1] = 0.0; + pyOrig[i * k1 + 1] = 1.0; // first order + } + + std::vector x_p(n, 0.0); + std::vector x_pOrig(n); + + for (size_t j = 0; j < n; j++) { + x_p[j] = 1; + x_pOrig[j] = 1; + tx[j * k1 + 1] = 1; + + fun.Forward(1, x_pOrig); + std::vector dwOrig = fun.Reverse(2, pyOrig); + std::vector dwInner = model.ReverseTwo(tx, ty, py); + + // only compare second order information + // (location of the elements is different then if py.size() == m) + ASSERT_EQ(dwOrig.size(), n * k1); + ASSERT_EQ(dwOrig.size(), dwInner.size()); + + for (size_t j2 = 0; j2 < n; j2++) { + ASSERT_TRUE(nearEqual(dwInner[j2 * k1], dwOrig[j2 * k1].getValue())); + } + + if(funWrapModel != nullptr) { + funWrapModel->Forward(1, x_p); + std::vector dwOuter = funWrapModel->Reverse(2, py); + ASSERT_EQ(dwOrig.size(), dwOuter.size()); + for (size_t j2 = 0; j2 < n; j2++) { + ASSERT_TRUE(nearEqual(dwOuter[j2 * k1], dwOrig[j2 * k1].getValue())); + } + } + + x_p[j] = 0; + x_pOrig[j] = 0; + tx[j * k1 + 1] = 0; + } } /** @@ -130,6 +296,7 @@ class CppADCGModelTest : public CppADCGTest { private: void testJacobianResults(GenericModel& model, + ADFun* funWrapModel, const std::vector& jac, const std::vector& x, bool customSparsity, @@ -141,7 +308,7 @@ class CppADCGModelTest : public CppADCGTest { model.SparseJacobian(x, jacCGen, row, col); std::vector jacCGenDense(jac.size()); - + for (size_t i = 0; i < jacCGen.size(); i++) { size_t p = row[i] * x.size() + col[i]; jacCGenDense[p] = jacCGen[i]; @@ -163,9 +330,30 @@ class CppADCGModelTest : public CppADCGTest { } ASSERT_TRUE(this->compareValues(jacCGenDense, jacAdFunPartial, epsilonR, epsilonA)); + + if (funWrapModel != nullptr) { + auto jacOuter = funWrapModel->SparseJacobian(x); + + if (verbose_) { + std::cout << "ADFun2 Jacobian" << std::endl; + print(jacOuter); + } + + ASSERT_TRUE(compareValues(jacOuter, jac, epsilonR, epsilonA)); + + // sparse reverse + const auto jacSparsityWrap = jacobianReverseSparsitySet>, Base>(*funWrapModel); + + sparse_jacobian_work workWrapFun; + std::vector jacWrapFunSparse(row.size()); + funWrapModel->SparseJacobianReverse(x, jacSparsityWrap, row, col, jacWrapFunSparse, workWrapFun); + + ASSERT_TRUE(compareValues(jacCGen, jacWrapFunSparse, epsilonR, epsilonA)); + } } void testHessianResults(GenericModel& model, + ADFun* funWrapModel, const std::vector& hess, const std::vector& x, bool customSparsity, @@ -200,26 +388,62 @@ class CppADCGModelTest : public CppADCGTest { } ASSERT_TRUE(this->compareValues(hessCGenDense, hessAdFunPartial, epsilonR, epsilonA)); + + if(funWrapModel != nullptr) { + auto hessFunWrap = funWrapModel->SparseHessian(x, w); + + if (verbose_) { + std::cout << "ADFun2 Hessian" << std::endl; + print(hessFunWrap); + } + + ASSERT_TRUE(compareValues(hessFunWrap, hessAdFunPartial, epsilonR, epsilonA)); + + const auto hessSparsityWrap = hessianSparsitySet>, Base>(*funWrapModel); + + sparse_hessian_work workWrapFun; + std::vector hessWrapFunSparse(row.size()); + funWrapModel->SparseHessian(x, w, hessSparsityWrap, row, col, hessWrapFunSparse, workWrapFun); + + ASSERT_TRUE(compareValues(hessCGen, hessWrapFunSparse, epsilonR, epsilonA)); + } } public: + static void testJacobianSparsity(ADFun& fun1, + ADFun& fun2) { + const std::vector jacSparsityOrig = jacobianForwardSparsity, CGD>(fun1); + const std::vector jacSparsityOuter = jacobianForwardSparsity, double>(fun2); + + compareBoolValues(jacSparsityOrig, jacSparsityOuter); + + const std::vector jacSparsityOrigRev = jacobianReverseSparsity, CGD>(fun1); + const std::vector jacSparsityOuterRev = jacobianReverseSparsity, double>(fun2); + + compareBoolValues(jacSparsityOrigRev, jacSparsityOrig); + compareBoolValues(jacSparsityOrigRev, jacSparsityOuterRev); + } + /** * Compares the results from a sparse Jacobian. * + * @param n_tests number of times to run this test * @param model * @param fun * @param x independent vector values + * @param customSparsity * @param epsilonR relative error * @param epsilonA absolute error */ - void testJacobianResults(ModelLibrary& lib, - GenericModel& model, - ADFun& fun, - const std::vector& x, - bool customSparsity, - double epsilonR = 1e-14, - double epsilonA = 1e-14) { + void testSparseJacobianResults(size_t n_tests, + GenericModel& model, + ADFun& fun, + ADFun* funWrapModel, + const std::vector& x, + bool customSparsity, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { ASSERT_EQ(model.Domain(), fun.Domain()); ASSERT_EQ(model.Range(), fun.Range()); @@ -231,13 +455,9 @@ class CppADCGModelTest : public CppADCGTest { print(jac); } - testJacobianResults(model, jac, x, customSparsity, epsilonR, epsilonA); - - if (lib.getThreadNumber() > 1) { - // sparse Jacobian again (make sure the second run is also OK) - testJacobianResults(model, jac, x, customSparsity, epsilonR, epsilonA); + for (size_t i = 0; i< n_tests; ++i) { + testJacobianResults(model, funWrapModel, jac, x, customSparsity, epsilonR, epsilonA); } - } /** @@ -249,13 +469,14 @@ class CppADCGModelTest : public CppADCGTest { * @param epsilonR relative error * @param epsilonA absolute error */ - void testHessianResults(ModelLibrary& lib, - GenericModel& model, - ADFun& fun, - const std::vector& x, - bool customSparsity, - double epsilonR = 1e-14, - double epsilonA = 1e-14) { + void testSparseHessianResults(size_t n_tests, + GenericModel& model, + ADFun& fun, + ADFun* funWrapModel, + const std::vector& x, + bool customSparsity, + double epsilonR = 1e-14, + double epsilonA = 1e-14) { ASSERT_EQ(model.Domain(), fun.Domain()); ASSERT_EQ(model.Range(), fun.Range()); @@ -270,66 +491,9 @@ class CppADCGModelTest : public CppADCGTest { print(hess); } - testHessianResults(model, hess, x, customSparsity, epsilonR, epsilonA); - - if (lib.getThreadNumber() > 1) { - // sparse Hessian again (make sure the second run is also OK) - testHessianResults(model, hess, x, customSparsity, epsilonR, epsilonA); - } - } - - /** - * Compares the results from Hessian, Jacobian, sparse Hessian and - * sparse Jacobian. - * - * @param model - * @param fun - * @param x independent vector values - * @param epsilonR relative error - * @param epsilonA absolute error - */ - void testModelResults(ModelLibrary& lib, - GenericModel& model, - ADFun& fun, - const std::vector& x, - bool customSparsity, - double epsilonR = 1e-14, - double epsilonA = 1e-14, - bool denseJacobian = true, - bool denseHessian = true) { - // dimensions - ASSERT_EQ(model.Domain(), fun.Domain()); - ASSERT_EQ(model.Range(), fun.Range()); - - testForwardZeroResults(model, fun, x, epsilonR, epsilonA); - - // Jacobian - if (denseJacobian) { - testDenseJacResults(model, fun, x, epsilonR, epsilonA); + for (size_t i = 0; i < n_tests; ++i) { + testHessianResults(model, funWrapModel, hess, x, customSparsity, epsilonR, epsilonA); } - - if (denseHessian) { - testDenseHessianResults(model, fun, x, epsilonR, epsilonA); - } - - // sparse Jacobian - testJacobianResults(lib, model, fun, x, customSparsity, epsilonR, epsilonA); - - // sparse Hessian - testHessianResults(lib, model, fun, x, customSparsity, epsilonR, epsilonA); - } - - inline ::testing::AssertionResult compareValues(const std::vector& depCGen, - const std::vector >& dep, - double epsilonR = 1e-14, double epsilonA = 1e-14) { - - std::vector depd(dep.size()); - - for (size_t i = 0; i < depd.size(); i++) { - depd[i] = dep[i].getValue(); - } - - return CppADCGTest::compareValues(depCGen, depd, epsilonR, epsilonA); } }; diff --git a/test/cppad/cg/model/dynamiclib/cg_atomic_generic_model.cpp b/test/cppad/cg/model/dynamiclib/cg_atomic_generic_model.cpp index 61f031f6..a8ef6fd4 100644 --- a/test/cppad/cg/model/dynamiclib/cg_atomic_generic_model.cpp +++ b/test/cppad/cg/model/dynamiclib/cg_atomic_generic_model.cpp @@ -50,23 +50,23 @@ class SingleVarAtomicGenericModelTest : public CGAtomicGenericModelTest { /** * @test */ -TEST_F(SingleVarAtomicGenericModelTest, TestForwardZero) { +TEST_F(SingleVarAtomicGenericModelTest, TestForwardZero) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelForwardZero(); } -TEST_F(SingleVarAtomicGenericModelTest, TestReverseOne) { +TEST_F(SingleVarAtomicGenericModelTest, TestReverseOne) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseOne(); } -TEST_F(SingleVarAtomicGenericModelTest, TestReverseTwo) { +TEST_F(SingleVarAtomicGenericModelTest, TestReverseTwo) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseTwo(); } -TEST_F(SingleVarAtomicGenericModelTest, TestJacobian) { +TEST_F(SingleVarAtomicGenericModelTest, TestJacobian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelJacobian(); } -TEST_F(SingleVarAtomicGenericModelTest, TestHessian) { +TEST_F(SingleVarAtomicGenericModelTest, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } @@ -96,23 +96,23 @@ class SingleVarAtomicGenericModelTest2 : public CGAtomicGenericModelTest { } // END cg namespace } // END CppAD namespace -TEST_F(SingleVarAtomicGenericModelTest2, TestForwardZero) { +TEST_F(SingleVarAtomicGenericModelTest2, TestForwardZero) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelForwardZero(); } -TEST_F(SingleVarAtomicGenericModelTest2, TestReverseOne) { +TEST_F(SingleVarAtomicGenericModelTest2, TestReverseOne) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseOne(); } -TEST_F(SingleVarAtomicGenericModelTest2, TestReverseTwo) { +TEST_F(SingleVarAtomicGenericModelTest2, TestReverseTwo) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseTwo(); } -TEST_F(SingleVarAtomicGenericModelTest2, TestJacobian) { +TEST_F(SingleVarAtomicGenericModelTest2, TestJacobian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelJacobian(); } -TEST_F(SingleVarAtomicGenericModelTest2, TestHessian) { +TEST_F(SingleVarAtomicGenericModelTest2, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } @@ -142,23 +142,23 @@ class MultiVarAtomicGenericModelTest : public CGAtomicGenericModelTest { } // END CppAD namespace -TEST_F(MultiVarAtomicGenericModelTest, TestForwardZero) { +TEST_F(MultiVarAtomicGenericModelTest, TestForwardZero) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelForwardZero(); } -TEST_F(MultiVarAtomicGenericModelTest, TestReverseOne) { +TEST_F(MultiVarAtomicGenericModelTest, TestReverseOne) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseOne(); } -TEST_F(MultiVarAtomicGenericModelTest, TestReverseTwo) { +TEST_F(MultiVarAtomicGenericModelTest, TestReverseTwo) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseTwo(); } -TEST_F(MultiVarAtomicGenericModelTest, TestJacobian) { +TEST_F(MultiVarAtomicGenericModelTest, TestJacobian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelJacobian(); } -TEST_F(MultiVarAtomicGenericModelTest, TestHessian) { +TEST_F(MultiVarAtomicGenericModelTest, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } @@ -176,7 +176,7 @@ class MultiVarAtomicGenericModelLowerTest : public MultiVarAtomicGenericModelTes } // END cg namespace } // END CppAD namespace -TEST_F(MultiVarAtomicGenericModelLowerTest, TestHessian) { +TEST_F(MultiVarAtomicGenericModelLowerTest, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } @@ -192,7 +192,7 @@ class MultiVarAtomicGenericModelUpperTest : public MultiVarAtomicGenericModelTes } // END cg namespace } // END CppAD namespace -TEST_F(MultiVarAtomicGenericModelUpperTest, TestHessian) { +TEST_F(MultiVarAtomicGenericModelUpperTest, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } @@ -233,22 +233,22 @@ class MultiVarAtomicGenericModelTest2 : public CGAtomicGenericModelTest { } // END CppAD namespace -TEST_F(MultiVarAtomicGenericModelTest2, TestForwardZero) { +TEST_F(MultiVarAtomicGenericModelTest2, TestForwardZero) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelForwardZero(); } -TEST_F(MultiVarAtomicGenericModelTest2, TestReverseOne) { +TEST_F(MultiVarAtomicGenericModelTest2, TestReverseOne) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseOne(); } -TEST_F(MultiVarAtomicGenericModelTest2, TestReverseTwo) { +TEST_F(MultiVarAtomicGenericModelTest2, TestReverseTwo) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelReverseTwo(); } -TEST_F(MultiVarAtomicGenericModelTest2, TestJacobian) { +TEST_F(MultiVarAtomicGenericModelTest2, TestJacobian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelJacobian(); } -TEST_F(MultiVarAtomicGenericModelTest2, TestHessian) { +TEST_F(MultiVarAtomicGenericModelTest2, TestHessian) { // NOLINT(cert-err58-cpp) this->testCGAtomicGenericModelHessian(); } diff --git a/test/cppad/cg/model/lang/latex/latex.cpp b/test/cppad/cg/model/lang/latex/latex.cpp index 86c422ef..ead13a7e 100644 --- a/test/cppad/cg/model/lang/latex/latex.cpp +++ b/test/cppad/cg/model/lang/latex/latex.cpp @@ -1,6 +1,7 @@ /* -------------------------------------------------------------------------- * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation: * Copyright (C) 2014 Ciengis + * Copyright (C) 2020 Joao Leal * * CppADCodeGen is distributed under multiple licenses: * @@ -14,7 +15,6 @@ */ #include -#include #include #include @@ -23,7 +23,7 @@ using namespace CppAD; using namespace CppAD::cg; -TEST(CppADCGLatexTest, latex) { +TEST(CppADCGLatexTest, latex) { // NOLINT(cert-err58-cpp) // use a special object for source code generation using CGD = CG; using ADCG = AD; @@ -76,7 +76,7 @@ TEST(CppADCGLatexTest, latex) { } -TEST(CppADCGLatexTest, latexJac) { +TEST(CppADCGLatexTest, latexJac) { // NOLINT(cert-err58-cpp) // use a special object for source code generation using CGD = CG; using ADCG = AD; diff --git a/test/cppad/cg/model/lang/mathml/CMakeLists.txt b/test/cppad/cg/model/lang/mathml/CMakeLists.txt index 49157b86..9eb32549 100644 --- a/test/cppad/cg/model/lang/mathml/CMakeLists.txt +++ b/test/cppad/cg/model/lang/mathml/CMakeLists.txt @@ -23,6 +23,15 @@ add_cppadcg_test(mathml.cpp) link_file("${CMAKE_CURRENT_SOURCE_DIR}/variableSelection.js" "${CMAKE_CURRENT_BINARY_DIR}/variableSelection.js") +link_file("${CMAKE_CURRENT_SOURCE_DIR}/mathml.css" + "${CMAKE_CURRENT_BINARY_DIR}/mathml.css") +link_file("${CMAKE_CURRENT_SOURCE_DIR}/head_extra.html" + "${CMAKE_CURRENT_BINARY_DIR}/head_extra.html") + + ADD_CUSTOM_TARGET(link_or_copy_variableSelectionJS - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/variableSelection.js") + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/variableSelection.js" + "${CMAKE_CURRENT_BINARY_DIR}/mathml.css" + "${CMAKE_CURRENT_BINARY_DIR}/head_extra.html") + ADD_DEPENDENCIES("mathml" link_or_copy_variableSelectionJS) \ No newline at end of file diff --git a/test/cppad/cg/model/lang/mathml/head_extra.html b/test/cppad/cg/model/lang/mathml/head_extra.html new file mode 100644 index 00000000..9943ce0c --- /dev/null +++ b/test/cppad/cg/model/lang/mathml/head_extra.html @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/test/cppad/cg/model/lang/mathml/mathml.cpp b/test/cppad/cg/model/lang/mathml/mathml.cpp index 56c38ed2..e0dfb07b 100644 --- a/test/cppad/cg/model/lang/mathml/mathml.cpp +++ b/test/cppad/cg/model/lang/mathml/mathml.cpp @@ -1,6 +1,7 @@ /* -------------------------------------------------------------------------- * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation: * Copyright (C) 2015 Ciengis + * Copyright (C) 2020 Joao Leal * * CppADCodeGen is distributed under multiple licenses: * @@ -14,7 +15,6 @@ */ #include -#include #include #include @@ -40,7 +40,7 @@ TEST(CppADCGLatexTest, latex) { // the model ADCG a = x[0] / 1. + x[1] * x[1]; ADCG b = a / 2e-6; - y[0] = b + 1 / (sign(b)*5 * a); + y[0] = b + 1 / (sign(b) * 5 * a); y[1] = x[1]; y[2] = CondExpLt(ADCG(1.0), x[0], x[1], b); y[3] = CondExpLe(x[0], ADCG(2.0), x[1], b); @@ -68,39 +68,18 @@ TEST(CppADCGLatexTest, latex) { langMathML.setSaveVariableRelations(true); // add some additional code to select variables - langMathML.setStyle(langMathML.getStyle() + "\n.selectedProp{background-color: #ccc;}" - "\n.faded{\n" - " opacity: 0.2;\n" - " filter: alpha(opacity=20); /* For IE8 and earlier */\n" - "}\n" - "\n.faded2{\n" - " opacity: 0.5;\n" - " filter: alpha(opacity=50); /* For IE8 and earlier */\n" - "}"); + langMathML.setStyle(langMathML.getStyle() + readStringFromFile("mathml.css")); // use block display - langMathML.setEquationMarkup("", ""); + langMathML.setEquationMarkup(R"()", ""); // use inline display //langMathML.setEquationMarkup("", "
"); // use MathJax (and align to the left) - langMathML.setHeadExtraMarkup("\n" - ""); - - std::ifstream jsFile; - jsFile.open("variableSelection.js"); - - std::stringstream strStream; - strStream << jsFile.rdbuf(); - - langMathML.setJavascript(strStream.str()); + langMathML.setHeadExtraMarkup(readStringFromFile("head_extra.html")); + + langMathML.setJavascript(readStringFromFile("variableSelection.js")); // create the HMTL file std::ofstream htmlFile; diff --git a/test/cppad/cg/model/lang/mathml/mathml.css b/test/cppad/cg/model/lang/mathml/mathml.css new file mode 100644 index 00000000..3ea386d8 --- /dev/null +++ b/test/cppad/cg/model/lang/mathml/mathml.css @@ -0,0 +1,13 @@ +.selectedProp { + background-color: #ccc; +} + +.faded { + opacity: 0.2; + filter: alpha(opacity=20); /* For IE8 and earlier */ +} + +.faded2 { + opacity: 0.5; + filter: alpha(opacity=50); /* For IE8 and earlier */ +} \ No newline at end of file diff --git a/test/cppad/cg/model/lang/mathml/variableSelection.js b/test/cppad/cg/model/lang/mathml/variableSelection.js index 30951916..6ce9c3fc 100644 --- a/test/cppad/cg/model/lang/mathml/variableSelection.js +++ b/test/cppad/cg/model/lang/mathml/variableSelection.js @@ -2,8 +2,8 @@ function contains(arr, o) { if (usages === null || usages === undefined) { return false; } - var l = arr.length; - for (var i = 0; i < l; i++) { + const l = arr.length; + for (let i = 0; i < l; i++) { if (arr[i] === o) { return true; } @@ -14,7 +14,7 @@ function contains(arr, o) { function findEquation(el) { if (el.classList.contains('indep')) return null; - while (el != document) { + while (el !== document) { if (el.classList.contains('equation')) { return el; } @@ -24,8 +24,8 @@ function findEquation(el) { } function isBranch(eq) { - var el = eq.parentNode; - while (el != document && el.id != 'algorithm') { + let el = eq.parentNode; + while (el !== document && el.id !== 'algorithm') { if (el.classList.contains('condBody')) { return true; } @@ -35,9 +35,9 @@ function isBranch(eq) { } function showEquations(eqId, level) { - var el = document.getElementById('v' + eqId); + const el = document.getElementById('v' + eqId); if (el !== null) { - var eq = findEquation(el); + const eq = findEquation(el); if (eq !== null && eq !== undefined) { eq.classList.remove('faded'); if (!eq.classList.contains('depEq')) { @@ -50,30 +50,30 @@ function showEquations(eqId, level) { } } - var deps = var2dep[eqId]; + const deps = var2dep[eqId]; if (deps === undefined || deps === null) { return; } - for (var i = 0; i < deps.length; i++) { - var id = deps[i]; + for (let i = 0; i < deps.length; i++) { + const id = deps[i]; showEquations(id, level + 1); } } function hideEquationForIds(ids, visibleId) { - for (var co in ids) { + for (const co in ids) { if (visibleId === ids[co]) continue; - var el = document.getElementById(ids[co]); + const el = document.getElementById(ids[co]); if (el !== null && el !== undefined) { - var eq = findEquation(el); + const eq = findEquation(el); if (eq !== null && eq !== undefined) eq.classList.add('faded'); } } } function clearAllClass(className) { - var list = document.getElementsByClassName(className); + const list = document.getElementsByClassName(className); if (list !== undefined) { while (list.length > 0) { list[0].classList.remove(className); @@ -82,19 +82,19 @@ function clearAllClass(className) { } function clickHandler(e) { - var t = e.target; + let t = e.target; clearAllClass('selectedProp'); clearAllClass('faded'); clearAllClass('faded2'); clearAllClass('depEq'); - while (t != document) { - if (t.id !== null && t.id !== '' && t.id.charAt(0) == 'v') { - var baseId = t.id.split('_')[0]; - var idval = baseId.substring(1); - var el = document.getElementById(baseId); - var n = 0; + while (t !== document) { + if (t.id !== null && t.id !== '' && t.id.charAt(0) === 'v') { + const baseId = t.id.split('_')[0]; + const idval = baseId.substring(1); + let el = document.getElementById(baseId); + let n = 0; while (el !== null) { el.classList.add('selectedProp'); n++; @@ -103,15 +103,15 @@ function clickHandler(e) { // fade other equations which do not use this variable usages = dep2var[idval]; - for (var i in var2dep) { + for (const i in var2dep) { if (i === idval) continue; - var vi = parseInt(i); + const vi = parseInt(i); if (!contains(usages, vi)) { - var el2 = document.getElementById('v' + i); + let el2 = document.getElementById('v' + i); n = 0; while (el2 !== null) { - var eq = findEquation(el2); + const eq = findEquation(el2); if (eq === null) break; eq.classList.add('faded'); diff --git a/test/cppad/cg/model/llvm/llvm_external_compiler.cpp b/test/cppad/cg/model/llvm/llvm_external_compiler.cpp index 65f08135..bd9479a0 100644 --- a/test/cppad/cg/model/llvm/llvm_external_compiler.cpp +++ b/test/cppad/cg/model/llvm/llvm_external_compiler.cpp @@ -28,7 +28,7 @@ class LlvmModelExternalCompilerTest : public LlvmModelTest { TEST_F(LlvmModelExternalCompilerTest, ForwardZero) { - testForwardZeroResults(*model, *fun, x); + testForwardZeroResults(*model, *fun, nullptr, x); } TEST_F(LlvmModelExternalCompilerTest, DenseJacobian) { @@ -40,9 +40,15 @@ TEST_F(LlvmModelExternalCompilerTest, DenseHessian) { } TEST_F(LlvmModelExternalCompilerTest, Jacobian) { - testJacobianResults(*llvmModelLib, *model, *fun, x, false); + // sparse Jacobian again (make sure the second run is also OK) + size_t n_tests = llvmModelLib->getThreadNumber() > 1 ? 2 : 1; + + testSparseJacobianResults(n_tests, *model, *fun, nullptr, x, false); } TEST_F(LlvmModelExternalCompilerTest, Hessian) { - testHessianResults(*llvmModelLib, *model, *fun, x, false); + // sparse Hessian again (make sure the second run is also OK) + size_t n_tests = llvmModelLib->getThreadNumber() > 1 ? 2 : 1; + + testSparseHessianResults(n_tests, *model, *fun, nullptr, x, false); } diff --git a/test/cppad/cg/model/llvm/llvm_link_clang.cpp b/test/cppad/cg/model/llvm/llvm_link_clang.cpp index 88f67b3a..240381da 100644 --- a/test/cppad/cg/model/llvm/llvm_link_clang.cpp +++ b/test/cppad/cg/model/llvm/llvm_link_clang.cpp @@ -27,7 +27,7 @@ class LlvmModelLinkLlvmTest : public LlvmModelTest { TEST_F(LlvmModelLinkLlvmTest, ForwardZero) { - testForwardZeroResults(*model, *fun, x); + testForwardZeroResults(*model, *fun, nullptr, x); } TEST_F(LlvmModelLinkLlvmTest, DenseJacobian) { @@ -39,9 +39,15 @@ TEST_F(LlvmModelLinkLlvmTest, DenseHessian) { } TEST_F(LlvmModelLinkLlvmTest, Jacobian) { - testJacobianResults(*llvmModelLib, *model, *fun, x, false); + // sparse Jacobian again (make sure the second run is also OK) + size_t n_tests = llvmModelLib->getThreadNumber() > 1 ? 2 : 1; + + testSparseJacobianResults(n_tests, *model, *fun, nullptr, x, false); } TEST_F(LlvmModelLinkLlvmTest, Hessian) { - testHessianResults(*llvmModelLib, *model, *fun, x, false); + // sparse Hessian again (make sure the second run is also OK) + size_t n_tests = llvmModelLib->getThreadNumber() > 1 ? 2 : 1; + + testSparseHessianResults(n_tests, *model, *fun, nullptr, x, false); }