diff --git a/include/clad/Differentiator/BuiltinDerivatives.h b/include/clad/Differentiator/BuiltinDerivatives.h index ac47237bc..a31b239bb 100644 --- a/include/clad/Differentiator/BuiltinDerivatives.h +++ b/include/clad/Differentiator/BuiltinDerivatives.h @@ -54,6 +54,8 @@ template struct ValueAndAdjoint { /// class for which constructor pushforward is defined. template class ConstructorPushforwardTag {}; +template class ConstructorReverseForwTag {}; + namespace custom_derivatives { #ifdef __CUDACC__ template @@ -329,6 +331,16 @@ using std::pow_pullback; using std::pow_pushforward; using std::sin_pushforward; using std::sqrt_pushforward; + +namespace class_functions { +template +void constructor_pullback(ValueAndPushforward* lhs, + ValueAndPushforward rhs, + ValueAndPushforward* d_lhs, + ValueAndPushforward* d_rhs) { + d_rhs->pushforward += d_lhs->pushforward; +} +} // namespace class_functions } // namespace custom_derivatives } // namespace clad diff --git a/include/clad/Differentiator/ReverseModeVisitor.h b/include/clad/Differentiator/ReverseModeVisitor.h index 5eeb60286..40feb6723 100644 --- a/include/clad/Differentiator/ReverseModeVisitor.h +++ b/include/clad/Differentiator/ReverseModeVisitor.h @@ -16,6 +16,7 @@ #include "clang/Sema/Sema.h" #include +#include #include #include #include @@ -689,6 +690,35 @@ namespace clad { } void PopSwitchStmtInfo() { m_SwitchStmtsData.pop_back(); } + + struct ConstructorPullbackCallInfo { + clang::CallExpr* pullbackCE = nullptr; + size_t thisAdjointArgIdx = std::numeric_limits::max(); + void updateThisParmArgs(clang::Expr* thisE, clang::Expr* dThisE) const; + ConstructorPullbackCallInfo() = default; + ConstructorPullbackCallInfo(clang::CallExpr* pPullbackCE, + size_t pThisAdjointArgIdx) + : pullbackCE(pPullbackCE), thisAdjointArgIdx(pThisAdjointArgIdx) {} + + bool empty() const { return !pullbackCE; } + }; + + void setConstructorPullbackCallInfo(clang::CallExpr* pullbackCE, + size_t thisAdjointArgIdx) { + m_ConstructorPullbackCallInfo = {pullbackCE, thisAdjointArgIdx}; + } + + ConstructorPullbackCallInfo getConstructorPullbackCallInfo() { + return m_ConstructorPullbackCallInfo; + } + + void resetConstructorPullbackCallInfo() { + m_ConstructorPullbackCallInfo = ConstructorPullbackCallInfo{}; + } + + private: + ConstructorPullbackCallInfo m_ConstructorPullbackCallInfo; + bool m_TrackConstructorPullbackInfo = false; }; } // end namespace clad diff --git a/include/clad/Differentiator/STLBuiltins.h b/include/clad/Differentiator/STLBuiltins.h index e1e0f5abb..147938080 100644 --- a/include/clad/Differentiator/STLBuiltins.h +++ b/include/clad/Differentiator/STLBuiltins.h @@ -231,6 +231,28 @@ void operator_subscript_pullback(::std::vector* vec, (*d_vec)[idx] += d_y; } +template +::clad::ValueAndAdjoint<::std::vector, ::std::vector> +constructor_reverse_forw(::clad::ConstructorReverseForwTag<::std::vector>, + S count, U val, + typename ::std::vector::allocator_type alloc, + S d_count, U d_val, + typename ::std::vector::allocator_type d_alloc) { + ::std::vector v(count, val); + ::std::vector d_v(count, 0); + return {v, d_v}; +} + +template +void constructor_pullback(::std::vector* v, S count, U val, + typename ::std::vector::allocator_type alloc, + ::std::vector* d_v, S* d_count, U* d_val, + typename ::std::vector::allocator_type* d_alloc) { + for (unsigned i = 0; i < count; ++i) + *d_val += (*d_v)[i]; + d_v->clear(); +} + } // namespace class_functions } // namespace custom_derivatives } // namespace clad diff --git a/include/clad/Differentiator/VisitorBase.h b/include/clad/Differentiator/VisitorBase.h index c209c8cee..e78149d3d 100644 --- a/include/clad/Differentiator/VisitorBase.h +++ b/include/clad/Differentiator/VisitorBase.h @@ -614,6 +614,12 @@ namespace clad { /// Returns type clad::Identify clang::QualType GetCladConstructorPushforwardTagOfType(clang::QualType T); + /// Returns clad::ConstructorReverseForwTag template declaration. + clang::TemplateDecl* GetCladConstructorReverseForwTag(); + + /// Returns type clad::ConstructorReverseForwTag + clang::QualType GetCladConstructorReverseForwTagOfType(clang::QualType T); + public: /// Rebuild a sequence of nested namespaces ending with DC. clang::NamespaceDecl* RebuildEnclosingNamespaces(clang::DeclContext* DC); @@ -661,6 +667,7 @@ namespace clad { private: clang::TemplateDecl* m_CladConstructorPushforwardTag = nullptr; + clang::TemplateDecl* m_CladConstructorReverseForwTag = nullptr; }; } // end namespace clad diff --git a/lib/Differentiator/DerivativeBuilder.cpp b/lib/Differentiator/DerivativeBuilder.cpp index a4defc87e..a569a90ae 100644 --- a/lib/Differentiator/DerivativeBuilder.cpp +++ b/lib/Differentiator/DerivativeBuilder.cpp @@ -247,7 +247,6 @@ static void registerDerivative(FunctionDecl* derivedFD, Sema& semaRef) { const std::string& Name, llvm::SmallVectorImpl& CallArgs, clang::Scope* S, clang::DeclContext* originalFnDC, bool forCustomDerv /*=true*/, bool namespaceShouldExist /*=true*/) { - CXXScopeSpec SS; LookupResult R = LookupCustomDerivativeOrNumericalDiff( Name, originalFnDC, SS, forCustomDerv, namespaceShouldExist); diff --git a/lib/Differentiator/ReverseModeVisitor.cpp b/lib/Differentiator/ReverseModeVisitor.cpp index 637b027cc..da656a33f 100644 --- a/lib/Differentiator/ReverseModeVisitor.cpp +++ b/lib/Differentiator/ReverseModeVisitor.cpp @@ -29,6 +29,10 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" +#include +#include +#include +#include #include "llvm/Support/SaveAndRestore.h" @@ -2763,6 +2767,8 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, if (isPointerType && VD->getInit() && isa(VD->getInit())) isInitializedByNewExpr = true; + ConstructorPullbackCallInfo constructorPullbackInfo; + // VDDerivedInit now serves two purposes -- as the initial derivative value // or the size of the derivative array -- depending on the primal type. if (const auto* AT = dyn_cast(VDType)) { @@ -2819,6 +2825,16 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, VDDerivedInit = initDiff.getForwSweepExpr_dx(); } + if (VDType->isStructureOrClassType()) { + m_TrackConstructorPullbackInfo = true; + initDiff = Visit(VD->getInit()); + m_TrackConstructorPullbackInfo = false; + constructorPullbackInfo = getConstructorPullbackCallInfo(); + resetConstructorPullbackCallInfo(); + if (initDiff.getForwSweepExpr_dx()) + VDDerivedInit = initDiff.getForwSweepExpr_dx(); + } + // FIXME: Remove the special cases introduced by `specialThisDiffCase` // once reverse mode supports pointers. `specialThisDiffCase` is only // required for correctly differentiating the following code: @@ -2880,9 +2896,10 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, } if (VD->getInit()) { - if (isa(VD->getInit())) - initDiff = Visit(VD->getInit()); - else + if (VDType->isStructureOrClassType()) { + if (!initDiff.getExpr()) + initDiff = Visit(VD->getInit()); + } else initDiff = Visit(VD->getInit(), derivedE); } @@ -2994,6 +3011,13 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, VDType != VDClone->getType())) m_DeclReplacements[VD] = VDClone; + if (!constructorPullbackInfo.empty()) { + Expr* thisE = + BuildOp(UnaryOperatorKind::UO_AddrOf, BuildDeclRef(VDClone)); + Expr* dThisE = + BuildOp(UnaryOperatorKind::UO_AddrOf, BuildDeclRef(VDDerived)); + constructorPullbackInfo.updateThisParmArgs(thisE, dThisE); + } return DeclDiff(VDClone, VDDerived); } @@ -4111,20 +4135,140 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, return {nullptr, nullptr}; } - // FIXME: Add support for differentiating calls to constructors. - // We currently assume that constructor arguments are non-differentiable. StmtDiff ReverseModeVisitor::VisitCXXConstructExpr(const CXXConstructExpr* CE) { - llvm::SmallVector clonedArgs; + + llvm::SmallVector primalArgs; + llvm::SmallVector adjointArgs; + llvm::SmallVector reverseForwAdjointArgs; + // It is used to store '_r0' temporary gradient variables that are used for + // differentiating non-reference args. + llvm::SmallVector prePullbackCallStmts; + + // Insertion point is required because we need to insert pullback call + // before the statements inserted by 'Visit(arg, ...)' calls for arguments. + std::size_t insertionPoint = getCurrentBlock(direction::reverse).size(); + + // FIXME: Restore arguments passed as non-const reference. for (const auto* arg : CE->arguments()) { - auto argDiff = Visit(arg, dfdx()); - clonedArgs.push_back(argDiff.getExpr()); + QualType ArgTy = arg->getType(); + StmtDiff argDiff{}; + Expr* adjointArg = nullptr; + if (utils::IsReferenceOrPointerArg(arg->IgnoreParenImpCasts())) { + argDiff = Visit(arg); + adjointArg = argDiff.getExpr_dx(); + } else { + // non-reference arguments are differentiated as follows: + // + // primal code: + // ``` + // SomeClass c(u, ...); + // ``` + // + // Derivative code: + // ``` + // // forward pass + // ... + // // reverse pass + // double _r0 = 0; + // SomeClass_pullback(c, u, ..., &_d_c, &_r0, ...); + // _d_u += _r0; + QualType dArgTy = getNonConstType(ArgTy, m_Context, m_Sema); + VarDecl* dArgDecl = BuildVarDecl(dArgTy, "_r", getZeroInit(dArgTy)); + prePullbackCallStmts.push_back(BuildDeclStmt(dArgDecl)); + adjointArg = BuildDeclRef(dArgDecl); + argDiff = Visit(arg, BuildDeclRef(dArgDecl)); + } + + if (utils::isArrayOrPointerType(ArgTy)) { + reverseForwAdjointArgs.push_back(adjointArg); + adjointArgs.push_back(adjointArg); + } else { + if (utils::IsReferenceOrPointerArg(arg->IgnoreParenImpCasts())) + reverseForwAdjointArgs.push_back(adjointArg); + else + reverseForwAdjointArgs.push_back(getZeroInit(ArgTy)); + adjointArgs.push_back(BuildOp(UnaryOperatorKind::UO_AddrOf, adjointArg, + m_DiffReq->getLocation())); + } + primalArgs.push_back(argDiff.getExpr()); + } + + // Try to create a pullback constructor call + llvm::SmallVector pullbackArgs; + QualType recordType = + m_Context.getRecordType(CE->getConstructor()->getParent()); + QualType recordPointerType = m_Context.getPointerType(recordType); + // thisE = object being created by this constructor call. + // dThisE = adjoint of the object being created by this constructor call. + // + // We cannot fill these args yet because these objects have not yet been + // created. The caller which triggers 'VisitCXXConstructExpr' is + // responsible for updating these args. + Expr* thisE = getZeroInit(recordPointerType); + Expr* dThisE = getZeroInit(recordPointerType); + + pullbackArgs.push_back(thisE); + pullbackArgs.append(primalArgs.begin(), primalArgs.end()); + pullbackArgs.push_back(dThisE); + pullbackArgs.append(adjointArgs.begin(), adjointArgs.end()); + + Stmts& curRevBlock = getCurrentBlock(direction::reverse); + Stmts::iterator it = std::begin(curRevBlock) + insertionPoint; + curRevBlock.insert(it, prePullbackCallStmts.begin(), + prePullbackCallStmts.end()); + it += prePullbackCallStmts.size(); + std::string customPullbackName = "constructor_pullback"; + if (Expr* customPullbackCall = + m_Builder.BuildCallToCustomDerivativeOrNumericalDiff( + customPullbackName, pullbackArgs, getCurrentScope(), + const_cast( + CE->getConstructor()->getDeclContext()))) { + curRevBlock.insert(it, customPullbackCall); + if (m_TrackConstructorPullbackInfo) { + setConstructorPullbackCallInfo(llvm::cast(customPullbackCall), + primalArgs.size() + 1); + m_TrackConstructorPullbackInfo = false; + } + } + // FIXME: If no compatible custom constructor pullback is found then try + // to automatically differentiate the constructor. + + // Create the constructor call in the forward-pass, or creates + // 'constructor_forw' call if possible. + + // This works as follows: + // + // primal code: + // ``` + // SomeClass c(u, v); + // ``` + // + // adjoint code: + // ``` + // // forward-pass + // clad::ValueAndAdjoint _t0 = + // constructor_forw(clad::ConstructorReverseForwTag{}, u, v, + // _d_u, _d_v); + // SomeClass _d_c = _t0.adjoint; + // SomeClass c = _t0.value; + // ``` + if (Expr* customReverseForwFnCall = BuildCallToCustomForwPassFn( + CE->getConstructor(), primalArgs, reverseForwAdjointArgs, + /*baseExpr=*/nullptr)) { + Expr* callRes = StoreAndRef(customReverseForwFnCall); + Expr* val = + utils::BuildMemberExpr(m_Sema, getCurrentScope(), callRes, "value"); + Expr* adjoint = + utils::BuildMemberExpr(m_Sema, getCurrentScope(), callRes, "adjoint"); + return {val, nullptr, adjoint}; } + Expr* clonedArgsE = nullptr; if (CE->getNumArgs() != 1) { if (CE->isListInitialization()) { - clonedArgsE = m_Sema.ActOnInitList(noLoc, clonedArgs, noLoc).get(); + clonedArgsE = m_Sema.ActOnInitList(noLoc, primalArgs, noLoc).get(); } else { if (CE->getNumArgs() == 0) { // ParenList is empty -- default initialisation. @@ -4132,10 +4276,10 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // parse' issue. return StmtDiff(); } - clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, clonedArgs).get(); + clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, primalArgs).get(); } } else { - clonedArgsE = clonedArgs[0]; + clonedArgsE = primalArgs[0]; } // `CXXConstructExpr` node will be created automatically by passing these // initialiser to higher level `ActOn`/`Build` Sema functions. @@ -4384,6 +4528,21 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, m_DiffReq->getLocation()); args.push_back(baseExpr); } + if (auto CD = llvm::dyn_cast(FD)) { + const RecordDecl* RD = CD->getParent(); + QualType constructorReverseForwTagT = + GetCladConstructorReverseForwTagOfType(m_Context.getRecordType(RD)); + Expr* constructorReverseForwTagArg = + m_Sema + .BuildCXXTypeConstructExpr( + m_Context.getTrivialTypeSourceInfo( + constructorReverseForwTagT, utils::GetValidSLoc(m_Sema)), + utils::GetValidSLoc(m_Sema), MultiExprArg{}, + utils::GetValidSLoc(m_Sema), + /*ListInitialization=*/false) + .get(); + args.push_back(constructorReverseForwTagArg); + } args.append(primalArgs.begin(), primalArgs.end()); args.append(derivedArgs.begin(), derivedArgs.end()); Expr* customForwPassCE = @@ -4392,4 +4551,10 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, const_cast(FD->getDeclContext())); return customForwPassCE; } + + void ReverseModeVisitor::ConstructorPullbackCallInfo::updateThisParmArgs( + Expr* thisE, Expr* dThisE) const { + pullbackCE->setArg(0, thisE); + pullbackCE->setArg(thisAdjointArgIdx, dThisE); + } } // end namespace clad diff --git a/lib/Differentiator/VisitorBase.cpp b/lib/Differentiator/VisitorBase.cpp index 113e01e4d..a3c3ec384 100644 --- a/lib/Differentiator/VisitorBase.cpp +++ b/lib/Differentiator/VisitorBase.cpp @@ -449,11 +449,12 @@ namespace clad { QualType VisitorBase::InstantiateTemplate(TemplateDecl* CladClassDecl, TemplateArgumentListInfo& TLI) { // This will instantiate tape type and return it. - QualType TT = - m_Sema.CheckTemplateIdType(TemplateName(CladClassDecl), noLoc, TLI); + QualType TT = m_Sema.CheckTemplateIdType(TemplateName(CladClassDecl), + utils::GetValidSLoc(m_Sema), TLI); // Get clad namespace and its identifier clad::. CXXScopeSpec CSS; - CSS.Extend(m_Context, GetCladNamespace(), noLoc, noLoc); + CSS.Extend(m_Context, GetCladNamespace(), utils::GetValidSLoc(m_Sema), + utils::GetValidSLoc(m_Sema)); NestedNameSpecifier* NS = CSS.getScopeRep(); // Create elaborated type with namespace specifier, @@ -853,4 +854,16 @@ namespace clad { VisitorBase::GetCladConstructorPushforwardTagOfType(clang::QualType T) { return InstantiateTemplate(GetCladConstructorPushforwardTag(), {T}); } + + clang::TemplateDecl* VisitorBase::GetCladConstructorReverseForwTag() { + if (!m_CladConstructorPushforwardTag) + m_CladConstructorReverseForwTag = + LookupTemplateDeclInCladNamespace("ConstructorReverseForwTag"); + return m_CladConstructorReverseForwTag; + } + + clang::QualType + VisitorBase::GetCladConstructorReverseForwTagOfType(clang::QualType T) { + return InstantiateTemplate(GetCladConstructorReverseForwTag(), {T}); + } } // end namespace clad diff --git a/test/Gradient/Functors.C b/test/Gradient/Functors.C index e2116ce9d..47b633078 100644 --- a/test/Gradient/Functors.C +++ b/test/Gradient/Functors.C @@ -227,11 +227,15 @@ int main() { // CHECK-NEXT: Experiment E(3, 5); // CHECK-NEXT: Experiment _t0 = E; // CHECK-NEXT: { + // CHECK-NEXT: double _r2 = 0; + // CHECK-NEXT: double _r3 = 0; + // CHECK-NEXT: _t0.operator_call_pullback(i, j, 1, &_d_E, &_r2, &_r3); + // CHECK-NEXT: *_d_i += _r2; + // CHECK-NEXT: *_d_j += _r3; + // CHECK-NEXT: } + // CHECK-NEXT: { // CHECK-NEXT: double _r0 = 0; // CHECK-NEXT: double _r1 = 0; - // CHECK-NEXT: _t0.operator_call_pullback(i, j, 1, &_d_E, &_r0, &_r1); - // CHECK-NEXT: *_d_i += _r0; - // CHECK-NEXT: *_d_j += _r1; // CHECK-NEXT: } // CHECK-NEXT: } @@ -265,12 +269,16 @@ int main() { // CHECK-NEXT: Experiment _d_E({}); // CHECK-NEXT: Experiment E(3, 5); // CHECK-NEXT: { - // CHECK-NEXT: Experiment _r0 = {}; + // CHECK-NEXT: Experiment _r2 = {}; + // CHECK-NEXT: double _r3 = 0; + // CHECK-NEXT: double _r4 = 0; + // CHECK-NEXT: FunctorAsArg_pullback(E, i, j, 1, &_r2, &_r3, &_r4); + // CHECK-NEXT: *_d_i += _r3; + // CHECK-NEXT: *_d_j += _r4; + // CHECK-NEXT: } + // CHECK-NEXT: { + // CHECK-NEXT: double _r0 = 0; // CHECK-NEXT: double _r1 = 0; - // CHECK-NEXT: double _r2 = 0; - // CHECK-NEXT: FunctorAsArg_pullback(E, i, j, 1, &_r0, &_r1, &_r2); - // CHECK-NEXT: *_d_i += _r1; - // CHECK-NEXT: *_d_j += _r2; // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Gradient/MemberFunctions.C b/test/Gradient/MemberFunctions.C index d12446025..fda50ca9a 100644 --- a/test/Gradient/MemberFunctions.C +++ b/test/Gradient/MemberFunctions.C @@ -485,6 +485,63 @@ double fn4(SimpleFunctions& v) { // CHECK-NEXT: _t0.operator_plus_plus_pullback({}, &(*_d_v)); // CHECK-NEXT: } +class SafeTestClass { + public: + SafeTestClass() {}; + SafeTestClass(double &x) { + } + SafeTestClass(double x, double* y) { + *y = x; + } +}; + +namespace clad { +namespace custom_derivatives { +namespace class_functions { + clad::ValueAndAdjoint + constructor_reverse_forw(clad::ConstructorReverseForwTag, double x, double* y, double d_x, double* d_y) { + return {SafeTestClass(x, y), SafeTestClass(d_x, d_y)}; + } + clad::ValueAndAdjoint + constructor_reverse_forw(clad::ConstructorReverseForwTag, double &x, double &d_x) { + return {SafeTestClass(x), SafeTestClass(d_x)}; + } + clad::ValueAndAdjoint + constructor_reverse_forw(clad::ConstructorReverseForwTag) { + return {SafeTestClass(), SafeTestClass()}; + } + + void constructor_pullback(SafeTestClass *c, double x, double* y, SafeTestClass *d_c, double* d_x, double* d_y) { + *d_x += *d_y; + *d_y = 0; + } +}}} + +double fn6(double u, double v) { + double &w = u; + SafeTestClass s1; + SafeTestClass s2(u, &v); + SafeTestClass s3(w); + return v; +} + +// CHECK: void fn6_grad(double u, double v, double *_d_u, double *_d_v) { +// CHECK-NEXT: double &_d_w = *_d_u; +// CHECK-NEXT: double &w = u; +// CHECK-NEXT: clad::ValueAndAdjoint _t0 = {{.*}}constructor_reverse_forw(clad::ConstructorReverseForwTag()); +// CHECK-NEXT: SafeTestClass _d_s1(_t0.adjoint); +// CHECK-NEXT: SafeTestClass s1(_t0.value); +// CHECK-NEXT: clad::ValueAndAdjoint _t1 = {{.*}}constructor_reverse_forw(clad::ConstructorReverseForwTag(), u, &v, *_d_u, &*_d_v); +// CHECK-NEXT: SafeTestClass _d_s2(_t1.adjoint); +// CHECK-NEXT: SafeTestClass s2(_t1.value); +// CHECK-NEXT: clad::ValueAndAdjoint _t2 = {{.*}}constructor_reverse_forw(clad::ConstructorReverseForwTag(), w, _d_w); +// CHECK-NEXT: SafeTestClass _d_s3(_t2.adjoint); +// CHECK-NEXT: SafeTestClass s3(_t2.value); +// CHECK-NEXT: *_d_v += 1; +// CHECK-NEXT: {{.*}}constructor_pullback(&s2, u, &v, &_d_s2, &*_d_u, &*_d_v); +// CHECK-NEXT: } + + int main() { auto d_mem_fn = clad::gradient(&SimpleFunctions::mem_fn); auto d_const_mem_fn = clad::gradient(&SimpleFunctions::const_mem_fn); @@ -534,6 +591,12 @@ int main() { d_fn4.execute(sf3, &d_sf); printf("%.2f", d_sf.x); //CHECK-EXEC: 2.00 + double dx = 0, dy = 0; + auto d_fn6 = clad::gradient(fn6); + d_fn6.execute(3, 5, &dx, &dy); + printf("%.2f", dx); //CHECK-EXEC: 1.00 + printf("%.2f", dy); //CHECK-EXEC: 0.00 + auto d_const_volatile_lval_ref_mem_fn_i = clad::gradient(&SimpleFunctions::const_volatile_lval_ref_mem_fn, "i"); // CHECK: void const_volatile_lval_ref_mem_fn_grad_0(double i, double j, volatile SimpleFunctions *_d_this, double *_d_i) const volatile & { @@ -573,14 +636,15 @@ int main() { // CHECK-NEXT: SimpleFunctions _d_sf({}); // CHECK-NEXT: SimpleFunctions sf(x, y); // CHECK-NEXT: SimpleFunctions _t0 = sf; -// CHECK-NEXT: { -// CHECK-NEXT: double _r0 = 0; -// CHECK-NEXT: double _r1 = 0; -// CHECK-NEXT: _t0.mem_fn_pullback(i, j, 1, &_d_sf, &_r0, &_r1); -// CHECK-NEXT: *_d_i += _r0; -// CHECK-NEXT: *_d_j += _r1; +// CHECK-NEXT: { +// CHECK-NEXT: double _r0 = 0; +// CHECK-NEXT: double _r1 = 0; +// CHECK-NEXT: _t0.mem_fn_pullback(i, j, 1, &_d_sf, &_r0, &_r1); +// CHECK-NEXT: *_d_i += _r0; +// CHECK-NEXT: *_d_j += _r1; +// CHECK-NEXT: } // CHECK-NEXT: } -// CHECK-NEXT: } + // CHECK: void ref_mem_fn_pullback(double i, double _d_y, SimpleFunctions *_d_this, double *_d_i) { // CHECK-NEXT: double _t0 = this->x; diff --git a/test/Gradient/NonDifferentiable.C b/test/Gradient/NonDifferentiable.C index 230c9a2b1..2542034f9 100644 --- a/test/Gradient/NonDifferentiable.C +++ b/test/Gradient/NonDifferentiable.C @@ -130,14 +130,18 @@ int main() { // CHECK-NEXT: SimpleFunctions1 obj(2, 3); // CHECK-NEXT: SimpleFunctions1 _t0 = obj; // CHECK-NEXT: { - // CHECK-NEXT: double _r0 = 0; - // CHECK-NEXT: double _r1 = 0; - // CHECK-NEXT: _t0.mem_fn_1_pullback(i, j, 1, &_d_obj, &_r0, &_r1); - // CHECK-NEXT: *_d_i += _r0; - // CHECK-NEXT: *_d_j += _r1; + // CHECK-NEXT: double _r2 = 0; + // CHECK-NEXT: double _r3 = 0; + // CHECK-NEXT: _t0.mem_fn_1_pullback(i, j, 1, &_d_obj, &_r2, &_r3); + // CHECK-NEXT: *_d_i += _r2; + // CHECK-NEXT: *_d_j += _r3; // CHECK-NEXT: *_d_i += 1 * j; // CHECK-NEXT: *_d_j += i * 1; // CHECK-NEXT: } + // CHECK-NEXT: { + // CHECK-NEXT: double _r0 = 0; + // CHECK-NEXT: double _r1 = 0; + // CHECK-NEXT: } // CHECK-NEXT: } // CHECK: void fn_s1_field_grad(double i, double j, double *_d_i, double *_d_j) { @@ -148,6 +152,10 @@ int main() { // CHECK-NEXT: *_d_i += 1 * j; // CHECK-NEXT: *_d_j += i * 1; // CHECK-NEXT: } + // CHECK-NEXT: { + // CHECK-NEXT: double _r0 = 0; + // CHECK-NEXT: double _r1 = 0; + // CHECK-NEXT: } // CHECK-NEXT: } // CHECK: void fn_s1_field_pointer_grad(double i, double j, double *_d_i, double *_d_j) { @@ -158,6 +166,10 @@ int main() { // CHECK-NEXT: *_d_i += 1 * j; // CHECK-NEXT: *_d_j += i * 1; // CHECK-NEXT: } + // CHECK-NEXT: { + // CHECK-NEXT: double _r0 = 0; + // CHECK-NEXT: double _r1 = 0; + // CHECK-NEXT: } // CHECK-NEXT: } // CHECK: void fn_s2_mem_fn_grad(double i, double j, double *_d_i, double *_d_j) { diff --git a/test/Gradient/PointersWithTBR.C b/test/Gradient/PointersWithTBR.C index a86f9cfb2..80cb20c4a 100644 --- a/test/Gradient/PointersWithTBR.C +++ b/test/Gradient/PointersWithTBR.C @@ -36,5 +36,5 @@ int main() { double arr[5] = {1, 2, 3, 4, 5}; double d_arr[5] = {0, 0, 0, 0, 0}; d_pointerParam.execute(arr, 5, d_arr); - printf("%.2f %.2f %.2f %.2f %.2f\n", d_arr[0], d_arr[1], d_arr[2], d_arr[3], d_arr[4]); // CHECK-EXEC: 0.00 1.00 2.00 3.00 4.00 + printf("%.2f %.2f %.2f %.2f %.2f\n", d_arr[0], d_arr[1], d_arr[2], d_arr[3], d_arr[4]); // CHECK-EXEC: 0.00 2.00 6.00 12.00 20.00 } diff --git a/test/Gradient/STLCustomDerivatives.C b/test/Gradient/STLCustomDerivatives.C index 3f01775dd..03886c7d8 100644 --- a/test/Gradient/STLCustomDerivatives.C +++ b/test/Gradient/STLCustomDerivatives.C @@ -95,15 +95,25 @@ double fn12(double u, double v) { return res; } +double fn13(double u, double v) { + double res = u; + std::vector::allocator_type allocator; + typename ::std::vector::size_type count = 3; + std::vector vec(count, u, allocator); + return vec[0] + vec[1] + vec[2]; +} + int main() { double d_i, d_j; INIT_GRADIENT(fn10); INIT_GRADIENT(fn11); INIT_GRADIENT(fn12); + INIT_GRADIENT(fn13); TEST_GRADIENT(fn10, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {1.00, 1.00} TEST_GRADIENT(fn11, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {2.00, 1.00} TEST_GRADIENT(fn12, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {4.00, 2.00} + TEST_GRADIENT(fn13, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {3.00, 0.00} } // CHECK: void fn10_grad(double u, double v, double *_d_u, double *_d_v) { @@ -342,4 +352,32 @@ int main() { // CHECK-NEXT: {{.*}} _r0 = 0; // CHECK-NEXT: {{.*}}class_functions::resize_pullback(&_t0, 3, &_d_vec, &_r0); // CHECK-NEXT: } -// CHECK-NEXT: } \ No newline at end of file +// CHECK-NEXT: } + +// CHECK-NEXT: void fn13_grad(double u, double v, double *_d_u, double *_d_v) { +// CHECK-NEXT: double _d_res = 0; +// CHECK-NEXT: double res = u; +// CHECK-NEXT: {{.*}}allocator_type _d_allocator({}); +// CHECK-NEXT: {{.*}}allocator_type allocator; +// CHECK-NEXT: {{.*}} _d_count = 0; +// CHECK-NEXT: {{.*}} count = 3; +// CHECK-NEXT: {{.*}}ValueAndAdjoint<{{.*}}vector<{{.*}}>, {{.*}}vector<{{.*}}> > _t0 = {{.*}}class_functions::constructor_reverse_forw(clad::ConstructorReverseForwTag >(), count, u, allocator, _d_count, *_d_u, _d_allocator); +// CHECK-NEXT: std::vector _d_vec(_t0.adjoint); +// CHECK-NEXT: std::vector vec(_t0.value); +// CHECK-NEXT: std::vector _t1 = vec; +// CHECK-NEXT: {{.*}}ValueAndAdjoint _t2 = {{.*}}class_functions::operator_subscript_reverse_forw(&vec, 0, &_d_vec, _r0); +// CHECK-NEXT: std::vector _t3 = vec; +// CHECK-NEXT: {{.*}}ValueAndAdjoint _t4 = {{.*}}class_functions::operator_subscript_reverse_forw(&vec, 1, &_d_vec, _r1); +// CHECK-NEXT: std::vector _t5 = vec; +// CHECK-NEXT: {{.*}}ValueAndAdjoint _t6 = {{.*}}class_functions::operator_subscript_reverse_forw(&vec, 2, &_d_vec, _r2); +// CHECK-NEXT: { +// CHECK-NEXT: {{.*}} _r0 = 0; +// CHECK-NEXT: {{.*}}operator_subscript_pullback(&_t1, 0, 1, &_d_vec, &_r0); +// CHECK-NEXT: {{.*}} _r1 = 0; +// CHECK-NEXT: {{.*}}operator_subscript_pullback(&_t3, 1, 1, &_d_vec, &_r1); +// CHECK-NEXT: {{.*}} _r2 = 0; +// CHECK-NEXT: {{.*}}operator_subscript_pullback(&_t5, 2, 1, &_d_vec, &_r2); +// CHECK-NEXT: } +// CHECK-NEXT: {{.*}}constructor_pullback(&vec, count, u, allocator, &_d_vec, &_d_count, &*_d_u, &_d_allocator); +// CHECK-NEXT: *_d_u += _d_res; +// CHECK-NEXT: } diff --git a/test/Gradient/UserDefinedTypes.C b/test/Gradient/UserDefinedTypes.C index ee8d6e62a..5c620ba21 100644 --- a/test/Gradient/UserDefinedTypes.C +++ b/test/Gradient/UserDefinedTypes.C @@ -145,6 +145,15 @@ double fn4(double i, double j) { // CHECK-NEXT: _d_q.second += 1 * j; // CHECK-NEXT: *_d_j += q.second * 1; // CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: {{.*}} _r2 = {}; +// CHECK-NEXT: int _r3 = 0; +// CHECK-NEXT: int _r4 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: int _r0 = 0; +// CHECK-NEXT: int _r1 = 0; +// CHECK-NEXT: } // CHECK-NEXT: } // CHECK: void someMemFn_grad(double i, double j, Tangent *_d_this, double *_d_i, double *_d_j) { diff --git a/test/Hessian/BuiltinDerivatives.C b/test/Hessian/BuiltinDerivatives.C index 416b7da26..8cb6ab2e0 100644 --- a/test/Hessian/BuiltinDerivatives.C +++ b/test/Hessian/BuiltinDerivatives.C @@ -197,18 +197,22 @@ int main() { // CHECK-NEXT: _d__t1.pushforward += 1; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: float _r2 = 0; -// CHECK-NEXT: float _r3 = 0; -// CHECK-NEXT: cos_pushforward_pullback(x, _d_x0, _d__t1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r2; -// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r3 = {}; +// CHECK-NEXT: clad::custom_derivatives::class_functions::constructor_pullback(&_t10, {{.*}}cos_pushforward(x, _d_x0), &_d__t1, &_r3); +// CHECK-NEXT: float _r4 = 0; +// CHECK-NEXT: float _r5 = 0; +// CHECK-NEXT: cos_pushforward_pullback(x, _d_x0, _r3, &_r4, &_r5); +// CHECK-NEXT: *_d_x += _r4; +// CHECK-NEXT: _d__d_x += _r5; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r0 = {}; +// CHECK-NEXT: clad::custom_derivatives::class_functions::constructor_pullback(&_t00, {{.*}}sin_pushforward(x, _d_x0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; -// CHECK-NEXT: sin_pushforward_pullback(x, _d_x0, _d__t0, &_r0, &_r1); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: _d__d_x += _r1; +// CHECK-NEXT: float _r2 = 0; +// CHECK-NEXT: sin_pushforward_pullback(x, _d_x0, _r0, &_r1, &_r2); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: _d__d_x += _r2; // CHECK-NEXT: } // CHECK-NEXT: } @@ -227,11 +231,13 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::exp_pushforward(x, _d_x0); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}exp_pushforward(x, _d_x0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; -// CHECK-NEXT: exp_pushforward_pullback(x, _d_x0, _d__t0, &_r0, &_r1); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: _d__d_x += _r1; +// CHECK-NEXT: float _r2 = 0; +// CHECK-NEXT: exp_pushforward_pullback(x, _d_x0, _r0, &_r1, &_r2); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: _d__d_x += _r2; // CHECK-NEXT: } // CHECK-NEXT: } @@ -250,11 +256,13 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::log_pushforward(x, _d_x0); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}log_pushforward(x, _d_x0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; -// CHECK-NEXT: log_pushforward_pullback(x, _d_x0, _d__t0, &_r0, &_r1); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: _d__d_x += _r1; +// CHECK-NEXT: float _r2 = 0; +// CHECK-NEXT: log_pushforward_pullback(x, _d_x0, _r0, &_r1, &_r2); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: _d__d_x += _r2; // CHECK-NEXT: } // CHECK-NEXT: } @@ -273,13 +281,15 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::pow_pushforward(x, 4.F, _d_x0, 0.F); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}} _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}pow_pushforward(x, 4.F, _d_x0, 0.F), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; // CHECK-NEXT: float _r2 = 0; // CHECK-NEXT: float _r3 = 0; -// CHECK-NEXT: pow_pushforward_pullback(x, 4.F, _d_x0, 0.F, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: _d__d_x += _r2; +// CHECK-NEXT: float _r4 = 0; +// CHECK-NEXT: pow_pushforward_pullback(x, 4.F, _d_x0, 0.F, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: _d__d_x += _r3; // CHECK-NEXT: } // CHECK-NEXT: } @@ -296,13 +306,15 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::pow_pushforward(2.F, x, 0.F, _d_x0); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}} _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}pow_pushforward(2.F, x, 0.F, _d_x0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; // CHECK-NEXT: float _r2 = 0; // CHECK-NEXT: float _r3 = 0; -// CHECK-NEXT: pow_pushforward_pullback(2.F, x, 0.F, _d_x0, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r1; -// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: float _r4 = 0; +// CHECK-NEXT: {{.*}}pow_pushforward_pullback(2.F, x, 0.F, _d_x0, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r2; +// CHECK-NEXT: _d__d_x += _r4; // CHECK-NEXT: } // CHECK-NEXT: } @@ -322,15 +334,17 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::pow_pushforward(x, y, _d_x0, _d_y0); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}} _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}pow_pushforward(x, y, _d_x0, _d_y0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; // CHECK-NEXT: float _r2 = 0; // CHECK-NEXT: float _r3 = 0; -// CHECK-NEXT: pow_pushforward_pullback(x, y, _d_x0, _d_y0, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: *_d_y += _r1; -// CHECK-NEXT: _d__d_x += _r2; -// CHECK-NEXT: _d__d_y += _r3; +// CHECK-NEXT: float _r4 = 0; +// CHECK-NEXT: {{.*}}pow_pushforward_pullback(x, y, _d_x0, _d_y0, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: *_d_y += _r2; +// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: _d__d_y += _r4; // CHECK-NEXT: } // CHECK-NEXT: } @@ -350,15 +364,17 @@ int main() { // CHECK-NEXT: ValueAndPushforward _t00 = clad::custom_derivatives{{(::std)?}}::pow_pushforward(x, y, _d_x0, _d_y0); // CHECK-NEXT: _d__t0.pushforward += 1; // CHECK-NEXT: { -// CHECK-NEXT: float _r0 = 0; +// CHECK-NEXT: {{.*}} _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, {{.*}}pow_pushforward(x, y, _d_x0, _d_y0), &_d__t0, &_r0); // CHECK-NEXT: float _r1 = 0; // CHECK-NEXT: float _r2 = 0; // CHECK-NEXT: float _r3 = 0; -// CHECK-NEXT: pow_pushforward_pullback(x, y, _d_x0, _d_y0, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: *_d_y += _r1; -// CHECK-NEXT: _d__d_x += _r2; -// CHECK-NEXT: _d__d_y += _r3; +// CHECK-NEXT: float _r4 = 0; +// CHECK-NEXT: {{.*}}pow_pushforward_pullback(x, y, _d_x0, _d_y0, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: *_d_y += _r2; +// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: _d__d_y += _r4; // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Hessian/NestedFunctionCalls.C b/test/Hessian/NestedFunctionCalls.C index afc737fc9..fe61e33e2 100644 --- a/test/Hessian/NestedFunctionCalls.C +++ b/test/Hessian/NestedFunctionCalls.C @@ -55,15 +55,17 @@ double f2(double x, double y){ // CHECK-NEXT: _d__t0.value += _d_ans0; // CHECK-NEXT: _d__t0.pushforward += _d__d_ans; // CHECK-NEXT: { -// CHECK-NEXT: double _r0 = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, f_pushforward(x, y, _d_x0, _d_y0), &_d__t0, &_r0); // CHECK-NEXT: double _r1 = 0; // CHECK-NEXT: double _r2 = 0; // CHECK-NEXT: double _r3 = 0; -// CHECK-NEXT: f_pushforward_pullback(x, y, _d_x0, _d_y0, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: *_d_y += _r1; -// CHECK-NEXT: _d__d_x += _r2; -// CHECK-NEXT: _d__d_y += _r3; +// CHECK-NEXT: double _r4 = 0; +// CHECK-NEXT: f_pushforward_pullback(x, y, _d_x0, _d_y0, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: *_d_y += _r2; +// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: _d__d_y += _r4; // CHECK-NEXT: } // CHECK-NEXT: } @@ -91,15 +93,17 @@ double f2(double x, double y){ // CHECK-NEXT: _d__t0.value += _d_ans0; // CHECK-NEXT: _d__t0.pushforward += _d__d_ans; // CHECK-NEXT: { -// CHECK-NEXT: double _r0 = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward _r0 = {}; +// CHECK-NEXT: {{.*}}constructor_pullback(&_t00, f_pushforward(x, y, _d_x0, _d_y0), &_d__t0, &_r0); // CHECK-NEXT: double _r1 = 0; // CHECK-NEXT: double _r2 = 0; // CHECK-NEXT: double _r3 = 0; -// CHECK-NEXT: f_pushforward_pullback(x, y, _d_x0, _d_y0, _d__t0, &_r0, &_r1, &_r2, &_r3); -// CHECK-NEXT: *_d_x += _r0; -// CHECK-NEXT: *_d_y += _r1; -// CHECK-NEXT: _d__d_x += _r2; -// CHECK-NEXT: _d__d_y += _r3; +// CHECK-NEXT: double _r4 = 0; +// CHECK-NEXT: f_pushforward_pullback(x, y, _d_x0, _d_y0, _r0, &_r1, &_r2, &_r3, &_r4); +// CHECK-NEXT: *_d_x += _r1; +// CHECK-NEXT: *_d_y += _r2; +// CHECK-NEXT: _d__d_x += _r3; +// CHECK-NEXT: _d__d_y += _r4; // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/unittests/Misc/CMakeLists.txt b/unittests/Misc/CMakeLists.txt index 07fb60885..02388cce8 100644 --- a/unittests/Misc/CMakeLists.txt +++ b/unittests/Misc/CMakeLists.txt @@ -7,6 +7,6 @@ add_clad_unittest(MiscTests # Create a library from the Defs.cpp file ADD_CLAD_LIBRARY(Defs Defs.cpp) - # Link the library to the test target_link_libraries(MiscTests PRIVATE Defs) +set_property(TARGET MiscTests PROPERTY CXX_STANDARD 11)