From 17d8b9ff94d122967a04f9f6c99f53f8654cbdc5 Mon Sep 17 00:00:00 2001 From: Mihail Mihov Date: Wed, 31 Jul 2024 17:38:01 +0300 Subject: [PATCH] CladFunction `constexpr` --- include/clad/Differentiator/Differentiator.h | 211 ++++++++++++------- include/clad/Differentiator/FunctionTraits.h | 23 +- 2 files changed, 145 insertions(+), 89 deletions(-) diff --git a/include/clad/Differentiator/Differentiator.h b/include/clad/Differentiator/Differentiator.h index cca1cd5cf..6d65cc73a 100644 --- a/include/clad/Differentiator/Differentiator.h +++ b/include/clad/Differentiator/Differentiator.h @@ -120,7 +120,7 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { template ::type = true> - CUDA_HOST_DEVICE return_type_t + constexpr CUDA_HOST_DEVICE return_type_t execute_with_default_args(list, F f, list, CUDA_ARGS CUDA_REST_ARGS Args&&... args) { #if defined(__CUDACC__) && !defined(__CUDA_ARCH__) @@ -148,7 +148,7 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { template ::type = true> - return_type_t + constexpr return_type_t execute_with_default_args(list, F f, list, CUDA_ARGS CUDA_REST_ARGS Args&&... args) { #if defined(__CUDACC__) && !defined(__CUDA_ARCH__) @@ -167,10 +167,10 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { template ::type = true> - CUDA_HOST_DEVICE auto + constexpr CUDA_HOST_DEVICE auto execute_with_default_args(list, ReturnType C::*f, Obj&& obj, - list, Args&&... args) - -> return_type_t { + list, + Args&&... args) -> return_type_t { return (static_cast(obj).*f)((fArgTypes)(args)..., static_cast(nullptr)...); } @@ -178,9 +178,10 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { template ::type = true> - auto execute_with_default_args(list, ReturnType C::*f, Obj&& obj, - list, Args&&... args) - -> return_type_t { + constexpr auto + execute_with_default_args(list, ReturnType C::*f, Obj&& obj, + list, + Args&&... args) -> return_type_t { return (static_cast(obj).*f)(static_cast(args)...); } @@ -192,7 +193,7 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { /// Default value of `Functor` here is temporary, and should be removed /// once all clad differentiation functions support differentiating functors. template , - bool EnablePadding = false> + bool HasCode = true, bool EnablePadding = false> class CladFunction { public: using CladFunctionType = F; @@ -200,7 +201,7 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { private: CladFunctionType m_Function; - char* m_Code; + const char* m_Code; FunctorType *m_Functor = nullptr; bool m_CUDAkernel = false; @@ -208,38 +209,55 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code, FunctorType* functor = nullptr, bool CUDAkernel = false) + requires(HasCode) : m_Function(f), m_Functor(functor), m_CUDAkernel(CUDAkernel) { #ifndef __CLAD_SO_LOADED static_assert(false, "clad doesn't appear to be loaded; make sure that " "you pass clad.so to clang."); #endif - size_t length = GetLength(code); char* temp = (char*)malloc(length + 1); m_Code = temp; while ((*temp++ = *code++)) ; } + + constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f, + FunctorType* functor = nullptr, + bool CUDAkernel = false) + requires(!HasCode) + : m_Function(f), m_Code(""), + m_Functor(functor), m_CUDAkernel(CUDAkernel) { +#ifndef __CLAD_SO_LOADED + static_assert(false, "clad doesn't appear to be loaded; make sure that " + "you pass clad.so to clang."); +#endif + } + /// Constructor overload for initializing `m_Functor` when functor /// is passed by reference. CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code, FunctorType& functor) : CladFunction(f, code, &functor) {}; + constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f, + FunctorType& functor) + : CladFunction(f, &functor) {}; + // Intentionally leak m_Code, otherwise we have to link against c++ runtime, // i.e -lstdc++. //~CladFunction() { /*free(m_Code);*/ } - CladFunctionType getFunctionPtr() { return m_Function; } + constexpr CladFunctionType getFunctionPtr() const { return m_Function; } template - typename std::enable_if::value, - return_type_t>::type - execute(Args&&... args) CUDA_HOST_DEVICE { - if (!m_Function) { - printf("CladFunction is invalid\n"); + typename std::enable_if< + !std::is_same::value, + return_type_t>::type constexpr execute(Args&&... args) + CUDA_HOST_DEVICE const { + if (!m_Function) return static_cast>(return_type_t()); - } if (m_CUDAkernel) { printf("Use execute_kernel() for global CUDA kernels\n"); return static_cast>(return_type_t()); @@ -278,19 +296,20 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { /// Error handling is handled in the clad side using clang diagnostics /// subsystem. template - typename std::enable_if::value, - return_type_t>::type - execute(Args&&... args) CUDA_HOST_DEVICE { + typename std::enable_if< + std::is_same::value, + return_type_t>::type constexpr execute(Args&&... args) + CUDA_HOST_DEVICE const { return static_cast>(0); } /// Return the string representation for the generated derivative. - const char* getCode() const { + constexpr const char* getCode() const { if (m_Code) return m_Code; - else - return ""; + return ""; } + void dump() const { printf("The code is: \n%s\n", getCode()); } @@ -315,8 +334,8 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { private: /// Helper function for executing non-member derived functions. template - CUDA_HOST_DEVICE return_type_t - execute_helper(Fn f, CUDA_ARGS Args&&... args) { + constexpr CUDA_HOST_DEVICE return_type_t + execute_helper(Fn f, CUDA_ARGS Args&&... args) const { // `static_cast` is required here for perfect forwarding. #if defined(__CUDACC__) if constexpr (sizeof...(Args) >= 2) { @@ -354,27 +373,25 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { /// Helper functions for executing member derived functions. /// If user have passed object explicitly, then this specialization will /// be used and derived function will be called through the passed object. - template < - class ReturnType, - class C, - class Obj, - class = typename std::enable_if< - std::is_same::type, C>::value>::type, - class... Args> - return_type_t - execute_helper(ReturnType C::*f, Obj&& obj, Args&&... args) { + template ::type, C>::value>::type, + class... Args> + constexpr return_type_t + execute_helper(ReturnType C::*f, Obj&& obj, Args&&... args) const { // `static_cast` is required here for perfect forwarding. - return execute_with_default_args( - DropArgs_t{}, f, static_cast(obj), - TakeNFirstArgs_t{}, - static_cast(args)...); + return execute_with_default_args( + DropArgs_t{}, f, + static_cast(obj), + TakeNFirstArgs_t{}, + static_cast(args)...); } /// If user have not passed object explicitly, then this specialization /// will be used and derived function will be called through the object /// saved in `CladFunction`. template - return_type_t execute_helper(ReturnType C::*f, - Args&&... args) { + constexpr return_type_t + execute_helper(ReturnType C::*f, Args&&... args) const { // `static_cast` is required here for perfect forwarding. return execute_with_default_args( DropArgs_t{}, f, *m_Functor, @@ -383,6 +400,19 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { } }; + /*template */ + /*constexpr auto create_lambda_with_args(list) {*/ + /* return [](ArgTys...) -> RetTy { return RetTy{}; };*/ + /*}*/ + /**/ + /*template */ + /*constexpr auto create_default_derived_lambda() {*/ + /* using RetTy = typename function_traits::return_type;*/ + /* using ArgTys = typename function_traits::argument_types;*/ + /* auto lambda = create_lambda_with_args(ArgTys{});*/ + /* return lambda;*/ + /*}*/ + // This is the function which will be instantiated with the concrete arguments // After that our AD library will have all the needed information. For eg: // which is the differentiated function, which is the argument with respect @@ -400,20 +430,49 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { /// function to differentiate \param[in] args independent parameter /// information \returns `CladFunction` object to access the corresponding /// derived function. - template , typename = typename std::enable_if< !clad::HasOption(GetBitmaskedOpts(BitMaskedOpts...), opts::vector_mode) && - !std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("D"))) - differentiate(F fn, ArgSpec args = "", - DerivedFnType derivedFn = static_cast(nullptr), - const char* code = "") { - return CladFunction>(derivedFn, - code); + !std::is_class>::value && + HasCode>::type> + CladFunction, HasCode> __attribute__(( + annotate("D"))) constexpr differentiate(F fn, const char* code, + ArgSpec args = "", + DerivedFnType derivedFn = + static_cast( + nullptr)) { + return CladFunction, HasCode>( + derivedFn, code); + } + + template , + typename = typename std::enable_if< + !clad::HasOption(GetBitmaskedOpts(BitMaskedOpts...), + opts::vector_mode) && + !std::is_class>::value && + !HasCode>::type> + CladFunction, HasCode> __attribute__(( + annotate("D"))) constexpr differentiate(F fn, ArgSpec args = "", + DerivedFnType derivedFn = + static_cast( + [](double a, double b) { + return 42.; + })) { + return CladFunction, HasCode>( + derivedFn); + } + + template + return_type_t constexpr differentiate_and_execute(F fn, ArgSpec args = "", + Args&&... args_) { + return differentiate(fn, args).execute( + std::forward(args_)...); } /// Specialization for differentiating functors. @@ -426,13 +485,13 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { !clad::HasOption(GetBitmaskedOpts(BitMaskedOpts...), opts::vector_mode) && std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("D"))) + constexpr CladFunction< + DerivedFnType, ExtractFunctorTraits_t> __attribute__((annotate("D"))) differentiate(F&& f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { - return CladFunction>(derivedFn, - code, f); + return CladFunction>(derivedFn, + code, f); } /// Generates function which computes derivative of `fn` argument w.r.t @@ -449,8 +508,8 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { clad::HasOption(GetBitmaskedOpts(BitMaskedOpts...), opts::vector_mode) && !std::is_class>::value>::type> - CladFunction, true> __attribute__(( - annotate("D"))) + constexpr CladFunction, + true> __attribute__((annotate("D"))) differentiate(F fn, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { @@ -469,8 +528,8 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = GradientDerivedFnTraits_t, typename = typename std::enable_if< !std::is_class>::value>::type> - CladFunction, true> __attribute__(( - annotate("G"))) CUDA_HOST_DEVICE + constexpr CladFunction, + true> __attribute__((annotate("G"))) CUDA_HOST_DEVICE gradient(F f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "", bool CUDAkernel = false) { @@ -485,13 +544,13 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = GradientDerivedFnTraits_t, typename = typename std::enable_if< std::is_class>::value>::type> - CladFunction, true> __attribute__(( - annotate("G"))) CUDA_HOST_DEVICE + constexpr CladFunction, + true> __attribute__((annotate("G"))) CUDA_HOST_DEVICE gradient(F&& f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { - return CladFunction, true>( - derivedFn /* will be replaced by gradient*/, code, f); + return CladFunction, true>( + derivedFn /* will be replaced by gradient*/, code, f); } /// Generates function which computes hessian matrix of the given function wrt @@ -505,8 +564,8 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = HessianDerivedFnTraits_t, typename = typename std::enable_if< !std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("H"))) + constexpr CladFunction< + DerivedFnType, ExtractFunctorTraits_t> __attribute__((annotate("H"))) hessian(F f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { @@ -521,13 +580,13 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = HessianDerivedFnTraits_t, typename = typename std::enable_if< std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("H"))) + constexpr CladFunction< + DerivedFnType, ExtractFunctorTraits_t> __attribute__((annotate("H"))) hessian(F&& f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { - return CladFunction>( - derivedFn /* will be replaced by hessian*/, code, f); + return CladFunction>( + derivedFn /* will be replaced by hessian*/, code, f); } /// Generates function which computes jacobian matrix of the given function @@ -541,8 +600,8 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = JacobianDerivedFnTraits_t, typename = typename std::enable_if< !std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("J"))) + constexpr CladFunction< + DerivedFnType, ExtractFunctorTraits_t> __attribute__((annotate("J"))) jacobian(F f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { @@ -557,18 +616,18 @@ CUDA_HOST_DEVICE T push(tape& to, ArgsT... val) { typename F, typename DerivedFnType = JacobianDerivedFnTraits_t, typename = typename std::enable_if< std::is_class>::value>::type> - CladFunction> __attribute__(( - annotate("J"))) + constexpr CladFunction< + DerivedFnType, ExtractFunctorTraits_t> __attribute__((annotate("J"))) jacobian(F&& f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { - return CladFunction>( - derivedFn /* will be replaced by Jacobian*/, code, f); + return CladFunction>( + derivedFn /* will be replaced by Jacobian*/, code, f); } template > - CladFunction __attribute__((annotate("E"))) + constexpr CladFunction __attribute__((annotate("E"))) estimate_error(F f, ArgSpec args = "", DerivedFnType derivedFn = static_cast(nullptr), const char* code = "") { diff --git a/include/clad/Differentiator/FunctionTraits.h b/include/clad/Differentiator/FunctionTraits.h index c15eeb270..bc568e51d 100644 --- a/include/clad/Differentiator/FunctionTraits.h +++ b/include/clad/Differentiator/FunctionTraits.h @@ -763,17 +763,15 @@ namespace clad { /// Specialization for free function pointer type template struct ExtractDerivedFnTraitsForwMode< - F*, - typename std::enable_if::value>::type> { + F*, typename std::enable_if::value>::type> { using type = remove_reference_and_pointer_t*; }; /// Specialization for member function pointer type template struct ExtractDerivedFnTraitsForwMode< - F, - typename std::enable_if< - std::is_member_function_pointer::value>::type> { + F, typename std::enable_if< + std::is_member_function_pointer::value>::type> { using type = typename std::decay::type; }; @@ -783,20 +781,19 @@ namespace clad { /// defines member typedef `type` as the type of `NoFunction*`. template struct ExtractDerivedFnTraitsForwMode< - F, - typename std::enable_if< - std::is_class>::value && - has_call_operator::value>::type> { + F, typename std::enable_if< + std::is_class>::value && + has_call_operator::value>::type> { using ClassType = typename std::decay>::type; using type = decltype(&ClassType::operator()); }; + template struct ExtractDerivedFnTraitsForwMode< - F, - typename std::enable_if< - std::is_class>::value && - !has_call_operator::value>::type> { + F, typename std::enable_if< + std::is_class>::value && + !has_call_operator::value>::type> { using type = NoFunction*; };