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.

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.

3 Likes