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 Functions with Constant Array Type Parameters in Jacobian Mode #478

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Nirhar
Copy link
Contributor

@Nirhar Nirhar commented Aug 3, 2022

Now One must be able to find the Jacobian of functions with Constant Arrays in the parameter list.
For example, a function of the form:

void func(double arr[3], double x, double y, double* output){
	output[0]=arr[2]*x*y;
	.
	.
	.
	output[n-1]=arr[0]*arr[1]*arr[2];
}

will generate a Jacobian of size n x 5.

Corresponding tests for the same have been written.

Previously this feature was not implememnted because it was necessary to know the size
of an array to correctly determine the size of the output jacobian matrix. This has now
been achieved for constant arrays, as we know the array sizes for them and hence we can
precisely locate where each jacobian entry for a array parameter is located. This is
achieved with the help of m_IndependentVarsSize, which stores the number of actual parameters
that each function parameter corresponds to.

Prior to this commit a std::map<ValueDecl, Expr> was used to map variable declarations to
their corresponding jacobian expression, so that we can lookup the latter during the reversemode
computations quickly. While this is fine for primitive variables(because each variable will only
correspond to one jacobian expression), an array declaration will correspond to multiple jacobian
expressions, depending on which index of the array is being referred to. Since the ValueDecl can
only refer to the name of the array and not name+index, we must update the map to use the name of
the array + index as the key. This can be done easily by using a string representation of the
ValueDecl name with the index as a suffix as the key of the map. Hence variables like
m_ExprVariables, m_VectorOutputString have been introduced to map array declarations, indexed by
position in array to their corresponding jacobian expressions.

Closes #472

@Nirhar
Copy link
Contributor Author

Nirhar commented Aug 3, 2022

cc @vgvassilev @sudo-panda for review

