Skip to content

RFC: Allow computations to semantically be evaluated when compute is invoked #478

Open
@stephenchouca

Description

@stephenchouca

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 assigned

This 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 assigned

Proposed 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 6

A 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions