Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove grad and gradx members from variable exprs #177

Merged
merged 12 commits into from
Aug 24, 2021

Conversation

jan-grimo
Copy link
Contributor

@jan-grimo jan-grimo commented Aug 19, 2021

Implementation of an idea from #160 removing grad and gradx members from VariableExpr in favor of value and expression pointers to which updates can be written during expression tree traversal.

Fixes the associated regressions in derivative calculation, too. The tests no longer leak memory.

Some details:

  • In principle, there's no need to add bind_value and bind_expr to the Expr<T> interface. For instance expr.bind_value(nullptr) can be replaced with a static_cast<VariableExpr<T>*>(expr.get())->gradPtr = nullptr; because this is only ever called on variable expressions. Any preference you might have there is easy to adopt.
  • The nullptr checking in VariableExpr<T>::propagate and ::propagatex could just be replaced with an assert for debug build checking if you have runtime performance concerns.

Copy link
Member

@allanleal allanleal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Jan, thanks a lot for the fix. In the end you managed to find a solution that required very little changes. Very good. I have just kindly requested you to add a short comment to two methods below, just to make it clear what are their roles. I've added a memory leak check in the CI.

@@ -254,6 +255,9 @@ struct Expr
/// Destructor (to avoid warning)
virtual ~Expr() {}

virtual void bind_value(T* /* grad */) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a short (Doxygen ///) comment here for these bind_* functions. I like your approach.

@jan-grimo
Copy link
Contributor Author

jan-grimo commented Aug 19, 2021

I'm glad you approve!

By the way, you could cut valgrind out of the CI and use sanitizers instead, with something like the following in tests/CMakeLists.txt:

if(AUTODIFF_SANITIZE_TESTS)
  target_compile_options(autodiff-cpptests PRIVATE "-fsanitize=address,undefined")
  target_link_options(autodiff-cpptests PRIVATE "-fsanitize=address,undefined")
endif()

... Although that only works for linux and mac and would need safing against windows

@allanleal
Copy link
Member

allanleal commented Aug 19, 2021 via email

@allanleal
Copy link
Member

Setting AUTODIFF_SANITIZE_TESTS to ON in the macOS build is failing. I'll probably enable this only for Linux, which should be enough.

@jan-grimo
Copy link
Contributor Author

jan-grimo commented Aug 19, 2021

Yeah, I think the cmake on osx doesn't much like your use of a target named tests. (That's not it). If you don't mind, I'm gonna try to fix that.

MSVC did add Asan a while ago, so I thought it might be worth trying via the compiler flag testing. Not sure what's been failing the CI pipeline though.

@@ -49,7 +49,7 @@ jobs:
shell: powershell
run: |
conda activate autodiff
cmake -S . -B .build
cmake -S . -B .build -DAUTODIFF_TEST_SANITIZE=ON
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this works in Windows. I'll remove this (the only thing missing to get CI fully passing)

@allanleal allanleal merged commit 2309fe0 into autodiff:master Aug 24, 2021
@adam-ce
Copy link

adam-ce commented Aug 25, 2021

hi,
i'm using autodiff reverse mode to unit test my own manual gradient derivatives for complex functions.

I do functions similar to the following
template <typename scalar_t, int DIMS> EXECUTION_DEVICES glm::mat<DIMS, DIMS, scalar_t> inverse(const glm::mat<DIMS, DIMS, scalar_t>& cov, const glm::mat<DIMS, DIMS, scalar_t>& incoming_grad);
which i want to unit test.
The thing is, that i have several in- and outputs, and i'm manually var.expr->propagate(grad) on each of the components and then reading the result from the input vars one by one in a loop. Something else that i do, is casting the expressions to variables, and then reading intermediate results of the gradient while debugging. that's now not possible any more. i think that the easiest workaround for me is to create my own autograd variable and call bind myself.

I understand the reasons for the changes, but if i understand it correctly, it could have been limited to the gradx case. imo it's also a nicer design, if you can get the gradient from the variable directly. anyway, what i actually want to say, is, that it would be nice if you also consider such use cases, i.e., don't try to completely hide the implementation, because that makes it harder to adapt for certain use cases (another example is, that some important functionality is now in detail, which also broke my code).

@allanleal
Copy link
Member

Hi Adam, many thanks for reporting this. You're right that the memory leak issue was caused only when gradx and propagatex were used (i.e., for higher-order derivatives).

I would accept changes that revert the grad and propagate methods. I won't be available for this soon. Would you kindly contribute with a PR that introduces back grad and propagate?

adam-ce added a commit to adam-ce/autodiff that referenced this pull request Aug 25, 2021
This reverts parts of commit 44e088c.

The var.grad() api is more convenient than requiring to first bind a
float and then reading it back. for instance, expr.propagate() and
var.grad() can be used in unit tests. see also
autodiff#177 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants