R/mcmc.R

Defines functions gp.sampler top.sampler map.sampler summary.sampler print.sampler steps.sampler steps burnin burnin.sampler burnin length.sampler statistics.sampler statistics samplers drawSamplesByStepCount drawSamplesByTime draw

Documented in burnin burnin.sampler draw drawSamplesByStepCount drawSamplesByTime gp.sampler length.sampler map.sampler print.sampler samplers statistics statistics.sampler steps steps.sampler summary.sampler top.sampler

# Part of the "structmcmc" package, https://github.com/rjbgoudie/structmcmc
#
# This software is distributed under the GPL-3 license.  It is free,
# open source, and has the attribution requirements (GPL Section 7) in
#   https://github.com/rjbgoudie/structmcmc
#
# Note that it is required that attributions are retained with each function.
#
# Copyright 2008 Robert J. B. Goudie, University of Warwick

#' Draw samples from a MCMC sampler.
#'
#' Draws samples from an MCMC sampler. The length of the run can be specified
#' either by the number of samples to be drawn, or the length of time that
#' the sampler runs.
#'
#' @param sampler A sampler object
#' @param n The number of samples to draw. Set this to \code{FALSE} if
#'   using the \code{time} argument.
#' @param time The number of seconds to spend drawing samples. Set this to
#'   \code{FALSE} if using the \code{n} argument.
#' @param burnin The number of samples to discard from the beginning of
#'   the sample.
#' @param thin The frequency with which samples should be kept. eg for
#'   \code{thin = 3}, every third sample will be kept.
#' @param verbose A logical. Should a progress bar be displayed?
#' @export
#' @seealso \code{\link{drawSamplesByTime}},
#'   \code{\link{drawSamplesByStepCount}}. \code{\link{BNSampler}},
#'   \code{\link{BNGibbsSampler}}, \code{\link{BNSamplerMJ}}
#' @examples
#' set.seed(310)
#' x1 <- factor(c(1, 1, 0, 1))
#' x2 <- factor(c(0, 1, 0, 1))
#' dat <- data.frame(x1 = x1, x2 = x2)
#'
#' prior <- function(net) 1
#' initial <- bn(c(), c())
#'
#' sampler <- BNSampler(dat, initial, prior)
#' samples <- draw(sampler, n = 5)
draw <- function(sampler, n = F, time = F, burnin = 0, thin = 1, verbose = T){
  stopifnot(identical(n, F) || inherits(n, c("integer", "numeric")),
            identical(n, F) || is.wholenumber(n),
            identical(F, time) || inherits(time, c("integer", "numeric")),
            inherits(burnin, c("integer", "numeric")),
            is.wholenumber(burnin),
            inherits(verbose, "logical"),
            inherits(thin, "logical") ||
              (inherits(thin, c("integer", "numeric")) &&
               is.wholenumber(thin)))

  nSteps <- get("nSteps", envir = environment(sampler))

  if (nSteps < burnin){
    burninleft <- burnin - nSteps
    if (inherits(time, c("integer", "numeric")) && thin > 1){
      burninleft <- burninleft + thin - burninleft %% thin
    }
  } else {
    burninleft <- 0
  }

  runLengthByTime <- identical(n, F)
  if (runLengthByTime){
    drawSamplesByTime(sampler    = sampler,
                      time       = time,
                      burnin     = burnin,
                      burninleft = burninleft,
                      thin       = thin,
                      verbose    = verbose)
  } else {
    lengthout <- max(0, floor((n - burninleft)/thin))
    drawSamplesByStepCount(sampler    = sampler,
                           n          = n,
                           burnin     = burnin,
                           burninleft = burninleft,
                           lengthout  = lengthout,
                           thin       = thin,
                           verbose    = verbose)
  }
}


#' Draw samples from a MCMC sampler, by time.
#'
#' Draws samples from an MCMC sampler for a specified number of seconds.
#'
#' @param sampler A sampler object
#' @param time The number of seconds to spend drawing samples.
#' @param burnin The number of samples to discard from the beginning of
#'   the sample (includes previous runs)
#' @param burninleft The number of samples still to be discarded.
#' @param thin The frequency with which samples should be kept. eg for
#'   \code{thin = 3}, every third sample will be kept.
#' @param verbose A logical. Should a progress bar be displayed?
#' @param samplesIncrement The size by which the samples should be
#'   incremented.
#' @export
#' @seealso \code{\link{draw}}, \code{\link{drawSamplesByStepCount}}
#' @examples
#' set.seed(310)
#' x1 <- factor(c(1, 1, 0, 1))
#' x2 <- factor(c(0, 1, 0, 1))
#' dat <- data.frame(x1 = x1, x2 = x2)
#'
#' prior <- function(net) 1
#' initial <- bn(c(), c())
#'
#' sampler <- BNSampler(dat, initial, prior)
#' samples <- draw(sampler, time = 15)
drawSamplesByTime <- function(sampler,
                              time,
                              burnin,
                              burninleft,
                              thin,
                              verbose,
                              samplesIncrement = 10000){
  # Note: the initial graph is NOT returned at the moment
  samples <- vector("list", samplesIncrement)
  if (verbose){
    cat("Drawing samples, for about", time, "seconds\n")
    flush.console()
    pb <- txtProgressBar(max = time, style = 3)
    setTxtProgressBar(pb, 0)
  }

  i <- 1
  elapsed <- 1
  start <- proc.time()
  while (elapsed < time + 1){

    out <- sampler(i, burnin = burnin)

    if (i %% thin == 0 && i > burninleft){
      class(out) <- c("bn", "parental")
      samples[[(i-burninleft)/thin]] <- out
    }

    if (i %% samplesIncrement == 0){
      oldLength <- length(samples)
      newLength <- oldLength + samplesIncrement
      newSamples <- vector("list", newLength)
      newSamples[1:oldLength] <- samples
      samples <- newSamples
    }

    elapsed <- (proc.time() - start)[[3]]
    if (verbose){
      setTxtProgressBar(pb, value = elapsed)
    }
    i <- i + 1
  }

  if (verbose){
    close(pb)
    cat("Drew", i, "samples in", elapsed, "seconds")
  }

  samples <- samples[seq_len(max(0,i-burninleft - 1))]
  class(samples) <- c("mcmcbn", "bn.list", "parental.list")
  samples
}

#' Draw samples from a MCMC sampler, by step count.
#'
#' Draws a specific number of samples from an MCMC sampler.
#'
#' @param sampler A sampler object
#' @param n The number of samples to draw.
#' @param burnin The number of samples to discard from the beginning of
#'   the sample (includes previous runs)
#' @param burninleft The number of samples still to be discarded.
#' @param lengthout The length of the final samples object
#' @param thin The frequency with which samples should be kept. eg for
#'   \code{thin = 3}, every third sample will be kept.
#' @param verbose A logical. Should a progress bar be displayed?
#' @export
#' @seealso \code{\link{draw}}, \code{\link{drawSamplesByTime}}
#' @examples
#' set.seed(310)
#' x1 <- factor(c(1, 1, 0, 1))
#' x2 <- factor(c(0, 1, 0, 1))
#' dat <- data.frame(x1 = x1, x2 = x2)
#'
#' prior <- function(net) 1
#' initial <- bn(c(), c())
#'
#' sampler <- BNSampler(dat, initial, prior)
#' samples <- draw(sampler, n = 100)
drawSamplesByStepCount <- function(sampler,
                                   n,
                                   burnin,
                                   burninleft,
                                   lengthout,
                                   thin,
                                   verbose){
  # Note: the initial graph is NOT returned at the moment
  samples <- vector("list", lengthout)
  if (verbose){
    cat("Drawing", n, "samples\n")
    flush.console()
    progress <- txtProgressBar(max = n, style = 3)
    setTxtProgressBar(progress, 0)
  }

  start <- proc.time()
  i <- 1
  while (i <= n){
    out <- sampler(i, burnin = burnin)

    if (i %% thin == 0 && i > burninleft){
      class(out) <- c("bn", "parental")
      samples[[(i-burninleft)/thin]] <- out
    }

    if (verbose){
      setTxtProgressBar(progress, i)
    }
    i <- i + 1
  }

  if (verbose){
    close(progress)
    elapsed <- (proc.time() - start)[[3]]
    cat("Drew", n, "samples in", elapsed, "seconds")
  }

  class(samples) <- c("mcmcbn", "bn.list", "parental.list")
  samples
}

#' List of MCMC Samplers
#'
#' method description
#'
#' @param ... Further arguments passed to method
#' @export
samplers <- function(...){
  x <- list(...)
  class(x) <- "samplers"
  x
}

#' Extract statistics from a sampler.
#'
#' Extracts the statistics collected during an MCMC run
#'
#' @param x A sampler
#' @param ... Further arguments passed to method
#' @return A list of statistics collected during the MCMC run.
#' @export
#' @seealso \code{\link{statistics.sampler}}
statistics <- function(x, ...){
  UseMethod("statistics")
}

#' Extract statistics from a sampler.
#'
#' Extracts the statistics collected during an MCMC run
#'
#' @param x A sampler
#' @param names Which statistics to extract? A character vector of
#'   statistics collected during the MCMC run.
#' @param ... Further arguments passed to method
#' @return A list of statistics collected during the MCMC run.
#' @export
#' @seealso \code{\link{statistics}}
statistics.sampler <- function(x, names, ...){
  stopifnot(inherits(x, "sampler"),
            length(names) > 0,
            inherits(names, "character"))

  statisticsTable <- get("statisticsTable", envir = environment(x))
  statisticsTable <- statisticsTable[, names, drop = F]
  nSteps <- get("nSteps", envir = environment(x))
  nBurnin <- get("nBurnin", envir = environment(x))
  statisticsTable[seq_len(nSteps - nBurnin), , drop = T]
}

#' Number of samples drawn.
#'
#' Returns the number of samples (MCMC steps) drawn in the supplied sampler.
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @return The number of samples (steps) that have been drawn.
#' @S3method length sampler
#' @method length sampler
length.sampler <- function(x, ...){
  stopifnot(inherits(x, "sampler"))
  steps(x) - burnin(x)
}

#' Extract burnin
#'
#' Extracts the amount of burnin used in a sampler
#'
#' @param x A sampler
#' @param ... Further arguments passed to method
#' @return An integer amount of burnin
#' @export
#' @seealso \code{\link{steps.sampler}}
burnin <- function(x, ...){
  UseMethod("burnin")
}

#' Extract burnin
#'
#' Extracts the amount of burnin used in a sampler
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @return An integer amount of burnin
#' @S3method burnin sampler
#' @method burnin sampler
burnin.sampler <- function(x, ...){
  stopifnot(inherits(x, "sampler"))
  get("nBurnin", envir = environment(x))
}

#' Extract burnin
#'
#' Extracts the amount of burnin used in a sampler
#'
#' @param x A sampler
#' @param ... Further arguments passed to method
#' @return An integer amount of burnin
#' @export
#' @seealso \code{\link{steps.sampler}}
burnin <- function(x, ...){
  UseMethod("burnin")
}

#' Number of samples drawn.
#'
#' Returns the number of samples (MCMC steps) drawn in the supplied sampler.
#'
#' @param x A sampler
#' @param ... Further arguments passed to method
#' @return The number of samples (steps) that have been drawn.
#' @export
#' @seealso \code{\link{steps.sampler}}
steps <- function(x, ...){
  UseMethod("steps")
}

#' Number of samples drawn.
#'
#' Returns the number of samples (MCMC steps) drawn in the supplied sampler.
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @return The number of samples (steps) that have been drawn.
#' @S3method length sampler
#' @method length sampler
steps.sampler <- function(x, ...){
  stopifnot(inherits(x, "sampler"))
  get("nSteps", envir = environment(x))
}

#' Print a sampler
#'
#' Prints a sampler
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @S3method print sampler
#' @method print sampler
print.sampler <- function(x, ...){
  cat("Number of steps: ", length(x))
  invisible(x)
}

#' Summarising MCMC samplers
#'
#' \code{summary} method for class "\code{sampler}"
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @S3method summary sampler
#' @method summary sampler
summary.sampler <- function(x, ...){
  cat("Number of steps: ", length(x))
  invisible(x)
}

#' Retreive MAP from sampler
#'
#' \code{map} method for class "\code{sampler}"
#'
#' @param x A sampler
#' @param ... Further arguments, currently unused
#' @S3method map sampler
#' @method map sampler
map.sampler <- function(x, ...){
  count <- get("count", envir = environment(x))
  count <- unlist(as.list(count))
  id <- names(count)[order(count, decreasing = T)[1]]
  as.bn(id)
}

#' Retreive top graph from sampler
#'
#' \code{top} method for class "\code{sampler}"
#'
#' @param x A sampler
#' @param head The number of graphs to consider
#' @param ... Further arguments, currently unused
#' @S3method top sampler
#' @method top sampler
top.sampler <- function(x, head = 10, ...){
  count <- get("count", envir = environment(x))
  count <- unlist(as.list(count))
  o <- order(count, decreasing = T)
  s <- seq_len(min(head, length(count)))
  id <- names(count)[o[s]]
  as.bn(id)
}

#' Retreive graph probabilities from sampler
#'
#' \code{gp} method for class "\code{sampler}"
#'
#' @param x A sampler
#' @param head The number of graphs to consider
#' @param ... Further arguments, currently unused
#' @S3method gp sampler
#' @method gp sampler
gp.sampler <- function(x, head = 10000, ...){
  count <- get("count", envir = environment(x))
  count <- unlist(as.list(count))
  o <- order(count, decreasing = T)
  s <- seq_len(min(head, length(count)))
  count <- count[o[s]]
  count/sum(count)
}
rjbgoudie/structmcmc documentation built on Nov. 3, 2020, 3:41 a.m.