stan icon indicating copy to clipboard operation
stan copied to clipboard

Lift the "(Transformed) parameters cannot be integers" restriction

Open nsiccha opened this issue 4 months ago • 5 comments

When working with ragged arrays, it's sometimes useful to me to have transformed parameters of the type

tuple(
    array[n_groups] int,
    vector[n_total]
)

However, this is not allowed, due to the restriction in the title. It would be nice if ths restriction could be lifted, though there are of course ways to work around it.

nsiccha avatar Oct 10 '25 10:10 nsiccha

Can you say a bit more? The fragment you provided doesn't look like valid Stan code to me, and I'm not clear why something like this wouldn't just be transformed data

Edit: ah I see, you want a tuple that contains two pieces, one of which is an integer. Presumably the integer items do not change per iteration? If so, I think you'd be better off making this a local in the model block

WardBrian avatar Oct 10 '25 16:10 WardBrian

you want a tuple that contains two pieces, one of which is an integer. Presumably the integer items do not change per iteration?

Yes, exactly! And yes, usually the integer values don't change.

If so, I think you'd be better off making this a local in the model block.

Wait that would work? And you can also write out integers in the generated quantities block, but you can't do so in the transformed parameters block? That seems a bit arbitrary, don't you think?

In any case, the reason why I'd prefer to be able to have transformed parameters of the above type is that then I don't have to worry about repeatedly packing and unpacking the tuples that I have represent ragged vectors - instead, a ragged vector is simply a tuple of the above type, but the ragged vector as a whole can of course logically live in any of the blocks, depending on whether the vector component is data / transformed data / parameter / generated quantity.

With the current restriction in place, even though I may have functions which logically act on ragged vectors and return ragged vectors, I'll always have to attach extra unpacking logic whenever the result gets stored in the transformed parameters bloc. But not at any other time, e.g. within UDFs, in the transformed data block or in the generated quantites block.

I don't think there's really any drawback in lifting the restriction - or can anything be made to break by this? It's already not allowed that size information depends on parameter variables, even if that size information is constant. It's backwards compatible, because it simply makes the language more permissive. The only potential drawback is that more data would get written out (e.g. via cmdstanpy to CSV) - but (IMO) that should be a trade off that the user can make if they want to.

I'd be curious to hear @bob-carpenter's thoughts as well.

nsiccha avatar Oct 13 '25 07:10 nsiccha

Yes, we could potentially allow integers as transformed parameters. There's a reason I didn't allow them in the original design. I think of transformed parameters as transforms of parameters. Our parameters are continuous and while you can in various ways launder them into integer values, it's strongly discouraged because it almost always breaks continuous differentiability. If the integer values don't change based on the parameters, then they should be defined in the transformed data block.

Do you have a use case where the integer value changes based on the parameters but it doesn't break autodiff? I didn't understand the ragged vectors example.

bob-carpenter avatar Oct 13 '25 17:10 bob-carpenter

Do you have a use case where the integer value changes based on the parameters but it doesn't break autodiff?

No - the integer values are generally constant for my use cases.

I'll share a minimal motivating example when I have the time - hopefully soon!

nsiccha avatar Oct 14 '25 13:10 nsiccha

If they're constant, is there a reason you can't define them in the transformed data block? Then they only need to get computed once. If a constant value goes into the transformed parameter block, it gets stored in the output and run through posterior diagnostics, which will all be NaN other than mean and zero variance.

bob-carpenter avatar Oct 14 '25 19:10 bob-carpenter