Bypassing autodiff?

133 views
Skip to first unread message

Glenn Goldsmith

unread,
Apr 17, 2017, 3:43:37 AM4/17/17
to Stan users mailing list
I'm in the process of building a Stan model, which I've tested successfully on small simulated datasets. However, when scaling up to datasets of more realistic size (50-100k observations), the memory used (I assume by autodiff) grows rapidly to >50GB before crashing my R session (I'm using rstan).

While I'm sure I could optimize my code somewhat to reduce the size of the expression graph required for autodiff, I suspect that the nature of the model means that it will remain prohibitively large. As a result, I was wondering if it was possible to bypass autodiff completely and substitute hand-coded derivatives instead? 

I should be able to code the derivatives in C++ without too much trouble, but have no idea how I would get this to play nicely with Stan. Any advice would be very gratefully received.

Daniel Lee

unread,
Apr 17, 2017, 3:52:07 AM4/17/17
to stan-...@googlegroups.com
Hi Glenn,

If you know how to compute the function value and the gradients with respect to each argument of the function that could possibly be a Stan parameter (or a function of one), then you can write a function in C++ that will add a single expression to the expression graph. We can walk you through that if you can describe the function better. At least what arguments it has and which can be Stan parameters. Before digging too deep, we should make sure you can compute enough gradients that are required. 

Regarding memory usage, my guess is that it's actually not the autodiff that's blowing out memory. I think there's a way to steam the output to disk without storing in memory in RStan, but if there isn't, try running it in CmdStan just to see how much memory it actually takes. If you haven't vectorized the program, it might also help to do that.


Daniel
--
You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.
To post to this group, send email to stan-...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Glenn Goldsmith

unread,
Apr 17, 2017, 10:43:30 AM4/17/17
to stan-...@googlegroups.com
Thanks Daniel,

The memory issue is occurring before any sampling (or optimization) happens, so I had assumed it must be related to autodiff. (To confirm this, I threw a bunch of print() statements into the code, and while it makes it through the transformed data block with no trouble, it doesn't get to anything in the transformed parameters or model blocks before crashing. But let me know if you think it could be something else. As you suggested, I'm also piping output to a file in rstan by default.)

In terms of calculating gradients, if necessary, I could code the derivatives of the full log-probability density wrt to the parameters, but I expect the memory issue is mainly down to one function in the transformed parameters block:

R = calculate_rlevels(lambda, lambda_select, M, ht_select, ht_gmap, Ht,
Z, Z_type, Zdvec_sq, Zdmat_segments);

where the function signature for calculate_rlevels() in the functions block is:

matrix calculate_rlevels(vector lambda, int[,] lambda_select, 
matrix M,
int[,,] ht_select, int[,,,] ht_gmap, int[,] Ht,
matrix Z, int[] Z_type, vector Zdvec, int[,] Zdmat_segments)

There are a whole string of user-defined functions sitting behind this, the details of which I assume aren't especially relevant, but you can roughly think of each element of R as a kernel regression prediction of M|Z, based on a somewhat awkward subset of the other observations, and where the lambda parameters control the bandwidths of a K_lambda-dimensional gaussian kernel. The underlying code involves a lot more looping than I'd ideally like, but more efficient versions using matrix operations are unfortunately too memory intensive.

The returned matrix R is NT x K_rho (with NT  ~50-100k and K_rho typically only 1 or 2), lambda is a parameter vector (with typical length ~5-10), and all the other arguments are either data, or transformed data (mainly arrays of indices designed to deal with the aforementioned awkward subsets).

If we just focus on this function, then (in addition to the R matrix), I assume I will also need to be able to return d R[i,j] / d lambda[k] for all i,j,k (~500k-2m terms), which should be feasible. Is that right?

Glenn.

To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.

To post to this group, send email to stan-...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.

Daniel Lee

unread,
Apr 17, 2017, 12:22:42 PM4/17/17
to stan-...@googlegroups.com


On Apr 17, 2017, at 10:43 AM, Glenn Goldsmith <glenn.g...@gmail.com> wrote:

Thanks Daniel,

The memory issue is occurring before any sampling (or optimization) happens, so I had assumed it must be related to autodiff.

That sounds more like the memory allocated for the draws, not autodiff. 

Hopefully an RStan expert can chime in with how to stream to disk instead of holding in memory. Or you can use CmdStan. 


To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.

Bob Carpenter

unread,
Apr 17, 2017, 3:21:35 PM4/17/17
to stan-...@googlegroups.com
RStan allocates the memory to hold the draws ahead of time.
That's about 8 bytes per scalar parameter per iteration per chain.
That would presumably blow out before you started evaluating
the Stan model with data.

Autodiff memory will be around 40 bytes per subexpression evaluated
in the expression graph, and it'll remain roughly constant.

Autodiff memory won't kick in until the first iteration. Data
gets read in and then transformed data calculated.

As Daniel says, you can definitely code functions in C++ with
derivatives and add them to Stan.

There's an extension way to do it built into RStan. Or you can
just add new functions to the Stan math lib and to Stan in the other
interfaces. We're working on a consistent way to do extensions
across interfaces.

- Bob
>>> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.
>>> To post to this group, send email to stan-...@googlegroups.com.
>>> For more options, visit https://groups.google.com/d/optout.
>>
>> --
>> You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
>> To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to stan-users+...@googlegroups.com.

Glenn Goldsmith

unread,
Apr 17, 2017, 6:36:02 PM4/17/17
to stan-...@googlegroups.com
Hi Daniel and Bob,

I might be missing something, but I'm pretty sure it's not memory allocated for the draws:
  1. A single draw should take max ~20MB to store
  2. The issue occurs even when I don't sample, and instead call the model with optimizing/L-BFGS (which I assume should only require space to store a single draw?)
  3. I'm already using the sample_file argument in rstan, which should stream the output to disk as you suggested
I've also tested all the individual functions in the transformed parameters block on real data (using rstan's expose_stan_functions()), and none of them individually appear to be causing any memory issues, so autodiff still seems to be the most likely culprit (and this makes sense to me given what's going on inside my calculate_reflevels() function).

Glenn.

Glenn Goldsmith

unread,
Apr 17, 2017, 7:55:38 PM4/17/17
to stan-...@googlegroups.com
P.S. My previous description may have been slightly misleading: when sampling, I do get a "SAMPLING FOR MODEL 'x' NOW" message before the memory starts growing and crashes R, but it doesn't make it to any of the print statements in the transformed parameters block. When calling via optimizing there are no messages after the print statement at the end of the transformed data block.  

Ben B

unread,
Apr 17, 2017, 9:17:56 PM4/17/17
to Stan users mailing list
Yo Glenn, I had to do this a while ago for other reasons.

I ended up using cmdStan for my problem (on Ben Goodrich's advice). It's a little different of an interface, but it's pretty straightforward to interface C++ with it. Have a look through the manual (http://mc-stan.org/documentation/). Bonus is, if you get your stuff working with cmdStan maybe the possibility of an RStan weirdness can be totally eliminated.

It'd still be a good idea to wait on Daniel and Bob's feedback before you dive into the code, but if you wanted to get started reading, you'll want to at least read the first 13 or so sections of the Stan Math paper: https://arxiv.org/abs/1509.07164 . That has all the nuts and bolts of how the autodiff works with lots of clean C++ examples.

Ben

Bob Carpenter

unread,
Apr 18, 2017, 2:30:36 PM4/18/17
to stan-...@googlegroups.com
Not sure what RStan does for allocation for L-BFGS, but it
does require full autodiff per iteration.

No reason not to think it's in the autodiff. That can happen
with huge expression graphs.

- Bob
>>> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.
>>> To post to this group, send email to stan-...@googlegroups.com.
>>> For more options, visit https://groups.google.com/d/optout.
>>
>> --
>> You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
>> To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to stan-users+...@googlegroups.com.
>> To post to this group, send email to stan-...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.
>> To post to this group, send email to stan-...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to stan-users+...@googlegroups.com.
> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
>
> --
> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.

Glenn Goldsmith

unread,
Apr 19, 2017, 5:35:09 AM4/19/17
to stan-...@googlegroups.com
OK, so I've now had another shot at reading through the autodiff paper (thanks Ben for the prompt to go back to this), and I think I have a broad idea of what I need to do now. However, there are still some pieces I'm not totally clear on, and I wanted to check I'm not on completely the wrong track before proceeding further.

As a recap, I want to bypass the full autodiff expression graph for a user-defined function, calculate_r(lambda, ...) that takes a parameter vector lambda (typically k=~5 rows) as its main argument (plus a bunch of data arguments "..." that I'll set aside for now), and returns another vector r with length NT=50-100k.* From Dan's previous email, and looking through the paper, I assume this means I need to:
  1. Generate (a) a vector containing the 50-100k return values and (b) an NT x k matrix containing the 250-500k gradients d r[i] / d lambda [k]
  2. Get both of these into an appropriate vari-derived storage class
Below is a bit more detail on how I'm thinking about how to do this, with specific questions underlined.

Storage class
It's going to be much quicker to calculate the gradients eagerly at the same time as calculating the r vector, so I think I should be using use something like the precomputed_gradients_vari class described in section 12.1.1. of the autodiff paper:

 struct precomputed_gradients_vari : public vari {
   const size_t size_;
   vari** varis_;
   double* gradients_;
 
   precomputed_gradients_vari(double val, size_t size,
                              vari** varis, double* gradients)
     : vari(val), size_(size),
       varis_(varis), gradients_(gradients) { }
 
   void chain() {
     for (size_t i = 0; i < size_; ++i)
       varis_[i]->adj_ += adj_ * gradients_[i];
   }
 };

This seems basically right for each row of r, but the bit I'm not sure about is what I need to do set this up to hold a vector of vals and matrix of gradients d r[i] / d lambda [k] - and in particular, how to adjust the chain method appropriately? (Or can this be dealt with in the function itself rather than changing the class?)

Implementation
In terms of implementing the function itself, I've taken the stan-generated cpp code for the calculate_r function as a base, and made the following adjustments (see attached code skeleton for details):
  1. Removed the templating (as I know the types of the all the arguments and the return value, though maybe this wasn't necessary);
  2. In addition to the original return vector r, calculated a matrix of derivatives of each r[i] wrt to each lamdba[k];
  3. Used the log_determinant() code described in section 12.1.2 of the autodiff paper as a base to return the values, operands and gradients in a new vari implementation (currently based on precomputed_gradients_vari, although this won't be quite right yet)
    • (a) Sticking the return values into a val vector seems straightforward.
    • (b) However, while I'm sure this should be obvious, I'm not totally clear on what needs to populate that operands array. Is it just the elements of lambda, or am I on completely the wrong track here?
    • (c) I'm assuming once the appropriate chain method is in place in the storage class definition, filling the gradients array appropriately shouldn't be too difficult
In addition to the main function stan also generates a struct calculate_reflevel_functor__ { ... }. Is this still needed? (If so, I assume the templating - or lack thereof - should match the main function?)


Apologies if some (all?) of this is super obvious; I've never worked directly in C++ before, so am trying to figure things out as I go!

Thanks so much again for the help,

Glenn.

* In a previous email, I focused on another function, calculate_rlevels() that returned a matrix, R, but that function (usually) just loops a couple of times over calculate_r(), and it seems easier to deal with the latter.  

>>> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.

>>> To post to this group, send email to stan-...@googlegroups.com.
>>> For more options, visit https://groups.google.com/d/optout.
>>
>> --
>> You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
>> To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.

>> To post to this group, send email to stan-...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.

>> To post to this group, send email to stan-...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.

> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
>
> --
> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.

> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.
calculate_reflevels_skeleton.hpp

Ben B

unread,
Apr 19, 2017, 1:35:22 PM4/19/17
to Stan users mailing list
Good stuff! Looks like you've done your homework.

I went through the file you posted and modified it: https://gist.github.com/bbbales2/e351f1b3815f3cc875a36b39d5ef69ea

(I can get rid of that gist after this thread is over if you don't want that floating around)

There's a class in Stan, stored_gradient_vari, that does what precomputed_gradients_vari does, so you don't have to worry about that.

1. I'm not 100% sure, but I think you need to keep the templating to keep the linker happy. Not sure. The issue I had was that Stan wanted a copy of my function with the parameters instantiated as vars as well as a version with doubles. I think you need to define the function with all the templating, and then provide the two template specializations the linker wants. This is just for linking with an actual Stan model though. You can prototype this as just a Stan Math thing without having to deal with the templating at first.

2. I changed around how you were doing this (https://gist.github.com/bbbales2/e351f1b3815f3cc875a36b39d5ef69ea). The big thing is you want the input and output of your new autograd-friendly function to be the same shapes as if the autograd wasn't there. The derivatives hide inside the vars. You don't want to flatten that structure.

3. (b) Operands is just a list of pointers to other varis, I think you did it right

Hahaha, you've picked a good place to start learning C++ (this isn't trivial)!

I'd recommend that you build a little cpp test file separately to get this stuff working. Don't try to get it all linking up with Stan in one go. Just pretend you're adding another operator to the Stan Math library. Get that working first (by comparing with finite difference derivs) and then move on from there.

Here are links to the code I wrote (if it helps):
This make sense at all?

Ben

Glenn Goldsmith

unread,
Apr 20, 2017, 11:00:17 AM4/20/17
to stan-...@googlegroups.com
Thanks Ben, that's super helpful. 

I'm still working through the process of debugging this at the moment, but will let you know how I go.

P.S. You were right about needing to keep (some of) the templating (I think Stan does some calls with the parameters as doubles rather than vars).

--

Glenn Goldsmith

unread,
Apr 20, 2017, 1:20:23 PM4/20/17
to stan-...@googlegroups.com
Just a quick update to let you know that I've now got the custom autodiff working, and that it has indeed brought the size of the overall expression graph down to a totally manageable level.

Thanks again for the help!

Ben B

unread,
Apr 20, 2017, 3:37:07 PM4/20/17
to Stan users mailing list
Very cool! Good work! That was fast!

Ben

Bob Carpenter

unread,
Apr 20, 2017, 3:43:37 PM4/20/17
to stan-...@googlegroups.com

> On Apr 19, 2017, at 1:35 PM, Ben B <bbba...@gmail.com> wrote:
>
> Good stuff! Looks like you've done your homework.
>
> I went through the file you posted and modified it: https://gist.github.com/bbbales2/e351f1b3815f3cc875a36b39d5ef69ea
>
> (I can get rid of that gist after this thread is over if you don't want that floating around)
>
> There's a class in Stan, stored_gradient_vari, that does what precomputed_gradients_vari does, so you don't have to worry about that.
>
> 1. I'm not 100% sure, but I think you need to keep the templating to keep the linker happy. Not sure. The issue I had was that Stan wanted a copy of my function with the parameters instantiated as vars as well as a version with doubles. I think you need to define the function with all the templating, and then provide the two template specializations the linker wants. This is just for linking with an actual Stan model though. You can prototype this as just a Stan Math thing without having to deal with the templating at first.

It depends how you put your libs together.

If your function takes N arguments and you want to include it in Stan,
it needs to admit all 3^N combinations of integer/double/autodiff var.
You can do this with a single templated function, multiple templated
functions (either overloads or specializations), or with pure overloading.
Which is easiest will depend on the size of N and the nature of the function.

> 2. I changed around how you were doing this (https://gist.github.com/bbbales2/e351f1b3815f3cc875a36b39d5ef69ea). The big thing is you want the input and output of your new autograd-friendly function to be the same shapes as if the autograd wasn't there. The derivatives hide inside the vars. You don't want to flatten that structure.

Exactly what you need to do.

> 3. (b) Operands is just a list of pointers to other varis, I think you did it right
>
> Hahaha, you've picked a good place to start learning C++ (this isn't trivial)!

No kidding on the non-trivial part. It tooks us years to
work up to this, which is why I tried to write it all out
in the autodiff paper. Wish we'd had that before we started (but
then I always write papers aimed at my younger self).

> I'd recommend that you build a little cpp test file separately to get this stuff working. Don't try to get it all linking up with Stan in one go. Just pretend you're adding another operator to the Stan Math library. Get that working first (by comparing with finite difference derivs) and then move on from there.

Absolutely the only sensible way to proceed. People always think writing
tests will slow them down, but the key thing to do is to design top down,
but code bottom up, testing each infrastructure layer very thoroughly so
that when you go a layer up, you can trust your foundation. Anything else
is too unpredictable and prone to lead to impossibly tangled debugging
situations.

- Bob

Bob Carpenter

unread,
Apr 20, 2017, 3:44:37 PM4/20/17
to stan-...@googlegroups.com
Awesome! Thanks for reporting back.

Is this some kind of generally useful funtion that we should add to Stan?
If you have it coded, the rest is just boilerplate.

- Bob
> To unsubscribe from this group and all its topics, send an email to stan-users+...@googlegroups.com.
> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.

Glenn Goldsmith

unread,
Apr 21, 2017, 12:48:19 AM4/21/17
to stan-...@googlegroups.com
>Is this some kind of generally useful function that we should add to Stan?

If you have it coded, the rest is just boilerplate.

I don't think so - I imagine it's pretty specialised to my particular application. (Unless anyone chimes in really wanting a function that spits out (gaussian Nadaraya-Watson) kernel density point predictions of y | (awkward subsets of) X. And even then, given how integrated this is with a whole series of other user-defined functions in my stan code (both generating the awkward subset indices to pass in as arguments, and being called by the custom c++ function itself) it's probably still a non-trivial exercise to get it working as a standalone function.)

> To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.

> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.
> To post to this group, send email to stan-...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Stan users mailing list" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/stan-users/2eVrJh5MKEI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to stan-users+unsubscribe@googlegroups.com.

Glenn Goldsmith

unread,
Apr 21, 2017, 12:48:39 AM4/21/17
to stan-...@googlegroups.com
Thanks Bob,

One (hopefully) last question re the templating:

I ended up needing to code two separate specializations for the function depending on whether the first argument (and therefore return type) was var or double.

However, the way I ended defining the double specialisation was somewhat unsatisfactory: I just copied wholesale the var specialisation and then manually removed all the pieces that weren't related to the gradient calculations. This isn't great from a maintainability perspective, as the code is 80% identical, with only 4 short code chunks that aren't needed in the double version (a set of declarations; extraction of the var elements into a double for further calculations; calculation of the gradient matrix; and population and return of the stored_gradient_vari).

I'd ideally like (and thought I should be able) to have a single templated version of the code, just with conditional execution of the gradient/var-related bits. However, my naive attempt to do this using type traits didn't work. In particular, e.g.:
  • I defined a bool ret_type_double = is_same<fun_return_scalar_t__, double>::value, to switch the gradient-related code off
  • But the compiler would complain if I tried to use something like if (!ret_type_double) lambda_double(k) = lambda_var(k).val() to extract the var values as doubles (because .val() doesn't work on doubles, even though the condition could only be met if lambda_var were a var).
If anyone could suggest a (better) way to achieve this sort of type-dependent execution, while minimising the amount of duplicated code floating around, future me will likely be very grateful when it inevitably comes time to tweak something.

Also, as an aside, I notice that the struct calculate_r_functor__ {...} code that stan generated from my original user-defined function calculate_r() is no longer generated if I just declare the function in the .stan file and define it in calculate_r.hpp. Everything seems to be working fine without it, so I assume that it isn't needed any more, but is there any reason to include it in the .hpp file? 

Thanks again,

Glenn.




Bob Carpenter

unread,
Apr 21, 2017, 11:19:39 AM4/21/17
to stan-...@googlegroups.com

> On Apr 21, 2017, at 12:48 AM, Glenn Goldsmith <glenn.g...@gmail.com> wrote:
>
> Thanks Bob,
>
> One (hopefully) last question re the templating:
>
> I ended up needing to code two separate specializations for the function depending on whether the first argument (and therefore return type) was var or double.
>
> However, the way I ended defining the double specialisation was somewhat unsatisfactory: I just copied wholesale the var specialisation and then manually removed all the pieces that weren't related to the gradient calculations. This isn't great from a maintainability perspective, as the code is 80% identical, with only 4 short code chunks that aren't needed in the double version (a set of declarations; extraction of the var elements into a double for further calculations; calculation of the gradient matrix; and population and return of the stored_gradient_vari).

We usually have the autodiff version call the double version when we do that.
Or you can break out functions.

>
> I'd ideally like (and thought I should be able) to have a single templated version of the code, just with conditional execution of the gradient/var-related bits.

That's challenging. It's what we do with our pdf and pmf implementations, so have a look at the normal_lpdf implementation. But it involves a lot of template metaprogramming to not run afoul of the kinds of problems you run into (that everything needs to be instantiatable).

Sean's busy cleaning up the infrastructure in operators_and_partials so
that'll be a cleaner piece of the puzzle in 2.16.

> However, my naive attempt to do this using type traits didn't work. In particular, e.g.:
> • I defined a bool ret_type_double = is_same<fun_return_scalar_t__, double>::value, to switch the gradient-related code off
> • But the compiler would complain if I tried to use something like if (!ret_type_double) lambda_double(k) = lambda_var(k).val() to extract the var values as doubles (because .val() doesn't work on doubles, even though the condition could only be met if lambda_var were a var).
> If anyone could suggest a (better) way to achieve this sort of type-dependent execution, while minimising the amount of duplicated code floating around, future me will likely be very grateful when it inevitably comes time to tweak something.
>
> Also, as an aside, I notice that the struct calculate_r_functor__ {...} code that stan generated from my original user-defined function calculate_r() is no longer generated if I just declare the function in the .stan file and define it in calculate_r.hpp. Everything seems to be working fine without it, so I assume that it isn't needed any more, but is there any reason to include it in the .hpp file?

Sounds like something R specific. I'd ask a different question with
a new subject to get the RStan dev's attention.

- Bob

Glenn Goldsmith

unread,
Apr 22, 2017, 9:47:38 AM4/22/17
to stan-...@googlegroups.com
Thanks Bob, I'll take a look at the normal_lpdf and see how I go.

I don't think the struct user_function_functor__ {...} code that stan generates is r-specific: cmdstan also creates these, and they appear to exist for every user-defined function that isn't #included from an external .hpp file. That said, I'm not too worried about this, given that everything seems to be working fine without it.


- Bob

Daniel Lee

unread,
Apr 22, 2017, 11:59:06 AM4/22/17
to stan-...@googlegroups.com


On Apr 22, 2017, at 9:47 AM, Glenn Goldsmith <glenn.g...@gmail.com> wrote:

Thanks Bob, I'll take a look at the normal_lpdf and see how I go.

I don't think the struct user_function_functor__ {...} code that stan generates is r-specific: cmdstan also creates these, and they appear to exist for every user-defined function that isn't #included from an external .hpp file. That said, I'm not too worried about this, given that everything seems to be working fine without it.

That's around so functions can be passed into the ode integrators. If you're not trying to use your function in that manner, you should be ok. 

You received this message because you are subscribed to the Google Groups "Stan users mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+...@googlegroups.com.

Glenn Goldsmith

unread,
Apr 23, 2017, 9:36:32 PM4/23/17
to stan-...@googlegroups.com
@Daniel: Ah - good to know, thanks!

@Bob: Thanks again for the advice on templating. The solution was pretty straightforward in the end - I just needed a to call a couple of templated sub-functions inside the main function, and everything worked fine, with much less redundant code duplication.

Cheers,

Glenn. 

To unsubscribe from this group and stop receiving emails from it, send an email to stan-users+unsubscribe@googlegroups.com.

To post to this group, send email to stan-...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages