-
Notifications
You must be signed in to change notification settings - Fork 196
Description
Motivation
Currently, the semantics of the C++ library is such that, from the user's perspective, computations always appear as if they are evaluated at the point of assignment:
Tensor<double> a, b;
a(0) = 1.0;
a(1) = 2.0;
b = a(i);
std::cout << b << std::endl; // Prints out 3
a(2) = 3.0;
std::cout << b << std::endl; // Still prints out 3, since `a` is modified after `b` has already been assignedThis makes sense when a user is relying on the lazy evaluation mechanism to trigger computations. However, this is not as intuitive when computations are triggered by explicitly invoking evaluate (or assemble and compute), since users tend to expect that the computation will actually be (re)performed whenever evaluate is invoked:
Tensor<double> a, b;
a(0) = 1.0;
a(1) = 2.0;
b = a(i);
b.evaluate();
std::cout << b << std::endl; // Prints out 3
a(2) = 3.0;
b.evaluate();
std::cout << b << std::endl; // Still prints out 3, since `a` is modified after `b` has already been assignedProposed Changes
This RFC proposes that users be able to specify whether computations should semantically happen at the point of assignment or whenever evaluate/assemble/compute is explicitly invoked. Specifically, the C++ library would expose a new global function:
void setEvaluateAtAssign(bool evalAtAssign);which the user may invoke before any computation is defined. If setEvaluateAtAssign is invoked with the argument evalAtAssign being true, then any computation that is defined afterwards will appear as if it is evaluated at the point of assignment (i.e., as in the examples above). On the other hand, if setEvaluateAtAssign is invoked with the argument evalAtAssign being false, then any computation that is defined afterwards will be (re)performed whenever evaluate/assemble/compute is explicitly invoked:
Tensor<double> a, b;
setEvaluateAtAssign(false);
a(0) = 1.0;
a(1) = 2.0;
b = a(i);
b.evaluate();
std::cout << b << std::endl; // Prints out 3
a(2) = 3.0;
b.evaluate();
std::cout << b << std::endl; // Prints out 6A prototype of this RFC is implemented in the eval_at_assign branch of the repo.
This feature can be particularly useful in, for instance, iterative applications where the structure of the output does not change (I believe this is the use case @stkaplan was interested in?):
Tensor<double> y, x, A;
y(i) = A(i,j) * x(j);
y.assemble();
while(...) {
// Make some modifications to x...
x.insert(...);
// Recompute y with modified x
y.compute();
}As pointed out in #408, this feature can also be useful for benchmarking.