Giter Club home page Giter Club logo

Comments (5)

parth-07 avatar parth-07 commented on July 24, 2024

Hi @maxaehle

Thank you for opening the issue. The behaviour is expected and I am not sure if it's something that we would want to change in the near future. We should definitely report a proper warning/note if a global variable is used within a function being differentiated.

This behaviour is okay because most mathematical functions are pure functions and do not rely on global variables. It's also very difficult to obtain correct and consistent derivatives when global variables are involved because we cannot make any assumption on their initial values.

from clad.

vgvassilev avatar vgvassilev commented on July 24, 2024

Why we can't support that?

from clad.

parth-07 avatar parth-07 commented on July 24, 2024

Why we can't support that?

We can support it if we want to. However, it would be non-trivial and require effective usage of global tapes. For instance, please consider this example:

double global_var = 7;

double modify_global_var(double u) {
  global_var += u;
}

double fn(double u, double v) {
  global_var += v;
  modify_global_var(u);
  return global_var;
}

auto fn_grad = clad::gradient(fn);

To correctly differentiate the function fn, we need to create an adjoint variable (_d_global_var) for global_var. But the issue is that it is unclear where to store _d_global_var. We cannot store the adjoint _d_global_var in the scope of fn_grad because then the function calls inside fn_grad would not be able to access _d_global_var. We cannot create a global variable _d_global_var because global variables come with their own sets of issues. One of them is, what if two functions that use global_var are being differentiated. We will now need two global _d_global_var for correctness reasons, but that is not possible.

We can use global tapes/vectors to support the correct differentiation of global variables, but this will be a considerable effort. So far, we treat global variables as constants due to these reasons.

from clad.

maxaehle avatar maxaehle commented on July 24, 2024

Thanks @parth-07 for your detailed analysis!

Just as a side info for triaging, this issue does not have to do with the HEP simulation I'm currently working on; I was only wondering how source-transformation AD deals with global variables.

Concerning whether support for global variables is desirable, my perspective is that it's good to support as much as of the language standard as possible. I agree that "ideal" use cases might be assumed to be written in a fully functional style without a global state. However, when adding AD functionality to an existing code written without AD in mind, global variables may easily be present.

Tapenade handles global variables, transforming

double g;

double f(double u){
  g = u;
  return g*g;
}

to

/*        Generated by TAPENADE     (INRIA, Ecuador team)
    Tapenade 3.16 (bugfix_servletAD) -  4 Jan 2024 17:44
*/
#include <adStack.h>
double g;
double gb;

/*
  Differentiation of f in reverse (adjoint) mode:
   gradient     of useful results: f
   with respect to varying inputs: u
   RW status of diff variables: g:(loc) f:in-killed u:out
*/
void f_b(double u, double *ub, double fb) {
    g = u;
    f = g*g;
    gb = 2*g*fb;
    *ub = gb;
}

so they introduce a global adjoint variable. I know to little about Clang/LLVM and Clad to see how complex it would be to do this in Clad.

So far I don't understand the issue that you think could arise when a global variable is accessed from several places. Local variables can be accessed multiple times (from within their scope) without problems. Does it help if we assume that only a single Clad evaluation like df.execute(...) is running at a time (i.e. no multi-threading or nested Clad functions, don't know if such constructs are supported anyway)? And zero the adjoints of all globals in between any two Clad evaluations (if they're not automatically zeroed by the reverse evaluation)?

from clad.

parth-07 avatar parth-07 commented on July 24, 2024

Hi @maxaehle

Thank you for the detailed analysis!

so they introduce a global adjoint variable.

So tapenade uses global adjoint variables. I don't think it's a good solution. Using globals, in general, has issues. Using global adjoints has more issues on top of the general global issues. From the top of my head, there are two main issues:

  1. Clad is also designed to be indirectly used. By indirect use, I mean users may not want to use Clad directly as a clang plugin. Instead, to use clad-generated derivatives in their projects, users can can separately use Clad to generate derivative codes, then copy the generated derivatives to their project source code and include necessary Clad header files. When used this way, users are not using Clad directly. As a side-effect, Clad cannot insert global adjoints. This situation is more complex in C++ than C because in C++, we can have deeply nested global / static variables as well. For large projects, user may himself not be aware of static / global variables and thus it would be difficut for users to manually add the adjoint variables in the project source code at the right places.

  2. If we use the same global adjoint variable in different adjoint functions, then those adjoint functions will have undefined behavior if they are run concurrently.

Does it help if we assume that only a single Clad evaluation like df.execute(...) is running at a time

We cannot assume this and we should not.

(i.e. no multi-threading or nested Clad functions, don't know if such constructs are supported anyway)?

Running adjoint functions parallelly works and needs no support from Clad per se. For example:

auto d_fn1 = clad::gradient(fn1);
auto d_fn2 = clad::gradient(fn2);

// Execute d_fn1.execute(...) and d_fn2.execute(...) parallelly.

In the above example, Clad creates the adjoint functions and stores the function address in the objects d_fn1 and d_fn2. Clad does not control or care about how users use these adjoint functions.


This all being said, thank you for opening the issue. We need to decide if we want to support global and static variables. There are ways to support them without the cons of using global adjoint variables. If we decide not to support them, then we should give a proper warning to users if the code being differentiated depends upon global / static variables.

Please let me know if you have any comments / questions.

from clad.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.