File: wendland.family.R

package info (click to toggle)
r-cran-fields 14.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 6,332 kB
  • sloc: fortran: 1,021; ansic: 288; sh: 35; makefile: 2
file content (377 lines) | stat: -rw-r--r-- 12,774 bytes parent folder | download
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#
# fields  is a package for analysis of spatial data written for
# the R software environment.
# Copyright (C) 2022 Colorado School of Mines
# 1500 Illinois St., Golden, CO 80401
# Contact: Douglas Nychka,  douglasnychka@gmail.edu,
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the R software environment if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# or see http://www.r-project.org/Licenses/GPL-2
##END HEADER
Wendland2.2 <- function(d, aRange = 1, theta=NULL) {
  # theta argument has been deopreciated.
  if( !is.null( theta)){
    aRange<- theta
  }
    # Cari's test function  with explicit form  for d=2 k=2
    # taper range is 1.0
    d <- d/aRange
    if (any(d < 0)) 
        stop("d must be nonnegative")
    return(((1 - d)^6 * (35 * d^2 + 18 * d + 3))/3 * (d < 1))
}
#
#
# the monster
#
"wendland.cov" <- function(x1, x2=NULL, aRange = 1, V = NULL, 
    k = 2, C = NA, marginal = FALSE, Dist.args = list(method = "euclidean"), 
    spam.format = TRUE, derivative = 0,  verbose = FALSE, 
    theta=NULL) {
    #
    # theta argument has been deopreciated.
    if( !is.null( theta)){
       aRange<- theta
     }
    #   if marginal variance is needed
    #  this is a quick return
    if (marginal) {
        return(rep(1, nrow(x1)))
    }
    #  the rest of the possiblities require some computing
    # setup the two matrices of locations
    #
    if (!is.matrix(x1)) {
        x1 <- as.matrix(x1)
        }
    if( is.null( x2) ) {
    	 x2<- x1}  
    if (!is.matrix(x2) ) {
        x2 <- as.matrix(x2)
        }
    d <- ncol(x1)
    n1 <- nrow(x1)
    n2 <- nrow(x2)
    # logical to figure out if this is great circle distance or not
    # great circle needs to handled specially due to how things are scaled.
    great.circle <- Dist.args$method == "greatcircle"
    # derivatives are tricky for great circle and other distances and have not been implemented ...
    if (Dist.args$method != "euclidean" & derivative > 0) {
        stop("derivatives not supported for this distance metric")
    }
    # catch bad aRange format
    if (length(aRange) > 1) {
        stop("aRange as a matrix or vector has been depreciated")
    }
    # catch using V with great circle
    if (!is.null(V) & great.circle) {
        stop("V is not implemented with great circle distance")
    }
    if (!is.null(V)) {
        if (aRange != 1) {
            stop("can't specify both aRange and V!")
        }
        x1 <- x1 %*% t(solve(V))
        x2 <- x2 %*% t(solve(V))
    }
    # if great circle distance set the delta cutoff to be in scale of angular latitude.
    # also figure out if scale is in miles or kilometers
    if (great.circle) {
        miles <- ifelse(is.null(Dist.args$miles), TRUE, Dist.args$miles)
        delta <- (180/pi) * aRange/ifelse(miles, 3963.34, 6378.388)
    }
    else {
        delta <- aRange
    }
    if (verbose) {
        print(delta)
    }
    # once scaling is done taper is applied with default range of 1.0
    # find polynomial coeffients that define
    # wendland on [0,1]
    #  d dimension and  k is the order
    #  first  find sparse matrix of Euclidean distances
    #  ||x1-x2||**2 (or any other distance that may be specified by
    #   the method component in Dist.args
    #  any distance beyond delta is set to zero -- anticipating the
    # tapering to zero by the Wendland.
    #
    sM <- do.call("nearest.dist", c(list(x1, x2, delta = delta, 
        upper = NULL), Dist.args))
    # scale distances by aRange
    # note: if V is passed then aRange==1 and all the scaling should be done with the V matrix.
    # there are two possible actions listed below:
    # find  Wendland cross covariance matrix
    # return either in sparse or matrix format
    if (is.na(C[1])) {
        sM@entries <- Wendland(sM@entries/aRange, k = k, dimension = d)
        if (!spam.format) {
            return(as.matrix(sM))
        }
        else {
            return(sM)
        }
    }
    else {
        #
        # multiply cross covariance matrix by the matrix C where
        # columns are usually the  'c' coefficients
        #  note multiply happens in spam format
        #
        if (derivative == 0) {
            sM@entries <- Wendland(sM@entries/aRange, k = k, dimension = d)
            return(sM %*% C)
        }
        else {
            #        otherwise evaluate partial derivatives with respect to x1
            #        big mess of code and an explicit for loop!
            #         this only is for euclidean distance
            if (is.matrix(C)) {
                if (ncol(C) > 1) {
                  stop("C should be a vector")
                }
            }
            L <- length(coef)
            #         loop over dimensions and accumulate partial derivative matrix.
            tempD <- sM@entries
            tempW <- Wendland(tempD/aRange, k = k, dimension = d, 
                derivative = derivative)
            # loop over dimensions and knock out each partial accumulate these in
            # in temp
            temp <- matrix(NA, ncol = d, nrow = n1)
            # Create rowindices vector
            sMrowindices <- rep(1:n1, diff(sM@rowpointers))
            for (kd in 1:d) {
                #
                #            Be careful if the distance (tempD) is close to zero.
                #            Note that the x1 and x2 are in transformed ( V inverse) scale
                #
                #
                sM@entries <- ifelse(tempD == 0, 0, (tempW * 
                  (x1[sMrowindices, kd] - x2[sM@colindices, kd])/(aRange * 
                  tempD)))
                #
                # accumlate the new partial
                temp[, kd] <- sM %*% C
            }
            # transform back to original coordinates.
            if (!is.null(V)) {
                temp <- temp %*% t(solve(V))
            }
            return(temp)
        }
    }
    # should not get here!
}
#
#
#

