R/hybrid.R

Defines functions splitHybridInteraction is.hybrid.ppm is.hybrid.interact is.hybrid

Documented in is.hybrid is.hybrid.interact is.hybrid.ppm splitHybridInteraction

#
#
#    hybrid.R
#
#    $Revision: 1.12 $	$Date: 2022/03/07 03:35:48 $
#
#    Hybrid of several interactions
#
#    Hybrid()    create a hybrid of several interactions
#                 [an object of class 'interact']
#	
#
# -------------------------------------------------------------------
#	

Hybrid <- local({

  Hybrid <- function(...) {
    interlist <- list(...)
    n <- length(interlist)
    if(n == 0)
      stop("No arguments given")
    #' arguments may be interaction objects or ppm objects
    isinter <- unlist(lapply(interlist, is.interact))
    isppm   <- unlist(lapply(interlist, is.ppm))
    if(any(nbg <- !(isinter | isppm)))
      stop(paste(ngettext(sum(nbg), "Argument", "Arguments"),
                 paste(which(nbg), collapse=", "),
                 ngettext(sum(nbg), "is not an interaction",
                          "are not interactions")))
    #' ensure the list contains only interaction objects
    if(any(isppm))
      interlist[isppm] <- lapply(interlist[isppm], as.interact)
    #' recursively expand any components that are themselves hybrids
    while(any(ishybrid <- unlist(lapply(interlist, is.hybrid)))) {
      i <- min(which(ishybrid))
      n <- length(interlist)
      expandi <- interlist[[i]]$par
      interlist <- c(if(i > 1) interlist[1:(i-1L)] else NULL,
                     expandi,
                     if(i < n) interlist[(i+1L):n] else NULL)
    }
    #' 
    ncomponents <- length(interlist)
    if(ncomponents == 1) {
      #' single interaction - return it
      return(interlist[[1L]])
    }
    #' ensure all components have names
    names(interlist) <- good.names(names(interlist),
                                   "HybridComponent", 1:ncomponents)
    #' check for infinite potential values
    haveInf <- lapply(interlist, getElement, name="hasInf")
    haveInf <- !sapply(haveInf, identical, y=FALSE)
    hasInf <- any(haveInf)
    #' determine interaction order
    maxorder <- max(sapply(interlist, interactionorder))
    #' build object
    out <- 
      list(
        name     = "Hybrid interaction",
        creator  = "Hybrid",
        family   = hybrid.family,
        order    = maxorder, # Overrides family$order
        pot      = NULL,
        par      = interlist,
        parnames = names(interlist),
        hasInf   = hasInf,
        selfstart = function(X, self) {
          ilist <- self$par
          sslist <- lapply(ilist, getElement, name="selfstart")
          has.ss <- sapply(sslist, is.function)
          if(any(has.ss)) {
            ilist[has.ss] <- lapply(ilist[has.ss], invokeSelfStart, Y=X)
            self$par <- ilist
          }
          return(self)
        },
        init     = NULL,
        update = NULL,  # default OK
        print = function(self, ..., family=FALSE, brief=FALSE) {
          if(family)
            print.isf(self$family)
          ncomponents <- length(self$par)
          clabs <- self$parnames
          splat("Hybrid of", ncomponents, "components:",
                commasep(sQuote(clabs)))
          for(i in 1:ncomponents) {
            splat(paste0(clabs[i], ":"))
            print(self$par[[i]], ..., family=family, brief=brief)
          }
          parbreak()
          return(invisible(NULL))
        },
        interpret =  function(coeffs, self) {
          interlist <- self$par
          result <- list(param=list(),
                         inames=character(0),
                         printable=list())
          for(i in 1:length(interlist)) {
            interI <- interlist[[i]]
            nameI  <- names(interlist)[[i]]
            nameI. <- paste(nameI, ".", sep="")
            #' find coefficients with prefix that exactly matches nameI.
            Cname  <- names(coeffs)
            prefixlength <- nchar(nameI.)
            Cprefix <- substr(Cname, 1, prefixlength)
            relevant <- (Cprefix == nameI.)
            #' extract them
            if(any(relevant)) {
              Crelevant <- coeffs[relevant]
              names(Crelevant) <-
                substr(Cname[relevant], prefixlength+1, max(nchar(Cname)))
              #' invoke the self-interpretation of interI
              interpretI <- interI$interpret
              if(is.function(interpretI)) {
                resultI <- interpretI(Crelevant, interI)
                paramI  <- resultI$param
                prinI   <- resultI$printable
                inamesI <- resultI$inames
                inamesI <- paste(nameI, inamesI)
                if(length(prinI) > 0) {
                  result$param     <- append(result$param, paramI)
                  result$printable <- append(result$printable, list(prinI))
                  result$inames <- c(result$inames, inamesI)
                }
              }
            }
          }
          return(result)
        },
        valid = function(coeffs, self) {
          #' check validity via mechanism used for 'rmhmodel' 
          siminfo <- .Spatstat.Rmhinfo[["Hybrid interaction"]]
          Z <- siminfo(coeffs, self)
          cifs   <- Z$cif
          pars   <- Z$par
          ntypes <- Z$ntypes
          if((Ncif <- length(cifs)) == 1) {
            #' single cif
            pars <- append(pars, list(beta=rep.int(1, ntypes)))
          } else {
            for(i in 1:Ncif) 
              pars[[i]] <- append(pars[[i]], list(beta=rep.int(1, ntypes[i])))
          }
          RM <- rmhmodel(cif=cifs, par=pars, types=1:max(ntypes), 
                         stopinvalid=FALSE)
          return(RM$integrable)
        },
        project = function(coeffs, self) {
          if((self$valid)(coeffs, self)) return(NULL)
          #' separate into components
          spl <- splitHybridInteraction(coeffs, self)
          interlist <- spl$interlist
          coeflist  <- spl$coeflist
          #' compute projection for each component interaction
          Ncif <- length(interlist)
          projlist <- vector(mode="list", length=Ncif)
          nproj    <- integer(Ncif)
          for(i in 1:Ncif) {
            coefsI <- coeflist[[i]]
            interI <- interlist[[i]]
            if(!is.interact(interI))
              stop("Internal error: interlist entry is not an interaction")
            projI <- interI$project
            if(is.null(projI))
              stop(paste("Projection is not yet implemented for a",
                         interI$name))
            p <- projI(coefsI, interI)
            #' p can be NULL (indicating no projection required for interI)
            #' or a single interaction or a list of interactions.
            if(is.null(p)) {
              if(Ncif == 1) return(NULL) # no projection required
              p <- list(NULL)
              nproj[i] <- 0
            } else if(is.interact(p)) {
              p <- list(p)
              nproj[i] <- 1L
            } else if(is.list(p) && all(unlist(lapply(p, is.interact)))) {
              nproj[i] <- length(p)
            } else
              stop("Internal error: result of projection had wrong format")
            projlist[[i]] <- p
          }
          #' for interaction i there are nproj[i] **new** interactions to try.
          if(all(nproj == 0))
            return(NULL)
          if(spatstat.options("project.fast")) {
            #' Single interaction required.
            #' Extract first entry from each list
            #' (there should be only one entry, but...)
            qlist <- lapply(projlist, "[[", i=1L)
            #' replace NULL entries by corresponding original interactions
            isnul <- unlist(lapply(qlist, is.null))
            if(all(isnul))
              return(NULL)
            if(any(isnul))
              qlist[isnul] <- interlist[isnul]
            names(qlist) <- names(interlist)
            #' build hybrid and return
            result <- do.call(Hybrid, qlist)
            return(result)
          } 
          #' Full case
          result <- list()
          for(i in which(nproj > 0)) {
            ntry <- nproj[i]
            tries <- projlist[[i]]
            for(j in 1:ntry) {
              #' assemble list of component interactions for hybrid
              qlist <- interlist
              qlist[[i]] <- tries[[j]]
              #' eliminate Poisson
              ispois <- unlist(lapply(qlist, is.poisson))
              if(all(ispois)) {
                #' collapse to single Poisson
                h <- Poisson()
              } else {
                if(any(ispois)) qlist <- qlist[!ispois]
                h <- do.call(Hybrid, qlist)
              }
              result <- append(result, list(h))
            }
          }
          #' 'result' is a list of interactions, each a hybrid
          if(length(result) == 1)
            result <- result[[1L]]
          return(result)
        },
        irange = function(self, coeffs=NA, epsilon=0, ...) {
          interlist <- self$par
          answer <- 0
          for(i in 1:length(interlist)) {
            interI <- interlist[[i]]
            nameI  <- names(interlist)[[i]]
            nameI. <- paste(nameI, ".", sep="")
            #' find coefficients with prefix that exactly matches nameI.
            if(all(is.na(coeffs)))
              Crelevant <- NA
            else {
              Cname  <- names(coeffs)
              prefixlength <- nchar(nameI.)
              Cprefix <- substr(Cname, 1, prefixlength)
              relevant <- (Cprefix == nameI.)
              #' extract them
              Crelevant <- coeffs[relevant]
              names(Crelevant) <-
                substr(Cname[relevant], prefixlength+1, max(nchar(Cname)))
            }
            #' compute reach 
            reachI <- interI$irange
            if(is.function(reachI)) {
              resultI <- reachI(interI,
                                coeffs=Crelevant, epsilon=epsilon, ...)
              answer <- max(answer, resultI)
            }
          }
          return(answer)
        },
        hardcore = function(self, coeffs=NA, epsilon=0, ...) {
          interlist <- self$par
          n <- length(interlist)
          results <- vector(mode="list", length=n)
          for(i in seq_len(n)) {
            interI <- interlist[[i]]
            nameI  <- names(interlist)[[i]]
            nameI. <- paste(nameI, ".", sep="")
            #' find coefficients with prefix that exactly matches nameI.
            if(all(is.na(coeffs)))
              Crelevant <- NA
            else {
              Cname  <- names(coeffs)
              prefixlength <- nchar(nameI.)
              Cprefix <- substr(Cname, 1, prefixlength)
              relevant <- (Cprefix == nameI.)
              #' extract them
              Crelevant <- coeffs[relevant]
              names(Crelevant) <-
                substr(Cname[relevant], prefixlength+1, max(nchar(Cname)))
            }
            #' compute hard core for component i
            hardI <- interI$hardcore
            if(is.function(hardI)) 
              results[[i]] <- hardI(interI,
                                    coeffs=Crelevant, epsilon=epsilon, ...)
          }
          ## collate answers
          results <- results[!sapply(results, is.null)]
          if(length(results) == 0)
            return(0)
          values <- Reduce(function(...) pmax(..., na.rm=TRUE), results)
          dims <- lapply(results, dim)
          dims <- dims[!sapply(dims, is.null)]
          if(length(dims) == 0)
            return(values)
          dims <- unique(dims)
          if(length(dims) > 1)
            stop("Incompatible matrix dimensions of hardcore distance matrices in hybrid")
          d <- dims[[1]]
          dn <- unique(lapply(results, dimnames))[[1]]
          answer <- matrix(values, d[1], d[2], dimnames=dn)
          return(answer)
        },
        version=versionstring.spatstat()
        )
    class(out) <- "interact"
    return(out)
  }

  invokeSelfStart <- function(inte, Y) {
    ss <- inte$selfstart
    if(!is.function(ss)) return(inte)
    return(ss(Y, inte))
  }

  Hybrid
})


