| 1 | #This R file can be sourced to provide all of the functions necessary to produce k-extended LHCs. |
|---|
| 2 | #The last two lines in this file can be uncommented out and they will generate the same type of orthogonal maximin LHC used to compare performance against sliced LHCs in section 3.1. For further information on how to use the code, please contact the author. |
|---|
| 3 | |
|---|
| 4 | require(lhs) |
|---|
| 5 | require(DoE.wrapper) |
|---|
| 6 | require(DiceDesign) |
|---|
| 7 | require(parallel) |
|---|
| 8 | toInts <- function(LH){ |
|---|
| 9 | n <- length(LH[,1]) |
|---|
| 10 | ceiling(n*LH) |
|---|
| 11 | } |
|---|
| 12 | |
|---|
| 13 | # The correlation component criterion |
|---|
| 14 | RhoSq <- function(LH){ |
|---|
| 15 | A <- cor(LH) |
|---|
| 16 | m <- dim(LH)[2] |
|---|
| 17 | 2*sum(A[upper.tri(A)]^2)/(m*(m-1)) |
|---|
| 18 | } |
|---|
| 19 | # The inter-site distance for column j of MC |
|---|
| 20 | oneColDist <- function(LH, whichCol, k){ |
|---|
| 21 | D <- dist(LH[,whichCol],method="manhattan") |
|---|
| 22 | D[D==0] <- 1/k |
|---|
| 23 | D |
|---|
| 24 | } |
|---|
| 25 | |
|---|
| 26 | # The coverage criterion |
|---|
| 27 | newphiP <- function(LH, k, p){ |
|---|
| 28 | tD <- oneColDist(LH,1,k) |
|---|
| 29 | for(i in 2:(dim(LH)[2])){ |
|---|
| 30 | tD <- tD + oneColDist(LH,i,k) |
|---|
| 31 | } |
|---|
| 32 | sum(tD^(-p))^(1/p) |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | dbar <- function(n,m,k,tc){ |
|---|
| 36 | (m*n*tc*(tc*k*(n^2-1) + 3*(tc-1)))/(6*k*choose(tc*n,2)) |
|---|
| 37 | } |
|---|
| 38 | |
|---|
| 39 | # The coverage criterion lower band |
|---|
| 40 | newphiPL <- function(n, k, bard, p){ |
|---|
| 41 | choose(k*n,2)^(1/p)/bard |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | # The coverage criterion upper band |
|---|
| 45 | newphiPU <- function(n, m, k, p, tc){ |
|---|
| 46 | ((n*k^(p)*tc*(tc-1))/(2*m) + sum( tc^2*(n-c(1:(n-1)))/(m*c(1:(n-1))^p)))^(1/p) |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | # The final criterion |
|---|
| 50 | objectiveFun <- function(LH, w, p, phiU, phiL, k){ |
|---|
| 51 | rho <- RhoSq(LH) |
|---|
| 52 | phiP <- newphiP(LH, k, p) |
|---|
| 53 | w*rho + (1-w)*(phiP - phiL)/(phiU-phiL) |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | newphiL1 <- function(n, bard, p){ |
|---|
| 57 | upp <- ceiling(bard) |
|---|
| 58 | low <- floor(bard) |
|---|
| 59 | (choose(n,2)*(((upp - bard)/low^p) + ((bard - low)/upp^p)))^(1/p) |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | Sim.anneal.k <- function(tLHS, tc, k, n, m, w, p, Imax=1000, FAC_t=0.9, t0=NULL){ |
|---|
| 63 | Dcurrent <- tLHS |
|---|
| 64 | tbard <- dbar(n=n,m=m,k=k,tc=tc) |
|---|
| 65 | phiU <- newphiPU(n=n, m=m, k=k, p=p, tc=tc) |
|---|
| 66 | if(tc<2) |
|---|
| 67 | phiL <- newphiL1(n=n, bard=tbard, p=p) |
|---|
| 68 | else |
|---|
| 69 | phiL <- newphiPL(n=n, k=k, bard=tbard, p=p) |
|---|
| 70 | # Deriving the initial temperature value |
|---|
| 71 | if(is.null(t0)){ |
|---|
| 72 | delta <- 1/k |
|---|
| 73 | rdis <- runif(n=choose(tc*n,2), min = 0.5*tbard, max=1.5*tbard) |
|---|
| 74 | t0curr <- sum(rdis^(-p))^(1/p) |
|---|
| 75 | rdis[which.min(rdis)] <- rdis[which.min(rdis)] - delta |
|---|
| 76 | t0new <- sum(rdis^(-p))^(1/p) |
|---|
| 77 | t0 <- (-1)*(t0new - t0curr)/log(0.99) |
|---|
| 78 | } |
|---|
| 79 | # Calculate the psi criterion value for the current LHC |
|---|
| 80 | psiDcurrent <- objectiveFun(Dcurrent, w=w, p=p, phiU=phiU, phiL=phiL, k=k) |
|---|
| 81 | Dbest <- Dcurrent |
|---|
| 82 | psiDbest <- psiDcurrent |
|---|
| 83 | FLAG <- 1 |
|---|
| 84 | t <- t0 |
|---|
| 85 | while(FLAG==1){ |
|---|
| 86 | FLAG <- 0 |
|---|
| 87 | I <- 1 |
|---|
| 88 | while(I < Imax){ # Repeating the simulated annealing algorithm |
|---|
| 89 | Dtry <- Dcurrent |
|---|
| 90 | # Randomly select column in (c-1)n+1,..., cn |
|---|
| 91 | j <- sample(1:m, 1) |
|---|
| 92 | i12 <- sample(c(((tc-1)*n+1):(tc*n)),2,replace=FALSE) |
|---|
| 93 | # Permute elements of the rows within randomly selected column |
|---|
| 94 | Dtry[i12,j] <- Dcurrent[c(i12[2],i12[1]),j] |
|---|
| 95 | # Calculate the psi criterion value for the newly generated LHC |
|---|
| 96 | psiDtry <- objectiveFun(Dtry, w=w, p=p, phiU=phiU, phiL=phiL, k=k) |
|---|
| 97 | if(psiDtry < psiDcurrent){ |
|---|
| 98 | Dcurrent <- Dtry |
|---|
| 99 | psiDcurrent <- psiDtry |
|---|
| 100 | FLAG <- 1 |
|---|
| 101 | } |
|---|
| 102 | # Accept with acceptance probability |
|---|
| 103 | else if((runif(1) < exp(-(psiDtry - psiDcurrent)/t))&(psiDtry!=psiDcurrent)){ |
|---|
| 104 | Dcurrent <- Dtry |
|---|
| 105 | psiDcurrent <- psiDtry |
|---|
| 106 | FLAG <- 1 |
|---|
| 107 | } |
|---|
| 108 | if(psiDtry < psiDbest){ |
|---|
| 109 | Dbest <- Dtry |
|---|
| 110 | psiDbest <- psiDtry |
|---|
| 111 | #print(paste("New Dbest with psi = ", psiDbest, " found, reset I from I = ", I, sep="")) |
|---|
| 112 | I <- 1 |
|---|
| 113 | } |
|---|
| 114 | else{ |
|---|
| 115 | I <- I + 1 |
|---|
| 116 | } |
|---|
| 117 | } |
|---|
| 118 | # Reducing t (temperature) |
|---|
| 119 | t <- t*FAC_t |
|---|
| 120 | #print(t) |
|---|
| 121 | } |
|---|
| 122 | Dbest |
|---|
| 123 | } |
|---|
| 124 | # Draw a Latin Hypercube Sample from a set of uniform distributions for use in creating a Latin Hypercube |
|---|
| 125 | #design. This sample is taken in a random manner without regard to optimization |
|---|
| 126 | ### Inputs |
|---|
| 127 | # n: number of points |
|---|
| 128 | # m: number of inputs |
|---|
| 129 | # p: (not sure) |
|---|
| 130 | # w: weights |
|---|
| 131 | |
|---|
| 132 | ### Output |
|---|
| 133 | # n by m dimension matrix of integers |
|---|
| 134 | FirstRankLHS <- function(n, m, p, w){ |
|---|
| 135 | tLHS <- randomLHS(n,m) |
|---|
| 136 | # Provide order of rows in each column |
|---|
| 137 | tLHS <- apply(tLHS, 2, order) |
|---|
| 138 | Sim.anneal.k(tLHS=tLHS, tc=1, k=1, n=n, m=m, w=w, p=p, Imax=1000, FAC_t=0.8, t0=NULL) |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | ###Generate a new integer LHC |
|---|
| 142 | ### Input: |
|---|
| 143 | # CurrentBig: the original n-integer LHC |
|---|
| 144 | # n: numeber of points |
|---|
| 145 | # m: number of inputs |
|---|
| 146 | # k: number of extensions |
|---|
| 147 | |
|---|
| 148 | ### Output |
|---|
| 149 | # n by m dimension matrix of integers |
|---|
| 150 | MakeValidNewLHS <- function(CurrentBig, n, m, k){ |
|---|
| 151 | if(k*n <= n^m){ |
|---|
| 152 | Found <- FALSE |
|---|
| 153 | while(!Found){ |
|---|
| 154 | anLHC <- apply(randomLHS(n,m),2,order) |
|---|
| 155 | if(!any(dist(rbind(CurrentBig,anLHC), method="manhattan")==0)) |
|---|
| 156 | Found <- TRUE |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | else{ |
|---|
| 160 | anLHC <- apply(randomLHS(n,m),2,order) |
|---|
| 161 | } |
|---|
| 162 | return(anLHC) |
|---|
| 163 | } |
|---|
| 164 | ### Produce k n-point integer LHC with desirable properties (orthonormal maximin LHC) |
|---|
| 165 | ### Inputs: |
|---|
| 166 | # n: number of points |
|---|
| 167 | # m: number of inputs |
|---|
| 168 | # k: number of extensions |
|---|
| 169 | # w: weights |
|---|
| 170 | # p: (?) |
|---|
| 171 | # FAC_t: simulated annealing parameter (temperature increment) |
|---|
| 172 | # startLHS: first n-point integer LHS |
|---|
| 173 | |
|---|
| 174 | ### Output: |
|---|
| 175 | # k n-point integer LHC |
|---|
| 176 | MakeRankExtensionCubes <- function(n, m, k, w, p=50, FAC_t=0.8, startLHS=NULL, Imax){ |
|---|
| 177 | # Generate n-point integer LHC |
|---|
| 178 | if(is.null(startLHS)) |
|---|
| 179 | lhs1 <- FirstRankLHS(n, m, p, w) |
|---|
| 180 | else |
|---|
| 181 | lhs1 <- as.matrix(toInts(startLHS)) |
|---|
| 182 | BigExtendedMatrix <- matrix(NA, nrow=k*n, ncol=m) |
|---|
| 183 | # Assign first n-point integer LHC |
|---|
| 184 | BigExtendedMatrix[1:n,] <- lhs1 |
|---|
| 185 | # Generate k extension to original n-point integer LHC |
|---|
| 186 | for(i in 2:k){ |
|---|
| 187 | print(paste("Current extension = ", i, " of ", k, sep="")) |
|---|
| 188 | newLHC <- MakeValidNewLHS(BigExtendedMatrix[1:((i-1)*n),],n=n,m=m,k=i) |
|---|
| 189 | BigExtendedMatrix[((i-1)*n + 1):(i*n),] <- newLHC |
|---|
| 190 | BigExtendedMatrix[1:(i*n),] <- Sim.anneal.k(tLHS=BigExtendedMatrix[1:(i*n),], tc=i, k=k, n=n, m=m, w=w, p=p, Imax=1000, FAC_t=FAC_t, t0=NULL) |
|---|
| 191 | } |
|---|
| 192 | lapply(1:k, function(e) BigExtendedMatrix[(1+(e-1)*n):(e*n),]) |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | oneIN <- function(LHcolumn,left,right){ |
|---|
| 196 | any(which(LHcolumn>=left)%in%which(LHcolumn<right)) |
|---|
| 197 | } |
|---|
| 198 | manyIN <- Vectorize(oneIN, c("left","right")) |
|---|
| 199 | |
|---|
| 200 | ### Produce a list of sub-solid for which the members of current |
|---|
| 201 | ### ExtendedLHC falls within this sub-solid |
|---|
| 202 | ### Inputs: |
|---|
| 203 | # rankLHCrow: rectangular solid representation |
|---|
| 204 | # currentExtendedLHC: currently constructed cn-point LHC |
|---|
| 205 | # n: number of points |
|---|
| 206 | # d:number of dimensions |
|---|
| 207 | # increment: sub-solid parameter |
|---|
| 208 | # k: number of sub-solids |
|---|
| 209 | |
|---|
| 210 | ### Output: |
|---|
| 211 | # list |
|---|
| 212 | getPointers <- function(rankLHCrow,currentExtendedLHC, n, d, increment,k){ |
|---|
| 213 | leftmostedges <- (rankLHCrow-1)/n |
|---|
| 214 | tleftmosts <- matrix(leftmostedges,nrow=k+1,ncol=d,byrow=T) |
|---|
| 215 | leftedges <- matrix(rep(0:k,d),nrow=k+1,ncol=d) |
|---|
| 216 | leftedges <- leftedges*increment + tleftmosts |
|---|
| 217 | rightedges <- leftedges+increment |
|---|
| 218 | sapply(1:d, function(j) which(manyIN(currentExtendedLHC[,j],leftedges[,j],rightedges[,j]))) |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | ### Sample sub-solid at random and return its value |
|---|
| 222 | ### Inputs |
|---|
| 223 | # pointersRow: a list of sub-solid for which the members of current |
|---|
| 224 | # ExtendedLHC falls within this sub-solid |
|---|
| 225 | # rankLHCrow: solid of which pointersRow sub-solid is part of |
|---|
| 226 | # increment: sub-solid parameter |
|---|
| 227 | # n: number of points |
|---|
| 228 | # d: number of inputs |
|---|
| 229 | # k: number of subsolids |
|---|
| 230 | |
|---|
| 231 | ###Output |
|---|
| 232 | # a vector of sampled value |
|---|
| 233 | sampleNewPoint <- function(pointersRow, rankLHCrow, increment, n, d, k){ |
|---|
| 234 | location <- runif(d) |
|---|
| 235 | if(length(pointersRow[,1]) < k) |
|---|
| 236 | newPoint <- sapply(1:d, function(j) sample(c(1:(k+1))[-pointersRow[,j]],1)) |
|---|
| 237 | else |
|---|
| 238 | newPoint <- sapply(1:d, function(j) c(1:(k+1))[-pointersRow[,j]]) |
|---|
| 239 | (rankLHCrow-1)/n + (newPoint-1)*increment + increment*location |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | ### Function to generate K-extended Latin Hypercube |
|---|
| 243 | ### Input: |
|---|
| 244 | # rankLHClist: a k list of n by m integer LHC |
|---|
| 245 | # LHCmaster: n-point LHC |
|---|
| 246 | |
|---|
| 247 | ###Output: |
|---|
| 248 | # a kn by m matrix, K-extended Latin Hypercube |
|---|
| 249 | |
|---|
| 250 | NewExtendingLHS <- function(rankLHClist, LHCmaster=NULL){ |
|---|
| 251 | #there are k+1 rank LHCs in rankLHClist, which can be constructed using MakeRankExtensionCubes |
|---|
| 252 | k <- length(rankLHClist) -1 |
|---|
| 253 | tdims <- dim(rankLHClist[[1]]) |
|---|
| 254 | n <- tdims[1] |
|---|
| 255 | d <- tdims[2] |
|---|
| 256 | increment=1/(n*(k+1)) |
|---|
| 257 | if(is.null(LHCmaster)){ |
|---|
| 258 | LHCmaster <- rankLHClist[[1]] |
|---|
| 259 | LHCmaster <- (LHCmaster + runif(n*d) - 1)/n |
|---|
| 260 | } |
|---|
| 261 | pointers <- mclapply(1:n, function(e) {getPointers(rankLHClist[[2]][e,], LHCmaster,n=n,d=d,increment=increment,k=k)}) |
|---|
| 262 | pointers <- lapply(pointers, function(e) {dim(e) <- c(1,d);e}) |
|---|
| 263 | NewLHC1 <- t(sapply(1:n, function(l) sampleNewPoint(pointers[[l]], rankLHClist[[2]][l,], increment, n, d, k))) |
|---|
| 264 | ExtendedLHC <- matrix(NA, nrow=n*(k+1), ncol=d) |
|---|
| 265 | ExtendedLHC[1:n,] <- LHCmaster |
|---|
| 266 | ExtendedLHC[(n+1):(2*n),] <- NewLHC1 |
|---|
| 267 | for(l in 3:(k+1)){ |
|---|
| 268 | pointers <- mclapply(1:n, function(e) getPointers(rankLHClist[[l]][e,],na.omit(ExtendedLHC),n=n,d=d,increment=increment,k=k)) |
|---|
| 269 | newLHC <- t(sapply(1:n, function(j) sampleNewPoint(pointers[[j]],rankLHClist[[l]][j,], increment, n,d,k))) |
|---|
| 270 | ExtendedLHC[((l-1)*n+1):(l*n),] <- newLHC |
|---|
| 271 | } |
|---|
| 272 | ExtendedLHC |
|---|
| 273 | } |
|---|
| 274 | |
|---|