############## basic evaluation of Wendland and its derivatives.
###########################
# n: Wendland interpolation matrix is positive definite on R^n, i.e.  n is
# the dimension of the locations.
# k: Wendland function is 2k times continuously
# differentiable.
# The proofs can be found in the work of Wendland(1995).
#  H. Wendland. Piecewise polynomial , positive definite and compactly supported radial
#  functions of minimal degree. AICM 4(1995), pp 389-396.
#########################################
## top level function:
Wendland = function(d, aRange = 1, dimension, k, derivative = 0, 
    phi = NA, theta=NULL) {
# theta argument has been deopreciated.
  if( !is.null( theta)){
    aRange<- theta
  }
  
    if (!is.na(phi)) {
        stop("phi argument has been depreciated")
    }
    if (any(d < 0)) {
        stop("d must be nonnegative")
    }
    # find scaling so that function at zero is 1.
    scale.constant <- wendland.eval(0, n = dimension, k, derivative = 0)
    # adjust by aRange
    if (derivative > 0) {
        scale.constant <- scale.constant * (aRange^(derivative))
    }
    # scale distances by aRange.
    if( aRange!=1){
         d <- d/aRange}
    # at this point d the distances shouls be scaled so that
    # covariance is zero beyond 1
    if( (k==2)& (dimension==2) & (derivative==0)){
      ((1 - d)^6 * (35 * d^2 + 18 * d + 3))/3 * (d < 1)}
    else{
    ifelse(d < 1, wendland.eval(d, n = dimension, k, derivative)/scale.constant, 
        0)
    }
  }

####################
# [M] = wm(n, k)
# Compute the matrix coeficient in Wendland(1995)
# Input:
#\tn: Wendland interpolation matrix is positive definite on R^n
# \tk: Wendland function is 2k times continuously differentiable
####################
Wendland.beta = function(n, k) {
    l = floor(n/2) + k + 1
    M = matrix(0, nrow = k + 1, ncol = k + 1)
    #
    # top corner is 1
    #
    M[1, 1] = 1
    #
    # Compute across the columns and down the rows, filling out upper triangle of M (including diagonal). The indexing is done from 0, thus we have to adjust by +1 when accessing our matrix element.
    #
    if (k == 0) {
        stop
    }
    else {
        for (col in 0:(k - 1)) {
            #
            # Filling out the col+1 column
            #
            # As a special case, we need a different formula for the top row
            #
            row = 0
            beta = 0
            for (m in 0:col) {
                beta = beta + M[m + 1, col + 1] * fields.pochdown(m + 
                  1, m - row + 1)/fields.pochup(l + 2 * col - 
                  m + 1, m - row + 2)
            }
            M[row + 1, col + 2] = beta
            #
            # Now do the rest of rows
            #
            for (row in 1:(col + 1)) {
                beta = 0
                for (m in (row - 1):col) {
                  beta = beta + M[m + 1, col + 1] * fields.pochdown(m + 
                    1, m - row + 1)/fields.pochup(l + 2 * col - 
                    m + 1, m - row + 2)
                }
                M[row + 1, col + 2] = beta
            }
        }
    }
    M
}
########################################
# [phi] = wendland.eval(r, n, k, derivative).
# Compute the compacted support basis function in Wendland(1995).
# Input:
#\tr: a scalar representing the distance between locations. r should be scaled into [0,1] beforehand.
# \tn: Wendland interpolation matrix is positive definite on R^n. Or, we could say n is the dimension of the locations.
# \tk: Wendland function is 2k times continuously differentiable.
#\tderivative: the derivative of wendland function.
# Output:
#\tphi: a scalar evaluated by the Wendland function at distance r.
# example:
#\tr = 0.5
#\tphi = wendland.eval(r, 2, 1,derivative = 1 )
# The proofs can be found in the work of Wendland(1995).
# H. Wendlamd. Piecewise polynomial , positive definite and compactly supported radial functions of minimal degree. AICM 4(1995), pp 389-396.
#########################################
wendland.eval = function(r, n, k, derivative = 0) {
    #
    # check if the distances are between [0,1]
    #
    beta = Wendland.beta(n, k)
    l = floor(n/2) + k + 1
    if (derivative == 0) {
        #
        # first evaluate outside for loop with  m =0
        phi = beta[1, k + 1] * (1 - r)^(l + 2 * k)
        # now accumulate terms for other m values up to k
        for (m in 1:k) {
            phi = phi + beta[m + 1, k + 1] * r^m * (1 - r)^(l + 
                2 * k - m)
        }
    }
    else {
        # evaluate derivative note use of symbolic differtiation.
        f.my = expression((1 - r)^(l + 2 * k))
        f.deriv = fields.D(f.my, "r", order = derivative)
        f.eval = eval(f.deriv)
        phi = beta[1, k + 1] * f.eval
        for (m in 1:k) {
            f.my = expression(r^m * (1 - r)^(l + 2 * k - m))
            f.deriv = fields.D(f.my, "r", order = derivative)
            f.eval = eval(f.deriv)
            phi = phi + beta[m + 1, k + 1] * f.eval
        }
    }
    phi
}
#######################
# [n] = fields.pochup(q, k)
# Calculate the Pochhammer symbol for rising factorial q(q+1)(q+2)...(q+k-1)
#######################
fields.pochup = function(q, k) {
    n = q
    if (k == 0) {
        n = 1
    }
    else {
        for (j in 1:(k - 1)) {
            if ((k - 1) < 1) {
                stop
            }
            else {
                n = n * (q + j)
            }
        }
    }
    n
}
#########################
# [n] = fields.pochdown(q, k)
# Calculate the Pochhammer symbol for falling factorial q(q-1)(q-2)...(q-k+1)
#########################
fields.pochdown = function(q, k) {
    n = q
    if (k == 0) {
        n = 1
    }
    else {
        for (j in 1:(k - 1)) {
            if ((k - 1) < 1) {
                stop
            }
            else {
                n = n * (q - j)
            }
        }
    }
    n
}
#############################
# fields.D(f,name = x,order = n) forms the n-th derivative of function f with respect to the variable  x
################################
fields.D = function(f, name, order = 1) {
    if (order < 1) {
        stop("'order' must be >= 1")
    }
    if (order == 1) {
        d = D(f, name)
    }
    else {
        fields.D(D(f, name), name, order - 1)
    }
}