is.hybrid <- function(x) { UseMethod("is.hybrid") }

is.hybrid.interact <- function(x) {
  return(is.interact(x) && (x$name == "Hybrid interaction"))
}

is.hybrid.ppm <- function(x) {
  return(is.hybrid(as.interact(x)))
}

splitHybridInteraction <- function(coeffs, inte) {
  # For hybrids, $par is a list of the component interactions,
  # but coeffs is a numeric vector. 
  # Split the coefficient vector into the relevant coeffs for each interaction
  interlist <- inte$par
  N <- length(interlist)
  coeflist <- vector(mode="list", length=N)
  for(i in 1:N) {
    interI <- interlist[[i]]
    # forbid hybrids-of-hybrids - these should not occur anyway
    if(interI$name == "Hybrid interaction")
      stop("A hybrid-of-hybrid interactions is not implemented")
    # nameI is the tag that identifies I-th component in hybrid
    nameI  <- names(interlist)[[i]]
    nameI. <- paste(nameI, ".", sep="")
    # find coefficients with prefix that exactly matches nameI.
    Cname  <- names(coeffs)
    prefixlength <- nchar(nameI.)
    Cprefix <- substr(Cname, 1, prefixlength)
    relevant <- (Cprefix == nameI.)
    # extract coefficients
    #   (there may be none, if this interaction is Poisson or an 'offset')
    coeffsI <- coeffs[relevant]
    # remove the prefix so the coefficients are recognisable to interaction
    if(any(relevant)) 
      names(coeffsI) <-
        substr(Cname[relevant], prefixlength+1, max(nchar(Cname)))
    # store
    coeflist[[i]] <- coeffsI
  }
  names(coeflist) <- names(interlist)
  return(list(coeflist=coeflist, interlist=interlist))
}

Hybrid <- intermaker(Hybrid, list(creator="Hybrid",
                                  name="general hybrid Gibbs process",
                                  par=list("..."=42),
                                  parnames=list("any list of interactions")))

Try the spatstat.model package in your browser

Any scripts or data that you put into this service are public.

spatstat.model documentation built on June 8, 2025, 12:26 p.m.