Real to integer conversion?

Can you or are there any plans for introducing real to integer conversion?

In my use case I’ve an array of pre-computed matrices: matrix[20, 20] A_inv[10]. I would like to be able to choose a matrix like A_inv[idx], where idx is calculated from real variables, eq idx = to_integer(ceil(x/5.0)).

The reference manual 2.17 chapter 41.7 states: “The rounding functions cannot be used as indices to arrays because they return real values. Stan may introduce integer-valued versions of these in the future, but as of now, there is no good workaround.”

No. It messes up NUTS.

Only if used for sampling. The other day I wanted to round something to use in the generated quantities block and couldn’t find a way to do it. Is there a trick for that situation?

1 Like

No, but I don’t think it matters whether it is int or real with no decimal places. In R at least, it is going to be stored with double precision anyway.

I want to use it for indices, shouldn’t affect the sampling (currently I’m using if-else statements to figure out indices from real variables, and that doesn’t mess up the sampling either).

Well, there still is no conversion from real to int and indices have to be int.

If you’re dead-set on it, the best way would probably be to write a binary search function to find the corresponding integer. I’m guessing there’s a good chance that this would not play well with NUTS.

  int bin_search(real x, int min_val, int max_val){
    // This assumes that min_val >= 0 is the minimum integer in range, 
    //  max_val > min_val,
    // and that x has already been rounded. 
    //  It should find the integer equivalent to x.
    int range = (max_val - min_val+1)/2; // We add 1 to make sure that truncation doesn't exclude a number
    int mid_pt = min_val + range;
    int out;
    while(range > 0) {
      if(x == mid_pt){
        out = mid_pt;
        range = 0;
      } else {
        // figure out if range == 1
        range =  (range+1)/2; 
        mid_pt = x > mid_pt ? mid_pt + range: mid_pt - range; 
        }
    }
    return out;
  }

Thanks, that worked.

Right—not if used to remove derivatives from an important calculation. For instance, you can’t do discrete sampling this way and get the right information flow.

You can simplify the code a bit with direct returns.

int bin_search(real x, int min_val, int max_val) {
  if (min_val > x || max_val < x) 
    reject("require min < x < max, found min = ", min_val,  "; max = ", max_val, "; x = ", x);
  real y = round(x);
  int range = max_val - min_val;
  real mid_pt = min_val;
  while (1)  {
    if (range == 0) return mid_pt; 
    range = (range + 1) / 2;
    mid_pt += y > mid_pt ? range : -range;
  }
}

The rounding makes the function platform specific, I’m afraid. I wrote a more general one of these recently that rounded to closest negative numbers, too.

This won’t quite work as written because Stan’s not clever enough (yet) to figure out that this function either loops infinitely or returns—it needs a final return min_val after the loop to satisfy the compiler (we need to fix that—if the only way out of a loop is to return, it’s an OK final statement in a non-void function).

Unfortunately, if y is an autodiff variable, all that arithmetic for mid_pt gets added to the autodiff stack to no good end.

I wonder to know what are the min and max values?
for example I want to change a real number to integer and use this int as an index in a loop, how can I define the min and max for this function?
Thanks,

No. You can hack it with a while loop that increments an integer until it is greater than a real number.

1 Like

The min value and max value are determined by your problem. It actually won’t work with the absolute min and max of integers (about -10^31 and +10^31) because the range + 1 will overflow. You have to be more careful with arithmetic if you really need that large a range.

Double values can be much larger than the max integer, like 10^300. So this can’t work in general.

3 Likes

It of course makes sense that you can’t convert real to integer in parameters, transformed parameters , or model block, but I see the complete “ban” somewhat restrictive as it is not that uncommon (at least for me) that you would need an index variable like int idx = floor(something / 2); in transformed data or generated quantities blocks. Luckily so far I have been able tell the value of idx beforehand, so I can create it in R and pass it as a data to Stan, but anyway…

3 Likes

It’s not so much a ban as we don’t distinguish legal signatures for our functions on a block-by-block basis other than the _rng functions. It wouldn’t cause derivative problems in transformed data, so we could potentially allow it with some fiddling on the parser and top-level notion of what signatures are allowed where.

4 Likes

Hello. It would be great to have this functionality for indices. I also need it for indexing.

2 Likes

me too :) :)

1 Like

Now that we have a data modifier for variables, we could add this safely for indices. The problem is when you apply it to parameters in a non-smooth way.

2 Likes

Having this available in transformed data would suffice for a subset of indexing issues. My use case is in a model with multiple GPs that I sample with different frequencies. Given the conversion between the indices is known at the start of the simulation, you can build maps in transformed data.

I would like this as well. In my case for generated quantities. I’m trying to write a more-strongly-typed code generator for Stan (from Haskell) and in trying to write the densities/rng for a normal-approximation to the binomial, I’ve got the problem that the rng function should return an array[] int but the normal approximation is returning vector. Which I somehow need to convert. All the other functions (pdf, lupdf, et.) are okay since they involve array[] int -> vector but rng needs to go the other way.

FWIW, I recently wanted this in the model block for indexing. I had an integer N and needed to access the element at position 2^N. I got around this by computing 2^N via repeated integer multiplication. An alternative would have been to pass an integer array of powers of 2 as data.

1 Like