Skip to content

Commit 311b514

Browse files
PetroZarytskyivgvassilev
authored andcommitted
Don't allow non-differentiable non-const array parameters in forward mode
All parameters w.r.t. which we don't differentiate still require adjoints. Since the user doesn't provide it, we have to construct it ourselves. However, if the parameter is a pointer array (e.g., `double*`), we don't know its size and, therefore, cannot initialize the adjoint. If the array is const, it's guaranteed that it doesn't require an adjoint. If it's a const array (e.g., `double[3]`), we can initialize it. In other cases, we have to produce an error. We've been doing the same in the reverse mode for a while. Fixes #1035.
1 parent b245235 commit 311b514

6 files changed

Lines changed: 45 additions & 13 deletions

File tree

benchmark/BenchmarkedFunctions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ inline double product(double p[], int n) {
4141
}
4242

4343
///\returns the weighted sum of the elements in \p
44-
inline double weightedSum(double p[], double w[], int n) {
44+
inline double weightedSum(const double p[], const double w[], int n) {
4545
double sum = 0;
4646
for (int i = 0; i < n; i++)
4747
sum += p[i] * w[i];

demos/Arrays.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// Necessary for clad to work include
1515
#include "clad/Differentiator/Differentiator.h"
1616

17-
double weighted_avg(double* arr, const double* weights) {
17+
double weighted_avg(const double* arr, const double* weights) {
1818
return (arr[0] * weights[0] + arr[1] * weights[1] + arr[2] * weights[2]) / 3;
1919
}
2020

lib/Differentiator/BaseForwardModeVisitor.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,16 +317,34 @@ void BaseForwardModeVisitor::GenerateSeeds(const clang::FunctionDecl* dFD) {
317317
// relation among them, thus it is safe (correct) to use the corresponding
318318
// non-reference type for creating the derivatives.
319319
QualType dParamType = param->getType().getNonReferenceType();
320-
// We do not create derived variable for array/pointer parameters.
321-
if (!utils::IsDifferentiableType(dParamType) ||
322-
utils::isArrayOrPointerType(dParamType))
323-
continue;
324320
Expr* dParam = nullptr;
325-
if (dParamType->isRealType()) {
321+
if (!utils::IsDifferentiableType(dParamType))
322+
continue;
323+
// If the parameter type decayed const array type, we can still initialize
324+
// it. Therefore, we don't have to produce the error.
325+
if (const auto* DT = dyn_cast<DecayedType>(dParamType))
326+
dParamType = DT->getOriginalType();
327+
if (dParamType->isConstantArrayType()) {
328+
if (param == m_IndependentVar)
329+
continue;
330+
dParam = getZeroInit(dParamType);
331+
} else if (dParamType->isRealType()) {
326332
// If param is independent variable, its derivative is 1, otherwise 0.
327333
int dValue = (param == m_IndependentVar);
328334
dParam =
329335
ConstantFolder::synthesizeLiteral(m_Context.IntTy, m_Context, dValue);
336+
} else if (utils::isArrayOrPointerType(dParamType)) {
337+
// We cannot initialize a pointer array adjoint ourselves.
338+
// Produce an error.
339+
if (param != m_IndependentVar &&
340+
!utils::GetValueType(dParamType).isConstQualified()) {
341+
// FIXME: Use diagnostics style as in #1596
342+
diag(DiagnosticsEngine::Error, param->getLocation(),
343+
"dependent non-const pointer and array parameters "
344+
"are not supported; differentiate w.r.t. '%0' or mark it const",
345+
{param->getNameAsString()});
346+
}
347+
continue;
330348
}
331349
// For each function arg, create a variable _d_arg to store derivatives
332350
// of potential reassignments, e.g.:

test/Arrays/ArrayErrorsForwardMode.C

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,24 @@ double addDoubleArr(Double *arr) { // expected-error {{attempted differentiation
1414
return arr[0].n + arr[1].n + arr[2].n + arr[3].n;
1515
}
1616

17+
double nonConstNonDiffArr(double x, double *y) { // expected-error {{dependent non-const pointer and array parameters are not supported; differentiate w.r.t. 'y' or mark it const}}
18+
y[0] = 3; // expected-warning {{derivative of an assignment attempts to assign to unassignable expr, assignment ignored}}
19+
return x * y[0];
20+
}
21+
22+
double nonConstNonDiffConstArr(double x, double y[3]) {
23+
y[0] = 3;
24+
return x * y[0];
25+
}
26+
1727
int main() {
1828
clad::differentiate(addArr, "arr[1:2]"); // expected-error {{Forward mode differentiation w.r.t. several parameters at once is not supported, call 'clad::differentiate' for each parameter separately}}
1929

2030
clad::differentiate(addArr, "arr[2:1]"); // expected-error {{Range specified in 'arr[2:1]' is in incorrect format}}
2131

2232
clad::differentiate(addDoubleArr, "arr[1]");
33+
34+
clad::differentiate(nonConstNonDiffArr, "x");
35+
36+
clad::differentiate(nonConstNonDiffConstArr, "x");
2337
}

test/Arrays/ArrayInputsForwardMode.C

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ double addArr(const double *arr, int n) {
4141
//CHECK-NEXT: return _d_ret;
4242
//CHECK-NEXT: }
4343

44-
double numMultIndex(double* arr, size_t n, double x) {
44+
double numMultIndex(const double* arr, size_t n, double x) {
4545
// compute x * i, where arr[i] = x
4646
// if x is not present in arr, return 0
4747
bool flag = false;
@@ -56,7 +56,7 @@ double numMultIndex(double* arr, size_t n, double x) {
5656
return flag ? idx * x : 0;
5757
}
5858

59-
// CHECK: double numMultIndex_darg2(double *arr, size_t n, double x) {
59+
// CHECK: double numMultIndex_darg2(const double *arr, size_t n, double x) {
6060
// CHECK-NEXT: size_t _d_n = 0;
6161
// CHECK-NEXT: double _d_x = 1;
6262
// CHECK-NEXT: bool _d_flag = 0;

test/FirstDerivative/CallArguments.C

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,24 +132,24 @@ float f_const_args_func_8(const float x, float y) {
132132
// CHECK-NEXT: return _t0.pushforward + _t1.pushforward - _d_y;
133133
// CHECK-NEXT: }
134134

135-
float f_literal_helper(float x, char ch, float* p, float* q) {
135+
float f_literal_helper(float x, char ch, const float* p, float* q) {
136136
if (ch == 'a')
137137
return x * x;
138138
return -x * x;
139139
}
140140

141-
// CHECK: clad::ValueAndPushforward<float, float> f_literal_helper_pushforward(float x, char ch, float *p, float *q, float _d_x, char _d_ch, float *_d_p, float *_d_q) {
141+
// CHECK: clad::ValueAndPushforward<float, float> f_literal_helper_pushforward(float x, char ch, const float *p, float *q, float _d_x, char _d_ch, const float *_d_p, float *_d_q) {
142142
// CHECK-NEXT: if (ch == 'a')
143143
// CHECK-NEXT: return {x * x, _d_x * x + x * _d_x};
144144
// CHECK-NEXT: return {-x * x, -_d_x * x + -x * _d_x};
145145
// CHECK-NEXT: }
146146

147-
float f_literal_args_func(float x, float y, float *z) {
147+
float f_literal_args_func(float x, float y, const float *z) {
148148
printf("hello world ");
149149
return x * f_literal_helper(x, 'a', z, nullptr);
150150
} // x ^ 3
151151

152-
// CHECK: float f_literal_args_func_darg0(float x, float y, float *z) {
152+
// CHECK: float f_literal_args_func_darg0(float x, float y, const float *z) {
153153
// CHECK-NEXT: float _d_x = 1;
154154
// CHECK-NEXT: float _d_y = 0;
155155
// CHECK-NEXT: printf("hello world ");

0 commit comments

Comments
 (0)