rjhogan / Adept-2

Combined array and automatic differentiation library in C++
http://www.met.reading.ac.uk/clouds/adept/
Apache License 2.0
164 stars 29 forks source link

Providing user-defined gradient to Adept #27

Closed Aerokaur closed 1 year ago

Aerokaur commented 1 year ago

Hello I am trying to perform automatic differentiation of a code using Adept. However, I am not able to provide a user-defined gradient to Adept. I have a function that I want to differentiate using Adept but I want to provide my gradient for a variable, say, x_a . I use x_a.set_gradient(xgrad) where xgrad is user-defined gradient value. It seems like I have to do stack.new_recording() to make adept use my gradient, but isn't stack.new_recording() clears all the previous gradients calculated so far using Adept ? Can you please let me know if I am doing this wrong ?

Thanks and Regards Sharan

rjhogan commented 1 year ago

Hi, you're certainly using it wrong, but it might be useful to know what you're trying to do mathematically. The simplest operation is shown on the Adept Wikipedia page, or on page 9 on the v2.1 user guide. You record your algorithm first, which could be (in a very simple scalar case) with

adouble x = 10.0; stack.new_recording(); adouble y = algorithm(x); // Some scalar algorithm computing y from x

At this point we have the differential statements relating y to x in memory. You can then compute the gradient dy_dx in either of two ways:

x.set_gradient(1.0); stack.forward(); // same as compute_tangent_linear() double dy_dx = y.get_gradient();

or

y.set_gradient(1.0); stack.reverse(); // same as compute_adjoint() double dy_dx = x.get_gradient()

If you put 2.0 instead of 1.0 in set_gradient, you will get the same result but scaled by 2.0. Perhaps this is what you mean by "user-defined gradient"?

Aerokaur commented 1 year ago

Ok.. here is an example code of what I am trying. I have y = x^2 - r^2 + 4.0, x= 4r^2 and I want to find the derivative of y w.r.t r . So my question is, can I use x_a.set_gradient(16.0) to overwrite the gradient of x_a provided by the adept ? Please let me know if I am doing something wrong here.


#include <iostream>
#include <iomanip>
#include <fstream>
#include "adept.h"
using adept::adouble;
adept::Stack stack;
adouble computeFy(adouble r_a)
{
    adouble x_a = 4.0 * r_a * r_a;
    return x_a;
}

adouble computeSens(adouble r_a)
{
        adouble x_a = computeFy(r_a);
        // stack.new_recording();  // set_gradient works only when I start new recording
        x_a.set_gradient(16.0); // set the gradient of x_a explicitly
        adouble y_a = x_a * x_a + 4.0 - r_a * r_a;
        return y_a;

}
int main(int argc, char *argv[])
{

        adouble r_a = 2.0;
        stack.new_recording();
        adouble y_a = computeSens(r_a);
        double dy_dr;
        r_a.set_gradient(1.0);
        stack.compute_tangent_linear();
        y_a.get_gradient(dy_dr);
        std::cout << "dy_dr " << dy_dr << std::endl;
        return 0;
}
Aerokaur commented 1 year ago

Just to give you context on why I need to provide a derivative myself: I am differentiating a C++ library that uses Lapack for some functions. I cannot use adept to differentiate the Lapack part of the code; however, I do have analytical expressions for those derivatives that I can provide.

rjhogan commented 1 year ago

You can do that by changing your code as follows:

// Ordinary function (not active) double computeFy(double r_a) { double x_a = 4.0 r_a r_a; return x_a; }

adouble computeSens(adouble r_a) { // Extract the double underlying r_a using value(r_a), in order to // pass it to an ordinary function double x_a_passive = computeFy(value(r_a)); adouble x_a; // Assign to x_a without adding derivative to the stack x_a.set_value(x_a_passive); // Set the gradient of x_a explicitly x_a.add_derivative_dependence(r_a, 16.0);

adouble y_a = x_a x_a + 4.0 - r_a r_a; return y_a; }

See sections 2.7 and 2.9 of the v2.1 user guide for more information.

Aerokaur commented 1 year ago

That seems to work. Thank you so much for your help.