Skip to content

Commit c18a748

Browse files
committed
Yield a zero derivative when a forward-mode call base has no tangent.
A member/operator call whose base object does not depend on the differentiation variable has no forward tangent, and so no pushforward to call. VisitCallExpr took the address of that tangent regardless: where it was void this failed with "cannot take the address of an rvalue of type 'void'", and otherwise a null argument was passed on to the pushforward. Return a zero derivative instead, since such a call contributes nothing to the directional derivative. Type it after the call's own result rather than int, so it composes with a caller expecting the callee's type.
1 parent d786da5 commit c18a748

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

lib/Differentiator/BaseForwardModeVisitor.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,12 @@ StmtDiff BaseForwardModeVisitor::VisitCallExpr(const CallExpr* CE) {
10761076
baseOriginalE = OCE->getArg(0);
10771077
baseDiff = Visit(baseOriginalE);
10781078
Expr* baseDerivative = baseDiff.getExpr_dx();
1079+
// A base that does not depend on the differentiation variable has no
1080+
// tangent, hence no pushforward to call, and contributes nothing to the
1081+
// directional derivative.
1082+
if (!baseDerivative || baseDerivative->getType()->isVoidType())
1083+
return StmtDiff(Clone(CE),
1084+
getZeroInit(CE->getType().getNonReferenceType()));
10791085
if (!baseDerivative->getType()->isPointerType())
10801086
baseDerivative = BuildOp(UnaryOperatorKind::UO_AddrOf, baseDerivative);
10811087
diffArgs.push_back(baseDerivative);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %cladclang %s -I%S/../../include -oNonActiveCallBase.out | %filecheck %s
2+
// RUN: ./NonActiveCallBase.out | %filecheck_exec %s
3+
4+
// Regression test: forward mode over a member/operator call whose base object
5+
// has no tangent, i.e. does not depend on the differentiation variable. Such a
6+
// base has no pushforward to call; clad used to take the address of its absent
7+
// tangent and fail with "cannot take the address of an rvalue of type 'void'".
8+
9+
#include "clad/Differentiator/Differentiator.h"
10+
11+
#include <cstdio>
12+
13+
struct Arr {
14+
double d[3];
15+
double operator()(int i) const { return d[i]; }
16+
};
17+
18+
namespace clad {
19+
namespace custom_derivatives {
20+
namespace class_functions {
21+
clad::ValueAndPushforward<double, double>
22+
operator_call_pushforward(const Arr* a, int i, const Arr* d_a, int /*d_i*/) {
23+
return {(*a)(i), (*d_a)(i)};
24+
}
25+
} // namespace class_functions
26+
} // namespace custom_derivatives
27+
} // namespace clad
28+
29+
// g is read but never differentiated with respect to, so it has no tangent.
30+
static Arr g{{2, 3, 4}};
31+
32+
double reads_nonactive(double x) {
33+
double s = 0;
34+
for (int i = 0; i < 3; ++i)
35+
s += x * g(i);
36+
return s;
37+
}
38+
39+
// CHECK: double reads_nonactive_darg0(double x) {
40+
41+
int main() {
42+
auto dx = clad::differentiate(reads_nonactive, "x");
43+
// d/dx sum_i x * g(i) = sum_i g(i) = 9
44+
printf("%.2f\n", dx.execute(1.5)); // CHECK-EXEC: 9.00
45+
}

0 commit comments

Comments
 (0)