Skip to content

Snapshot of array arguments of primal call expression should be used in the derived call expression in Reverse Mode AD #429

@parth-07

Description

@parth-07

Differentiation of call expressions that takes array arguments give incorrect derivatives if the said array is modified between the
primal call expression and the derived call expression.

A reproducible example:

double some_fn(double i, double *arr, int n) {
  return arr[0]*i;
}

double fn(double i, double *arr, int n) {
  double res = 0;
  arr[0] = 1;
  res = some_fn(i, arr, n);
  arr[0] = 5;
  return res;
}

int main() {
  double arr[5] = {1, 2, 3, 4, 5};
  double d_arr[5] = {};

  auto d_fn = clad::gradient(fn);
  clad::array_ref<double> d_arr_ref(d_arr, 5);
  double d_i = 0, d_n = 0;
  d_fn.execute(3, arr, 5, &d_i, d_arr_ref, &d_n);
  std::cout<<"d_i: "<<d_i<<"\n";
}

Expected result:
d_i: 1
Actual result
d_i: 5

Root cause of the error and solution
Derived call expression needs the same values of arguments that were passed to primal call expression. We do clone
all the arguments in the forward pass and use the cloned values in the reverse pass. This works well for ordinary non-pointer variables. But in C++, cloning pointers leads to shallow cloning -- the value pointed by the pointer isn't cloned. Therefore, if the array changes at any point between the primal call expression and the derived call expression, then even though derived call expression is using the cloned pointer, it will still use the modified array values. Therefore, we should deep clone pointers/arrays in the forward pass and use the cloned value in the derived call expression.

Deep cloning an array can be expensive and might be unnecessary if the array is not being modified anywhere between the primal call expression and derived call expression. Data flow and active variable analysis can help us to avoid creating unnecessary clone in those cases and generate optimal derived functions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions