vgvassilev / clad

clad -- automatic differentiation for C/C++
GNU Lesser General Public License v3.0
288 stars 122 forks source link

Calling templated functions inside function doesn't work in backwards mode #1125

Open guitargeek opened 4 days ago

guitargeek commented 4 days ago

Reproducer as a ROOT macro:

#include <Math/CladDerivator.h>

template <class T>
inline double foo(double *params) { return params[0]; }
inline double bar(double *params) { return params[0]; }

double wrapper(double *params)
{
   return foo<int>(params); // doesn't work!
   // return bar(params); // works!
}

#pragma clad ON
void gradient_request() { clad::gradient(wrapper, "params"); }
#pragma clad OFF

// To run as a ROOT macro
void macro()
{
   std::vector<double> parametersVec{.5};
   std::vector<double> gradientVec(parametersVec.size());

   auto func = [&](std::span<double> params) { return wrapper(params.data()); };
   auto grad = [&](std::span<double> params, std::span<double> out) {
      return wrapper_grad(parametersVec.data(), out.data());
   };

   grad(parametersVec, gradientVec);

   auto numDiff = [&](int i) {
      const double eps = 1e-6;
      std::vector<double> p{parametersVec};
      p[i] = parametersVec[i] - eps;
      double funcValDown = func(p);
      p[i] = parametersVec[i] + eps;
      double funcValUp = func(p);
      return (funcValUp - funcValDown) / (2 * eps);
   };

   for (std::size_t i = 0; i < parametersVec.size(); ++i) {
      std::cout << i << ":" << std::endl;
      std::cout << "  numr : " << numDiff(i) << std::endl;
      std::cout << "  clad : " << gradientVec[i] << std::endl;
   }
}

Output:

Processing macro.C...
0:
  numr : 1
  clad : 0

I could work around this limitation in RooFit by avoiding template functions in the generated code, but at some point it becomes unavoidable if we want to use functors as parameters.

This follows up on the following issue:

If this works, this would finally enable us to use more accurate routines for numeric integration that take function parameters, closing the gap between RooFit AD and regular RooFit.

parth-07 commented 2 days ago

I am unable to reproduce this issue. I tried to reproduce it outside the ROOT environment.

#include "clad/Differentiator/Differentiator.h"
#include <iostream>
#define show(x) std::cout<< #x << ": " << x << "\n";

template <class T>
inline double foo(double *params) { return params[0]; }
inline double bar(double *params) { return params[0]; }

double wrapper(double *params)
{
   return foo<int>(params); // doesn't work!
  //  return bar(params); // works!
}

int main() {
  auto wrapper_grad = clad::gradient(wrapper, "params");
  std::vector<double> params(1, 0), d_params(1, 0);
  wrapper_grad.execute(params.data(), d_params.data());
  show(d_params[0]);
}

This outputs: d_params[0]: 1 and generates the correct derivative functions.

Can you please tell which Clad version are you using?

guitargeek commented 2 days ago

Oh that is surprising. I'm using ROOT master with Clad master, by editing this line here and re-building ROOT from scratch: https://github.com/root-project/root/blob/master/interpreter/cling/tools/plugins/clad/CMakeLists.txt#L86

I also converted your smaller reproducer to a ROOT macro, by replacing the int main() signature with void macro() and and renaming the file to macro.C. I still get the wrong result: d_params[0]: 0.

You have a ROOT build at hand to try this out too? I'll also try it with building Clad standalone.

parth-07 commented 2 days ago

This is surprising indeed.

You have a ROOT build at hand to try this out too?

Unfortunately, I do not have a root build handy. I will try building ROOT and reproducing the issue..

parth-07 commented 2 days ago

I also converted your smaller reproducer to a ROOT macro, by replacing the int main() signature with void macro() and and renaming the file to macro.C. I still get the wrong result: d_params[0]: 1.

I am assuming that d_params[0]: 1 is a mistype here and the code actually outputs d_params[0]: 0 when used with ROOT macro. Please correct me if I am wrong.

guitargeek commented 2 days ago

Yes, that was a typo, sorry! I copy pasted from your post, instead of from my terminal output!

vgvassilev commented 2 days ago

@parth-07, ROOT has not moved to clang-repl and still does not really use the delay-until-end-of-tu logic. Maybe that’s the difference.

@guitargeek what error do you get?