From 645d2b6beb5e616e5a1d133ada6e7a3fbe8f93e0 Mon Sep 17 00:00:00 2001 From: parth-07 Date: Sun, 26 May 2024 14:54:46 +0530 Subject: [PATCH] Add primitive support for custom constructor pushforward functions This commit adds primitive support for custom pushforward functions for constructors. Custom constructor pushforward function support will enable the below features: - Class differentiation support for classes whose constructor Clad cannot automatically differentiate. Now, We can enable differentiation of entire C++ standard library by providing custom derivatives. - Remove the restriction of default-constructible for class types. This was a troublesome restriction. Now, the only restriction for class types is to have a sensible copy-constructor. That is, copy constructor should copy the class members and after copy-construction, both the objects should be equivalent, mathematically speaking. Constructor pushforward functions differ from ordinary pushforward functions in two important ways: - Constructor pushforward functions initialize the primal class object and the corresponding derivative object. Ordinary member function pushforwards takes an already-existing primal class object and the corresponding derivative object as inputs. - Constructor pushforward functions return a value even though constructor do not return anything. Constructor pushforward functions return initialized primal object and the derivative object. These are then used to initialize primal object and the derivative in the derivative function code. How to write custom constructor pushforward functions ---------------------------------------- Let's see how to write custom pushforward function for a constructor: - Custom constructor pushforwards must have the name `constructor_pushforward` - Custom constructor pushforwards must be defined in `::clad::custom_derivatives::class_functions` namespace. - The parameters of the custom constructor pushforward must be: {`::clad::ConstructorPushforwardTag`, original params..., derivative params...}. 'original parameters...' and 'derivative parameters...' is same as what we have for other pushforward functions. We will soon see why do we need `::clad::ConstructorPushforwardTag` for constructor custom pushforwards. Let's see a basic example of how to write custom constructor pushforward. ```cpp class Coordinates { Coordinates(double px, double py, double pz) : x(px), y(py), z(pz) {} public: double x, y, z; } namespace clad { namespace custom_derivatives { namespace class_functions { // custom constructor pushforward function clad::ValueAndPushforward constructor_pushforward(clad::ConstructorPushforwardTag, double x, double y, double z, double d_x, double d_y, double d_z) { return {Coordinates(x, y, z), Coordinates(d_x, d_y, d_z) }; } } // namespace class_functions } // namespace custom_derivatives } // namespace clad // custom constructor pushforward is used as follows: // primal code Constructor c(u, v, w); // derivative code clad::ValueAndPushforward _t0 = constructor_pushforward(clad::ConstructorPushforwardTag, u, v, w, _d_u, _d_v, _d_w); Coordinates _d_c = _t0.pushforward; Coordinates c = _t0.value; ``` Now, let's see a bit advanced example based on `std::vector` constructor. ```cpp namespace clad { namespace custom_derivatives { namespace class_functions { // Custom pushforward for: vector(size_t n, const typename ::std::vector::allocator_type alloc) template clad::ValueAndPushforward<::std::vector, ::std::vector> constructor_pushforward( ConstructorPushforwardTag<::std::vector>, size_t n, const typename ::std::vector::allocator_type alloc, size_t d_n, const typename ::std::vector::allocator_type d_alloc) { ::std::vector v(n, alloc); ::std::vector d_v(n, 0, alloc); return {v, d_v}; } // Custom pushfoward for: vector(size_t n, T val, const typename ::std::vector::allocator_type alloc) template clad::ValueAndPushforward<::std::vector, ::std::vector> constructor_pushforward( ConstructorPushforwardTag<::std::vector>, size_t n, T val, const typename ::std::vector::allocator_type alloc, size_t d_n, T d_val, const typename ::std::vector::allocator_type d_alloc) { ::std::vector v(n, val, alloc); ::std::vector d_v(n, d_val, alloc); return {v, d_v}; } } // namespace class_functions } // namespace custom_derivatives } // namespace clad // The custom constructor pushforwards is used as follows: // Primal code: std::vector v(10, u); // Derivative code: clad::ValueAndPushforward, std::vector> _t0 = clad::custom_derivatives::class_functions::constructor_pushforward( clad::ConstructorPushforwardTag >(), 10, u, allocator_type(), 0, _d_u, allocator_type()); std::vector d_v = _t0.pushforward; std::vector v = _t0.value; ``` Why `clad::ConstructorPushforwardTag`? ------------------------ So, why do we need clad::ConstructorPushforwardTag? For a constructor that takes two parameters of types `size_t` and `double`, the custom pushforward will have the following signature if we do not include `::clad::ConstructorPushforwardTag`: ```cpp clad::ValueAndPushforward constructor_pushforward(size_t n, double val, size_t d_n, double d_val); ``` Now, the question is: How to distinguish custom constructor pushforwards for different classes? ```cpp MyClassA a(3, 5.0); MyClassB b(7, 9.0); ``` There is no way for overload resolution selector to distinguish constructor_pushforward for classes `MyClassA` and `MyClassB`. `clad::ConstructorPushforwardTag` is used to identify the class for which custom constructor pushforward is defined. Please note that we cannot use the same strategy which we use for custom member function pushforwards because member function pushforwards always have parameters of the class type which are used for identifying the class. We also cannot simply ask users to define the pushforwards inside the declaration context of the class because it may not always be feasible to modify the source code of external libraries. -------------------------------------------- Fixes #965 --- .../Differentiator/BaseForwardModeVisitor.h | 11 ++ .../clad/Differentiator/BuiltinDerivatives.h | 20 +++ include/clad/Differentiator/STLBuiltins.h | 87 +++++++++++- include/clad/Differentiator/VisitorBase.h | 11 +- lib/Differentiator/BaseForwardModeVisitor.cpp | 67 +++++++-- lib/Differentiator/CladUtils.cpp | 3 + lib/Differentiator/VisitorBase.cpp | 12 ++ test/ForwardMode/STLCustomDerivatives.C | 129 ++++++++++++++++++ test/ForwardMode/UserDefinedTypes.C | 4 +- 9 files changed, 330 insertions(+), 14 deletions(-) create mode 100644 test/ForwardMode/STLCustomDerivatives.C diff --git a/include/clad/Differentiator/BaseForwardModeVisitor.h b/include/clad/Differentiator/BaseForwardModeVisitor.h index 311fc5769..507698135 100644 --- a/include/clad/Differentiator/BaseForwardModeVisitor.h +++ b/include/clad/Differentiator/BaseForwardModeVisitor.h @@ -3,6 +3,7 @@ #include "Compatibility.h" #include "VisitorBase.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Sema/Sema.h" @@ -140,6 +141,16 @@ class BaseForwardModeVisitor /// \return active switch case label after processing `stmt` clang::SwitchCase* DeriveSwitchStmtBodyHelper(const clang::Stmt* stmt, clang::SwitchCase* activeSC); + + /// Tries to build custom derivative constructor pushforward call for the + /// given CXXConstructExpr. + /// + /// \return A call expression if a suitable custom derivative is found; + /// Otherwise returns nullptr. + clang::Expr* BuildCustomDerivativeConstructorPFCall( + const clang::CXXConstructExpr* CE, + llvm::SmallVectorImpl& clonedArgs, + llvm::SmallVectorImpl& derivedArgs); }; } // end namespace clad diff --git a/include/clad/Differentiator/BuiltinDerivatives.h b/include/clad/Differentiator/BuiltinDerivatives.h index 0482043b0..85937f72b 100644 --- a/include/clad/Differentiator/BuiltinDerivatives.h +++ b/include/clad/Differentiator/BuiltinDerivatives.h @@ -21,6 +21,26 @@ template struct ValueAndPushforward { T value; U pushforward; }; + +/// It is used to identify constructor custom pushforwards. For +/// constructor custom pushforward functions, we cannot use the same +/// strategy which we use for custom pushforward for member +/// functions. Member functions custom pushforward have the following +/// signature: +/// +/// mem_fn_pushforward(ClassName *c, ..., ClassName *d_c, ...) +/// +/// We use the first argument 'ClassName *c' to determine the class of member +/// function for which the pushforward is defined. +/// +/// In the case of constructor pushforward, there are no objects of the class +/// type passed to the constructor. Therefore, we cannot simply use arguments +/// to determine the class. To solve this, 'ConstructorPushforwardTag' is +/// used. A custom_derivative pushforward for constructor is required to have +/// 'ConstructorPushforwardTag' as the first argument, where 'T' is the +/// class for which constructor pushforward is defined. +template class ConstructorPushforwardTag {}; + namespace custom_derivatives { #ifdef __CUDACC__ template diff --git a/include/clad/Differentiator/STLBuiltins.h b/include/clad/Differentiator/STLBuiltins.h index 39e786aba..12a8048f7 100644 --- a/include/clad/Differentiator/STLBuiltins.h +++ b/include/clad/Differentiator/STLBuiltins.h @@ -1,8 +1,9 @@ #ifndef CLAD_STL_BUILTINS_H #define CLAD_STL_BUILTINS_H +#include +#include #include -#include "clad/Differentiator/BuiltinDerivatives.h" namespace clad { namespace custom_derivatives { @@ -43,6 +44,90 @@ end_pushforward(const ::std::initializer_list* il, const ::std::initializer_list* d_il) { return {il->end(), d_il->end()}; } + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, + const typename ::std::vector::allocator_type& alloc, + typename ::std::vector::size_type d_n, + const typename ::std::vector::allocator_type& d_alloc) { + ::std::vector v(n, alloc); + ::std::vector d_v(n, 0, alloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, T val, + const typename ::std::vector::allocator_type& alloc, + typename ::std::vector::size_type d_n, T d_val, + const typename ::std::vector::allocator_type& d_alloc) { + ::std::vector v(n, val, alloc); + ::std::vector d_v(n, d_val, alloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + ::std::initializer_list list, + const typename ::std::vector::allocator_type& alloc, + ::std::initializer_list dlist, + const typename ::std::vector::allocator_type& dalloc) { + ::std::vector v(list, alloc); + ::std::vector d_v(dlist, dalloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, + typename ::std::vector::size_type d_n) { + ::std::vector v(n); + ::std::vector d_v(n, 0); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, T val, + typename ::std::vector::size_type d_n, T d_val) { + ::std::vector v(n, val); + ::std::vector d_v(n, d_val); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + ::std::initializer_list list, + ::std::initializer_list dlist) { + ::std::vector v(list); + ::std::vector d_v(dlist); + return {v, d_v}; +} + +template +ValueAndPushforward +operator_subscript_pushforward(::std::vector* v, unsigned idx, + ::std::vector* d_v, unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + +template +ValueAndPushforward +operator_subscript_pushforward(const ::std::vector* v, unsigned idx, + const ::std::vector* d_v, unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + } // namespace class_functions } // namespace custom_derivatives } // namespace clad diff --git a/include/clad/Differentiator/VisitorBase.h b/include/clad/Differentiator/VisitorBase.h index 67c295904..d61031f19 100644 --- a/include/clad/Differentiator/VisitorBase.h +++ b/include/clad/Differentiator/VisitorBase.h @@ -18,7 +18,6 @@ namespace clad { #include "clang/AST/StmtVisitor.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/Sema.h" - #include #include #include @@ -600,6 +599,13 @@ namespace clad { bool isDerived); clang::QualType DetermineCladArrayValueType(clang::QualType T); + + /// Returns clad::Identify template declaration. + clang::TemplateDecl* GetCladConstructorPushforwardTag(); + + /// Returns type clad::Identify + clang::QualType GetCladConstructorPushforwardTagOfType(clang::QualType T); + public: /// Rebuild a sequence of nested namespaces ending with DC. clang::NamespaceDecl* RebuildEnclosingNamespaces(clang::DeclContext* DC); @@ -644,6 +650,9 @@ namespace clad { void ComputeEffectiveDOperands(StmtDiff& LDiff, StmtDiff& RDiff, clang::Expr*& derivedL, clang::Expr*& derivedR); + + private: + clang::TemplateDecl* m_CladConstructorPushforwardTag = nullptr; }; } // end namespace clad diff --git a/lib/Differentiator/BaseForwardModeVisitor.cpp b/lib/Differentiator/BaseForwardModeVisitor.cpp index 2e26444e4..4dd6fd8f1 100644 --- a/lib/Differentiator/BaseForwardModeVisitor.cpp +++ b/lib/Differentiator/BaseForwardModeVisitor.cpp @@ -1985,6 +1985,26 @@ BaseForwardModeVisitor::VisitCXXConstructExpr(const CXXConstructExpr* CE) { clonedArgs.push_back(argDiff.getExpr()); derivedArgs.push_back(argDiff.getExpr_dx()); } + + Expr* pushforwardCall = + BuildCustomDerivativeConstructorPFCall(CE, clonedArgs, derivedArgs); + if (pushforwardCall) { + auto valueAndPushforwardE = StoreAndRef(pushforwardCall); + Expr* valueE = utils::BuildMemberExpr(m_Sema, getCurrentScope(), + valueAndPushforwardE, "value"); + Expr* pushforwardE = utils::BuildMemberExpr( + m_Sema, getCurrentScope(), valueAndPushforwardE, "pushforward"); + return StmtDiff(valueE, pushforwardE); + } + + // Custom derivative not found. Create simple constructor calls based on the + // given arguments. For example, if the primal constructor call is + // 'C(a, b, c)' then we use the constructor call 'C(d_a, d_b, d_c)' for the + // derivative. + // FIXME: This is incorrect. It only works for very simple types such as + // std::complex. We should ideally treat a constructor like a function and + // thus differentiate its body, create a pushforward and use the pushforward + // in the derivative code instead of the original constructor. Expr* clonedArgsE = nullptr; Expr* derivedArgsE = nullptr; // FIXME: Currently if the original initialisation expression is `{a, 1, @@ -1996,17 +2016,14 @@ BaseForwardModeVisitor::VisitCXXConstructExpr(const CXXConstructExpr* CE) { if (CE->isListInitialization()) { clonedArgsE = m_Sema.ActOnInitList(noLoc, clonedArgs, noLoc).get(); derivedArgsE = m_Sema.ActOnInitList(noLoc, derivedArgs, noLoc).get(); + } else if (CE->getNumArgs() == 0) { + // ParenList is empty -- default initialisation. + // Passing empty parenList here will silently cause 'most vexing + // parse' issue. + return StmtDiff(); } else { - if (CE->getNumArgs() == 0) { - // ParenList is empty -- default initialisation. - // Passing empty parenList here will silently cause 'most vexing - // parse' issue. - return StmtDiff(); - } else { - clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, clonedArgs).get(); - derivedArgsE = - m_Sema.ActOnParenListExpr(noLoc, noLoc, derivedArgs).get(); - } + clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, clonedArgs).get(); + derivedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, derivedArgs).get(); } } else { clonedArgsE = clonedArgs[0]; @@ -2045,6 +2062,7 @@ StmtDiff BaseForwardModeVisitor::VisitCXXTemporaryObjectExpr( clonedArgs.push_back(argDiff.getExpr()); derivedArgs.push_back(argDiff.getExpr_dx()); } + Expr* clonedTOE = m_Sema .ActOnCXXTypeConstructExpr(OpaquePtr::make(TOE->getType()), @@ -2183,4 +2201,33 @@ StmtDiff BaseForwardModeVisitor::VisitCXXStdInitializerListExpr( const clang::CXXStdInitializerListExpr* ILE) { return Visit(ILE->getSubExpr()); } + +clang::Expr* BaseForwardModeVisitor::BuildCustomDerivativeConstructorPFCall( + const clang::CXXConstructExpr* CE, + llvm::SmallVectorImpl& clonedArgs, + llvm::SmallVectorImpl& derivedArgs) { + llvm::SmallVector customPushforwardArgs; + QualType constructorPushforwardTagT = GetCladConstructorPushforwardTagOfType( + CE->getType().withoutLocalFastQualifiers()); + // Builds clad::ConstructorPushforwardTag declaration + Expr* constructorPushforwardTagArg = + m_Sema + .BuildCXXTypeConstructExpr( + m_Context.getTrivialTypeSourceInfo(constructorPushforwardTagT, + utils::GetValidSLoc(m_Sema)), + noLoc, MultiExprArg{}, noLoc, /*ListInitialization=*/false) + .get(); + customPushforwardArgs.push_back(constructorPushforwardTagArg); + customPushforwardArgs.append(clonedArgs.begin(), clonedArgs.end()); + customPushforwardArgs.append(derivedArgs.begin(), derivedArgs.end()); + std::string customPushforwardName = + clad::utils::ComputeEffectiveFnName(CE->getConstructor()) + + GetPushForwardFunctionSuffix(); + // FIXME: We should not use const_cast to get the decl context here. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + Expr* pushforwardCall = m_Builder.BuildCallToCustomDerivativeOrNumericalDiff( + customPushforwardName, customPushforwardArgs, getCurrentScope(), + const_cast(CE->getConstructor()->getDeclContext())); + return pushforwardCall; +} } // end namespace clad diff --git a/lib/Differentiator/CladUtils.cpp b/lib/Differentiator/CladUtils.cpp index 73fd1c5e8..96b35d6aa 100644 --- a/lib/Differentiator/CladUtils.cpp +++ b/lib/Differentiator/CladUtils.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Sema/Lookup.h" #include "llvm/ADT/SmallVector.h" +#include #include "clad/Differentiator/Compatibility.h" using namespace clang; @@ -98,6 +99,8 @@ namespace clad { case OverloadedOperatorKind::OO_Subscript: return "operator_subscript"; default: + if (isa(FD)) + return "constructor"; return FD->getNameAsString(); } } diff --git a/lib/Differentiator/VisitorBase.cpp b/lib/Differentiator/VisitorBase.cpp index 780184274..eb462a7fb 100644 --- a/lib/Differentiator/VisitorBase.cpp +++ b/lib/Differentiator/VisitorBase.cpp @@ -811,4 +811,16 @@ namespace clad { return m_Sema.ActOnCallExpr(getCurrentScope(), pushDRE, noLoc, args, noLoc) .get(); } + + clang::TemplateDecl* VisitorBase::GetCladConstructorPushforwardTag() { + if (!m_CladConstructorPushforwardTag) + m_CladConstructorPushforwardTag = + LookupTemplateDeclInCladNamespace("ConstructorPushforwardTag"); + return m_CladConstructorPushforwardTag; + } + + clang::QualType + VisitorBase::GetCladConstructorPushforwardTagOfType(clang::QualType T) { + return InstantiateTemplate(GetCladConstructorPushforwardTag(), {T}); + } } // end namespace clad diff --git a/test/ForwardMode/STLCustomDerivatives.C b/test/ForwardMode/STLCustomDerivatives.C new file mode 100644 index 000000000..dfaf5fd0f --- /dev/null +++ b/test/ForwardMode/STLCustomDerivatives.C @@ -0,0 +1,129 @@ +// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out | %filecheck %s +// RUN: ./UserDefinedTypes.out | %filecheck_exec %s + +// CHECK-NOT: {{.*error|warning|note:.*}} + +#include "clad/Differentiator/Differentiator.h" +#include "clad/Differentiator/STLBuiltins.h" + +#include +#include + +#include "../TestUtils.h" +#include "../PrintOverloads.h" + +double fnVec1(double u, double v) { + std::vector V1(2); + std::vector V2(4); + V1[0] = u; + V1[1] = v; + V2[0] = v; + V2[1] = u; + double res = V1[0] * V1[1] + V2[0] * V2[1]; + return res; +} + +// CHECK: double fnVec1_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, {{.*}}0{{.*}}); +// CHECK-NEXT: std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: std::vector V1(_t0.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t1 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 4, {{.*}}0{{.*}}); +// CHECK-NEXT: std::vector _d_V2(_t1.pushforward); +// CHECK-NEXT: std::vector V2(_t1.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: _t2.pushforward = _d_u; +// CHECK-NEXT: _t2.value = u; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t3 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 1, &_d_V1, 0); +// CHECK-NEXT: _t3.pushforward = _d_v; +// CHECK-NEXT: _t3.value = v; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t4 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 0, &_d_V2, 0); +// CHECK-NEXT: _t4.pushforward = _d_v; +// CHECK-NEXT: _t4.value = v; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t5 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: _t5.pushforward = _d_u; +// CHECK-NEXT: _t5.value = u; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t6 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t7 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 1, &_d_V1, 0); +// CHECK-NEXT: double &_t8 = _t6.value; +// CHECK-NEXT: double &_t9 = _t7.value; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t10 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 0, &_d_V2, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t11 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: long double &_t12 = _t10.value; +// CHECK-NEXT: long double &_t13 = _t11.value; +// CHECK-NEXT: double _d_res = _t6.pushforward * _t9 + _t8 * _t7.pushforward + _t10.pushforward * _t13 + _t12 * _t11.pushforward; +// CHECK-NEXT: double res = _t8 * _t9 + _t12 * _t13; +// CHECK-NEXT: return _d_res; +// CHECK-NEXT: } + +double fnVec2(double u, double v) { + const std::vector V1(2, u); + const std::vector V2(2, v); + return V1[0] * V2[1]; +} + +// CHECK: double fnVec2_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, u, {{.*}}0, _d_u{{.*}}); +// CHECK-NEXT: const std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: const std::vector V1(_t0.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t1 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, v, {{.*}}0, _d_v{{.*}}); +// CHECK-NEXT: const std::vector _d_V2(_t1.pushforward); +// CHECK-NEXT: const std::vector V2(_t1.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t3 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: const double _t4 = _t2.value; +// CHECK-NEXT: const double _t5 = _t3.value; +// CHECK-NEXT: return _t2.pushforward * _t5 + _t4 * _t3.pushforward; +// CHECK-NEXT: } + +double fnVec3(double u, double v) { + std::vector V1(2, u); + std::vector &V2 = V1; + return V1[0] + V2[1]; +} + +// CHECK: double fnVec3_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, u, {{.*}}0, _d_u{{.*}}); +// CHECK-NEXT: std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: std::vector V1(_t0.value); +// CHECK-NEXT: std::vector &_d_V2 = _d_V1; +// CHECK-NEXT: std::vector &V2 = V1; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: return _t1.pushforward + _t2.pushforward; +// CHECK-NEXT: } + +double fnVec4(double u, double v) { + std::vector V{u, v, u * v}; + return V[0] * V[2]; +} + +// CHECK: double fnVec4_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), {u, v, u * v}, {{.*}}{_d_u, _d_v, _d_u * v + u * _d_v}{{.*}}); +// CHECK-NEXT: std::vector _d_V_t0.pushforward; +// CHECK-NEXT: std::vector V_t0.value; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V, 0, &_d_V, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V, 2, &_d_V, 0); +// CHECK-NEXT: double &_t3 = _t1.value; +// CHECK-NEXT: double &_t4 = _t2.value; +// CHECK-NEXT: return _t1.pushforward * _t4 + _t3 * _t2.pushforward; +// CHECK-NEXT: } + +int main() { + INIT_DIFFERENTIATE(fnVec1, "u"); + INIT_DIFFERENTIATE(fnVec2, "u"); + INIT_DIFFERENTIATE(fnVec3, "u"); + INIT_DIFFERENTIATE(fnVec4, "u"); + + TEST_DIFFERENTIATE(fnVec1, 3, 5); // CHECK-EXEC: {10.00} + TEST_DIFFERENTIATE(fnVec2, 3, 5); // CHECK-EXEC: {5.00} + TEST_DIFFERENTIATE(fnVec3, 3, 5); // CHECK-EXEC: {2.00} + TEST_DIFFERENTIATE(fnVec4, 3, 5); // CHECK-EXEC: {30.00} +} \ No newline at end of file diff --git a/test/ForwardMode/UserDefinedTypes.C b/test/ForwardMode/UserDefinedTypes.C index b590a7571..7d5f77b18 100644 --- a/test/ForwardMode/UserDefinedTypes.C +++ b/test/ForwardMode/UserDefinedTypes.C @@ -881,10 +881,10 @@ double fn14(double i, double j) { // CHECK-NEXT: vectorD _d_v; // CHECK-NEXT: vectorD v; // CHECK-NEXT: clad::custom_derivatives::class_functions::resize_pushforward(&v, 5, 0, &_d_v, 0, 0); -// CHECK-NEXT: clad::ValueAndPushforward<{{.*}}, {{.*}}> _t0 = v.operator_subscript_pushforward(0, &_d_v, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}, {{.*}}> _t0 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&v, 0, &_d_v, 0); // CHECK-NEXT: _t0.pushforward = 0 * i + 9 * _d_i; // CHECK-NEXT: _t0.value = 9 * i; -// CHECK-NEXT: clad::ValueAndPushforward<{{.*}}, {{.*}}> _t1 = v.operator_subscript_pushforward(1, &_d_v, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}, {{.*}}> _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&v, 1, &_d_v, 0); // CHECK-NEXT: _t1.pushforward = 0 * i + 11 * _d_i; // CHECK-NEXT: _t1.value = 11 * i; // CHECK-NEXT: clad::ValueAndPushforward _t2 = begin_pushforward(v, _d_v);