Real to integer conversion?


#1

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.”


#2

No. It messes up NUTS.


#3

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?


#4

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.


#5

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).


#6

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


#7

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;
  }

#8

Thanks, that worked.


#9

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.