Skip to content

foreach supplied object is not found within dofuture #783

@m-yday

Description

@m-yday

Describe the bug
in my code below: I am experiencing a strange "object 'nu' not found" coming from the traceback soon after the foreach function is run. The thing is, nu is defined within that foreach which is going directly into the %dofuture%!

Judging by the fact that I simply merged the structure of supplied code from both progressr and dofuture to fit my needs, I am really shocked that this didn't work, and that I'm unable to figure out precisely why.

This file is found on my partner's GitHub repository under Ass2.qmd. This should make this easier to see how the error actually came up.
I've now spent a couple of hours debugging this without coming to any reasonable conclusions about how the nu value is not available to the dofuture section

The line of code in question begins "Val_error<-" within the "with_progress" block

#| label: validation-analysis

# the lengthy-execution part
n_val <- 10
nu_seq <- exp(seq(-6,2,length= n_val))

#should give us a progress bar, while computing the validation error for this
#neural net, for the given nu values.
# instead, somehow the nu value—defined in foreach—is not found? 
# it may have just been faster to process this sequentially, but i am (perhaps
# foolishly) committed to learning these. 
# I feel so close to a breakthrough I SWEAR

with_progress({
  p <- progressor(along=nu_seq)
  Val_error <- foreach(nu = nu_seq,
                       .combine = rbind) %dofuture%{
    
    #Validation analysis, per nu
    res_opt = nlm(obj_pen,theta_rand,iterlim=max_iter)
    
    res_val = af_net(valid_X,valid_Y,res_opt$estimate,m,0) 
    #we are done with the penalised values now
    error = res_val$E1
    
    p(sprintf("nu=%g",nu))
    data.frame(nu=nu, 
               error=error, 
               pid=Sys.getpid() # curiosity may kill this cat
               )
  } %seed% FALSE #no random numbers are generated within this as far as i'm aware, but double-check for consistency
},handlers=handler_pbcol())

This is a chunk within a qmd file.

Reproduce example

If I paste in the other user defined sections that will get this to run, I get this:

library(tidyverse,attach.required = T)
library(future)
library(progressr)
library(doFuture)
library(parallelly)

options(parallelly.availableCores.omit = 1)

if(supportsMulticore()) # multicore = mac, linux, etc. (outside of RStudio)
  plan(multicore) else plan(multisession) #multisession = windows and other

# to ensure that the workers are correctly shut down
on.exit(
  plan(sequential)
) 


#dummy data
dat <- data.frame(X1=runif(90,-4,4),X2=runif(90,-4,4),X3=sample(c(1,0),90,replace=T),Y1=c(rep(1,30),rep(0,60)),Y2=c(rep(0,30),rep(1,30),rep(0,30)),Y3=c(rep(0,60),rep(1,30))) |> as_tibble()


# ------------------------------- model data ----------------------------------
# matrices for use in the NN functions

m <- 4 #hidden layer size
p <- 3 #predictors
q <- 3 #responses
N <- dat |> nrow()
X <- dat |> select(  1:p) |> as.matrix(ncol=p, nrow=N)
Y <- dat |> select(p+1:q) |> as.matrix(ncol=q, nrow=N)

npars <- p*p +2*p*m +m*m +m*q +p+m+m+q


g <- function(Yhat, Y){
  N <- nrow(Y)
  C <- numeric(N)
  for(i in 1:N){
    C[i] <- -log(Yhat[i, which.max(Y[i,])])
  }
  return(sum(C)) 
}

# --- activation functions ----------------------------------------------------

# augmentation layer
sig1 <- function(z)
{
  tanh(z)
}
# first m hidden layer
sig2 <- function(z)
{
  tanh(z)
}
# second m hidden layer
sig3 <- function(z)
{
  tanh(z)
}
# output layer
sig4 <- function(z)
{
  ez <- exp(z) 
  ez/matrix(1,q,q)%*%ez
}

# --- neural network ----------------------------------------------------------

af_net <- function(X, Y, theta, m, nu)
{
  N <- X |> nrow()
  p <- X |> ncol()
  q <- Y |> ncol()
  
  # allocating theta to weight and biases
  index <- 1:(p*p)
  # to auto-feature layer
  W1 <- matrix(theta[index],p,p)    # p*p
  index <- max(index)+1:(2*p*m)
  # from auto-feature (as well as from regular input) layer
  W2 <- matrix(theta[index],2*p,m)  # 2p*m
  index <- max(index)+1:(m*m)
  W3 <- matrix(theta[index],m,m)    # m*m
  index <- max(index)+1:(m*q)
  W4 <- matrix(theta[index],m,q)    # m*q
    
  index <- max(index)+1:p
  b1 <- matrix(theta[index],p,1)    # p*1
  index <- max(index)+1:m
  b2 <- matrix(theta[index],m,1)    # m*1
  index <- max(index)+1:m
  b3 <- matrix(theta[index],m,1)    # m*1
  index <- max(index)+1:q
  b4 <- matrix(theta[index],q,1)    # q*1
  
  
  # Forward Evaluation of network
  
  ones <- matrix(1,1,N) # vector of 1s transposed 1*N
  
  # auto-features inputs  
  A0   <- t(X)                        # p*N
  # regular inputs, followed by auto-features
  A1   <- sig1(t(W1)%*%A0 +b1%*%ones) # p*N
  # from auto-feature and regular inputs
  A1.  <- rbind(t(X),A1)              # 2p*N
  A2   <- sig2(t(W2)%*%A1.+b2%*%ones) # m*N
  A3   <- sig3(t(W3)%*%A2 +b3%*%ones) # m*N
  A4   <- sig4(t(W4)%*%A3 +b4%*%ones) # q*N
  Yhat <- t(A4)                       # N*q
  
  # Calculating error
  E1 <- g(Yhat,Y)
  # Penalised error
  E2 <- E1/N -nu/N*(sum(W1^2,W2^2,W3^2,W4^2)) #MSE_penalised
  
  # returning predictions and error
  return(list(Yhat = Yhat, E1 = E1, E2 = E2))
}

