运行 NetLogo 模型使用 parApply 时仅使用一个处理器

Only one processor being used while running NetLogo models using parApply

我正在使用 'RNetLogo' 包对我的 NetLogo 模型进行 运行 灵敏度分析。我的模型有 24 个我需要改变的参数——所以并行化这个过程是最理想的!我一直在关注 Thiele 的“Parallel processing with the RNetLogo package”小插图中的示例,该小插图将 'parallel' 包与 'RNetLogo' 结合使用。

我已经设法让 R 在我的所有 12 个处理器上初始化 NetLogo 模型,我已经使用 gui=TRUE 验证了这一点。当我尝试 运行 使用 'parApply' 跨 12 个处理器的模拟代码时,问题就来了。这行 运行s 没有错误,但它只在其中一个处理器上 运行s(使用了我总 CPU 功率的大约 8%)。这是我的 R 代码文件的模型 - 我在最后包含了一些注释掉的代码,展示了我如何 运行 模拟而不尝试并行化:

### Load packages
library(parallel)

### Set up initialisation function
prepro <- function(dummy, gui, nl.path, model.path) {
          library(RNetLogo)
          NLStart(nl.path, gui=gui)
          NLLoadModel(model.path)
}

### Set up finalisation function
postpro <- function(x) {
           NLQuit()
}

### Set paths
# For NetLogo
nl.path <- "C:/Program Files/NetLogo 6.0/app"
nl.jarname <- "netlogo-6.0.0.jar"
# For the model
model.path <- "E:/Model.nlogo"
# For the function "sim" code
sim.path <- "E:/sim.R"

### Set base values for parameters
base.param <- c('prey-max-velocity'                  = 25,
                'prey-agility'                       = 3.5,
                'prey-acceleration'                  = 20,
                'prey-deceleration'                  = 25,
                'prey-vision-distance'               = 10,
                'prey-vision-angle'                  = 240,
                'time-to-turn'                       = 5,
                'time-to-return-to-foraging'         = 300,
                'time-spent-circling'                = 2,
                'predator-max-velocity'              = 35,
                'predator-agility'                   = 3.5,
                'predator-acceleration'              = 20,
                'predator-deceleration'              = 25,
                'predator-vision-distance'           = 20,
                'predator-vision-angle'              = 200,
                'time-to-give-up'                    = 120,
                'number-of-safe-zones'               = 1,
                'number-of-target-patches'           = 5,
                'proportion-obstacles'               = 0.05,
                'obstacle-radius'                    = 2.0,
                'obstacle-radius-range'              = 0.5,
                'obstacle-sensitivity-for-prey'      = 0.95,
                'obstacle-sensitivity-for-predators' = 0.95,
                'safe-zone-attractiveness'           = 500
)

## Get names of parameters
param.names <- names(base.param)

### Load the code of the simulation function (name: sim)
source(file=sim.path)

### Convert "base.param" to a matrix, as required by parApply
base.param <- matrix(base.param, nrow=1, ncol=24)

### Get the number of simulations we want to run
design.combinations <- length(base.param[[1]])
already.processed <- 0

### Initialise NetLogo
processors <- detectCores()
cl <- makeCluster(processors)
clusterExport(cl, 'sim')
gui <- FALSE
invisible(parLapply(cl, 1:processors, prepro, gui=gui, nl.path=nl.path, model.path=model.path))

### Run the simulation across all processors, using parApply
sim.result.base <- parApply(cl, base.param, 1, sim,
                            param.names, 
                            no.repeated.sim = 100,
                            trace.progress = FALSE, 
                            iter.length = design.combinations, 
                            function.name = "base parameters")

### Run the simulation on a single processor
#sim.result.base <- sim(base.param, 
#                       param.names, 
#                       no.repeated.sim = 100, 
#                       my.nl1, 
#                       trace.progress = TRUE, 
#                       iter.length = design.combinations, 
#                       function.name = "base parameters")

这是 'sim' 函数的模型(改编自 Thiele 的论文 "Facilitating parameter estimation and sensitivity analyses of agent-based models - a cookbook using NetLogo and R"):

sim <- function(param.set, parameter.names, no.repeated.sim, trace.progress, iter.length, function.name) {

  # Some security checks
  if (length(param.set) != length(parameter.names))
  { stop("Wrong length of param.set!") }
  if (no.repeated.sim <= 0)
  { stop("Number of repetitions must be > 0!") }
  if (length(parameter.names) <= 0)
  { stop("Length of parameter.names must be > 0!") }

   # Create an empty list to save the simulation results
   eval.values <- NULL

   # Run the repeated simulations (to control stochasticity)
   for (i in 1:no.repeated.sim)
   {
     # Create a random-seed for NetLogo from R, based on min/max of NetLogo's random seed
     NLCommand("random-seed",runif(1,-2147483648,2147483647))

     ## This is the stuff for one simulation
     cal.crit <- NULL

     # Set NetLogo parameters to current parameter values
     lapply(seq(1:length(parameter.names)), function(x) {NLCommand("set ",parameter.names[x], param.set[x])})
     NLCommand("setup")
     # This should run "go" until prey-win =/= 5, i.e. when the pursuit ends
     NLDoCommandWhile("prey-win = 5", "go")

     # Report a value
     prey <- NLReport("prey-win")
     # Report another value
     pred <- NLReport("predator-win")

     ## Extract the values we are interested in
     cal.crit <- rbind(cal.crit, c(prey, pred))

     # append to former results
     eval.values <- rbind(eval.values,cal.crit)
   }

   ## Make sure eval.values has column names
   names(eval.values) <- c("PreySuccess", "PredSuccess")

   # Return the mean of the repeated simulation results
   if (no.repeated.sim > 1) {
     return(colMeans(eval.values))
   }
   else {
     return(eval.values)
   }
}

我认为问题可能在于 "nl.obj" 字符串,RNetLogo 使用该字符串来识别您想要 运行 代码的 NetLogo 实例 - 但是,我尝试了几种不同的修复方法这个,我还没有想出一个可行的解决方案。当我使用 Thiele 示例中提供的代码跨所有处理器初始化 NetLogo 时,我没有为每个实例设置 "nl.obj" 值,所以我猜 RNetLogo 使用某种默认列表?但是,在 Thiele 的原始代码中,"sim" 函数要求您指定要在哪个 NetLogo 实例上 运行 - 所以当我尝试 运行 最后一行时 R 会报错(checkForRemoteErrors(val) 中的错误:一个节点产生错误:缺少参数 "nl.obj",没有默认值)。我已经修改了 "sim" 函数代码,这样它就不需要这个参数并且只接受 nl.obj 的默认设置 - 但是我的模拟只在单个处理器上 运行s。所以,我认为默认情况下,"sim" 必须只在 NetLogo 的单个实例上 运行ning 代码。我不确定如何修复它。

这也是我第一次使用 'parallel' 包,所以我可能遗漏了一些与 'parApply' 明显相关的东西。任何见解将不胜感激!

提前致谢!

我仍在应用类似的技术对我的 NetLogo 模型执行莫里斯基本效应筛选。对我来说,并行执行工作正常。我将您的脚本与我的脚本进行了比较,发现在我的版本中,模拟函数 (simfun) 的 'parApply' 调用嵌入在函数语句中(见下文)。也许包括该功能已经解决了您的问题。

  sim.results.morris <- parApply(cl, mo$X, 1, function(x) {simfun(param.set=x, 
                                                           no.repeated.sim=no.repeated.sim, 
                                                           parameter.names=input.names, 
                                                           iter.length=iter.length, 
                                                           fixed.values=fixed.values, 
                                                           model.seed=new.model.seed, 
                                                           function.name="Morris")})