Debugging foreach in R using dorng

Viewed 636

I am parallelizing a task in R using foreach loop with reproducible results using the dorng operator. It is a complex code, and I have an error that I have not been able to identify even though I have run the same code with a regular for loop.

My fundamental question is: how do I debug a function within of a foreach loop assuming that I have reproducible results? Below is my current tentative.

In the vignette of the doRNG package, it says that a sequence of random seeds will be generated and set at the beginning of each iteration using the R number generator "L’Ecuyer-CMRG". The sequence of random seeds can be defined using set.seed before the foreach loop:

library(doRNG)
library(doParallel)
library(foreach)

registerDoParallel(2)
set.seed(1234)
f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r01 <- attr(f01, "rng")

set.seed(1234)
f02 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r02 <- attr(f02, "rng")

The objects r01 and r02 contain the sequence of seeds used in f01 and f02,

identical(f01, f02)
identical(r01, r02)

such that the results from the foreach loop and their seeds are identical as expected!

Then, let's consider the case when the foreach loop will give me a random error:

set.seed(1234)
f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  u <- floor(runif(1, 1, 101))
  
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
  }

}
Error in { : task 67 failed - "non-numeric argument to binary operator"

The error occurs at iteration 67 and it is very easy to understand the error. Unfortunately, it is not the same in my case.

I would like to be able to use debug and walk through my function to understand the error. From the best of my knowlegde, I cannot use debug inside a foreach loop in R.

Then, I thought about capturing the error in a regular for loop, but running my code is very slow and, apparently, I cannot observe the error with a low number of iterations. I need to understand the error using foreach.

Although I can't recover the sequence of seeds from f03, I know that they will be identical to r01 or r02. For iteration 67, I have

r01[[67]]
[1]       10407  1484283582  -741709185   513087691   132931819
[6]  1318506528 -1383054295

Therefore, I guess that fixing my seed at r01[[67]] should give me the same error:

i <- 67
set.seed(r01[[67]], kind = "L'Ecuyer-CMRG")
u <- floor(runif(1, 1, 101))
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
}
u
[1] 74

which is not true.

In the doRNG vignette, page 6, they have an example of using a seed in a loop from a previous loop:

set.seed(1234)
ex01 <- foreach(i=1:5) %dorng% { runif(3) }
ex02 <- foreach(i=1:5, .options.RNG=attr(ex01, 'rng')[[2]]) %dorng% { runif(3) }
identical(ex02[1:4], ex01[2:5])

What am I missing?

1 Answers

I think the issue comes from the fact that doRNG gives you the random seed state and you're using that as the input of set.seed, which requires just an integer. I expect set.seed is only taking the first integer provided to the function to set the random seed state. Instead, what you should do is set the seed state in R. First I get the seed states and verify I can reproduce the error:

library(doRNG)
library(doParallel)
library(foreach)

registerDoParallel(2)
kind <- RNGkind()
kind
[1] "Mersenne-Twister" "Inversion"        "Rejection"   

set.seed(1234, kind = kind[1]) #explicitly set RNG kind so I can reproduce more easily if I run the code multiple times
f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r01 <- attr(f01, "rng")

set.seed(1234, kind = kind[1])
f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  u <- floor(runif(1, 1, 101))
  
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
  }

}
Error in { : task 39 failed - "non-numeric argument to binary operator"

I got a different iteration number for the failure. Not sure why but I still get the error as you did. Then I see if we can replicate by setting .Random.seed directly:

i <- 39
RNGkind("L'Ecuyer-CMRG")
.Random.seed <- r01[[39]]
u <- floor(runif(1, 1, 101))
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
}
Error in "a" + "b" : non-numeric argument to binary operator
u
[1] 39

Looks like we can! Full code below:

library(doRNG)
library(doParallel)
library(foreach)

registerDoParallel(2)
kind <- RNGkind()
kind

set.seed(1234, kind = kind[1]) #explicitly set RNG kind so I can reproduce more easily if I run the code multiple times
f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r01 <- attr(f01, "rng")

set.seed(1234, kind = kind[1])
f02 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r02 <- attr(f02, "rng")

identical(f01, f02)
identical(r01, r02)

set.seed(1234, kind = kind[1])
f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  u <- floor(runif(1, 1, 101))
  
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
  }

}

identical(r01, r03)

i <- 39
RNGkind("L'Ecuyer-CMRG")
.Random.seed <- r01[[39]]
u <- floor(runif(1, 1, 101))
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
}
u

#restore RNG kind to original
RNGkind(kind[1])
Related