Intermediate NaN evaluation tanks the autodiff stack

Just saying. It’s not necessarily a bug, but we don’t have a good way around it. I was looking at math#533 and boiled it down to this (save this in the math library under test/nan_test.cpp and you can run with ./runTests.py test/nan_test.cpp):

#include <stan/math/rev/scal.hpp>
#include <gtest/gtest.h>

TEST(foo, bar) {
  stan::math::var a = 5;
  stan::math::var z = 10;
  stan::math::var x;

  if (a < sqrt(-z)) {
    x = a * z;
  } else {
    x = a / z;
  }

  stan::math::print_stack(std::cout);
  std::vector<stan::math::var> vars;
  vars.push_back(a);
  vars.push_back(z);
  std::vector<double> grad;

  x.grad(vars, grad);
  for (size_t n = 0; n < grad.size(); n++)
    std::cout << "grad[" << n << "] = " << grad[n] << std::endl;
  std::cout << std::endl;
  EXPECT_EQ(0.1, grad[0]);
  EXPECT_EQ(-0.05, grad[1]);
}

The output is below. Notice the nan in the 4th position on the stack. That’s from the evaluation of sqrt(-z) which is just used for the conditional; IEEE math always works out the same, so the conditional will evaluate to false, but there’s no recovery from this:

test/unit/nan_test --gtest_output="xml:test/unit/nan_test.xml"
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from foo
[ RUN      ] foo.bar
STACK, size=5
0  5:0  5 : 0
1  10:0  10 : 0
2  -10:0  -10 : 0
3  nan:0  nan : 0
4  0.5:0  0.5 : 0
grad[0] = 0.1
grad[1] = nan

test/unit/nan_test.cpp:26: Failure
Value of: grad[1]
  Actual: nan
Expected: -0.05
[  FAILED  ] foo.bar (0 ms)
[----------] 1 test from foo (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] foo.bar

 1 FAILED TEST
1 Like

Hey, thanks for the clean example, I’ll take a look at it…

1 Like

We could

  1. Do everything with values in conditionals. This would require a complete parallel expression generation routine that wraps all the variables in value_of. Easier with a new intermediate rep we’re considering.

  2. Put a conditional saying not to propagate derivatives if the partial is zero.

I’m not entirely sure (1) is general enough or (2) is sufficient.