File: multistrhard.R

package info (click to toggle)
r-cran-spatstat.core 2.4-4-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 6,440 kB
  • sloc: ansic: 4,402; sh: 13; makefile: 5
file content (357 lines) | stat: -rw-r--r-- 13,039 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#
#
#    multistrhard.S
#
#    $Revision: 2.40 $	$Date: 2022/03/07 02:20:59 $
#
#    The multitype Strauss/hardcore process
#
#    MultiStraussHard()
#                 create an instance of the multitype Strauss/ harcore
#                 point process
#                 [an object of class 'interact']
#	
# -------------------------------------------------------------------
#	

doMultiStraussHard <- local({
  
  # ........  define potential ......................

  MSHpotential <- function(d, tx, tu, par) {
     # arguments:
     # d[i,j] distance between points X[i] and U[j]
     # tx[i]  type (mark) of point X[i]
     # tu[i]  type (mark) of point U[j]
     #
     # get matrices of interaction radii
     r <- par$iradii
     h <- par$hradii

     # get possible marks and validate
     if(!is.factor(tx) || !is.factor(tu))
	stop("marks of data and dummy points must be factor variables")
     lx <- levels(tx)
     lu <- levels(tu)
     if(length(lx) != length(lu) || any(lx != lu))
	stop("marks of data and dummy points do not have same possible levels")

     if(!identical(lx, par$types))
        stop("data and model do not have the same possible levels of marks")
     if(!identical(lu, par$types))
        stop("dummy points and model do not have the same possible levels of marks")
                   
     # ensure factor levels are acceptable for column names (etc)
     lxname <- make.names(lx, unique=TRUE)

     # list all UNORDERED pairs of types to be counted
     # (the interaction must be symmetric in type, and scored as such)
     uptri <- (row(r) <= col(r)) & !is.na(r)
     mark1 <- (lx[row(r)])[uptri]
     mark2 <- (lx[col(r)])[uptri]
     # corresponding names
     mark1name <- (lxname[row(r)])[uptri]
     mark2name <- (lxname[col(r)])[uptri]
     vname <- apply(cbind(mark1name,mark2name), 1, paste, collapse="x")
     vname <- paste("mark", vname, sep="")
     npairs <- length(vname)
     # list all ORDERED pairs of types to be counted
     # (to save writing the same code twice)
     different <- mark1 != mark2
     mark1o <- c(mark1, mark2[different])
     mark2o <- c(mark2, mark1[different])
     nordpairs <- length(mark1o)
     # unordered pair corresponding to each ordered pair
     ucode <- c(1:npairs, (1:npairs)[different])
     #
     # create numeric array for result
     z <- array(0, dim=c(dim(d), npairs),
                dimnames=list(character(0), character(0), vname))
     # go....
     if(length(z) > 0) {
       # apply the relevant interaction distance to each pair of points
       rxu <- r[ tx, tu ]
       str <- (d < rxu)
       str[is.na(str)] <- FALSE
       # and the relevant hard core distance
       hxu <- h[ tx, tu ]
       forbid <- (d < hxu)
       forbid[is.na(forbid)] <- FALSE
       # form the potential
       value <- str
       value[forbid] <- -Inf
       # assign value[i,j] -> z[i,j,k] where k is relevant interaction code
       for(i in 1:nordpairs) {
         # data points with mark m1
         Xsub <- (tx == mark1o[i])
         # quadrature points with mark m2
         Qsub <- (tu == mark2o[i])
         # assign
         z[Xsub, Qsub, ucode[i]] <- value[Xsub, Qsub]
       }
     }
     return(z)
   }
  # ............... end of potential function ...................

  # .......... auxiliary functions .................
  
  delMSH <- function(which, types, iradii, hradii, ihc) {
    iradii[which] <- NA
    if(any(!is.na(iradii))) {
      # some gamma interactions left
      # return modified MultiStraussHard with fewer gamma parameters
      return(MultiStraussHard(types, iradii, hradii))
    } else if(any(!ihc)) {
      # no gamma interactions left, but some active hard cores
      return(MultiHard(types, hradii))
    } else return(Poisson())
  }

  # ...........................................................
  
  # Set up basic object except for family and parameters

  BlankMSHobject <- 
    list(
         name     = "Multitype Strauss Hardcore process",
         creator  = "MultiStraussHard",
         family   = "pairwise.family", # evaluated later
         pot      = MSHpotential,
         par      = list(types=NULL, iradii=NULL, hradii=NULL),  # to be added
         parnames = c("possible types",
                      "interaction distances",
                      "hardcore distances"),
         pardesc  = c("vector of possible types",
                      "matrix of interaction distances",
                      "matrix of hardcore distances"),
         hasInf   = TRUE,
         selfstart = function(X, self) {
           types <- self$par$types
           hradii <- self$par$hradii
           if(!is.null(types) && !is.null(hradii)) return(self)
           if(is.null(types)) types <- levels(marks(X))
           if(is.null(hradii)) {
             marx <- marks(X)
             d <- nndist(X, by=marx)
             h <- aggregate(d, by=list(from=marx), min)
             h <- as.matrix(h[, -1, drop=FALSE])
             m <- table(marx)
             mm <- outer(m, m, pmin)
             hradii <- h * mm/(mm+1)
             dimnames(hradii) <- list(types, types)
           }
           MultiStraussHard(types=types,hradii=hradii,iradii=self$par$iradii)
	 },
         init     = function(self) {
           types <- self$par$types
           iradii <- self$par$iradii
           hradii <- self$par$hradii
           # hradii could be NULL
           if(!is.null(types)) {
             if(!is.null(dim(types)))
               stop(paste("The", sQuote("types"),
                          "argument should be a vector"))
             if(length(types) == 0)
               stop(paste("The", sQuote("types"),"argument should be",
                          "either NULL or a vector of all possible types"))
             if(anyNA(types))
               stop("NA's not allowed in types")
             if(is.factor(types)) {
               types <- levels(types)
             } else {
               types <- levels(factor(types, levels=types))
             }
             nt <- length(types)
             MultiPair.checkmatrix(iradii, nt, sQuote("iradii"))
             if(!is.null(hradii))
               MultiPair.checkmatrix(hradii, nt, sQuote("hradii"))
           }
           ina <- is.na(iradii)
           if(all(ina))
             stop(paste("All entries of", sQuote("iradii"),
                        "are NA"))
           if(!is.null(hradii)) {
             hna <- is.na(hradii)
             both <- !ina & !hna
             if(any(iradii[both] <= hradii[both]))
               stop("iradii must be larger than hradii")
           }
         },
         update = NULL,  # default OK
         print = function(self) {
           types <- self$par$types
           iradii <- self$par$iradii
           hradii <- self$par$hradii
           nt <- nrow(iradii)
           if(waxlyrical('gory')) {
             splat(nt, "types of points")
             if(!is.null(types)) {
               splat("Possible types:")
               print(noquote(types))
             } else splat("Possible types:\t not yet determined")
           }
           splat("Interaction radii:")
           dig <- getOption("digits")
           print(signif(iradii, dig))
           if(!is.null(hradii)) {
             splat("Hardcore radii:")
             print(signif(hradii, dig))
           } else splat("Hardcore radii: not yet determined")
           invisible()
         },
        interpret = function(coeffs, self) {
          # get possible types
          typ <- self$par$types
          ntypes <- length(typ)
          # get matrices of interaction radii
          r <- self$par$iradii
          h <- self$par$hradii
          # list all relevant unordered pairs of types
          uptri <- (row(r) <= col(r)) & !is.na(r)
          index1 <- (row(r))[uptri]
          index2 <- (col(r))[uptri]
          npairs <- length(index1)
          # extract canonical parameters; shape them into a matrix
          gammas <- matrix(, ntypes, ntypes)
          dimnames(gammas) <- list(typ, typ)
          expcoef <- exp(coeffs)
          gammas[ cbind(index1, index2) ] <- expcoef
          gammas[ cbind(index2, index1) ] <- expcoef
          #
          return(list(param=list(gammas=gammas),
                      inames="interaction parameters gamma_ij",
                      printable=dround(gammas)))
        },
        valid = function(coeffs, self) {
           # interaction radii r[i,j]
           iradii <- self$par$iradii
           # hard core radii r[i,j]
           hradii <- self$par$hradii
           # interaction parameters gamma[i,j]
           gamma <- (self$interpret)(coeffs, self)$param$gammas
           # Check that we managed to estimate all required parameters
           required <- !is.na(iradii)
           if(!all(is.finite(gamma[required])))
             return(FALSE)
           # Check that the model is integrable
           # inactive hard cores ...
           ihc <- (is.na(hradii) | hradii == 0)
           # .. must have gamma <= 1
           return(all(gamma[required & ihc] <= 1))
         },
         project = function(coeffs, self) {
           # types
           types <- self$par$types
           # interaction radii r[i,j]
           iradii <- self$par$iradii
           # hard core radii r[i,j]
           hradii <- self$par$hradii
           # interaction parameters gamma[i,j]
           gamma <- (self$interpret)(coeffs, self)$param$gammas
           # required gamma parameters
           required <- !is.na(iradii)
           # active hard cores
           activehard <- !is.na(hradii) & (hradii > 0)
           ihc <- !activehard
           # problems
           gammavalid <- is.finite(gamma) & (activehard | gamma <= 1)
           naughty    <- required & !gammavalid
           if(!any(naughty))
             return(NULL)
           #
           if(spatstat.options("project.fast")) {
             # remove ALL naughty terms simultaneously
             return(delMSH(naughty, types, iradii, hradii, ihc))
           } else {
             # present a list of candidates
             rn <- row(naughty)
             cn <- col(naughty)
             uptri <- (rn <= cn) 
             upn <- uptri & naughty
             rowidx <- as.vector(rn[upn])
             colidx <- as.vector(cn[upn])
#             matindex <- function(v) { matrix(c(v, rev(v)),
#                                              ncol=2, byrow=TRUE) }
             mats <- lapply(as.data.frame(rbind(rowidx, colidx)), matindex)
             inters <- lapply(mats, delMSH,
                              types=types, iradii=iradii,
                              hradii=hradii, ihc=ihc)
             return(inters)           }
         },
         irange = function(self, coeffs=NA, epsilon=0, ...) {
           r <- self$par$iradii
           h <- self$par$hradii
           ractive <- !is.na(r)
           hactive <- !is.na(h)
           if(any(!is.na(coeffs))) {
             gamma <- (self$interpret)(coeffs, self)$param$gammas
             gamma[is.na(gamma)] <- 1
             ractive <- ractive & (abs(log(gamma)) > epsilon)
           }
           if(!any(c(ractive,hactive)))
             return(0)
           else
             return(max(c(r[ractive],h[hactive])))
         },
         hardcore = function(self, coeffs=NA, epsilon=0, ...) {
           h <- self$par$hradii
           active <- !is.na(h)
           return(max(0, h[active]))
         },
         version=NULL # to be added
         )
  class(BlankMSHobject) <- "interact"

  matindex <- function(v) { matrix(c(v, rev(v)), ncol=2, byrow=TRUE) }
  
  # Finally define MultiStraussHard function
  doMultiStraussHard <- function(iradii, hradii=NULL, types=NULL) {
    iradii[iradii == 0] <- NA
    if(!is.null(hradii)) hradii[hradii == 0] <- NA
    out <- instantiate.interact(BlankMSHobject,
                                list(types=types,
                                     iradii = iradii, hradii = hradii))
    if(!is.null(types)) {
      dn <- list(types, types)
      dimnames(out$par$iradii) <- dn
      if(!is.null(out$par$hradii)) dimnames(out$par$hradii) <- dn
    }
    return(out)
  }

  doMultiStraussHard
})


MultiStraussHard <- local({

  MultiStraussHard <- function(iradii, hradii, types=NULL) {
    ## try new syntax
    newcall <- match.call()
    newcall[[1]] <- as.name('doMultiStraussHard')
    out <- try(eval(newcall, parent.frame()), silent=TRUE)
    if(is.interact(out))
      return(out)
    ## try old syntax
    oldcall <- match.call(function(types=NULL, iradii, hradii) {})
    oldcall[[1]] <- as.name('doMultiStraussHard')
    out <- try(eval(oldcall, parent.frame()), silent=TRUE)
    if(is.interact(out))
      return(out)
    ## Syntax is wrong: generate error using new syntax rules
    if(missing(hradii)) hradii <- NULL
    doMultiStraussHard(iradii=iradii, hradii=hradii, types=types)
  }


  BlankMSHobject <- get("BlankMSHobject",
                        envir=environment(doMultiStraussHard))
  
  MultiStraussHard <- intermaker(MultiStraussHard, BlankMSHobject)

  MultiStraussHard
})