Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for differentiation of immediate functions #1109

Merged
merged 11 commits into from
Oct 30, 2024

Conversation

MihailMihov
Copy link
Collaborator

No description provided.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

class CladFunction {
public:
using CladFunctionType = F;
using FunctorType = FunctorT;

private:
CladFunctionType m_Function;
char* m_Code;
const char* m_Code;
FunctorType *m_Functor = nullptr;
bool m_CUDAkernel = false;

public:
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code,
FunctorType* functor = nullptr,
bool CUDAkernel = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: expected ';' at end of declaration list [clang-diagnostic-error]

Suggested change
bool CUDAkernel = false)
bool CUDAkernel = false);

/// 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]

include/clad/Differentiator/Differentiator.h:203:

-     const char* m_Code;
+     const char* m_Code{};

return static_cast<return_type_t<F>>(0);
}

/// Return the string representation for the generated derivative.
const char* getCode() const {
constexpr const char* getCode() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'getCode' should be marked [[nodiscard]] [modernize-use-nodiscard]

Suggested change
constexpr const char* getCode() const {
[[nodiscard]] constexpr const char* getCode() const {

template <unsigned... BitMaskedOpts, typename ArgSpec = const char*,
typename F, typename... Args>
return_type_t<F> constexpr differentiate_and_execute(F fn, ArgSpec args = "",
Args&&... args_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for parameter 'args_' [readability-identifier-naming]

Suggested change
Args&&... args_) {
Args&&... args) {

include/clad/Differentiator/Differentiator.h:474:

-         std::forward<Args>(args_)...);
+         std::forward<Args>(args)...);

call->setArg(codeArgIdx, newArg);
if (derivedFnArgIdx != -1) {
// Add the "&" operator
auto newUnOp =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'auto newUnOp' can be declared as 'auto *newUnOp' [llvm-qualified-auto]

Suggested change
auto newUnOp =
auto *newUnOp =

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems a good suggestion.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

template <unsigned... BitMaskedOpts, typename ArgSpec = const char*,
typename F, typename... Args>
return_type_t<F> constexpr differentiate_and_execute(F fn, ArgSpec args = "",
Args&&... args_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: invalid case style for parameter 'args_' [readability-identifier-naming]

Suggested change
Args&&... args_) {
Args&&... args) {

include/clad/Differentiator/Differentiator.h:471:

-         std::forward<Args>(args_)...);
+         std::forward<Args>(args)...);

@MihailMihov MihailMihov force-pushed the constexpr-support branch 2 times, most recently from 3b4cbd3 to 26d7071 Compare October 19, 2024 13:52
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

CUDA_HOST_DEVICE std::size_t size() const { return m_size; }
CUDA_HOST_DEVICE PUREFUNC T* ptr() const { return m_arr; }
CUDA_HOST_DEVICE PUREFUNC T*& ptr_ref() { return m_arr; }
constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'size' should be marked [[nodiscard]] [modernize-use-nodiscard]

Suggested change
constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; }
[[nodiscard]] constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; }

bool CUDAkernel = false)
: 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 "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: static assertion failed: clad doesn't appear to be loaded; make sure that you pass clad.so to clang. [clang-diagnostic-error]

      static_assert(false, "clad doesn't appear to be loaded; make sure that "
      ^

/// Constructor overload for initializing `m_Functor` when functor
/// is passed by reference.
CUDA_HOST_DEVICE
CladFunction(CladFunctionType f, const char* code, FunctorType& functor)
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]

include/clad/Differentiator/Differentiator.h:203:

-     const char* m_Code;
+     const char* m_Code{};

: CladFunction(f, code, &functor) {};

constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]

    constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f,
    ^

@MihailMihov MihailMihov force-pushed the constexpr-support branch 3 times, most recently from d5954b9 to c213322 Compare October 21, 2024 20:23
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

@@ -65,6 +65,9 @@ struct DiffRequest {
/// A flag to enable TBR analysis during reverse-mode differentiation.
bool EnableTBRAnalysis = false;
bool EnableVariedAnalysis = false;
/// A flag specifying whether this differentiation is to be used
/// in immediate contexts.
bool ImmediateMode = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: member variable 'ImmediateMode' has public visibility [cppcoreguidelines-non-private-member-variables-in-classes]

  bool ImmediateMode = false;
       ^

@MihailMihov MihailMihov force-pushed the constexpr-support branch 3 times, most recently from 482d0dc to fa19625 Compare October 23, 2024 14:25
@MihailMihov MihailMihov marked this pull request as ready for review October 23, 2024 15:12
Copy link

codecov bot commented Oct 25, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 94.44%. Comparing base (21e4b4f) to head (27f1892).
Report is 11 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1109      +/-   ##
==========================================
+ Coverage   94.43%   94.44%   +0.01%     
==========================================
  Files          50       50              
  Lines        8751     8770      +19     
==========================================
+ Hits         8264     8283      +19     
  Misses        487      487              
Files with missing lines Coverage Δ
include/clad/Differentiator/CladConfig.h 100.00% <ø> (ø)
include/clad/Differentiator/DiffPlanner.h 68.18% <ø> (ø)
include/clad/Differentiator/DynamicGraph.h 84.21% <100.00%> (+0.28%) ⬆️
lib/Differentiator/DiffPlanner.cpp 98.64% <100.00%> (+0.04%) ⬆️
tools/ClangPlugin.cpp 96.13% <100.00%> (+0.05%) ⬆️
Files with missing lines Coverage Δ
include/clad/Differentiator/CladConfig.h 100.00% <ø> (ø)
include/clad/Differentiator/DiffPlanner.h 68.18% <ø> (ø)
include/clad/Differentiator/DynamicGraph.h 84.21% <100.00%> (+0.28%) ⬆️
lib/Differentiator/DiffPlanner.cpp 98.64% <100.00%> (+0.04%) ⬆️
tools/ClangPlugin.cpp 96.13% <100.00%> (+0.05%) ⬆️

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

/// Constructor overload for initializing `m_Functor` when functor
/// is passed by reference.
CUDA_HOST_DEVICE
CladFunction(CladFunctionType f, const char* code, FunctorType& functor)
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]

include/clad/Differentiator/Differentiator.h:202:

-     const char* m_Code;
+     const char* m_Code{};

include/clad/Differentiator/DynamicGraph.h Show resolved Hide resolved
Comment on lines 142 to 150
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) {
if (!request.ImmediateMode ||
(!request.Function->isConstexpr() &&
!request.Function->isImmediateFunction()))
continue;

m_DiffRequestGraph.setCurrentProcessingNode(request);
ProcessDiffRequest(request);
m_DiffRequestGraph.markCurrentNodeProcessed();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) {
if (!request.ImmediateMode ||
(!request.Function->isConstexpr() &&
!request.Function->isImmediateFunction()))
continue;
m_DiffRequestGraph.setCurrentProcessingNode(request);
ProcessDiffRequest(request);
m_DiffRequestGraph.markCurrentNodeProcessed();
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) {
if (request.ImmediateMode &&
(request.Function->isConstexpr() ||
request.Function->isImmediateFunction())) {
m_DiffRequestGraph.setCurrentProcessingNode(request);
ProcessDiffRequest(request);
m_DiffRequestGraph.markCurrentNodeProcessed();
}

I am not sure why we need to iterate over all of the requests, though...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this method HandleTopLevelDeclForClad we are given the new DeclGroupRef and then the DiffCollector adds the new nodes to the differentiation graph, but we don't really have a way to see what is new. I don't think it would be correct to move the processing part inside the collector as that's not it's purpose. Maybe there should be some way for the collector to give references to just the nodes that it added on it's last traversal? Should I do that or do you have something else in mind?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we shouldn't check if there is a FunctionDecl in DGR and ask it if it were an immediate function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DGR is the top level decl, so to find the just the immediate FunctionDecls we'd need to traverse all of it again, which doesn't sound like a great idea, when that's what DiffCollector already does, or am I misunderstanding?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes you are right. Please then just invert the condition as suggested and I think we might be good to go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll do that, but also I think that I'll have some documentation ready by tomorrow, so maybe I can include that in this PR.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We need a demo but that can be in a separate PR.

include/clad/Differentiator/Differentiator.h Outdated Show resolved Hide resolved
The derivatives that Clad generates are valid C++ code, which
could in theory be executed at compile-time (or in an immediate
context as the C++ standard calls it). When a function is differentiated
all attributes, such as `constexpr` and `consteval` are kept, but
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
all attributes, such as `constexpr` and `consteval` are kept, but
all specifiers, such as `constexpr` and `consteval` are kept, but

Usage of Clad's immediate mode
================================================

The following code snippet shows how one can request Clad to use the immediate mode for
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to break at 80 columns.

loops, etc. are not yet usable in an immediate context.

Both `constexpr` and `consteval` are supported as Clad doesn't actually rely on these
specific keywords for it's support, but instead uses clang's API to determine if the functions
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
specific keywords for it's support, but instead uses clang's API to determine if the functions
specific keywords for its support, but instead uses clang's API to determine if the functions

for (DiffRequest& request : m_DiffRequestGraph.getNodes()) {
if (request.ImmediateMode &&
(request.Function->isConstexpr() ||
request.Function->isImmediateFunction())) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance to write a test that hits here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like isImmediateFunction isn't being hit, because isConstexpr is checking the ConstexprSpecKind, which contains information for constexpr, consteval and constinit and even if I remove the isImmediateFunction check the tests still pass, but I believe that is only the case, because with the current implementation we are guaranteed that clang would've set the ConstexprSpecKind before we process the FunctionDecl. So would it be better to keep that assumption and remove the second check or keep it, even though it's not tested currently?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Removing the check makes sense.

Copy link
Owner

@vgvassilev vgvassilev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm!

@vgvassilev vgvassilev merged commit cddc21d into vgvassilev:master Oct 30, 2024
90 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants