Broadcast hangs after successful execution over all array elements

When I call the function INSIDE_WHICH_INTVAL below, it correctly returns IVL_NUM for input of type INTY :: Float64. When I broadcast the function to cover all elements of an array INTY_ARRAY also, as INSIDE_WHICH_INTVAL.(INTY_ARRAY, Ref(INT_BOU), Ref(N_INTEN)), it does execute correctly without issues, but hangs after the last array element is done with. Any help?

function INSIDE_WHICH_INTVAL(INTY,   INT_BOU,     N_INTEN)
    IS_OUTS     ::         Bool      =                true
    INT_MIN     =                               INT_BOU[1]
    INT_MAX     =                               INT_BOU[2]
    INT_INC     =             (INT_MAX - INT_MIN)/ N_INTEN
    IVL_NUM     =   0
    I           =   1
    while IS_OUTS
        IVL_LOW =     INT_MIN   +     (I - 1)* INT_INC
        IVL_HIH =     INT_MIN   +     (I - 0)* INT_INC
        IS_INSI = (IVL_LOW < INTY) && (INTY < IVL_HIH)
        if IS_INSI   ==     1
            IVL_NUM  =      I
            IS_OUTS  =  false
            break
        end
        I += 1
    end
    println("IVL_NUM = ", IVL_NUM)
    return IVL_NUM
end

I just added the `println` to confirm that `INSIDE_WHICH_INTVAL` does finish evaluating all array elements. 

In particular, tell us exactly how to call your code to see the error.

Also, can I suggest using more descriptive variable names and following the Julia variable naming conventions: Style Guide · The Julia Language
This will not only help us understand your code so we can help you, it will help you write maintainable code in the long run.

2 Likes

When INTY is a scalar Float64, here’s a no issues run…

julia> IMG_BOU
2-element Vector{Float64}:
 0.019607843137254943
 1.0
julia> INTY
0.5529411764705883

julia> N_INTEN
4

and here is the call

julia> INSIDE_WHICH_INTVAL(INTY, IMG_BOU, 4)
IVL_NUM = 3
3

When I run with INTY_ARRAY a vector say, defined as

julia> INTY
11-element Vector{Float64}:
 0.45882352941176474
 0.5529411764705883
 0.6313725490196078
 0.7019607843137254
 0.7568627450980392
 0.8196078431372549
 0.8509803921568627
 0.9254901960784314
 1.0
 1.0
 1.0

julia> IVL_VEC = INSIDE_WHICH_INTVAL.(INTY, Ref(IMG_BOU), Ref(N_INTEN))
IVL_NUM = 2
IVL_NUM = 3
IVL_NUM = 3
IVL_NUM = 3
IVL_NUM = 4
IVL_NUM = 4
IVL_NUM = 4
IVL_NUM = 4


It hangs after! To test with any other data, just note 0.0 < INTY < 1.0, INT_BOU is a 2- element vector with elements in (0.0,1.0), N_INTEN is an I64 scalar >0 that specifies the number of intervals within one of which INTY lies.

Regarding suggestion on style, appearance, kindly bear with me. I’ve been coding in this caps- dominant style for decades, across many languages from Ada 2012 thru MATLAB and now Julia 1.10.4. Indeed I find it lot easier to comprehend and extend a longer set made up of multiple short lines of code than a shorter set with longer lines that’ve lots of nesting and associations. Surely will think over style once I’ve got seasoned enough with Julia.

Just occurred to me if INTY[9] = 1.0 is why it’s hanging. The while might after all be unable to figure IVL_NUM when any INTY[k] ==1.0 unlike with elements 1..8 for which INTY[k] < 1.0. Will modify the IS_INSI assignment and try.

You need to make sure the number under test is guaranteed to be in one of the tested intervals. It’s difficult to figure out what the code is supposed to be doing because of your choice of variable names, but 1.0 does indeed seem to fall between the cracks.
It might be better to test only one comparison per loop iteration. For instance, if I read it right, the lower bound should not be necessary, since the limits increase monotonically. You only have to check whether the number is below the increasing upper bound.

1 Like

Here’s an example of what I mean. Does this do what you expect your function to do?

"""
    which_interval(x, grand_interval, num_intervals)
Give which of `num_intervals` subdivisions of the left-open interval `grand_interval` that contains number `x`.
For instance, `which_interval(0.5, (0, 3), 3) == 1`, because `0.5` is in the first third of the interval ``(0, 3]``.

If `x` does not lie within `grand_interval`, returns `nothing`.
"""
function which_interval(x, grand_interval, num_intervals)
    grand_interval[1] < x <= grand_interval[2] || return nothing
    increment = (grand_interval[2]-grand_interval[1])/num_intervals
    for i = 1:num_intervals-1
        x <= grand_interval[1] + increment*i && return i
    end
    return num_intervals
end
2 Likes

Thanks for suggesting a code, @gustaphe. It was indeed the runtime issue of me not checking for INTY being equal to either of the interval’s limits. I also simplified the function, as my bounds (grand_interval in your code) would never be outside [0.0, 1.0]. The function now reads as follows:

function INSIDE_WHICH_INTVAL(INTY        ,      N_INTEN)
    IS_OUTS     :: Bool =   true
    INT_INC     =   1.0/ N_INTEN
    IVL_NUM     =   0
    I           =   1
    while IS_OUTS
        IVL_LOW =                      (I - 1) * INT_INC
        IVL_HIH =                      (I - 0) * INT_INC
        IS_INSI = (IVL_LOW <= INTY) && (INTY <= IVL_HIH)
        if IS_INSI   ==     1
            IVL_NUM  =      I
            IS_OUTS  =  false
            break
        end
        I += 1
    end
    return IVL_NUM
end

Both lower and upper interval bounds are necessary, as otherwise all values INTY > Lower_Bound OR all values INTY < Upper_Boundcould qualify to fire IS_INSI as true.

Just a few runtime stats from the simplified code, to close this discussion:

julia> IVL_ARR = @time INSIDE_WHICH_INTVAL.(IMG_TST, Ref(N_INTEN));
  6.594793 seconds (4 allocations: 12.810 MiB, 99.75% gc time)

julia> IVL_ARR = @time INSIDE_WHICH_INTVAL.(IMG_TST, Ref(N_INTEN));
  0.017390 seconds (4 allocations: 12.810 MiB)

julia> IVL_ARR = @time INSIDE_WHICH_INTVAL.(IMG_TST, Ref(N_INTEN));
  0.018327 seconds (4 allocations: 12.810 MiB)

julia> IVL_ARR = @time INSIDE_WHICH_INTVAL.(IMG_TST, Ref(N_INTEN));
  0.017548 seconds (4 allocations: 12.810 MiB)


The above stats are for the array size and N_INTEN:

julia> size(IMG_TST)
(990, 1696)
N_INTEN
4

But as long as you test whether it is inside the full interval before you start the loop, it will only get to the next iteration if it did not pass in the last, meaning that half of your comparisons are superfluous. And that extra complexity is what caused your bug.

Nice you got it working though.

Not quite so. INSIDE_WHICH_INTVAL is a simple function, similar to returning on which step one is, given the x in a flight of stairs of total span 0.0 <= x <= 1.0; see the sketch here.

Right, but you only need to check that the number is larger than 0, and then upper bound checks. Because the only way you’re getting to the loop iteration where the lower bound is 3/8, is to advance from the iteration where the upper bound was 3/8. So you have already found that x !< 3/8, no need to also check that x >= 3/8.

Oh, you’re right, can get going with checking for exceeding only one of the two bounds. But typically N_INTEN is quite small, < 10, so it wouldn’t matter a lot, in my opinion.