set.seed(2025)

valid_split <- 0.8

selected <- sample(1:N,valid_split*N,replace = F)

train_X  <- X[ selected,,drop=F]
train_Y  <- Y[ selected,,drop=F]
valid_X  <- X[-selected,,drop=F]
valid_Y  <- Y[-selected,,drop=F]

nu <- 0

obj_pen <- function(pars)
{
  res_model <- af_net(train_X,train_Y,pars,m,nu)
  return(res_model$E2)
}

max_iter   <- 1000
theta_rand <- runif(npars,-1,1)

res_opt <- nlm(obj_pen,theta_rand,iterlim = max_iter)

res_fitted <- af_net(X,Y,res_opt$estimate,m,0)

# the lengthy-execution part
n_val <- 10
nu_seq <- exp(seq(-6,2,length= n_val))

#should give us a progress bar, while computing the validation error for this
#neural net, for the given nu values.
# instead, somehow the nu value—defined in foreach—is not found? 
# it may have just been faster to process this sequentially, but i am (perhaps
# foolishly) committed to learning these. 
# I feel so close to a breakthrough I SWEAR

with_progress({
  p <- progressor(along=nu_seq)
  Val_error <- foreach(nu = nu_seq,
                       .combine = rbind) %dofuture%{
    
    #Validation analysis, per nu
    res_opt = nlm(obj_pen,theta_rand,iterlim=max_iter)
    
    res_val = af_net(valid_X,valid_Y,res_opt$estimate,m,0) 
    #we are done with the penalised values now
    error = res_val$E1
    
    p(sprintf("nu=%g",nu))
    data.frame(nu=nu, 
               error=error, 
               pid=Sys.getpid() # curiosity may kill this cat
               )
  } %seed% FALSE #no random numbers are generated within this as far as i'm aware, but double-check for consistency
},handlers=handler_pbcol())

Expected behavior

The nu value should be one of the most accessible values to the parallelised function, so this error really does feel shocking.

Session information

R version 4.4.2 (2024-10-31)
Platform: aarch64-apple-darwin20
Running under: macOS Sequoia 15.3.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Africa/Johannesburg
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] doFuture_1.0.2    future_1.40.0     foreach_1.5.2     progressr_0.15.1  plotly_4.10.4    
 [6] parallelly_1.43.0 futureverse_0.1.0 lubridate_1.9.4   forcats_1.0.0     stringr_1.5.1    
[11] dplyr_1.1.4       purrr_1.0.4       readr_2.1.5       tidyr_1.3.1       tibble_3.2.1     
[16] ggplot2_3.5.2     tidyverse_2.0.0  

loaded via a namespace (and not attached):
 [1] generics_0.1.3      stringi_1.8.7       listenv_0.9.1       hms_1.1.3          
 [5] digest_0.6.37       magrittr_2.0.3      evaluate_1.0.3      grid_4.4.2         
 [9] timechange_0.3.0    RColorBrewer_1.1-3  iterators_1.0.14    fastmap_1.2.0      
[13] jsonlite_2.0.0      httr_1.4.7          crosstalk_1.2.1     viridisLite_0.4.2  
[17] scales_1.4.0        codetools_0.2-20    lazyeval_0.2.2      cli_3.6.5          
[21] crayon_1.5.3        rlang_1.1.6         future.apply_1.11.3 yaml_2.3.10        
[25] withr_3.0.2         tools_4.4.2         parallel_4.4.2      tzdb_0.5.0         
[29] globals_0.17.0      vctrs_0.6.5         R6_2.6.1            lifecycle_1.0.4    
[33] htmlwidgets_1.6.4   pkgconfig_2.0.3     pillar_1.10.2       gtable_0.3.6       
[37] glue_1.8.0          data.table_1.17.0   highr_0.11          xfun_0.52          
[41] tidyselect_1.2.1    rstudioapi_0.17.1   knitr_1.50          farver_2.1.2       
[45] htmltools_0.5.8.1   labeling_0.4.3      compiler_4.4.2 

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions