## Paul E. Johnson <pauljohn@ku.edu>
## Portable Parallel Streams Project.
##' Creates a portable sets of seeds to be used for replication of simulations.
##' The seeds are suitable for replicatable parallel computations. This creates
##' the seed object and, if the file argument is specified, it writes the object
##' on disk.
##'
##' Using the L'Ecuyer-CMRG random generator provided by the R package parallel,
##' this follows the strategy outlined in L'Ecuyer, et. al (2002). The very very
##' long random stream is subdivided into smaller streams, thus allowing the
##' creation of several separate streams of random numbers for each of the runs
##' of a simulation exercise.
##'
##' This makes it possible to execute a batch of simulations, and then later
##' re-start any particular run of interest. This works whether the calculations
##' are done in parallel on a cluster or serial on a single computer. This
##' function takes into account the possibility that, within each run of the
##' model, it may be necessary to draw values from several separate streams of
##' random numbers (for example, to keep separate streams for the generation of
##' data about teachers, students, school buildings, and so forth). This
##' function creates the seeds, allowing for accurate replication on a diverse
##' network of computing devices. It generates the seeds of many separate
##' streams and returns them in an object (and also writes them in a file). The
##' seeds to re-start a simulation are kept in a list object.
##'
##' The user specifies the number of simulation runs, "nReps," so the output
##' list will have "nReps" elements. In each of those objects inside the list,
##' there will be seeds to initialize "streamsPerRep" streams.
##'
##' @export seedCreator
##' @param nReps Number of replications for which starting seed values are to be
##' created.
##' @param streamsPerRep Number of streams to be created for each separate
##' simulation.
##' @param seed An integer seed value that can be used to initialize the
##' creation of the many-separate-substreams.
##' @param file The file name in which the list of stream seeds is to be
##' collected, by default, that file is called "projSeeds.rds".
##' @return A list that includes "nReps" elements. Each element is a vector of
##' "streamsPerRep" stream starting values.
##' @author Paul E. Johnson <pauljohn@@ku.edu>
##' @import parallel
##' @importFrom stats runif
##' @seealso snowft, streams, parallel
##' @references L'Ecuyer, P. (1999). Good Parameters and Implementations for
##' Combined Multiple Recursive Random Number Generators. Operations Research,
##' 47(1), 159-164. L'Ecuyer, P., Simard, R., Chen, E. J., & Kelton, W. D.
##' (2002). An Object-Oriented Random-Number Package with Many Long Streams
##' and Substreams. Operations Research, 50(6), 1073-1075.
##' @examples
##' projSeeds <- seedCreator(2000, 3, seed = 123456, file = "fruits.rds")
##' A1 <- projSeeds[[787]]
##'
##' rm(projSeeds)
##' # read from file, take run 787's seed
##' myFruitySeeds <- readRDS("fruits.rds")
##' B1 <- myFruitySeeds[[787]]
##'
##' identical(A1, B1) # check
##' unlink("fruits.rds") #delete file
seedCreator <- function(nReps = 2000, streamsPerRep = 3, seed, file = NULL){
RNGkind("L'Ecuyer-CMRG")
## projSeeds=list of lists of stream seeds
projSeeds <- vector(mode="list", length = nReps)
for (i in 1:nReps) projSeeds[[i]] <- vector(mode="list", streamsPerRep)
if (!exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE))
runif(1) ##establishes .Random.seed
if (!missing(seed)) set.seed(seed)
##Grab first seed
s <- .Random.seed
origSeed <- s
for (i in 1:nReps) {
for (j in 1:streamsPerRep){
projSeeds[[i]][[j]] <- s
s <- parallel::nextRNGStream(s)
}
}
class(projSeeds) <- "portableSeeds"
if (!is.null(file)) saveRDS(projSeeds, file)
invisible(projSeeds)
}
##' Add initializing streams for more runs to a portableSeeds object
##'
##' After seedCreator has created a collection of seeds, sometimes it
##' is necessary to conduct additional runs that require more
##' initializing values. This function fulfills that need.
##'
##' @param object A portableSeeds object created by seedCreator
##' @param more An integer indicating the number of additional runs
##' for which seeds are to be created.
##' @param file A quoted character string representing file name into
##' which the portableSeeds object is to be placed.
##' @param ... Additional arguments, none of which are put to any use
##' @return A \code{portableSeeds} object that includes the original set, with
##' \code{more} seeds added to the end. The entirety of this returned set
##' should equal the set that would have been generated by seedCreator if
##' the same number of seeds had been requested.
##' @author Paul E. Johnson <pauljohn@@ku.edu>
##' @export
##' @rdname update.portableSeeds
##' @method update portableSeeds
##' @examples
##' require(parallel)
##' projSeeds <- seedCreator(2000, 3, seed = 123456, file = "someSeeds.rds")
##' projSeeds2 <- update(projSeeds, more = 19, file = "somePlusSeeds.rds")
##' identical(projSeeds[[1]], projSeeds2[[1]])
##' identical(projSeeds[[2000]], projSeeds2[[2000]])
##' projSeeds2[[2001]]
##' projSeedsLast <- projSeeds[[2000]][[3]]
##' projSeedsNext <- parallel::nextRNGStream(projSeedsLast)
##' identical(projSeedsNext, projSeeds2[[2001]][[1]])
update.portableSeeds <- function(object, more, file = NULL, ...){
RNGkind("L'Ecuyer-CMRG")
if ( (missing(object)) | (!class(object)== "portableSeeds") ) {
stop("update requires an input object of type projSeeds")
}
if (missing(more)){
msg <- paste("update needs to know how many more",
"runs-worth of seeds you want to add to the collection")
stop(msg)
}
nReps <- length(object)
streamsPerRep <- length(object[[1]])
s <- object[[nReps]][[streamsPerRep]]
s <- parallel::nextRNGStream(s)
newSeeds <- vector(mode="list", length = more)
for (i in 1:more) newSeeds[[i]] <- vector(mode="list", streamsPerRep)
object <- c(object, newSeeds)
for (i in (1 + nReps):(nReps + more)) {
for (j in 1:streamsPerRep){
object[[i]][[j]] <- s
s <- parallel::nextRNGStream(s)
}
}
class(object) <- "portableSeeds"
if (!is.null(file)) saveRDS(object, file)
invisible(object)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.