Variable scope & reduce_sum

The new reduce_sum functionality is great, however, for large models I am finding it necessary to pack a large number of parameters (10’s) as function arguments. I realize the decision was made early on when functions were introduced in Stan to limit the scope of model parameters so they are not directly accessible. I assume this is for the same reasons limiting the accessibility of global variables is good in more general purpose languages, and for implementing autodiff. However, given that Stan is not a standard language, passing model parameters is quite cumbersome, and it seems this has been exacerbated with reduce_sum. The current flow actually makes things potentially more error prone in that it can be difficult to ensure the function call matches the function definition when there are 25+ arguments involved. Am I missing something about how to structure things? If not, has there been any talk of relaxing the parameter scope rules, perhaps via a flag that can be turned on and off on a case-by-case basis?

Hi Alec,

There shouldn’t be any argument packing required for reduce_sum, since the function can take any number of arguments. Could you provide some more detail (preferably with examples) about your implementation and why you’re packing arguments?

Packing was perhaps the wrong word. If the likelihood being parallelized depends on a large number of different parameters, each parameter has to be passed individually when calling reduce_sum, and these are then passed to the user function. However, passing large numbers of model parameters each as individual arguments can be cumbersome. For some models, this actually involves having 10’s of individual arguments passed to reduce_sum to then pass on to the user function. Ensuring 10’s of individual arguments appear in the correct order in the user function definition and in the call to reduce_sum is also error prone. (And in fact, packing might actually helps with this, if by packing you thought I had meant stuffing multiple parameters into a vector or other container, although this has other obvious problems.)

if by packing you thought I had meant stuffing multiple parameters into a vector or other container,

Yep, that’s what argument packing generally refers to.

So if I’m understanding correctly, the issue is the need to specify which arguments get passed to reduce_sum? For example, given the simple example:

  target += reduce_sum(partial_sum, y,
                       x, beta);

You’d prefer not to have to specify x and beta, and just call:

  target += reduce_sum(partial_sum, y,

Have I got that right?

Yes, the problem being that for some models we’re not dealing with just x and beta, but many parameters. For models with a small number of parameters (<10), the current scheme works well.

copy & paste is your friend here. Define the partial sum function, copy over its arguments, then delete type declarations is a way to handle it.

There are closures planned to be available, but that’s an undefined moment away from now.

(You can really be happy with variadic arguments rather than map_rect style things).

There are two main reasons why this is the case (for now). Firstly, when you write a partial_sum function, like:

  real partial_sum(int[] y_slice,
                   int start, int end,
                   vector x,
                   vector beta) {
    return bernoulli_logit_lpmf(y_slice | beta[1] + beta[2] * x[start:end]);

The parameter names (x and beta) do not refer to the parameters in the model, they’re just names given to the parameters passed to the function. So Stan doesn’t ‘know’ which variables the function is being applied to unless you pass them.

Additionally, if you did want to have every variable available without passing them to the function, this would require that the entire workspace (all variables) get copied to every thread, which is highly inefficient (and would be a big performance hit) if only a subset of those variables are needed, especially in large/complex models.