#
#
# geyer.S
#
# $Revision: 2.46 $ $Date: 2022/05/21 08:53:38 $
#
# Geyer's saturation process
#
# Geyer() create an instance of Geyer's saturation process
# [an object of class 'interact']
#
#
Geyer <- local({
# .......... template ..........
BlankGeyer <-
list(
name = "Geyer saturation process",
creator = "Geyer",
family = "pairsat.family", # evaluated later
pot = function(d, par) {
(d <= par$r) # same as for Strauss
},
par = list(r = NULL, sat=NULL), # filled in later
parnames = c("interaction distance","saturation parameter"),
hasInf = FALSE,
init = function(self) {
r <- self$par$r
sat <- self$par$sat
if(!is.numeric(r) || length(r) != 1 || r <= 0)
stop("interaction distance r must be a positive number")
if(!is.numeric(sat) || length(sat) != 1 || sat < 0)
stop("saturation parameter sat must be a positive number")
},
update = NULL, # default OK
print = NULL, # default OK
plot = function(fint, ..., d=NULL, plotit=TRUE) {
verifyclass(fint, "fii")
inter <- fint$interaction
unitz <- unitname(fint)
if(!identical(inter$name, "Geyer saturation process"))
stop("Tried to plot the wrong kind of interaction")
#' fitted interaction coefficient
theta <- fint$coefs[fint$Vnames]
#' interaction radius
r <- inter$par$r
sat <- inter$par$sat
xlim <- resolve.1.default(list(xlim=c(0, 1.25 * r)), list(...))
rmax <- max(xlim, d)
if(is.null(d)) {
d <- seq(from=0, to=rmax, length.out=1024)
} else {
stopifnot(is.numeric(d) &&
all(is.finite(d)) &&
all(diff(d) > 0))
}
#' compute interaction between two points at distance d
y <- exp(theta * sat * (d <= r))
#' compute `fv' object
fun <- fv(data.frame(r=d, h=y, one=1),
"r", substitute(h(r), NULL), "h", cbind(h,one) ~ r,
xlim, c("r", "h(r)", "1"),
c("distance argument r",
"maximal interaction h(r)",
"reference value 1"),
unitname=unitz)
if(plotit)
do.call(plot.fv,
resolve.defaults(list(fun),
list(...),
list(ylim=range(0,1,y))))
return(invisible(fun))
},
#' end of function 'plot'
interpret = function(coeffs, self) {
loggamma <- as.numeric(coeffs[1L])
gamma <- exp(loggamma)
return(list(param=list(gamma=gamma),
inames="interaction parameter gamma",
printable=dround(gamma)))
},
valid = function(coeffs, self) {
loggamma <- as.numeric(coeffs[1L])
sat <- self$par$sat
return(is.finite(loggamma) && (is.finite(sat) || loggamma <= 0))
},
project = function(coeffs, self) {
if((self$valid)(coeffs, self)) return(NULL) else return(Poisson())
},
irange = function(self, coeffs=NA, epsilon=0, ...) {
r <- self$par$r
if(any(!is.na(coeffs))) {
loggamma <- coeffs[1L]
if(!is.na(loggamma) && (abs(loggamma) <= epsilon))
return(0)
}
return(2 * r)
},
version=NULL, # evaluated later
# fast evaluation is available for the border correction only
can.do.fast=function(X,correction,par) {
return(all(correction %in% c("border", "none")))
},
fasteval=function(X,U,EqualPairs,pairpot,potpars,correction,
splitInf=FALSE,
..., halfway=FALSE, check=TRUE) {
#' fast evaluator for Geyer interaction
dont.complain.about(splitInf)
if(!all(correction %in% c("border", "none")))
return(NULL)
if(spatstat.options("fasteval") == "test")
message("Using fast eval for Geyer")
r <- potpars$r
sat <- potpars$sat
# first ensure all data points are in U
nX <- npoints(X)
nU <- npoints(U)
Xseq <- seq_len(nX)
if(length(EqualPairs) == 0) {
# no data points currently included
missingdata <- rep.int(TRUE, nX)
} else {
Xused <- EqualPairs[,1L]
missingdata <- !(Xseq %in% Xused)
}
somemissing <- any(missingdata)
if(somemissing) {
# add the missing data points
nmiss <- sum(missingdata)
U <- superimpose(U, X[missingdata], W=X$window, check=check)
# correspondingly augment the list of equal pairs
originalrows <- seq_len(nU)
newXindex <- Xseq[missingdata]
newUindex <- nU + seq_len(nmiss)
EqualPairs <- rbind(EqualPairs, cbind(newXindex, newUindex))
nU <- nU + nmiss
}
# determine saturated pair counts
counts <- strausscounts(U, X, r, EqualPairs)
satcounts <- pmin.int(sat, counts)
satcounts <- matrix(satcounts, ncol=1)
if(halfway) {
# trapdoor used by suffstat()
answer <- satcounts
} else if(sat == Inf) {
# no saturation: fast code
answer <- 2 * satcounts
} else {
# extract counts for data points
Uindex <- EqualPairs[,2L]
Xindex <- EqualPairs[,1L]
Xcounts <- integer(npoints(X))
Xcounts[Xindex] <- counts[Uindex]
# evaluate change in saturated counts of other data points
change <- geyercounts(U, X, r, sat, Xcounts, EqualPairs)
answer <- satcounts + change
answer <- matrix(answer, ncol=1)
}
if(somemissing)
answer <- answer[originalrows, , drop=FALSE]
return(answer)
},
delta2 = function(X,inte,correction, ..., sparseOK=TRUE) {
# Sufficient statistic for second order conditional intensity
# h(X[i] | X) - h(X[i] | X[-j])
# Geyer interaction
if(correction == "periodic") return(NULL)
r <- inte$par$r
sat <- inte$par$sat
result <- geyerdelta2(X, r, sat,
correction=correction, sparseOK=sparseOK)
return(result)
}
)
class(BlankGeyer) <- "interact"
Geyer <- function(r, sat) {
instantiate.interact(BlankGeyer, list(r = r, sat=sat))
}
Geyer <- intermaker(Geyer, BlankGeyer)
Geyer
})
# ........... externally visible auxiliary functions .........
geyercounts <- function(U, X, r, sat, Xcounts, EqualPairs) {
# evaluate effect of adding dummy point or deleting data point
# on saturated counts of other data points
stopifnot(is.numeric(r))
stopifnot(is.numeric(sat))
# for C calls we need finite numbers
stopifnot(is.finite(r))
stopifnot(is.finite(sat))
# sort in increasing order of x coordinate
oX <- fave.order(X$x)
oU <- fave.order(U$x)
Xsort <- X[oX]
Usort <- U[oU]
nX <- npoints(X)
nU <- npoints(U)
Xcountsort <- Xcounts[oX]
# inverse: data point i has sorted position i' = rankX[i]
rankX <- integer(nX)
rankX[oX] <- seq_len(nX)
rankU <- integer(nU)
rankU[oU] <- seq_len(nU)
# map from quadrature points to data points
Uindex <- EqualPairs[,2L]
Xindex <- EqualPairs[,1L]
Xsortindex <- rankX[Xindex]
Usortindex <- rankU[Uindex]
Cmap <- rep.int(-1L, nU)
Cmap[Usortindex] <- Xsortindex - 1L
# call C routine
zz <- .C(SC_Egeyer,
nnquad = as.integer(nU),
xquad = as.double(Usort$x),
yquad = as.double(Usort$y),
quadtodata = as.integer(Cmap),
nndata = as.integer(nX),
xdata = as.double(Xsort$x),
ydata = as.double(Xsort$y),
tdata = as.integer(Xcountsort),
rrmax = as.double(r),
ssat = as.double(sat),
result = as.double(numeric(nU)),
PACKAGE="spatstat.core")
result <- zz$result[rankU]
return(result)
}
geyerdelta2 <- local({
geyerdelta2 <- function(X, r, sat, ..., sparseOK=TRUE, correction="none") {
## Sufficient statistic for second order conditional intensity
## Geyer model
stopifnot(is.numeric(sat) && length(sat) == 1 && sat > 0)
## X could be a ppp or quad.
if(is.ppp(X)) {
# evaluate \Delta_{x_i} \Delta_{x_j} S(x) for data points x_i, x_j
# i.e. h(X[i]|X) - h(X[i]|X[-j]) where h is first order cif statistic
return(geydelppp(X, r, sat, correction, sparseOK))
} else if(is.quad(X)) {
# evaluate \Delta_{u_i} \Delta_{u_j} S(x) for quadrature points u_i, u_j
return(geydelquad(X, r, sat, correction, sparseOK))
} else stop("Internal error: X should be a ppp or quad object")
}
geydelppp <- function(X, r, sat, correction, sparseOK) {
# initialise
nX <- npoints(X)
result <- if(!sparseOK) matrix(0, nX, nX) else
sparseMatrix(i=integer(0), j=integer(0), x=numeric(0),
dims=c(nX, nX))
## identify all r-close pairs (ordered pairs i ~ j)
unweighted <- correction %in% c("border", "none")
if(unweighted) {
a <- closepairs(X, r, what="indices")
I <- a$i
J <- a$j
IJ <- cbind(I,J)
## count number of r-neighbours for each point
tvals <- table(factor(I, levels=1:nX))
} else {
a <- weightedclosepairs(X, r, correction=correction, what="indices")
I <- a$i
J <- a$j
IJ <- cbind(I,J)
wIJ <- a$weight
wmatrix <- sparseMatrix(i=I,j=J,x=wIJ, dims=c(nX,nX))
wJI <- wmatrix[cbind(J,I)]
## total edge-correction weight over r-neighbours for each point
tvals <- tapplysum(wIJ, list(factor(I, levels=1:nX)))
}
# Compute direct part
# (arising when i~j)
tI <- tvals[I]
tJ <- tvals[J]
if(unweighted) {
result[IJ] <-
pmin(sat, tI) - pmin(sat, tI - 1L) + pmin(sat, tJ) - pmin(sat, tJ - 1L)
} else {
result[IJ] <-
pmin(sat, tI) - pmin(sat, tI - wIJ) +
pmin(sat, tJ) - pmin(sat, tJ - wJI)
}
# Compute indirect part
# (arising when i~k and j~k for another point k)
# First find all such triples
ord <- (I < J)
vees <- edges2vees(I[ord], J[ord], nX)
# evaluate contribution of (k, i, j)
II <- vees$j
JJ <- vees$k
KK <- vees$i
tKK <- tvals[KK]
if(unweighted) {
contribKK <- pmin(sat, tKK) - 2 * pmin(sat, tKK-1L) + pmin(sat, tKK-2L)
} else {
wKKII <- wmatrix[cbind(KK, II)]
wKKJJ <- wmatrix[cbind(KK, JJ)]
contribKK <- (
pmin(sat, tKK)
- pmin(sat, tKK-wKKII)
- pmin(sat, tKK-wKKJJ)
+ pmin(sat, tKK-wKKII-wKKJJ)
)
}
# for each (i, j), sum the contributions over k
if(!sparseOK) {
II <- factor(II, levels=1:nX)
JJ <- factor(JJ, levels=1:nX)
# was:
# delta3 <- tapply(contribKK, list(I=II, J=JJ), sum)
# delta3[is.na(delta3)] <- 0
delta3 <- tapplysum(contribKK, list(I=II, J=JJ))
} else {
delta3 <- sparseMatrix(i=II, j=JJ, x=contribKK, dims=c(nX, nX))
}
# symmetrise and combine
result <- result + delta3 + t(delta3)
return(result)
}
geydelquad <- function(Q, r, sat, correction, sparseOK) {
Z <- is.data(Q)
U <- union.quad(Q)
nU <- npoints(U)
nX <- npoints(Q$data)
result <- if(!sparseOK) matrix(0, nU, nU) else
sparseMatrix(i=integer(0), j=integer(0), x=numeric(0),
dims=c(nU, nU))
## identify all r-close pairs U[i], U[j]
unweighted <- correction %in% c("none", "border")
if(unweighted) {
a <- closepairs(U, r, what="indices")
I <- a$i
J <- a$j
IJ <- cbind(I, J)
} else {
a <- weightedclosepairs(U, r, correction=correction, what="indices")
I <- a$i
J <- a$j
IJ <- cbind(I, J)
wIJ <- a$weight
wmatrix <- sparseMatrix(i=I, j=J, x=wIJ, dims=c(nU,nU))
wJI <- wmatrix[cbind(J,I)]
}
## tag which ones are data points
zI <- Z[I]
zJ <- Z[J]
# count t(U[i], X)
IzJ <- I[zJ]
JzJ <- J[zJ]
if(unweighted) {
tvals <- table(factor(IzJ, levels=1:nU))
} else {
wzJ <- wIJ[zJ]
tvals <- tapplysum(wzJ, list(factor(IzJ, levels=1:nU)))
}
## Compute direct part
## (arising when U[i]~U[j])
tI <- tvals[I]
tJ <- tvals[J]
if(unweighted) {
tIJ <- tI - zJ
tJI <- tJ - zI
result[IJ] <- pmin(sat, tIJ + 1L) - pmin(sat, tIJ) +
pmin(sat, tJI + 1L) - pmin(sat, tJI)
} else {
tIJ <- tI - zJ * wIJ
tJI <- tJ - zI * wJI
result[IJ] <- pmin(sat, tIJ + wIJ) - pmin(sat, tIJ) +
pmin(sat, tJI + wJI) - pmin(sat, tJI)
}
# Compute indirect part
# (arising when U[i]~X[k] and U[j]~X[k] for another point X[k])
# First find all such triples
# Group close pairs X[k] ~ U[j] by index k
spl <- split(IzJ, factor(JzJ, levels=1:nX))
grlen <- lengths(spl)
# Assemble list of triples U[i], X[k], U[j]
# by expanding each pair U[i], X[k]
JJ <- unlist(spl[JzJ])
II <- rep(IzJ, grlen[JzJ])
KK <- rep(JzJ, grlen[JzJ])
# remove identical pairs i = j
ok <- II != JJ
II <- II[ok]
JJ <- JJ[ok]
KK <- KK[ok]
# evaluate contribution of each triple
tKK <- tvals[KK]
zII <- Z[II]
zJJ <- Z[JJ]
if(unweighted) {
tKIJ <- tKK - zII - zJJ
contribKK <-
pmin(sat, tKIJ + 2L) - 2 * pmin(sat, tKIJ + 1L) + pmin(sat, tKIJ)
} else {
wKKII <- wmatrix[cbind(KK, II)]
wKKJJ <- wmatrix[cbind(KK, JJ)]
tKIJ <- tKK - zII * wKKII - zJJ * wKKJJ
contribKK <- (pmin(sat, tKIJ + wKKII + wKKJJ)
- pmin(sat, tKIJ + wKKII)
- pmin(sat, tKIJ + wKKJJ)
+ pmin(sat, tKIJ))
}
# for each (i, j), sum the contributions over k
if(!sparseOK) {
II <- factor(II, levels=1:nU)
JJ <- factor(JJ, levels=1:nU)
# was:
# delta4 <- tapply(contribKK, list(I=II, J=JJ), sum)
# delta4[is.na(delta4)] <- 0
delta4 <- tapplysum(contribKK, list(I=II, J=JJ))
} else {
delta4 <- sparseMatrix(i=II, j=JJ, x=contribKK, dims=c(nU, nU))
}
# combine
result <- result + delta4
return(result)
}
geyerdelta2
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.