CephBirk
CephBirk

Reputation: 6710

Get out of infinite while loop

What is the best way to have a while loop recognize when it is stuck in an infinite loop in R?

Here's my situation:

diff_val = Inf
last_val = 0

while(diff_val > 0.1){

    ### calculate val from data subset that is greater than the previous iteration's val
    val = foo(subset(data, col1 > last_val))

    diff_val = abs(val - last_val) ### how much did this change val?
    last_val = val ### set last_val for the next iteration
}

The goal is to have val get progressively closer and closer to a stable value, and when val is within 0.1 of the val from the last iteration, then it is deemed sufficiently stable and is released from the while loop. My problem is that with some data sets, val gets stuck alternating back and forth between two values. For example, iterating back and forth between 27.0 and 27.7. Thus, it never stabilizes. How can I break the while loop if this occurs?

I know of break but do not know how to tell the loop when to use it. I imagine holding onto the value from two iterations before would work, but I do not know of a way to keep values two iterations ago...

while(diff_val > 0.1){

    val = foo(subset(data, col1 > last_val))

    diff_val = abs(val - last_val)
    last_val = val

    if(val == val_2_iterations_ago) break
}

How can I create val_2_iterations_ago?

Apologies for the non-reproducible code. The real foo() and data that are needed to replicate the situation are not mine to share... they aren't key to figuring out this issue with control flow, though.

Upvotes: 0

Views: 1212

Answers (3)

Hong Ooi
Hong Ooi

Reputation: 57686

How this is generally done is that you have:

  • A convergence tolerance, so that when your objective function doesn't change appreciably, the algorithm is deemed to have converged
  • A limit on the number of iterations, so that the code is guaranteed to terminate eventually
  • A check that the objective function is actually decreasing, to catch the situation where it's diverging/cyclic (many optimisation algorithms are designed so this shouldn't happen, but in your case it does happen)

Pseudocode:

oldVal <- Inf
for(i in 1:NITERS)
{
    val <- objective(x)
    diffVal <- val - oldVal
    converged <- (diffVal <= 0 && abs(diffVal) < TOL)
    if(converged || diffVal > 0)
        break
    oldVal <- val
}

Upvotes: 2

Gregor Thomas
Gregor Thomas

Reputation: 145775

Another approach, perhaps a little more general, would be to track your iterations and set a maximum.

Pairing this with Tim's nice answer:

iter = 0
max_iter = 1e6
while (diff_val > 0.1 & iter < max_iter) {
    val <- foo(subset(data, col1 > last_val))

    if (val == val_2_iterations_ago) break

    diff_val = abs(val - last_val)
    val_2_iterations_ago <- last_val
    last_val <- val
    iter = iter + 1
}

Upvotes: 3

Tim Biegeleisen
Tim Biegeleisen

Reputation: 521239

I don't know if just keeping track of the previous two iterations will actually suffice, but it isn't too much trouble to add logic for this.

The logic is that at each iteration, the second to last value becomes the last value, the last value becomes the current value, and the current value is derived from foo(). Consider this code:

while (diff_val > 0.1) {
    val <- foo(subset(data, col1 > last_val))

    if (val == val_2_iterations_ago) break

    diff_val = abs(val - last_val)
    val_2_iterations_ago <- last_val
    last_val <- val
}

Upvotes: 3

Related Questions