@@ -38,6 +41,10 @@ namespace clad {
// several private/protected members of the visitor classes.
friend class ErrorEstimationHandler;
llvm::SmallVector<const clang::ValueDecl*, 16> m_IndependentVars;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would have been a cleaner solution if this was a vector of strings storing the identifier names. Similarly, all unordered maps should have been maps from identifier names as strings to jacobian expressions rather than ValueDecl to jacobian expressions

@codecov
Copy link

codecov bot commented Aug 3, 2022

Codecov Report

Merging #478 (b8d0fb6) into master (5505764) will increase coverage by 0.05%.
The diff coverage is 97.46%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #478      +/-   ##
==========================================
+ Coverage   92.72%   92.77%   +0.05%     
==========================================
  Files          35       35              
  Lines        5454     5496      +42     
==========================================
+ Hits         5057     5099      +42     
  Misses        397      397              
Impacted Files Coverage Δ
include/clad/Differentiator/ReverseModeVisitor.h 98.71% <ø> (ø)
lib/Differentiator/ReverseModeVisitor.cpp 96.07% <97.46%> (+0.16%) ⬆️
lib/Differentiator/ErrorEstimator.cpp 98.57% <0.00%> (-0.29%) ⬇️
lib/Differentiator/VisitorBase.cpp 97.65% <0.00%> (-0.03%) ⬇️
lib/Differentiator/ForwardModeVisitor.cpp 95.15% <0.00%> (-0.01%) ⬇️
tools/ClangPlugin.h 73.68% <0.00%> (ø)
include/clad/Differentiator/DerivativeBuilder.h 100.00% <0.00%> (ø)
lib/Differentiator/MultiplexExternalRMVSource.cpp 90.52% <0.00%> (ø)
...e/clad/Differentiator/MultiplexExternalRMVSource.h 100.00% <0.00%> (ø)
lib/Differentiator/CladUtils.cpp 97.62% <0.00%> (+<0.01%) ⬆️
... and 1 more
Impacted Files Coverage Δ
include/clad/Differentiator/ReverseModeVisitor.h 98.71% <ø> (ø)
lib/Differentiator/ReverseModeVisitor.cpp 96.07% <97.46%> (+0.16%) ⬆️
lib/Differentiator/ErrorEstimator.cpp 98.57% <0.00%> (-0.29%) ⬇️
lib/Differentiator/VisitorBase.cpp 97.65% <0.00%> (-0.03%) ⬇️
lib/Differentiator/ForwardModeVisitor.cpp 95.15% <0.00%> (-0.01%) ⬇️
tools/ClangPlugin.h 73.68% <0.00%> (ø)
include/clad/Differentiator/DerivativeBuilder.h 100.00% <0.00%> (ø)
lib/Differentiator/MultiplexExternalRMVSource.cpp 90.52% <0.00%> (ø)
...e/clad/Differentiator/MultiplexExternalRMVSource.h 100.00% <0.00%> (ø)
lib/Differentiator/CladUtils.cpp 97.62% <0.00%> (+<0.01%) ⬆️
... and 1 more

for (auto arg : args) {
// FIXME: fix when adding array inputs, now we are just skipping all
// array/pointer inputs (not treating them as independent variables).
if (utils::isArrayOrPointerType(arg->getType())) {
if (utils::isArrayOrPointerType(arg->getType())) { //is a array or pointer type parameter
if (arg->getName() == "p")
Copy link
Owner

Choose a reason for hiding this comment

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

Why we have this special case here? Wouldn't it removing it will help?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This special case was added by baidyanath in previous commits. He told me its something related to ROOT.

@@ -25,6 +25,9 @@ namespace clad {
class ExternalRMVSource;
class MultiplexExternalRMVSource;

using VectorOutputString =
std::vector<std::unordered_map<std::string, clang::Expr*>>;
Copy link
Owner

Choose a reason for hiding this comment

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

Can you update the commit message and explain the overall problem and your solution. I feel that we can avoid these vectors of maps of strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

have done so

@Nirhar Nirhar force-pushed the jacobian_array_error branch from 161d295 to fb567d0 Compare August 12, 2022 02:41
…bian Mode

Now One must be able to find the Jacobian of functions with Constant Arrays in the parameter list.
For example, a function of the form:
```cpp
void func(double arr[3], double x, double y, double* output){
	output[0]=arr[2]*x*y;
	.
	.
	.
	output[n-1]=arr[0]*arr[1]*arr[2];
}
```

will generate a Jacobian of size n x 5.

Corresponding tests for the same have been written.

Previously this feature was not implememnted because it was necessary to know the size
of an array to correctly determine the size of the output jacobian matrix. This has now
been achieved for constant arrays, as we know the array sizes for them and hence we can
precisely locate where each jacobian entry for a array parameter is located. This is
achieved with the help of m_IndependentVarsSize, which stores the number of actual parameters
that each function parameter corresponds to.

Prior to this commit a std::map<ValueDecl, Expr> was used to map variable declarations to
their corresponding jacobian expression, so that we can lookup the latter during the reversemode
computations quickly. While this is fine for primitive variables(because each variable will only
correspond to one jacobian expression), an array declaration will correspond to multiple jacobian
expressions, depending on which index of the array is being referred to. Since the ValueDecl can
only refer to the name of the array and not name+index, we must update the map to use the name of
the array + index as the key. This can be done easily by using a string representation of the
ValueDecl name with the index as a suffix as the key of the map. Hence variables like
m_ExprVariables, m_VectorOutputString have been introduced to map array declarations, indexed by
position in array to their corresponding jacobian expressions.

Closes vgvassilev#472
@Nirhar Nirhar force-pushed the jacobian_array_error branch from fb567d0 to b8d0fb6 Compare August 12, 2022 06:55
m_IndependentVarsSize.push_back(sizeOfArray);

for (int j = 0; j < sizeOfArray; j++) {
std::string name =
Copy link
Owner

Choose a reason for hiding this comment

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

I believe we can have a struct like:

struct IndexInfo {
  ValueDecl* Arg;
  unsigned Index;
};

Then we can replace the std::string in the maps and have efficient lookup based on the ValueDecl pointer and the index.

m_Sema.CreateBuiltinArraySubscriptExpr(m_Result, noLoc, i, noLoc)
.get();
m_Variables[arg] = result_at_i;
m_ExprVariables[arg->getNameAsString()] = result_at_i;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need m_ExprVariables? I do not see values being read from it anywhere.

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.

crash on jacobian with array inputs
4 participants