lhs/ 0000755 0001762 0000144 00000000000 13425544676 011060 5 ustar ligges users lhs/inst/ 0000755 0001762 0000144 00000000000 13425401600 012010 5 ustar ligges users lhs/inst/doc/ 0000755 0001762 0000144 00000000000 13425401600 012555 5 ustar ligges users lhs/inst/doc/lhs_faq.Rmd 0000644 0001762 0000144 00000023772 13420502241 014647 0 ustar ligges users ---
title: "Latin Hypercube Samples - Questions"
author: "Rob Carnell"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Latin Hypercube Samples - Questions}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
require(lhs)
```
## Question 1
I am looking for a package which gives me latin hyper cube samples
from a grid of values:
```{r q1}
a <- (1:10)
b <- (20:30)
dataGrid <- expand.grid(a, b)
```
### Answer
The `lhs` package returns a uniformly distributed stratified sample from the
unit hypercube. The marginal distributions can then be transformed to your
distribution of choice. If you wanted a uniform Latin hypercube on [1,10] and
[20,30] with 22 samples, you could do:
```{r a1}
X <- randomLHS(22, 2)
X[,1] <- 1 + 9*X[,1]
X[,2] <- 20 + 10*X[,2]
# OR
Y <- randomLHS(22, 2)
Y[,1] <- qunif(Y[,1], 1, 9)
Y[,2] <- qunif(Y[,2], 20, 30)
head(X)
head(Y)
```
If you want integers only in the sample,
then we must be careful about what we mean by a Latin hypercube
sample. If you wanted exactly 3 points, then you could divide up the range
[1,10] into three almost equal parts and sample from `1:3`, `4:6`, and `7:10`. The
problem is that it wouldn't be uniform sample across the range. (7 would be
sampled less often than 2 for example)
To do a Latin hypercube sample on the intgers, you should have a
number of integers on the margins which have the number of points sampled as a
common factor. For example if you sample 3 points from `1:9`, and `21:32` then
you could sample as follows:
```{r a12}
a <- c(sample(1:3,1), sample(4:6, 1), sample(7:9, 1))
b <- c(sample(21:24,1), sample(25:28, 1), sample(29:32,1))
```
and then randomly permute the entries of `a` and `b`.
Or more generally, take `n` samples from the list of integer groups:
```{r a13}
integerLHS <- function(n, intGroups)
{
stopifnot(all(lapply(intGroups, function(X) length(X) %% n) == 0))
stopifnot(require(lhs))
stopifnot(is.list(intGroups))
ranges <- lapply(intGroups, function(X) max(X) - min(X))
A <- matrix(nrow = n, ncol = length(intGroups))
for (j in 1:length(ranges))
{
sequ <- order(runif(n))
if (length(intGroups[[1]]) > 1)
{
spacing <- intGroups[[j]][2] - intGroups[[j]][1]
} else stop("must have more than 1 intGroup")
for (k in 1:n)
{
i <- sequ[k]
a <- min(intGroups[[j]]) + (i - 1)*(ranges[[j]] + spacing)/n
b <- min(intGroups[[j]]) + i*(ranges[[j]] + spacing)/n - 1
if (a < b)
{
A[k,j] <- sample(seq(a,b,spacing), 1)
} else if (a == b)
{
A[k,j] <- a
} else stop("error")
}
}
return(A)
}
integerLHS(10, list(1:10, 31:40))
integerLHS(5, list(1:10, 31:40))
integerLHS(2, list(1:10, 31:40))
integerLHS(5, list(1:20, 31:60, 101:115))
integerLHS(5, list(seq(2,20,2), 31:60, 101:115))
```
## Question 2
I am trying to do a Latin Hypercube Sampling (LHS) to a 5-parameter
design matrix. I want the combination of the
first three parameters to sum up to 1 (which obviously do not)
If I divide each of these parameters with the sum, the uniform distribution is
lost. Is there a way to maintain the random LHS (with uniformly
distributed parameters) so that the refered condition is fulfilled?
### Answer
In my experience with Latin hypercube samples, most people draw the sample on
a uniform hypercube and then transform the uniform cube to have new
distributions on the margins. The transformed distributions are not
necessarily uniform. It is possible to draw a Latin hypercube with correlated
margins and I hope to add that to my package in the future. I have also done
transforms such that the transformed marginal distributions are correlated (as
you have in your example). I have not seen a correlated set of uniform
marginal distributions such that the margins sum to one, however. I'll make a
quick example argument that explains the difficulty...
In two dimensions, you could draw this which is uniform and correlated.
```{r a21}
x <- seq(0.05, 0.95, length = 10)
y <- 1 - x
all.equal(x + y, rep(1, length(x)))
hist(x, main = "")
hist(y, main = "")
```
But in three dimensions, it is hard to maintain uniformity because large
samples on the first uniform margin overweight the small samples on the other
margins.
```{r a22}
x <- seq(0.05, 0.95, length = 10)
y <- runif(length(x), 0, 1 - x)
z <- 1 - x - y
hist(x, main = "")
hist(y, main = "")
hist(z, main = "")
```
The commmon practice in your situation is draw the `K` parameters together as a
uniform Latin hypercube on `0-1` and then transform the margins of the hypercube
to the desired distributions.
Easy Example
* Parameter 1: normal(1, 2)
* Parameter 2: normal(3, 4)
* Parameter 3: uniform(5, 10)
```{r a3, fig.width=5, fig.height=5}
N <- 1000
x <- randomLHS(N, 3)
y <- x
y[,1] <- qnorm(x[,1], 1, 2)
y[,2] <- qnorm(x[,2], 3, 4)
y[,3] <- qunif(x[,3], 5, 10)
par(mfrow = c(2,2))
dummy <- apply(x, 2, hist, main = "")
par(mfrow = c(2,2))
dummy <- apply(y, 2, hist, main = "")
```
The transformed distributions maintain their "Latin" properties, but are in
the form of new distributions.
In your case, you'd like the first three columns to be transformed into a
correlated set that sums to one. Still follow the pattern...
```{r a24, fig.width=5, fig.height=5}
x <- randomLHS(N, 5)
y <- x
y[,1] <- x[,1]/rowSums(x[,1:3])
y[,2] <- x[,2]/rowSums(x[,1:3])
y[,3] <- x[,3]/rowSums(x[,1:3])
y[,4] <- x[,4]
y[,5] <- x[,5]
par(mfrow = c(2,3))
dummy <- apply(x, 2, hist, main = "")
par(mfrow = c(2,3))
dummy <- apply(y, 2, hist, main = "")
all.equal(rowSums(y[,1:3]), rep(1, nrow(y)))
```
The uniform properties are gone as you can see here...
```{r a25}
par(mfrow = c(1,1))
pairs(x)
pairs(y, col = "red")
```
But, the "Latin" properties of the first three margins are maintained as in
this smaller example...
```{r a26}
N <- 10
x <- randomLHS(N, 5)
y <- x
y[,1] <- x[,1]/rowSums(x[,1:3])
y[,2] <- x[,2]/rowSums(x[,1:3])
y[,3] <- x[,3]/rowSums(x[,1:3])
y[,4] <- x[,4]
y[,5] <- x[,5]
pairs(x)
pairs(y, col = "red")
```
## Question 3
How do I create a Latin hypercube that ranges between between 0 and 1 and sums to 1?
### Answer
I have an imperfect solution to this problem using a Dirichlet distribution.
The Dirichlet seems to keep the range of the values larger once they are
normalized. The result is not uniformly distributed on (0,1) anymore, but
instead is Dirichlet distributed with the parameters alpha. The Latin
properties are maintained.
```{r qdirichlet}
qdirichlet <- function(X, alpha)
{
# qdirichlet is not an exact quantile function since the quantile of a
# multivariate distribtion is not unique
# qdirichlet is also not the quantiles of the marginal distributions since
# those quantiles do not sum to one
# qdirichlet is the quantile of the underlying gamma functions, normalized
# This has been tested to show that qdirichlet approximates the dirichlet
# distribution well and creates the correct marginal means and variances
# when using a latin hypercube sample
lena <- length(alpha)
stopifnot(is.matrix(X))
sims <- dim(X)[1]
stopifnot(dim(X)[2] == lena)
if (any(is.na(alpha)) || any(is.na(X)))
stop("NA values not allowed in qdirichlet")
Y <- matrix(0, nrow = sims, ncol = lena)
ind <- which(alpha != 0)
for (i in ind)
{
Y[,i] <- qgamma(X[,i], alpha[i], 1)
}
Y <- Y / rowSums(Y)
return(Y)
}
X <- randomLHS(1000, 7)
Y <- qdirichlet(X, rep(1,7))
stopifnot(all(abs(rowSums(Y) - 1) < 1E-12))
range(Y)
ws <- randomLHS(1000, 7)
wsSums <- rowSums(ws)
wss <- ws / wsSums
stopifnot(all(abs(rowSums(wss) - 1) < 1E-12))
range(wss)
```
## Question 5
I need to use Latin hypercube sampling for my own custome functions.
### Answer
```{r custom, fig.width=5, fig.height=5}
require(lhs)
# functions you described
T1 <- function(t) t*t
WL1 <- function(T1, t) T1*t
BE1 <- function(WL1, T1, t) WL1*T1*t
# t is distributed according to some pdf (e.g. normal)
# draw a lhs with 512 rows and 3 columns (one for each function)
y <- randomLHS(512, 3)
# transform the three columns to a normal distribution (these could be any
# distribution)
t <- apply(y, 2, function(columny) qnorm(columny, 2, 1))
# transform t using the functions provided
result <- cbind(
T1(t[,1]),
WL1(T1(t[,2]), t[,2]),
BE1(WL1(T1(t[,3]), t[,3]), T1(t[,3]), t[,3])
)
# check the results
# these should be approximately uniform
par(mfrow = c(2,2))
dummy <- apply(y, 2, hist, breaks = 50, main = "")
# these should be approximately normal
par(mfrow = c(2,2))
dummy <- apply(t, 2, hist, breaks = 50, main = "")
# these should be the results of the functions
par(mfrow = c(2,2))
dummy <- apply(result, 2, hist, breaks = 50, main = "")
```
## Question 6
I need a Latin hypercube sample on an integer set or a set of colors.
### Answer
```{r q6, fig.height=5, fig.width=5}
N <- 1000
set.seed(1919)
x <- randomLHS(N, 4)
y <- x
# uniform on 1-10
y[,1] <- ceiling(qunif(x[,1], 0, 10))
# three colors 1,2,3
y[,2] <- ceiling(qunif(x[,2], 0, 3))
# other distributions
y[,3] <- qunif(x[,3], 5, 10)
y[,4] <- qnorm(x[,4], 0, 2)
par(mfrow=c(2,2))
dummy <- apply(x, 2, hist, main="")
par(mfrow=c(2,2))
plot(1:10, c(table(y[,1])), type="h", col="blue", lwd=2, ylim=c(0,120),
ylab="Frequency", xlab="y[,1]")
plot(1:3, c(table(y[,2])), type="h", col="blue", lwd=2, ylim=c(0,400),
ylab="Frequency", xlab="y[,2]")
hist(y[,3], main="")
hist(y[,4], main="")
# change to color names
z <- as.data.frame(y)
z[,2] <- factor(y[,2], labels=c("R","G","B"))
z[1:10,]
```
lhs/inst/doc/lhs_basics.R 0000644 0001762 0000144 00000011743 13425401576 015034 0 ustar ligges users ## ----setup, include = FALSE----------------------------------------------
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
require(lhs)
source("VignetteCommonCode.R")
graph2dLHS <- function(Alhs)
{
stopifnot(ncol(Alhs) == 2)
sims <- nrow(Alhs)
par(mar = c(4,4,2,2))
plot.default(Alhs[,1], Alhs[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "Parameter 1", ylab = "Parameter 2", xaxs = "i",
yaxs = "i", main = "")
for (i in 1:nrow(Alhs))
{
rect(floor(Alhs[i,1]*sims)/sims, floor(Alhs[i,2]*sims)/sims,
ceiling(Alhs[i,1]*sims)/sims, ceiling(Alhs[i,2]*sims)/sims, col = "grey")
}
points(Alhs[,1], Alhs[,2], pch = 19, col = "red")
abline(v = (0:sims)/sims, h = (0:sims)/sims)
}
# transform is a function of the kind that takes a number
# transform <- function(x){return(qnorm(x,mean=0, std=1))}
graph2dLHSTransform <- function(Alhs, transform1, transform2, min1, max1, min2, max2)
{
stopifnot(ncol(Alhs) == 2)
stopifnot(all(Alhs[,1] <= max1 && Alhs[,1] >= min1))
stopifnot(all(Alhs[,2] <= max2 && Alhs[,2] >= min2))
sims <- nrow(Alhs)
breaks <- seq(0,1,length = sims + 1)[2:(sims)]
breaksTransformed1 <- sapply(breaks, transform1)
breaksTransformed2 <- sapply(breaks, transform2)
par(mar = c(4,4,2,2))
plot.default(Alhs[,1], Alhs[,2], type = "n",
ylim = c(min2, max2),
xlim = c(min1, max1),
xlab = "Parameter 1", ylab = "Parameter 2",
xaxs = "i", yaxs = "i", main = "")
for (si in 1:sims)
{
temp <- Alhs[si,]
for (i in 1:sims)
{
if ((i == 1 && min1 <= temp[1] && breaksTransformed1[i] >= temp[1]) ||
(i == sims && max1 >= temp[1] && breaksTransformed1[i - 1] <= temp[1]) ||
(breaksTransformed1[i - 1] <= temp[1] && breaksTransformed1[i] >= temp[1]))
{
for (j in 1:sims)
{
if ((j == 1 && min2 <= temp[2] && breaksTransformed2[j] >= temp[2]) ||
(j == sims && max2 >= temp[2] && breaksTransformed2[j - 1] <= temp[2]) ||
(breaksTransformed2[j - 1] <= temp[2] && breaksTransformed2[j] >= temp[2]))
{
if (i == 1)
{
xbot <- min1
xtop <- breaksTransformed1[i]
} else if (i == sims)
{
xbot <- breaksTransformed1[i - 1]
xtop <- max1
} else
{
xbot <- breaksTransformed1[i - 1]
xtop <- breaksTransformed1[i]
}
if (j == 1)
{
ybot <- min2
ytop <- breaksTransformed2[j]
} else if (j == sims)
{
ybot <- breaksTransformed2[j - 1]
ytop <- max2
} else
{
ybot <- breaksTransformed2[j - 1]
ytop <- breaksTransformed2[j]
}
rect(xbot, ybot, xtop, ytop, col = "grey")
}
}
}
}
}
points(Alhs[,1], Alhs[,2], pch = 19, col = "red")
abline(v = breaksTransformed1, h = breaksTransformed2)
}
#set.seed(1111)
#A <- randomLHS(5,4)
#f <- function(x){qnorm(x)}
#g <- function(x){qlnorm(x, meanlog=0.5, sdlog=1)}
#B <- A
#B[,1] <- f(A[,1])
#B[,2] <- g(A[,2])
#graph2dLHSTransform(B[,1:2], f, g, -4, 4, 0, 8)
#f <- function(x){qunif(x, 3, 5)}
#B <- apply(A, 2, f)
#graph2dLHSTransform(B[,1:2], f)
## ----block1--------------------------------------------------------------
# set the seed for reproducibility
set.seed(1111)
# a design with 5 samples from 4 parameters
A <- randomLHS(5, 4)
A
## ----figureX, fig.align='center', fig.height=5, fig.width=5, echo=FALSE----
graph2dLHS(A[,1:2])
## ----block 3-------------------------------------------------------------
B <- matrix(nrow = nrow(A), ncol = ncol(A))
B[,1] <- qnorm(A[,1], mean = 0, sd = 1)
B[,2] <- qlnorm(A[,2], meanlog = 0.5, sdlog = 1)
B[,3] <- A[,3]
B[,4] <- qunif(A[,4], min = 7, max = 10)
B
## ----figureY, fig.align='center', fig.height=5, fig.width=5, echo=FALSE----
f <- function(x){qnorm(x)}
g <- function(x){qlnorm(x, meanlog = 0.5, sdlog = 1)}
graph2dLHSTransform(B[,1:2], f, g, -4, 4, 0, 8)
## ----block 4-------------------------------------------------------------
set.seed(101)
A <- randomLHS(30, 10)
A1 <- optimumLHS(30, 10, maxSweeps = 4, eps = 0.01)
A2 <- maximinLHS(30, 10, dup = 5)
A3 <- improvedLHS(30, 10, dup = 5)
A4 <- geneticLHS(30, 10, pop = 1000, gen = 8, pMut = 0.1, criterium = "S")
A5 <- geneticLHS(30, 10, pop = 1000, gen = 8, pMut = 0.1, criterium = "Maximin")
## ----Z, fig.align='center', fig.height=7, fig.width=7, echo=FALSE--------
pairs(A, pch = 19, col = "blue", cex = 0.5)
## ----W, fig.align='center', fig.height=7, fig.width=7, echo=FALSE--------
pairs(A1, pch = 19, col = "blue", cex = 0.5)
## ----G, fig.align='center', fig.height=7, fig.width=7, echo=FALSE--------
pairs(A2, pch = 19, col = "blue", cex = 0.5)
lhs/inst/doc/lhs_faq.R 0000644 0001762 0000144 00000015077 13425401600 014327 0 ustar ligges users ## ----setup, include = FALSE----------------------------------------------
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
require(lhs)
## ----q1------------------------------------------------------------------
a <- (1:10)
b <- (20:30)
dataGrid <- expand.grid(a, b)
## ----a1------------------------------------------------------------------
X <- randomLHS(22, 2)
X[,1] <- 1 + 9*X[,1]
X[,2] <- 20 + 10*X[,2]
# OR
Y <- randomLHS(22, 2)
Y[,1] <- qunif(Y[,1], 1, 9)
Y[,2] <- qunif(Y[,2], 20, 30)
head(X)
head(Y)
## ----a12-----------------------------------------------------------------
a <- c(sample(1:3,1), sample(4:6, 1), sample(7:9, 1))
b <- c(sample(21:24,1), sample(25:28, 1), sample(29:32,1))
## ----a13-----------------------------------------------------------------
integerLHS <- function(n, intGroups)
{
stopifnot(all(lapply(intGroups, function(X) length(X) %% n) == 0))
stopifnot(require(lhs))
stopifnot(is.list(intGroups))
ranges <- lapply(intGroups, function(X) max(X) - min(X))
A <- matrix(nrow = n, ncol = length(intGroups))
for (j in 1:length(ranges))
{
sequ <- order(runif(n))
if (length(intGroups[[1]]) > 1)
{
spacing <- intGroups[[j]][2] - intGroups[[j]][1]
} else stop("must have more than 1 intGroup")
for (k in 1:n)
{
i <- sequ[k]
a <- min(intGroups[[j]]) + (i - 1)*(ranges[[j]] + spacing)/n
b <- min(intGroups[[j]]) + i*(ranges[[j]] + spacing)/n - 1
if (a < b)
{
A[k,j] <- sample(seq(a,b,spacing), 1)
} else if (a == b)
{
A[k,j] <- a
} else stop("error")
}
}
return(A)
}
integerLHS(10, list(1:10, 31:40))
integerLHS(5, list(1:10, 31:40))
integerLHS(2, list(1:10, 31:40))
integerLHS(5, list(1:20, 31:60, 101:115))
integerLHS(5, list(seq(2,20,2), 31:60, 101:115))
## ----a21-----------------------------------------------------------------
x <- seq(0.05, 0.95, length = 10)
y <- 1 - x
all.equal(x + y, rep(1, length(x)))
hist(x, main = "")
hist(y, main = "")
## ----a22-----------------------------------------------------------------
x <- seq(0.05, 0.95, length = 10)
y <- runif(length(x), 0, 1 - x)
z <- 1 - x - y
hist(x, main = "")
hist(y, main = "")
hist(z, main = "")
## ----a3, fig.width=5, fig.height=5---------------------------------------
N <- 1000
x <- randomLHS(N, 3)
y <- x
y[,1] <- qnorm(x[,1], 1, 2)
y[,2] <- qnorm(x[,2], 3, 4)
y[,3] <- qunif(x[,3], 5, 10)
par(mfrow = c(2,2))
dummy <- apply(x, 2, hist, main = "")
par(mfrow = c(2,2))
dummy <- apply(y, 2, hist, main = "")
## ----a24, fig.width=5, fig.height=5--------------------------------------
x <- randomLHS(N, 5)
y <- x
y[,1] <- x[,1]/rowSums(x[,1:3])
y[,2] <- x[,2]/rowSums(x[,1:3])
y[,3] <- x[,3]/rowSums(x[,1:3])
y[,4] <- x[,4]
y[,5] <- x[,5]
par(mfrow = c(2,3))
dummy <- apply(x, 2, hist, main = "")
par(mfrow = c(2,3))
dummy <- apply(y, 2, hist, main = "")
all.equal(rowSums(y[,1:3]), rep(1, nrow(y)))
## ----a25-----------------------------------------------------------------
par(mfrow = c(1,1))
pairs(x)
pairs(y, col = "red")
## ----a26-----------------------------------------------------------------
N <- 10
x <- randomLHS(N, 5)
y <- x
y[,1] <- x[,1]/rowSums(x[,1:3])
y[,2] <- x[,2]/rowSums(x[,1:3])
y[,3] <- x[,3]/rowSums(x[,1:3])
y[,4] <- x[,4]
y[,5] <- x[,5]
pairs(x)
pairs(y, col = "red")
## ----qdirichlet----------------------------------------------------------
qdirichlet <- function(X, alpha)
{
# qdirichlet is not an exact quantile function since the quantile of a
# multivariate distribtion is not unique
# qdirichlet is also not the quantiles of the marginal distributions since
# those quantiles do not sum to one
# qdirichlet is the quantile of the underlying gamma functions, normalized
# This has been tested to show that qdirichlet approximates the dirichlet
# distribution well and creates the correct marginal means and variances
# when using a latin hypercube sample
lena <- length(alpha)
stopifnot(is.matrix(X))
sims <- dim(X)[1]
stopifnot(dim(X)[2] == lena)
if (any(is.na(alpha)) || any(is.na(X)))
stop("NA values not allowed in qdirichlet")
Y <- matrix(0, nrow = sims, ncol = lena)
ind <- which(alpha != 0)
for (i in ind)
{
Y[,i] <- qgamma(X[,i], alpha[i], 1)
}
Y <- Y / rowSums(Y)
return(Y)
}
X <- randomLHS(1000, 7)
Y <- qdirichlet(X, rep(1,7))
stopifnot(all(abs(rowSums(Y) - 1) < 1E-12))
range(Y)
ws <- randomLHS(1000, 7)
wsSums <- rowSums(ws)
wss <- ws / wsSums
stopifnot(all(abs(rowSums(wss) - 1) < 1E-12))
range(wss)
## ----custom, fig.width=5, fig.height=5-----------------------------------
require(lhs)
# functions you described
T1 <- function(t) t*t
WL1 <- function(T1, t) T1*t
BE1 <- function(WL1, T1, t) WL1*T1*t
# t is distributed according to some pdf (e.g. normal)
# draw a lhs with 512 rows and 3 columns (one for each function)
y <- randomLHS(512, 3)
# transform the three columns to a normal distribution (these could be any
# distribution)
t <- apply(y, 2, function(columny) qnorm(columny, 2, 1))
# transform t using the functions provided
result <- cbind(
T1(t[,1]),
WL1(T1(t[,2]), t[,2]),
BE1(WL1(T1(t[,3]), t[,3]), T1(t[,3]), t[,3])
)
# check the results
# these should be approximately uniform
par(mfrow = c(2,2))
dummy <- apply(y, 2, hist, breaks = 50, main = "")
# these should be approximately normal
par(mfrow = c(2,2))
dummy <- apply(t, 2, hist, breaks = 50, main = "")
# these should be the results of the functions
par(mfrow = c(2,2))
dummy <- apply(result, 2, hist, breaks = 50, main = "")
## ----q6, fig.height=5, fig.width=5---------------------------------------
N <- 1000
set.seed(1919)
x <- randomLHS(N, 4)
y <- x
# uniform on 1-10
y[,1] <- ceiling(qunif(x[,1], 0, 10))
# three colors 1,2,3
y[,2] <- ceiling(qunif(x[,2], 0, 3))
# other distributions
y[,3] <- qunif(x[,3], 5, 10)
y[,4] <- qnorm(x[,4], 0, 2)
par(mfrow=c(2,2))
dummy <- apply(x, 2, hist, main="")
par(mfrow=c(2,2))
plot(1:10, c(table(y[,1])), type="h", col="blue", lwd=2, ylim=c(0,120),
ylab="Frequency", xlab="y[,1]")
plot(1:3, c(table(y[,2])), type="h", col="blue", lwd=2, ylim=c(0,400),
ylab="Frequency", xlab="y[,2]")
hist(y[,3], main="")
hist(y[,4], main="")
# change to color names
z <- as.data.frame(y)
z[,2] <- factor(y[,2], labels=c("R","G","B"))
z[1:10,]
lhs/inst/doc/augment_lhs.Rmd 0000644 0001762 0000144 00000014427 13416532121 015542 0 ustar ligges users ---
title: "An Example of Augmenting a Latin Hypercube"
author: "Rob Carnell"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{An Example of Augmenting a Latin Hypercube}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
%\VignetteAuthor{Rob Carnell}
%\VignetteKeyword{lhs}
%\VignetteKeyword{latin hypercube}
%\VignetteKeyword{augment}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
source("VignetteCommonCode.R")
require(lhs)
graph2DaugmentLHS1 <- function(sims, extras)
{
A <- randomLHS(sims, 2)
B <- augmentLHS(A, extras)
plot.default(A[,1], A[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "x1", ylab = "x2", xaxs = "i", yaxs = "i", main = "")
for (i in 1:length(A[,1]))
{
rect(floor(A[i,1]*sims)/sims, floor(A[i,2]*sims)/sims,
ceiling(A[i,1]*sims)/sims, ceiling(A[i,2]*sims)/sims, col = "grey")
}
points(A[,1], A[,2], pch = 19, col = "red")
abline(v = (0:sims)/sims, h = (0:sims)/sims)
return(list(A = A, B = B, sims = sims, extras = extras))
}
graph2DaugmentLHS2 <- function(X)
{
A <- X$A
B <- X$B
sims <- X$sims
extras <- X$extras
plot.default(A[,1], A[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "x1", ylab = "x2", xaxs = "i", yaxs = "i", main = "")
N <- sims + extras
for (i in 1:length(B[,1]))
{
rect(floor(B[i,1]*N)/N, floor(B[i,2]*N)/N,
ceiling(B[i,1]*N)/N, ceiling(B[i,2]*N)/N, col = "grey")
}
points(A[,1], A[,2], pch = 19, col = "red")
points(B[((sims + 1):(sims + extras)), 1], B[((sims + 1):(sims + extras)), 2],
pch = 19, col = "blue")
abline(v = (0:N)/N, h = (0:N)/N)
}
# X <- graph2DaugmentLHS1(5,5)
# graph2DaugmentLHS2(X)
```
Suppose that a computer simulation study is being designed that requires
expensive runs. A Latin hypercube design is desired for this simulation so
that the expectation of the simulation output can be estimated efficiently
given the distributions of the input variables. Latin hypercubes are most
often used in highly dimensional problems, but the example shown is of small
dimension. Suppose further that the total extent of funding is uncertain.
Enough money is available for 5 runs, and there is a chance that there will be
enough for 5 more. However, if the money for the additional 5 runs does not
materialize, then the first 5 runs must be a Latin hypercube alone. A design
for this situation can be created using the `lhs` package.
First create a random Latin hypercube using the `randomLHS(n, k)` command:
```{r randomlhs}
A <- randomLHS(5,2)
```
An example of this hypercube is shown in `r registerFigure("X")`. Note that
the *Latin* property of the hypercube requires that each of the 5 equal
probability intervals be filled (i.e. each row and each column is filled with
one point). Also notice that the exact location of the design point is randomly
sampled from within that cell using a uniform distribution for each marginal
variable.
-----
`r addFigureCaption("X", "A randomly produced Latin Hypercube with uniform marginal distributions for 2 parameters with 5 simulations", register=FALSE)`
```{r original5, echo=FALSE, fig.align='center', fig.height=5, fig.width=5}
set.seed(10)
X <- graph2DaugmentLHS1(5, 5)
```
-----
Next, in order to augment the design with more points use `augmentLHS(lhs, m)`. The following will add 5 more points to the design:
```{r augment5}
B <- augmentLHS(A, 5)
```
The `augmentLHS` function works by re-dividing the original design into
`n+m` intervals (e.g. 5+5=10) keeping the original design points exactly in the
same position. It then randomly fills the empty row-column sets. The results
are shown in `r registerFigure("Y")`.
-----
`r addFigureCaption("Y", "A randomly produced Latin Hypercube of 5 points (red) with 5 augmented points (blue). Each parameter has a uniform marginal distribution.", register=FALSE)`
```{r augmented10, fig.align='center', echo=FALSE, fig.height=5, fig.width=5}
graph2DaugmentLHS2(X)
```
-----
The `augmentLHS` function uses the following algorithm (see the documentation for `augmentLHS`):
* Create a new `(n+m)` by `k` matrix to hold the candidate points after
the design has been re-partitioned into `(n+m)^2` cells, where `n` is number
of points in the original `lhs` matrix.
* Then randomly sweep through each
column (1...`k`) in the repartitioned design to find the missing cells.
* For each column (variable), randomly search for an empty row, generate a
random value that fits in that row, record the value in the new matrix.
The new matrix can contain more than `m` points unless `m = 2n`,
in which case the new matrix will contain exactly `m` filled rows.
* Finally, keep only the first `m` rows of the new matrix. It is
guaranteed that there will be `m` full rows (points) in the new matrix. The
deleted rows are partially full. The additional candidate points are selected
randomly because of the random search used to find empty cells.
Also notice that because the original points are randomly placed within the
cells, depending on how you bin the marginal distributions, a histogram (of x1
for example) will not necessarily be exactly uniform.
Now, the augmenting points do not necessarily form a Latin Hypercube themselves.
The original design and augmenting points may form a Latin Hypercube, or there
may be more than one point per row in the augmented design. If the augmented
points are equal to the number of original points, then a strictly uniform
Latin hypercube is guaranteed. An example of an augmented design which is not
uniform in the marginal distributions is given in `r registerFigure("Z")` and `r registerFigure("W")`.
The commands were:
```{r random_and_augment}
A <- randomLHS(7, 2)
B <- augmentLHS(A, 3)
```
-----
`r addFigureCaption("Z", "Original design with 7 points", register=FALSE)`
```{r Z, echo=FALSE, fig.align='center', fig.height=5, fig.width=5}
set.seed(12)
X <- graph2DaugmentLHS1(7, 3)
```
-----
`r addFigureCaption("W", "Augmented design with 3 additional points. Note that row 9 has 2 points and row 3 has none.", register=FALSE)`
```{r W, echo=FALSE, fig.align='center', fig.height=5, fig.width=5}
graph2DaugmentLHS2(X)
```
lhs/inst/doc/lhs_faq.html 0000644 0001762 0000144 00000355107 13425401600 015073 0 ustar ligges users
Latin Hypercube Samples - Questions
Latin Hypercube Samples - Questions
Rob Carnell
2019-02-02
Question 1
I am looking for a package which gives me latin hyper cube samples from a grid of values:
a <-(1:10)
b <-(20:30)
dataGrid <-expand.grid(a, b)
Answer
The lhs package returns a uniformly distributed stratified sample from the unit hypercube. The marginal distributions can then be transformed to your distribution of choice. If you wanted a uniform Latin hypercube on [1,10] and [20,30] with 22 samples, you could do:
If you want integers only in the sample, then we must be careful about what we mean by a Latin hypercube sample. If you wanted exactly 3 points, then you could divide up the range [1,10] into three almost equal parts and sample from 1:3, 4:6, and 7:10. The problem is that it wouldn’t be uniform sample across the range. (7 would be sampled less often than 2 for example)
To do a Latin hypercube sample on the intgers, you should have a number of integers on the margins which have the number of points sampled as a common factor. For example if you sample 3 points from 1:9, and 21:32 then you could sample as follows:
a <-c(sample(1:3,1), sample(4:6, 1), sample(7:9, 1))
b <-c(sample(21:24,1), sample(25:28, 1), sample(29:32,1))
and then randomly permute the entries of a and b.
Or more generally, take n samples from the list of integer groups:
I am trying to do a Latin Hypercube Sampling (LHS) to a 5-parameter design matrix. I want the combination of the first three parameters to sum up to 1 (which obviously do not)
If I divide each of these parameters with the sum, the uniform distribution is lost. Is there a way to maintain the random LHS (with uniformly distributed parameters) so that the refered condition is fulfilled?
Answer
In my experience with Latin hypercube samples, most people draw the sample on a uniform hypercube and then transform the uniform cube to have new distributions on the margins. The transformed distributions are not necessarily uniform. It is possible to draw a Latin hypercube with correlated margins and I hope to add that to my package in the future. I have also done transforms such that the transformed marginal distributions are correlated (as you have in your example). I have not seen a correlated set of uniform marginal distributions such that the margins sum to one, however. I’ll make a quick example argument that explains the difficulty…
In two dimensions, you could draw this which is uniform and correlated.
x <-seq(0.05, 0.95, length =10)
y <-1-x
all.equal(x +y, rep(1, length(x)))
#> [1] TRUEhist(x, main ="")
hist(y, main ="")
But in three dimensions, it is hard to maintain uniformity because large samples on the first uniform margin overweight the small samples on the other margins.
x <-seq(0.05, 0.95, length =10)
y <-runif(length(x), 0, 1-x)
z <-1-x -y
hist(x, main ="")
hist(y, main ="")
hist(z, main ="")
The commmon practice in your situation is draw the K parameters together as a uniform Latin hypercube on 0-1 and then transform the margins of the hypercube to the desired distributions.
The uniform properties are gone as you can see here…
par(mfrow =c(1,1))
pairs(x)
pairs(y, col ="red")
But, the “Latin” properties of the first three margins are maintained as in this smaller example…
N <-10
x <-randomLHS(N, 5)
y <-x
y[,1] <-x[,1]/rowSums(x[,1:3])
y[,2] <-x[,2]/rowSums(x[,1:3])
y[,3] <-x[,3]/rowSums(x[,1:3])
y[,4] <-x[,4]
y[,5] <-x[,5]
pairs(x)
pairs(y, col ="red")
Question 3
How do I create a Latin hypercube that ranges between between 0 and 1 and sums to 1?
Answer
I have an imperfect solution to this problem using a Dirichlet distribution.
The Dirichlet seems to keep the range of the values larger once they are normalized. The result is not uniformly distributed on (0,1) anymore, but instead is Dirichlet distributed with the parameters alpha. The Latin properties are maintained.
qdirichlet <-function(X, alpha)
{
# qdirichlet is not an exact quantile function since the quantile of a # multivariate distribtion is not unique # qdirichlet is also not the quantiles of the marginal distributions since # those quantiles do not sum to one # qdirichlet is the quantile of the underlying gamma functions, normalized # This has been tested to show that qdirichlet approximates the dirichlet # distribution well and creates the correct marginal means and variances # when using a latin hypercube sample
lena <-length(alpha)
stopifnot(is.matrix(X))
sims <-dim(X)[1]
stopifnot(dim(X)[2] ==lena)
if (any(is.na(alpha)) ||any(is.na(X)))
stop("NA values not allowed in qdirichlet")
Y <-matrix(0, nrow = sims, ncol = lena)
ind <-which(alpha !=0)
for (i in ind)
{
Y[,i] <-qgamma(X[,i], alpha[i], 1)
}
Y <-Y /rowSums(Y)
return(Y)
}
X <-randomLHS(1000, 7)
Y <-qdirichlet(X, rep(1,7))
stopifnot(all(abs(rowSums(Y) -1) <1E-12))
range(Y)
#> [1] 1.926871e-05 7.941305e-01
ws <-randomLHS(1000, 7)
wsSums <-rowSums(ws)
wss <-ws /wsSums
stopifnot(all(abs(rowSums(wss) -1) <1E-12))
range(wss)
#> [1] 1.148462e-05 4.451393e-01
Question 5
I need to use Latin hypercube sampling for my own custome functions.
Answer
require(lhs)
# functions you described
T1 <-function(t) t*t
WL1 <-function(T1, t) T1*t
BE1 <-function(WL1, T1, t) WL1*T1*t
# t is distributed according to some pdf (e.g. normal) # draw a lhs with 512 rows and 3 columns (one for each function)
y <-randomLHS(512, 3)
# transform the three columns to a normal distribution (these could be any # distribution)
t <-apply(y, 2, function(columny) qnorm(columny, 2, 1))
# transform t using the functions provided
result <-cbind(
T1(t[,1]),
WL1(T1(t[,2]), t[,2]),
BE1(WL1(T1(t[,3]), t[,3]), T1(t[,3]), t[,3])
)
# check the results # these should be approximately uniform par(mfrow =c(2,2))
dummy <-apply(y, 2, hist, breaks =50, main ="")
# these should be approximately normal par(mfrow =c(2,2))
dummy <-apply(t, 2, hist, breaks =50, main ="")
# these should be the results of the functions par(mfrow =c(2,2))
dummy <-apply(result, 2, hist, breaks =50, main ="")
Question 6
I need a Latin hypercube sample on an integer set or a set of colors.
Answer
N <-1000set.seed(1919)
x <-randomLHS(N, 4)
y <-x
# uniform on 1-10
y[,1] <-ceiling(qunif(x[,1], 0, 10))
# three colors 1,2,3
y[,2] <-ceiling(qunif(x[,2], 0, 3))
# other distributions
y[,3] <-qunif(x[,3], 5, 10)
y[,4] <-qnorm(x[,4], 0, 2)
par(mfrow=c(2,2))
dummy <-apply(x, 2, hist, main="")
# change to color names
z <-as.data.frame(y)
z[,2] <-factor(y[,2], labels=c("R","G","B"))
z[1:10,]
#> V1 V2 V3 V4#> 1 9 R 9.944182 2.94805877#> 2 4 R 8.530678 -0.19388895#> 3 8 G 8.095066 -0.89251244#> 4 4 G 8.198067 -0.45032286#> 5 10 G 6.523280 -4.09957931#> 6 10 B 6.227534 -0.05631367#> 7 2 B 7.177990 3.84640466#> 8 8 G 6.881714 0.58980996#> 9 9 G 7.111466 0.45285007#> 10 2 R 6.172652 1.93023633
lhs/inst/doc/lhs_basics.html 0000644 0001762 0000144 00000275621 13425401576 015606 0 ustar ligges users
Basic Latin hypercube samples and designs with package lhs
Basic Latin hypercube samples and designs with package lhs
Rob Carnell
2019-02-02
Theory of Latin Hypercube Sampling
For the technical basis of Latin Hypercube Sampling (LHS) and Latin Hypercube Designs (LHD) please see: * Stein, Michael. Large Sample Properties of Simulations Using Latin Hypercube Sampling Technometrics, Vol 28, No 2, 1987. * McKay, MD, et.al. A Comparison of Three Methods for Selecting Values of Input Variables in the Analysis of Output from a Computer Code Technometrics, Vol 21, No 2, 1979.
This package was created to bring these designs to R and to implement many of the articles that followed on optimized sampling methods.
Create a Simple LHS
Basic LHS’s are created using randomLHS.
# set the seed for reproducibilityset.seed(1111)
# a design with 5 samples from 4 parameters
A <-randomLHS(5, 4)
A
#> [,1] [,2] [,3] [,4]#> [1,] 0.6328827 0.48424369 0.1678234 0.1974741#> [2,] 0.2124960 0.88111537 0.6069217 0.4771109#> [3,] 0.1277885 0.64327868 0.3612360 0.9862456#> [4,] 0.8935830 0.27182878 0.4335808 0.6052341#> [5,] 0.5089423 0.02269382 0.8796676 0.2036678
In general, the LHS is uniform on the margins until transformed (Figure 1):
Figure 1. Two dimensions of a Uniform random LHS with 5 samples
It is common to transform the margins of the design (the columns) into other distributions (Figure 2)
Figure 2. Two dimensions of a transformed random LHS with 5 samples
Optimizing the Design
The LHS can be optimized using a number of methods in the lhs package. Each method attempts to improve on the random design by ensuring that the selected points are as uncorrelated and space filling as possible. Table 1 shows some results. Figure 3, Figure 4, and Figure 5 show corresponding plots.
set.seed(101)
A <-randomLHS(30, 10)
A1 <-optimumLHS(30, 10, maxSweeps =4, eps =0.01)
A2 <-maximinLHS(30, 10, dup =5)
A3 <-improvedLHS(30, 10, dup =5)
A4 <-geneticLHS(30, 10, pop =1000, gen =8, pMut =0.1, criterium ="S")
A5 <-geneticLHS(30, 10, pop =1000, gen =8, pMut =0.1, criterium ="Maximin")
Table 1. Sample results and metrics of various LHS algorithms
Method
Min Distance btwn pts
Mean Distance btwn pts
Max Correlation btwn pts
randomLHS
0.6346585
1.2913235
0.5173006
optimumLHS
0.8717797
1.3001892
0.1268209
maximinLHS
0.595395
1.2835191
0.2983643
improvedLHS
0.6425673
1.2746711
0.5711527
geneticLHS (S)
0.8340751
1.3026543
0.3971539
geneticLHS (Maximin)
0.8105733
1.2933412
0.5605546
Figure 3. Pairwise margins of a randomLHS
Figure 4. Pairwise margins of a optimumLHS
Figure 5. Pairwise margins of a maximinLHS
lhs/inst/doc/augment_lhs.R 0000644 0001762 0000144 00000004274 13425401565 015227 0 ustar ligges users ## ----setup, include = FALSE----------------------------------------------
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
source("VignetteCommonCode.R")
require(lhs)
graph2DaugmentLHS1 <- function(sims, extras)
{
A <- randomLHS(sims, 2)
B <- augmentLHS(A, extras)
plot.default(A[,1], A[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "x1", ylab = "x2", xaxs = "i", yaxs = "i", main = "")
for (i in 1:length(A[,1]))
{
rect(floor(A[i,1]*sims)/sims, floor(A[i,2]*sims)/sims,
ceiling(A[i,1]*sims)/sims, ceiling(A[i,2]*sims)/sims, col = "grey")
}
points(A[,1], A[,2], pch = 19, col = "red")
abline(v = (0:sims)/sims, h = (0:sims)/sims)
return(list(A = A, B = B, sims = sims, extras = extras))
}
graph2DaugmentLHS2 <- function(X)
{
A <- X$A
B <- X$B
sims <- X$sims
extras <- X$extras
plot.default(A[,1], A[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "x1", ylab = "x2", xaxs = "i", yaxs = "i", main = "")
N <- sims + extras
for (i in 1:length(B[,1]))
{
rect(floor(B[i,1]*N)/N, floor(B[i,2]*N)/N,
ceiling(B[i,1]*N)/N, ceiling(B[i,2]*N)/N, col = "grey")
}
points(A[,1], A[,2], pch = 19, col = "red")
points(B[((sims + 1):(sims + extras)), 1], B[((sims + 1):(sims + extras)), 2],
pch = 19, col = "blue")
abline(v = (0:N)/N, h = (0:N)/N)
}
# X <- graph2DaugmentLHS1(5,5)
# graph2DaugmentLHS2(X)
## ----randomlhs-----------------------------------------------------------
A <- randomLHS(5,2)
## ----original5, echo=FALSE, fig.align='center', fig.height=5, fig.width=5----
set.seed(10)
X <- graph2DaugmentLHS1(5, 5)
## ----augment5------------------------------------------------------------
B <- augmentLHS(A, 5)
## ----augmented10, fig.align='center', echo=FALSE, fig.height=5, fig.width=5----
graph2DaugmentLHS2(X)
## ----random_and_augment--------------------------------------------------
A <- randomLHS(7, 2)
B <- augmentLHS(A, 3)
## ----Z, echo=FALSE, fig.align='center', fig.height=5, fig.width=5--------
set.seed(12)
X <- graph2DaugmentLHS1(7, 3)
## ----W, echo=FALSE, fig.align='center', fig.height=5, fig.width=5--------
graph2DaugmentLHS2(X)
lhs/inst/doc/lhs_basics.Rmd 0000644 0001762 0000144 00000017172 13416533265 015360 0 ustar ligges users ---
title: "Basic Latin hypercube samples and designs with package lhs"
author: "Rob Carnell"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Basic Latin hypercube samples and designs with package lhs}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
%\VignetteAuthor{Rob Carnell}
%\VignetteKeyword{lhs}
%\VignetteKeyword{latin hypercube}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
require(lhs)
source("VignetteCommonCode.R")
graph2dLHS <- function(Alhs)
{
stopifnot(ncol(Alhs) == 2)
sims <- nrow(Alhs)
par(mar = c(4,4,2,2))
plot.default(Alhs[,1], Alhs[,2], type = "n", ylim = c(0,1),
xlim = c(0,1), xlab = "Parameter 1", ylab = "Parameter 2", xaxs = "i",
yaxs = "i", main = "")
for (i in 1:nrow(Alhs))
{
rect(floor(Alhs[i,1]*sims)/sims, floor(Alhs[i,2]*sims)/sims,
ceiling(Alhs[i,1]*sims)/sims, ceiling(Alhs[i,2]*sims)/sims, col = "grey")
}
points(Alhs[,1], Alhs[,2], pch = 19, col = "red")
abline(v = (0:sims)/sims, h = (0:sims)/sims)
}
# transform is a function of the kind that takes a number
# transform <- function(x){return(qnorm(x,mean=0, std=1))}
graph2dLHSTransform <- function(Alhs, transform1, transform2, min1, max1, min2, max2)
{
stopifnot(ncol(Alhs) == 2)
stopifnot(all(Alhs[,1] <= max1 && Alhs[,1] >= min1))
stopifnot(all(Alhs[,2] <= max2 && Alhs[,2] >= min2))
sims <- nrow(Alhs)
breaks <- seq(0,1,length = sims + 1)[2:(sims)]
breaksTransformed1 <- sapply(breaks, transform1)
breaksTransformed2 <- sapply(breaks, transform2)
par(mar = c(4,4,2,2))
plot.default(Alhs[,1], Alhs[,2], type = "n",
ylim = c(min2, max2),
xlim = c(min1, max1),
xlab = "Parameter 1", ylab = "Parameter 2",
xaxs = "i", yaxs = "i", main = "")
for (si in 1:sims)
{
temp <- Alhs[si,]
for (i in 1:sims)
{
if ((i == 1 && min1 <= temp[1] && breaksTransformed1[i] >= temp[1]) ||
(i == sims && max1 >= temp[1] && breaksTransformed1[i - 1] <= temp[1]) ||
(breaksTransformed1[i - 1] <= temp[1] && breaksTransformed1[i] >= temp[1]))
{
for (j in 1:sims)
{
if ((j == 1 && min2 <= temp[2] && breaksTransformed2[j] >= temp[2]) ||
(j == sims && max2 >= temp[2] && breaksTransformed2[j - 1] <= temp[2]) ||
(breaksTransformed2[j - 1] <= temp[2] && breaksTransformed2[j] >= temp[2]))
{
if (i == 1)
{
xbot <- min1
xtop <- breaksTransformed1[i]
} else if (i == sims)
{
xbot <- breaksTransformed1[i - 1]
xtop <- max1
} else
{
xbot <- breaksTransformed1[i - 1]
xtop <- breaksTransformed1[i]
}
if (j == 1)
{
ybot <- min2
ytop <- breaksTransformed2[j]
} else if (j == sims)
{
ybot <- breaksTransformed2[j - 1]
ytop <- max2
} else
{
ybot <- breaksTransformed2[j - 1]
ytop <- breaksTransformed2[j]
}
rect(xbot, ybot, xtop, ytop, col = "grey")
}
}
}
}
}
points(Alhs[,1], Alhs[,2], pch = 19, col = "red")
abline(v = breaksTransformed1, h = breaksTransformed2)
}
#set.seed(1111)
#A <- randomLHS(5,4)
#f <- function(x){qnorm(x)}
#g <- function(x){qlnorm(x, meanlog=0.5, sdlog=1)}
#B <- A
#B[,1] <- f(A[,1])
#B[,2] <- g(A[,2])
#graph2dLHSTransform(B[,1:2], f, g, -4, 4, 0, 8)
#f <- function(x){qunif(x, 3, 5)}
#B <- apply(A, 2, f)
#graph2dLHSTransform(B[,1:2], f)
```
### Theory of Latin Hypercube Sampling
For the technical basis of Latin Hypercube Sampling (LHS) and Latin Hypercube Designs (LHD) please see:
* Stein, Michael. _Large Sample Properties of Simulations Using Latin Hypercube Sampling_ Technometrics, Vol 28, No 2, 1987.
* McKay, MD, et.al. _A Comparison of Three Methods for Selecting Values of Input Variables in the Analysis of Output from a Computer Code_ Technometrics, Vol 21, No 2, 1979.
This package was created to bring these designs to R and to implement many of the articles that followed on optimized sampling methods.
### Create a Simple LHS
Basic LHS's are created using `randomLHS`.
```{r block1}
# set the seed for reproducibility
set.seed(1111)
# a design with 5 samples from 4 parameters
A <- randomLHS(5, 4)
A
```
In general, the LHS is uniform on the margins until transformed (`r registerFigure("X")`):
`r addFigureCaption("X", "Two dimensions of a Uniform random LHS with 5 samples", register=FALSE)`
```{r figureX, fig.align='center', fig.height=5, fig.width=5, echo=FALSE}
graph2dLHS(A[,1:2])
```
It is common to transform the margins of the design (the columns) into other
distributions (`r registerFigure("Y")`)
```{r block 3}
B <- matrix(nrow = nrow(A), ncol = ncol(A))
B[,1] <- qnorm(A[,1], mean = 0, sd = 1)
B[,2] <- qlnorm(A[,2], meanlog = 0.5, sdlog = 1)
B[,3] <- A[,3]
B[,4] <- qunif(A[,4], min = 7, max = 10)
B
```
`r addFigureCaption("Y", "Two dimensions of a transformed random LHS with 5 samples", register=FALSE)`
```{r figureY, fig.align='center', fig.height=5, fig.width=5, echo=FALSE}
f <- function(x){qnorm(x)}
g <- function(x){qlnorm(x, meanlog = 0.5, sdlog = 1)}
graph2dLHSTransform(B[,1:2], f, g, -4, 4, 0, 8)
```
### Optimizing the Design
The LHS can be optimized using a number of methods in the `lhs` package. Each
method attempts to improve on the random design by ensuring that the selected
points are as uncorrelated and space filling as possible. `r registerTable("tab1")` shows
some results. `r registerFigure("Z")`, `r registerFigure("W")`, and `r registerFigure("G")`
show corresponding plots.
```{r block 4}
set.seed(101)
A <- randomLHS(30, 10)
A1 <- optimumLHS(30, 10, maxSweeps = 4, eps = 0.01)
A2 <- maximinLHS(30, 10, dup = 5)
A3 <- improvedLHS(30, 10, dup = 5)
A4 <- geneticLHS(30, 10, pop = 1000, gen = 8, pMut = 0.1, criterium = "S")
A5 <- geneticLHS(30, 10, pop = 1000, gen = 8, pMut = 0.1, criterium = "Maximin")
```
-----
`r addTableCaption("tab1", "Sample results and metrics of various LHS algorithms", register=FALSE)`
Method | Min Distance btwn pts | Mean Distance btwn pts | Max Correlation btwn pts
:-----|:-----:|:-----:|:-----:
randomLHS | `r min(dist(A))` | `r mean(dist(A))` | `r max(abs(cor(A)-diag(10)))`
optimumLHS | `r min(dist(A1))` | `r mean(dist(A1))` | `r max(abs(cor(A1)-diag(10)))`
maximinLHS | `r min(dist(A2))` | `r mean(dist(A2))` | `r max(abs(cor(A2)-diag(10)))`
improvedLHS | `r min(dist(A3))` | `r mean(dist(A3))` | `r max(abs(cor(A3)-diag(10)))`
geneticLHS (S) | `r min(dist(A4))` | `r mean(dist(A4))` | `r max(abs(cor(A4)-diag(10)))`
geneticLHS (Maximin) | `r min(dist(A5))` | `r mean(dist(A5))` | `r max(abs(cor(A5)-diag(10)))`
-----
`r addFigureCaption("Z", "Pairwise margins of a randomLHS", register=FALSE)`
```{r Z, fig.align='center', fig.height=7, fig.width=7, echo=FALSE}
pairs(A, pch = 19, col = "blue", cex = 0.5)
```
-----
`r addFigureCaption("W", "Pairwise margins of a optimumLHS", register=FALSE)`
```{r W, fig.align='center', fig.height=7, fig.width=7, echo=FALSE}
pairs(A1, pch = 19, col = "blue", cex = 0.5)
```
-----
`r addFigureCaption("G", "Pairwise margins of a maximinLHS", register=FALSE)`
```{r G, fig.align='center', fig.height=7, fig.width=7, echo=FALSE}
pairs(A2, pch = 19, col = "blue", cex = 0.5)
```
lhs/inst/doc/augment_lhs.html 0000644 0001762 0000144 00000103674 13425401565 015776 0 ustar ligges users
An Example of Augmenting a Latin Hypercube
An Example of Augmenting a Latin Hypercube
Rob Carnell
2019-02-02
Suppose that a computer simulation study is being designed that requires expensive runs. A Latin hypercube design is desired for this simulation so that the expectation of the simulation output can be estimated efficiently given the distributions of the input variables. Latin hypercubes are most often used in highly dimensional problems, but the example shown is of small dimension. Suppose further that the total extent of funding is uncertain. Enough money is available for 5 runs, and there is a chance that there will be enough for 5 more. However, if the money for the additional 5 runs does not materialize, then the first 5 runs must be a Latin hypercube alone. A design for this situation can be created using the lhs package.
First create a random Latin hypercube using the randomLHS(n, k) command:
A <-randomLHS(5,2)
An example of this hypercube is shown in Figure 1. Note that the Latin property of the hypercube requires that each of the 5 equal probability intervals be filled (i.e. each row and each column is filled with one point). Also notice that the exact location of the design point is randomly sampled from within that cell using a uniform distribution for each marginal variable.
Figure 1. A randomly produced Latin Hypercube with uniform marginal distributions for 2 parameters with 5 simulations
Next, in order to augment the design with more points use augmentLHS(lhs, m). The following will add 5 more points to the design:
B <-augmentLHS(A, 5)
The augmentLHS function works by re-dividing the original design into n+m intervals (e.g. 5+5=10) keeping the original design points exactly in the same position. It then randomly fills the empty row-column sets. The results are shown in Figure 2.
Figure 2. A randomly produced Latin Hypercube of 5 points (red) with 5 augmented points (blue). Each parameter has a uniform marginal distribution.
The augmentLHS function uses the following algorithm (see the documentation for augmentLHS):
Create a new (n+m) by k matrix to hold the candidate points after the design has been re-partitioned into (n+m)^2 cells, where n is number of points in the original lhs matrix.
Then randomly sweep through each column (1…k) in the repartitioned design to find the missing cells.
For each column (variable), randomly search for an empty row, generate a random value that fits in that row, record the value in the new matrix. The new matrix can contain more than m points unless m = 2n, in which case the new matrix will contain exactly m filled rows.
Finally, keep only the first m rows of the new matrix. It is guaranteed that there will be m full rows (points) in the new matrix. The deleted rows are partially full. The additional candidate points are selected randomly because of the random search used to find empty cells.
Also notice that because the original points are randomly placed within the cells, depending on how you bin the marginal distributions, a histogram (of x1 for example) will not necessarily be exactly uniform.
Now, the augmenting points do not necessarily form a Latin Hypercube themselves. The original design and augmenting points may form a Latin Hypercube, or there may be more than one point per row in the augmented design. If the augmented points are equal to the number of original points, then a strictly uniform Latin hypercube is guaranteed. An example of an augmented design which is not uniform in the marginal distributions is given in Figure 3 and Figure 4. The commands were:
A <-randomLHS(7, 2)
B <-augmentLHS(A, 3)
Figure 3. Original design with 7 points
Figure 4. Augmented design with 3 additional points. Note that row 9 has 2 points and row 3 has none.
lhs/tests/ 0000755 0001762 0000144 00000000000 13415250176 012206 5 ustar ligges users lhs/tests/testthat.R 0000644 0001762 0000144 00000000066 13415250176 014173 0 ustar ligges users library(testthat)
library(lhs)
test_check("lhs")
lhs/tests/testthat/ 0000755 0001762 0000144 00000000000 13425544676 014062 5 ustar ligges users lhs/tests/testthat/helper-lhs.R 0000644 0001762 0000144 00000001254 13422722473 016240 0 ustar ligges users # Copyright 2019 Robert Carnell
checkLatinHypercube <- function(X)
{
if (any(apply(X, 2, min) <= 0))
return(FALSE)
if (any(apply(X, 2, max) >= 1))
return(FALSE)
if (any(is.na(X)))
return(FALSE)
# check that the matrix is a latin hypercube
g <- function(Y)
{
# check that this column contains all the cells
breakpoints <- seq(0, 1, length = length(Y) + 1)
h <- hist(Y, plot = FALSE, breaks = breakpoints)
all(h$counts == 1)
}
# check all the columns
return(all(apply(X, 2, g)))
}
checkOA <- function(X)
{
# check that the matrix is an orthogonal array
Y <- t(X) %*% X
all(abs(Y[upper.tri(Y)]) < 1E-9)
}
lhs/tests/testthat/test-createoa.R 0000644 0001762 0000144 00000012466 13420473711 016740 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-createoa")
test_that("createBose works", {
B <- createBose(2, 3, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
expect_true(checkOA(B))
B <- createBose(3, 4, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBose(3, 3)
expect_equal(nrow(B), 3^2)
expect_equal(ncol(B), 3)
B <- createBose(3, 4)
expect_equal(nrow(B), 3^2)
expect_equal(ncol(B), 4)
B <- createBose(5, 4)
expect_equal(nrow(B), 5^2)
expect_equal(ncol(B), 4)
expect_error(.Call("oa_type1", "bose", 3, 3L, FALSE))
expect_error(.Call("oa_type1", 0, 3L, 3L, FALSE))
expect_error(.Call("oa_type1", "bose", c(3L, 4L), 3L, FALSE))
expect_error(.Call("oa_type1", "bose", as.integer(NA), 3L, FALSE))
expect_error(.Call("oa_type1", "bob", 3L, 3L, FALSE))
})
test_that("createBoseBush works", {
B <- createBoseBush(2, 4, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
expect_true(checkOA(B))
B <- createBoseBush(4, 8, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- -1/3
B[which(B == 2, arr.ind = TRUE)] <- 1/3
B[which(B == 3, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBoseBush(4, 5)
expect_equal(nrow(B), 2*4^2)
expect_equal(ncol(B), 5)
B <- createBoseBush(4, 4)
expect_equal(nrow(B), 2*4^2)
expect_equal(ncol(B), 4)
B <- createBoseBush(8, 3)
expect_equal(nrow(B), 2*8^2)
expect_equal(ncol(B), 3)
})
test_that("createBush works", {
B <- createBush(3, 3, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBush(4, 5, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- -1/3
B[which(B == 2, arr.ind = TRUE)] <- 1/3
B[which(B == 3, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBush(3, 3)
expect_equal(nrow(B), 3^3)
expect_equal(ncol(B), 3)
B <- createBush(3, 4)
expect_equal(nrow(B), 3^3)
expect_equal(ncol(B), 4)
B <- createBush(5, 4)
expect_equal(nrow(B), 5^3)
expect_equal(ncol(B), 4)
})
test_that("createAddelKemp works", {
B <- createAddelKemp(2, 4, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
expect_true(checkOA(B))
B <- createAddelKemp(3, 6, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createAddelKemp(4, 5)
expect_equal(nrow(B), 2*4^2)
expect_equal(ncol(B), 5)
B <- createAddelKemp(4, 4)
expect_equal(nrow(B), 2*4^2)
expect_equal(ncol(B), 4)
B <- createAddelKemp(5, 3)
expect_equal(nrow(B), 2*5^2)
expect_equal(ncol(B), 3)
})
test_that("createAddelKemp3 works", {
B <- createAddelKemp3(2, 13, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
expect_true(checkOA(B))
B <- createAddelKemp3(3, 25, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createAddelKemp3(4, 5)
expect_equal(nrow(B), 2*4^3)
expect_equal(ncol(B), 5)
B <- createAddelKemp3(4, 4)
expect_equal(nrow(B), 2*4^3)
expect_equal(ncol(B), 4)
B <- createAddelKemp3(5, 3)
expect_equal(nrow(B), 2*5^3)
expect_equal(ncol(B), 3)
})
test_that("createBusht works", {
B <- createBusht(3, 4, 2, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBusht(3, 4, 3, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
expect_error(.Call("oa_type2", "busht", 3, 4L, 3L, FALSE))
expect_error(.Call("oa_type2", 0, 3L, 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "busht", c(3L, 4L), 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "busht", as.integer(NA), 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "bosebushl", as.integer(NA), 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "addelkempn", as.integer(NA), 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "bob", as.integer(NA), 4L, 3L, FALSE))
expect_error(.Call("oa_type2", "bob", 3L, 4L, 3L, FALSE))
X <- .Call("oa_type2", "busht", 3L, 4L, 3L, TRUE)
expect_equal(nrow(X), 64)
})
test_that("createBoseBushl works", {
B <- createBoseBushl(3, 5, 3, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
B <- createBoseBushl(4, 4, 16, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- -1/3
B[which(B == 2, arr.ind = TRUE)] <- 1/3
B[which(B == 3, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
})
test_that("createAddelKempN works", {
B <- createAddelKempN(2, 3, 3, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
expect_true(checkOA(B))
B <- createAddelKempN(3, 4, 4, FALSE)
B[which(B == 0, arr.ind = TRUE)] <- -1
B[which(B == 1, arr.ind = TRUE)] <- 0
B[which(B == 2, arr.ind = TRUE)] <- 1
expect_true(checkOA(B))
})
lhs/tests/testthat/test-improvedlhs.r 0000644 0001762 0000144 00000002277 13423215476 017555 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-improvedlhs")
test_that("improvedLHS works", {
expect_error(improvedLHS(-1, 2))
expect_error(improvedLHS(10, -30))
expect_error(improvedLHS(10, 2, -2))
expect_error(improvedLHS(NA, 2))
expect_error(improvedLHS(NaN, 2))
expect_warning(expect_error(improvedLHS(Inf, 2)))
expect_error(improvedLHS(10, NA, 2))
expect_error(improvedLHS(10, NaN, 2))
expect_warning(expect_error(improvedLHS(10, Inf, 2)))
expect_error(improvedLHS(10, 2, NA))
expect_error(improvedLHS(10, 2, NaN))
expect_warning(expect_error(improvedLHS(10, 2, Inf)))
set.seed(1976)
expect_true(checkLatinHypercube(improvedLHS(4, 2)))
set.seed(1977)
expect_true(checkLatinHypercube(improvedLHS(3, 3, 5)))
set.seed(1111)
A <- improvedLHS(20, 6)
set.seed(1111)
B <- improvedLHS(20, 6)
expect_true(all(A == B))
D <- improvedLHS(20, 6)
expect_true(any(A != D))
A <- improvedLHS(1, 4)
expect_equal(nrow(A), 1)
expect_true(checkLatinHypercube(A))
})
test_that("improvedLHS errors work", {
expect_error(.Call("improvedLHS_cpp", 3, 4L, 4L))
X <- .Call("improvedLHS_cpp", 1L, 4L, 4L)
expect_equal(nrow(X), 1)
})
lhs/tests/testthat/test-randomlhs.r 0000644 0001762 0000144 00000004342 13422725136 017201 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-randomlhs")
test_that("randomLHS works", {
A <- randomLHS(4,2)
expect_true(all(A > 0 & A < 1))
expect_equal(4, nrow(A))
expect_equal(2, ncol(A))
expect_true(checkLatinHypercube(A))
# doubles are truncated in n and k
A <- randomLHS(4.4, 2)
expect_true(all(A > 0 & A < 1))
expect_equal(4, nrow(A))
expect_equal(2, ncol(A))
expect_true(checkLatinHypercube(A))
A <- randomLHS(4, 2.8)
expect_true(all(A > 0 & A < 1))
expect_equal(4, nrow(A))
expect_equal(2, ncol(A))
expect_true(checkLatinHypercube(A))
A <- randomLHS(4.4, 2.8)
expect_true(all(A > 0 & A < 1))
expect_equal(4, nrow(A))
expect_equal(2, ncol(A))
expect_true(checkLatinHypercube(A))
expect_error(randomLHS(-1, 2))
expect_error(randomLHS(10, -30))
expect_error(randomLHS(NA, 2))
expect_error(randomLHS(NaN, 2))
expect_warning(expect_error(randomLHS(Inf, 2)))
expect_error(randomLHS(10, NA))
expect_error(randomLHS(10, NaN))
expect_warning(expect_error(randomLHS(10, Inf)))
A <- randomLHS(1, 5)
expect_equal(1, nrow(A))
expect_equal(5, ncol(A))
expect_true(checkLatinHypercube(A))
expect_error(randomLHS(c(1,2,3), c(3,4)))
expect_error(randomLHS(-1, 2, preserveDraw = TRUE))
expect_error(randomLHS(10, -30, preserveDraw = TRUE))
expect_error(randomLHS(NA, 2, preserveDraw = TRUE))
expect_error(randomLHS(NaN, 2, preserveDraw = TRUE))
expect_warning(expect_error(randomLHS(Inf, 2, preserveDraw = TRUE)))
expect_error(randomLHS(10, NA, preserveDraw = TRUE))
expect_error(randomLHS(10, NaN, preserveDraw = TRUE))
expect_warning(expect_error(randomLHS(10, Inf, preserveDraw = TRUE)))
A <- randomLHS(4, 2, preserveDraw = TRUE)
expect_true(all(A > 0 & A < 1))
expect_true(checkLatinHypercube(A))
set.seed(4)
A <- randomLHS(5, 3, preserveDraw = TRUE)
set.seed(4)
B <- randomLHS(5, 5, preserveDraw = TRUE)
expect_equal(A, B[,1:3], tolerance = 1E-12)
expect_true(checkLatinHypercube(A))
expect_true(checkLatinHypercube(B))
expect_error(.Call("randomLHS_cpp", 3, 4L, FALSE))
})
test_that("degenerate LHS problem is fixed", {
A <- randomLHS(1, 3)
expect_true(checkLatinHypercube(A))
})
lhs/tests/testthat/test-optimumlhs.R 0000644 0001762 0000144 00000003552 13423217051 017346 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-optimumlhs")
test_that("optimumLHS works", {
expect_error(optimumLHS(-1, 2))
expect_error(optimumLHS(10, -30))
expect_error(optimumLHS(10, 2, -2))
expect_error(optimumLHS(10, 2, 3, -1))
expect_error(optimumLHS(10, 2, 3, 1.5))
expect_error(optimumLHS(10, 2, 3, 1))
expect_error(optimumLHS(10, 2, 3, 0))
expect_error(optimumLHS(NA, 2))
expect_error(optimumLHS(NaN, 2))
expect_warning(expect_error(optimumLHS(Inf, 2)))
expect_error(optimumLHS(10, NA))
expect_error(optimumLHS(10, NaN))
expect_warning(expect_error(optimumLHS(10, Inf)))
expect_error(optimumLHS(10, 2, NA))
expect_error(optimumLHS(10, 2, NaN))
expect_warning(expect_error(optimumLHS(10, 2, Inf)))
expect_error(optimumLHS(10, 2, 2, NA))
expect_error(optimumLHS(10, 2, 2, NaN))
expect_error(optimumLHS(10, 2, 2, Inf))
set.seed(1976)
rTemp <- optimumLHS(4, 2)
expect_true(checkLatinHypercube(rTemp))
set.seed(1977)
rTemp <- optimumLHS(3, 3, 5)
expect_true(checkLatinHypercube(rTemp))
set.seed(1978)
rTemp <- optimumLHS(5, 2, 5, .5)
expect_true(checkLatinHypercube(rTemp))
set.seed(2010)
for (i in 2:6)
{
for (j in 2:6)
{
A <- optimumLHS(i, j)
expect_true(checkLatinHypercube(A))
}
}
set.seed(2011)
for (i in 2:6)
{
for (j in 2:6)
{
A <- optimumLHS(i, j, 5)
expect_true(checkLatinHypercube(A))
}
}
set.seed(2012)
for (i in 2:6)
{
for (j in 2:6)
{
A <- optimumLHS(i, j, 5, 0.05)
expect_true(checkLatinHypercube(A))
}
}
expect_error(.Call("optimumLHS_cpp", 3, 4L, 4L, 0.01, FALSE))
X <- .Call("optimumLHS_cpp", 1L, 4L, 4L, 0.01, FALSE)
expect_equal(nrow(X), 1)
A <- optimumLHS(1, 4)
expect_equal(nrow(A), 1)
expect_true(checkLatinHypercube(A))
})
lhs/tests/testthat/test-geneticlhs.R 0000644 0001762 0000144 00000003013 13423214736 017271 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-geneticlhs")
test_that("geneticLHS works", {
expect_error(geneticLHS(-1, 2))
expect_error(geneticLHS(10, -30))
expect_error(geneticLHS(10, 2, -2))
expect_error(geneticLHS(NA, 2))
expect_error(geneticLHS(NaN, 2))
expect_warning(expect_error(geneticLHS(Inf, 2)))
expect_error(geneticLHS(10, NA))
expect_error(geneticLHS(10, NaN))
expect_warning(expect_error(geneticLHS(10, Inf)))
expect_error(geneticLHS(10, 2, NA))
expect_error(geneticLHS(10, 2, NaN))
expect_warning(expect_error(geneticLHS(10, 2, Inf)))
set.seed(1976)
expect_true(checkLatinHypercube(geneticLHS(4, 2)))
set.seed(1977)
expect_true(checkLatinHypercube(geneticLHS(3, 3, 6)))
expect_error(geneticLHS(10, 2, 4, -1))
expect_error(geneticLHS(10, 2, 4, 4, -.1))
expect_error(geneticLHS(10, 2, 4, 4, 1.1))
expect_error(geneticLHS(10, 2, 2, NA))
expect_error(geneticLHS(10, 2, 2, NaN))
expect_warning(expect_error(geneticLHS(10, 2, 2, Inf)))
#expect_error(geneticLHS(10, 2, 2, 4, NA))
#expect_error(geneticLHS(10, 2, 2, 4, NaN))
expect_error(geneticLHS(10, 2, 2, 4, Inf))
set.seed(1976)
expect_true(checkLatinHypercube(geneticLHS(20, 5, pop = 100, gen = 4,
pMut = 0.2, criterium = "S")))
capture_output(X <- .Call("geneticLHS_cpp", 1L, 4L, 10L, 4L, 0.01, "S", TRUE))
expect_equal(nrow(X), 1)
A <- geneticLHS(1, 4)
expect_equal(nrow(A), 1)
expect_true(checkLatinHypercube(A))
})
lhs/tests/testthat/test-optseededlhs.R 0000644 0001762 0000144 00000002626 13423214206 017630 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-optseededlhs")
test_that("optseededLHS works", {
expect_error(optSeededLHS(randomLHS(10, 4), NA))
expect_error(optSeededLHS(randomLHS(10, 4), NaN))
expect_error(optSeededLHS(randomLHS(10, 4), Inf))
expect_error(optSeededLHS(randomLHS(10, 4), 2, NA))
expect_error(optSeededLHS(randomLHS(10, 4), 2, NaN))
expect_warning(expect_error(optSeededLHS(randomLHS(10, 4), 2, Inf)))
expect_error(optSeededLHS(randomLHS(10, 4), 2, 5, NA))
expect_error(optSeededLHS(randomLHS(10, 4), 2, 5, NaN))
expect_error(optSeededLHS(randomLHS(10, 4), 2, 5, Inf))
temp <- randomLHS(10, 4)
temp[1,1] <- NA
expect_error(optSeededLHS(temp, 5))
temp <- randomLHS(10, 4)
temp[1,1] <- 2
expect_error(optSeededLHS(temp, 5))
set.seed(1976)
A <- optSeededLHS(randomLHS(4, 2), 2)
expect_true(checkLatinHypercube(A))
set.seed(1977)
B <- optSeededLHS(randomLHS(3, 3), 3, 3, .05)
expect_true(checkLatinHypercube(B))
A <- optSeededLHS(randomLHS(10, 4), m = 0)
expect_true(checkLatinHypercube(A))
expect_error(.Call("optSeededLHS_cpp", 3, 4L, 4L, 0.01, matrix(1L, 2, 2), FALSE))
X <- .Call("optSeededLHS_cpp", 1L, 4L, 4L, 0.01, matrix(runif(4), nrow = 1, ncol = 4), FALSE)
expect_equal(nrow(X), 1)
expect_true(checkLatinHypercube(X))
expect_error(.Call("optSeededLHS_cpp", 3L, 4L, 4L, 0.01, matrix(1L, 2, 2), FALSE))
})
lhs/tests/testthat/test-oa_to_oalhs.R 0000644 0001762 0000144 00000005252 13425356377 017454 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-oa_to_oalhs")
test_that("oa_to_oalhs works with internal oa generation", {
oa <- createBose(3, 4, TRUE)
oalhs <- oa_to_oalhs(9, 4, oa)
expect_true(checkLatinHypercube(oalhs))
oa <- createBose(3, 4, FALSE)
oalhs <- oa_to_oalhs(9, 4, oa)
expect_true(checkLatinHypercube(oalhs))
oa <- createBoseBush(8, 5, TRUE)
oalhs <- oa_to_oalhs(128, 5, oa)
expect_true(checkLatinHypercube(oalhs))
# check a mismatch in n causes an error
expect_error(oa_to_oalhs(3, 5, oa))
# check a mismatch in k causes an error
expect_error(oa_to_oalhs(128, 8, oa))
# check a wrong sized oa causes an error
expect_error(oa_to_oalhs(128, 5, oa[1:100,1:3]))
# check wrong type
expect_error(oa_to_oalhs(3, 5, matrix(1.2, nrow = 3, ncol = 5)))
oa <- createAddelKemp(3, 4, FALSE)
oalhs <- oa_to_oalhs(18, 4, oa)
expect_true(checkLatinHypercube(oalhs))
oa <- createAddelKemp3(4, 20, TRUE)
oalhs <- oa_to_oalhs(128, 20, oa)
expect_true(checkLatinHypercube(oalhs))
expect_error(.Call("oa_to_lhs", 4, 20L, oa, FALSE))
expect_error(.Call("oa_to_lhs", 4L, 20L, oa, 5))
expect_error(.Call("oa_to_lhs", as.integer(NA), 20L, oa, FALSE))
})
test_that("oa_to_oalhs works with DoE.base", {
# note: trying to ensure that a lack of DoE.base does not break the tests
# also trying to avoid attaching the DoE.base package because it causes warnings in the test suite
testthat::skip_if_not_installed("DoE.base")
# 12 rows, two columns of 1,2 and one column of 1:6
my_oa <- DoE.base::oa.design(ID = DoE.base::L12.2.2.6.1)
oalhs <- oa_to_oalhs(12, 3, my_oa)
expect_true(checkLatinHypercube(oalhs))
# 20 rows, 19 columns of 1,2
my_oa <- DoE.base::oa.design(ID = DoE.base::L20.2.19)
oalhs <- oa_to_oalhs(20, 19, my_oa)
expect_true(checkLatinHypercube(oalhs))
# can I get the oa back?
#c(ifelse(floor(oalhs*20) < 20/2, 1, 2)) == as.integer(as.matrix(my_oa))
# can I verify that the oalhs is an oa?
temp <- t(ifelse(floor(oalhs*20) < 20/2, -1, 1)) %*% ifelse(floor(oalhs*20) < 20/2, -1, 1)
expect_true(all(temp[upper.tri(temp)] == 0))
# 20 rows, 19 columns of 1,2
my_oa <- DoE.base::oa.design(ID = DoE.base::L9.3.4)
oalhs <- oa_to_oalhs(9, 4, my_oa)
expect_true(checkLatinHypercube(oalhs))
# can I verify that the oalhs is an oa?
temp1 <- ifelse(floor(oalhs*9) < 9/3, -1, ifelse(floor(oalhs*9) < 2*9/3, 0, 1))
temp <- t(temp1) %*% temp1
expect_true(all(temp[upper.tri(temp)] == 0))
})
test_that("Edge cases", {
A <- matrix(1L, nrow = 1, ncol = 4)
B <- oa_to_oalhs(1, 4, A)
expect_equal(nrow(B), 1)
expect_true(checkLatinHypercube(B))
})
lhs/tests/testthat/test-create_oalhs.R 0000644 0001762 0000144 00000002526 13420500406 017572 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-create_oalhs")
test_that("create_oalhs works", {
oalhs <- create_oalhs(9, 4, TRUE, FALSE)
expect_true(checkLatinHypercube(oalhs))
expect_equal(nrow(oalhs), 9)
expect_equal(ncol(oalhs), 4)
# ask for an achievable design
oalhs <- create_oalhs(4, 2, TRUE, FALSE)
expect_true(checkLatinHypercube(oalhs))
expect_equal(nrow(oalhs), 4)
expect_equal(ncol(oalhs), 2)
# ask for a design that needs more rows
oalhs <- create_oalhs(20, 3, TRUE, FALSE)
expect_true(checkLatinHypercube(oalhs))
expect_equal(nrow(oalhs), 25)
expect_equal(ncol(oalhs), 3)
# ask for a design but ask for less rows
oalhs <- create_oalhs(20, 3, FALSE, FALSE)
expect_true(checkLatinHypercube(oalhs))
expect_equal(nrow(oalhs), 18)
expect_equal(ncol(oalhs), 3)
oalhs <- create_oalhs(20, 10, TRUE, FALSE)
expect_true(checkLatinHypercube(oalhs))
expect_equal(nrow(oalhs), 54)
expect_equal(ncol(oalhs), 10)
# check repeatability
set.seed(1001)
X <- create_oalhs(9, 4, TRUE, FALSE)
set.seed(1001)
Y <- create_oalhs(9, 4, TRUE, FALSE)
expect_true(all(X == Y))
expect_error(.Call("create_oalhs", 3, 4L, FALSE, FALSE))
expect_error(.Call("create_oalhs", 3L, 4L, 5, FALSE))
expect_error(.Call("create_oalhs", 3L, as.integer(NA), FALSE, FALSE))
})
lhs/tests/testthat/test-optaugmentlhs.R 0000644 0001762 0000144 00000002033 13423216603 020032 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-optaugmentlhs")
test_that("optAugmentLHS works", {
expect_error(optAugmentLHS(randomLHS(10, 4), NA))
expect_error(optAugmentLHS(randomLHS(10, 4), NaN))
expect_error(optAugmentLHS(randomLHS(10, 4), Inf))
expect_error(optAugmentLHS(randomLHS(10, 4), 2, NA))
expect_error(optAugmentLHS(randomLHS(10, 4), 2, NaN))
expect_error(optAugmentLHS(randomLHS(10, 4), 2, Inf))
temp <- randomLHS(10, 4)
temp[1,1] <- NA
expect_error(optAugmentLHS(temp, 5))
temp <- randomLHS(10, 4)
temp[1,1] <- 2
expect_error(optAugmentLHS(temp, 5))
set.seed(1976)
expect_true(checkLatinHypercube(optAugmentLHS(randomLHS(4, 2), 2)))
set.seed(1977)
expect_true(checkLatinHypercube(optAugmentLHS(randomLHS(3, 3), 3, 3)))
expect_error(optAugmentLHS(c(1,2), m = 4, mult = 2))
expect_error(optAugmentLHS(randomLHS(10, 4), c(1,2)))
expect_error(optAugmentLHS(randomLHS(10, 4), -2))
A <- optAugmentLHS(randomLHS(1,4), 1)
expect_true(checkLatinHypercube(A))
})
lhs/tests/testthat/test-maximinlhs.R 0000644 0001762 0000144 00000005610 13423215572 017321 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-maximinlhs")
test_that("maximinLHS works", {
expect_error(maximinLHS(-1, 2))
expect_error(maximinLHS(10, -30))
expect_error(maximinLHS(10, 2, dup = -2))
expect_error(maximinLHS(NA, 2))
expect_error(maximinLHS(NaN, 2))
expect_warning(expect_error(maximinLHS(Inf, 2)))
expect_error(maximinLHS(10, NA))
expect_error(maximinLHS(10, NaN))
expect_warning(expect_error(maximinLHS(10, Inf)))
expect_error(maximinLHS(10, 2, dup = NA))
expect_error(maximinLHS(10, 2, dup = NaN))
expect_warning(expect_error(maximinLHS(10, 2, dup = Inf)))
set.seed(1976)
expect_true(checkLatinHypercube(maximinLHS(4, 2)))
set.seed(1977)
expect_true(checkLatinHypercube(maximinLHS(3, 3, dup = 5)))
expect_error(maximinLHS(10, 4, method = "none"))
expect_error(maximinLHS(10, 4, method = "build", optimize.on = "none"))
expect_warning(maximinLHS(10, 4, method = "build", optimize.on = "result"))
expect_error(maximinLHS(10, c(4,5), method = "iterative"))
expect_error(maximinLHS(10, NA, method = "iterative"))
expect_error(maximinLHS(10, Inf, method = "iterative"))
expect_error(maximinLHS(12.2, 4, method = "iterative"))
expect_error(maximinLHS(12, 4.3, method = "iterative"))
expect_error(maximinLHS(12, 4, dup = 10.2, method = "iterative"))
A <- maximinLHS(12, 4, dup = 10, method = "iterative", optimize.on = "result")
expect_true(checkLatinHypercube(A))
A <- maximinLHS(20, 5, dup = 3, method = "iterative", optimize.on = "grid")
expect_true(checkLatinHypercube(A))
A <- maximinLHS(1, 4)
expect_equal(nrow(A), 1)
expect_true(checkLatinHypercube(A))
})
test_that("maximinLHS works with expanded capability", {
expect_true(checkLatinHypercube(maximinLHS(4, 3, method = "build", dup = 2)))
expect_true(checkLatinHypercube(maximinLHS(4, 3, method = "iterative", eps = 0.05, maxIter = 100, optimize.on = "grid")))
expect_true(checkLatinHypercube(maximinLHS(4, 3, method = "iterative", eps = 0.05, maxIter = 100, optimize.on = "result")))
})
test_that("maximinLHS debug capability for code coverage", {
capture_output(X <- maximinLHS(10, 4, method = "build",
optimize.on = "grid", debug = TRUE))
expect_equal(nrow(X), 10)
expect_warning(capture_output(X <- maximinLHS(10, 4, method = "build",
optimize.on = "result", debug = TRUE)))
expect_equal(nrow(X), 10)
capture_output(X <- maximinLHS(10, 10, method = "iterative",
optimize.on = "result", eps = 1E-9, debug = TRUE))
expect_equal(nrow(X), 10)
capture_output(X <- maximinLHS(5, 5, method = "iterative",
optimize.on = "result", eps = 1, debug = TRUE))
expect_error(.Call("maximinLHS_cpp", 3, 4L, 4L))
X <- .Call("maximinLHS_cpp", 1L, 4L, 4L)
expect_equal(nrow(X), 1)
})
lhs/tests/testthat/test-augmentlhs.R 0000644 0001762 0000144 00000002473 13423214555 017323 0 ustar ligges users # Copyright 2019 Robert Carnell
context("test-augmentlhs")
test_that("augment works", {
expect_error(augmentLHS(randomLHS(10, 4), NA))
expect_error(augmentLHS(randomLHS(10, 4), NaN))
expect_error(augmentLHS(randomLHS(10, 4), Inf))
set.seed(1976)
temp <- randomLHS(10, 4)
temp[1,1] <- NA
expect_error(augmentLHS(temp, 5))
set.seed(1976)
temp <- randomLHS(10, 4)
temp[1,1] <- 2
expect_error(augmentLHS(temp, 5))
set.seed(1976)
expect_true(checkLatinHypercube(augmentLHS(randomLHS(4, 2), 4)))
set.seed(1977)
expect_true(checkLatinHypercube(augmentLHS(randomLHS(3, 3), 3)))
set.seed(1977)
expect_true(checkLatinHypercube(augmentLHS(randomLHS(4, 1), 2)))
# this test addresses a bug where an error ocurred on adding 1 row in
# augmentLHS
temp <- randomLHS(7, 2)
temp <- augmentLHS(temp, 1)
expect_equal(nrow(temp), 8)
expect_true(checkLatinHypercube(augmentLHS(randomLHS(7, 2), 7)))
expect_true(checkLatinHypercube(augmentLHS(randomLHS(10, 5), 10)))
# test exceptions
expect_error(augmentLHS(c(1,2), 5))
expect_error(augmentLHS(randomLHS(10,3), c(5,9)))
expect_error(augmentLHS(randomLHS(10,3), -1))
expect_error(augmentLHS(randomLHS(10,3), 2.2))
A <- augmentLHS(randomLHS(1,4), 1)
expect_true(checkLatinHypercube(A))
})
lhs/src/ 0000755 0001762 0000144 00000000000 13425401602 011624 5 ustar ligges users lhs/src/Makevars 0000644 0001762 0000144 00000000050 13425401602 013313 0 ustar ligges users PKG_CPPFLAGS=-DRCOMPILE
CXX_STD = CXX11
lhs/src/oa_r_utils.cpp 0000644 0001762 0000144 00000003032 13425401602 014466 0 ustar ligges users /**
* @file oa_r_utils.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* @license GNU General Public License (GPL v3)
* 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 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "oa_r_utils.h"
namespace oarutils {
void randomizeOA(Rcpp::IntegerMatrix & oa, int q)
{
// get the random number scope from R
Rcpp::RNGScope scope;
size_t rows = oa.rows();
size_t cols = oa.cols();
Rcpp::NumericVector perm;
std::vector ranks(q);
// Permute the symbols in each column
for (size_t j = 0; j < cols; j++)
{
perm = Rcpp::runif(q);
oacpp::rutils::findranks_zero(Rcpp::as >(perm), ranks);
for (size_t i = 0; i < rows; i++)
{
oa(i,j) = ranks[oa(i,j)];
}
}
}
} // end namespace
lhs/src/akconst.cpp 0000644 0001762 0000144 00000006407 13425401602 014001 0 ustar ligges users /**
* @file akconst.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* License: This file may be freely used and shared according to the original license.
*
* Reference:
*
* These programs construct and manipulate orthogonal arrays. They were prepared by
*
* - Art Owen
* - Department of Statistics
* - Sequoia Hall
* - Stanford CA 94305
*
* They may be freely used and shared. This code comes
* with no warranty of any kind. Use it at your own
* risk.
*
* I thank the Semiconductor Research Corporation and
* the National Science Foundation for supporting this
* work.
*
*/
#include "ak.h"
namespace oacpp
{
namespace oaaddelkemp
{
/* Find constants for Addelman Kempthorne designs
when q is even. */
int akeven(GF & gf, int* kay, std::vector & b, std::vector & c, std::vector & k)
{
size_t q = static_cast(gf.q);
if (q > 4)
{
std::string msg = "Addelman Kempthorne designs not yet available for \n even q >4.";
throw std::runtime_error(msg.c_str());
}
*kay = 1;
if (q == 2)
{
b[1] = c[1] = k[1] = 1;
}
if (q == 4)
{
b[1] = c[1] = 2;
b[2] = c[2] = 1;
b[3] = c[3] = 3;
k[1] = 1;
k[2] = 2;
k[3] = 3;
}
// TODO: isn't this redundant to the above for q <= 4
for (size_t i = 1; i < q; i++)
{
k[i] = static_cast(i);
}
return 0;
}
int akodd(GF & gf, int* kay, std::vector & b, std::vector & c, std::vector & k)
{
int num, den, four;
size_t q = static_cast(gf.q);
size_t p = static_cast(gf.p);
if (p != 3)
{
four = 4;
}
else
{
four = 1;
}
*kay = 0;
for (size_t i = 2; i < q; i++)
{
if (gf.root[i] == -1)
{
*kay = static_cast(i);
}
}
if (*kay == 0)
{
std::ostringstream s;
s << "Problem: no rootless element in GF(" << gf.n << ").\n";
const std::string ss = s.str();
throw std::runtime_error(ss.c_str());
}
for (size_t i = 1; i < q; i++)
{
num = gf.plus(*kay,p - 1); /* -1 = +(p-1) */
den = gf.times(*kay,four);
den = gf.times(den,i);
b[i] = gf.times(num,gf.inv[den]);
k[i] = gf.times(*kay,i);
c[i] = gf.times(i,i);
c[i] = gf.times(c[i],num);
c[i] = gf.times(c[i],gf.inv[four]);
}
return 0;
}
} // end namespace
} // end namespace
lhs/src/primes.h 0000644 0001762 0000144 00000004271 13425401602 013300 0 ustar ligges users /**
* @file primes.h
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* License: This file may be freely used and shared according to the original license.
*
* Reference:
*
* These programs construct and manipulate orthogonal arrays. They were prepared by
*
* - Art Owen
* - Department of Statistics
* - Sequoia Hall
* - Stanford CA 94305
*
* They may be freely used and shared. This code comes
* with no warranty of any kind. Use it at your own
* risk.
*
* I thank the Semiconductor Research Corporation and
* the National Science Foundation for supporting this
* work.
*
*/
#ifndef PRIMES_H
#define PRIMES_H
#include "OACommonDefines.h"
/**
* Macro to indicate that a number is prime
*/
#define ISPRIMETRUE 1
/**
* Macro to indicate that a number is not prime
*/
#define ISPRIMEFALSE 0
namespace oacpp {
/**
* Utilities related to prime numbers
*/
namespace primes
{
/**
* returns 1 for prime argument
* @param n number to test
* @return 1 if p is prime
*/
int isprime(unsigned int n);
/**
* Is the number prime
* @deprecated due to slowness
* @param p number
* @return 1 if prime, 0 otherwise
*/
int isprime_old(int p);
/**
* find q=p^n if q is a prime power with n>0
* @param q integer that is a prime power
* @param p the prime base
* @param n the integer power
* @param isit an indicator of completion
*/
void primepow(int q, int* p, int* n, int* isit);
/**
* returns 1 for prime power argument
* @param q
* @return
*/
int isprimepow(int q );
/**
* pow() with integer arguments and value
* @param a
* @param b
* @return
*/
int ipow( int a, int b );
} // end namespace
}// end namespace
#endif
lhs/src/geneticLHS.cpp 0000644 0001762 0000144 00000022576 13425401602 014331 0 ustar ligges users /**
* @file geneticLHS.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2014, Robert Carnell
*
* @license GNU Lesser General Public License (LGPL v3)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "LHSCommonDefines.h"
#include "utilityLHS.h"
namespace lhslib
{
// TODO: multi-thread the iterations over population
void geneticLHS(int n, int k, int pop, int gen, double pMut, std::string criterium,
bool bVerbose, bclib::matrix & result, bclib::CRandom & oRandom)
{
if (n < 1 || k < 1)
{
throw std::runtime_error("nsamples are less than 1 (n) or nparameters less than 1 (k)");
}
msize_type m_n = static_cast(n);
msize_type m_k = static_cast(k);
if (result.rowsize() != m_n || result.colsize() != m_k)
{
throw std::runtime_error("result should be n x k for the lhslib::geneticLHS call");
}
if (gen < 1 || pop < 1)
{
throw std::invalid_argument("pop, and gen should be integers greater than 0");
}
msize_type m_pop = static_cast(pop);
msize_type m_gen = static_cast(gen);
if (pMut <= 0 || pMut >= 1)
{
throw std::invalid_argument("pMut should be between 0 and 1");
}
if (m_pop % 2 != 0)
{
throw std::invalid_argument("pop should be an even number");
}
std::vector > A = std::vector >(m_pop);
for (msize_type i = 0; i < m_pop; i++)
{
A[i] = bclib::matrix(m_n, m_k);
}
for (msize_type i = 0; i < m_pop; i++)
{
// fill A with random hypercubes
randomLHS(static_cast(m_n), static_cast(m_k), A[i], oRandom);
#ifdef _DEBUG
if (!lhslib::isValidLHS(A[i])) PRINT_MACRO("A is not valid at %d in randomLHS\n", static_cast(i));
#endif
}
std::vector B;
std::vector > J;
bclib::matrix dist;
std::vector::iterator it;
std::vector distnonzero = std::vector();
for (msize_type v = 0; v < m_gen; v++)
{
B = std::vector(m_pop);
for (msize_type i = 0; i < m_pop; i++)
{
if (criterium == "S")
{
B[i] = calculateSOptimal(A[i]);
}
else if (criterium == "Maximin")
{
//B[i] <- min(dist(A[, , i]))
dist = bclib::matrix(A[i].rowsize(), A[i].rowsize());
calculateDistance(A[i], dist);
// we want to find the minimum distance element, but there are zeros in the dist matrix
distnonzero.clear();
for (bclib::matrix::const_iterator mit = dist.begin(); mit != dist.end(); ++mit)
{
if (*mit > 0.0)
{
distnonzero.push_back(*mit);
}
}
it = std::min_element(distnonzero.begin(), distnonzero.end());
B[i] = *it;
}
else
{
std::stringstream msg;
msg << "Criterium not recognized: S and Maximin are available: " << criterium.c_str() << " was provided.\n";
const std::string smsg = msg.str();
throw std::invalid_argument(smsg.c_str());
}
}
// H is used as an index on vector of matrices, A, so it should be using zero based order
std::vector H = std::vector(B.size());
bclib::findorder_zero(B, H);
int posit = static_cast(std::max_element(B.begin(), B.end()) - B.begin());
J = std::vector >(m_pop);
for (std::vector >::iterator i = J.begin(); i != J.end(); ++i)
{
*i = bclib::matrix(m_n, m_k);
}
#ifdef _DEBUG
if (!lhslib::isValidLHS(A[posit])) PRINT_MACRO("A is not valid at %d in randomLHS\n", static_cast(posit));
#endif
// the first half of the next population gets the best hypercube from the first population
for (msize_type i = 0; i < (m_pop / 2); i++)
{
J[i] = A[posit];
}
if (m_pop / 2 == 1)
{
break;
}
// the second half of the next population gets the decreasingly best hypercubes from the first population
for (msize_type i = 0; i < (m_pop / 2); i++)
{
J[i + m_pop / 2] = A[H[i]];
#ifdef _DEBUG
if (!lhslib::isValidLHS(J[i + m_pop / 2]))
{
PRINT_MACRO("J is not valid at %d %d %d in 2nd half setup\n", static_cast(i + m_pop / 2), static_cast(i), static_cast(m_pop/2));
PRINT_MACRO("J is equal to A[H[i]], 1 is true %d", (int)(J[i+m_pop/2] == A[H[i]]));
PRINT_MACRO("\n%s\n", J[i + m_pop / 2].toString());
PRINT_MACRO("\n%s\n", A[H[i]].toString());
PRINT_MACRO("H: ");
for (vsize_type iv = 0; iv < H.size(); iv++)
{
PRINT_MACRO("%d,", H[iv]);
}
PRINT_MACRO("\n");
return;
}
#endif
}
int temp1, temp2;
// skip the first best hypercube in the next generation
// in the others in the first half of the population, randomly permute a column from the second half into the first half
for (msize_type i = 1; i < (m_pop / 2); i++)
{
runifint(0, static_cast(m_k)-1, &temp1, oRandom);
runifint(0, static_cast(m_k)-1, &temp2, oRandom);
for (msize_type irow = 0; irow < m_n; irow++)
{
J[i](irow, temp1) = J[i + m_pop / 2](irow, temp2);
}
#ifdef _DEBUG
if (!lhslib::isValidLHS(J[i]))
{
PRINT_MACRO("J is not valid at %d in 1st half permute\n", static_cast(i));
PRINT_MACRO("\n%s\n", J[i].toString());
return;
}
#endif
}
// for the second half of the population, randomly permute a column from the best hypercube
for (msize_type i = m_pop / 2; i < m_pop; i++)
{
runifint(0, static_cast(m_k)-1, &temp1, oRandom);
runifint(0, static_cast(m_k)-1, &temp2, oRandom);
for (msize_type irow = 0; irow < m_n; irow++)
{
J[i](irow, temp1) = A[posit](irow, temp2);
}
if (!lhslib::isValidLHS(J[i])) PRINT_MACRO << "J is not valid at " << i << " in second half permute\n";
}
// randomly exchange two numbers in pMut percent of columns
std::vector y = std::vector(m_k);
for (msize_type i = 1; i < m_pop; i++)
{
runif_std(static_cast(m_k), y, oRandom);
for (msize_type j = 0; j < m_k; j++)
{
if (y[j] <= pMut)
{
std::vector z = std::vector(2);
runifint(2u, 0, static_cast(m_n-1), z, oRandom);
int a = J[i](z[0], j);
int b = J[i](z[1], j);
J[i](z[0], j) = b;
J[i](z[1], j) = a;
}
}
}
// put all of J back into A to start the next round
A = J;
if (v != m_gen && bVerbose)
{
PRINT_MACRO << "Generation " << v << " completed\n"; // LCOV_EXCL_LINE
}
}
if (bVerbose)
{
PRINT_MACRO << "Last generation completed\n"; // LCOV_EXCL_LINE
}
#ifdef _DEBUG
if (!lhslib::isValidLHS(J[0])) PRINT_MACRO("J[0] is not valid\n");
#endif
std::vector eps = std::vector(m_n*m_k);
runif_std(static_cast(m_n * m_k), eps, oRandom);
unsigned int count = 0;
for (unsigned int j = 0; j < static_cast(m_k); j++)
{
for (unsigned int i = 0; i < static_cast(m_n); i++)
{
result(i,j) = (static_cast(J[0](i,j)) - 1.0 + eps[count]) / static_cast(m_n);
count++;
}
}
}
}
lhs/src/primes.cpp 0000644 0001762 0000144 00000007771 13425401602 013643 0 ustar ligges users /**
* @file primes.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* License: This file may be freely used and shared according to the original license.
*
* Reference:
*
* These programs construct and manipulate orthogonal arrays. They were prepared by
*
* - Art Owen
* - Department of Statistics
* - Sequoia Hall
* - Stanford CA 94305
*
* They may be freely used and shared. This code comes
* with no warranty of any kind. Use it at your own
* risk.
*
* I thank the Semiconductor Research Corporation and
* the National Science Foundation for supporting this
* work.
*
*/
#include "primes.h"
namespace oacpp
{
namespace primes
{
int isprime_old(int p) // LCOV_EXCL_START
{
if (p < 2)
{
return ISPRIMEFALSE;
}
/* This is not the fastest, but it is likely to
take negligible time compared to that used in constructing
the Galois field or the experimental design
*/
double maxDivisor = sqrt(static_cast(p + 1));
for (int k = 2; static_cast(k) < maxDivisor; k++)
{
if ((p / k) * k == p)
{
return ISPRIMEFALSE;
}
}
return ISPRIMETRUE;
} // LCOV_EXCL_STOP
int isprime(unsigned int n)
{
// 0, 1
if (n < 2)
{
return ISPRIMEFALSE;
}
// 2, 3
if (n < 4)
{
return ISPRIMETRUE;
}
// if n is divisible by 2, it is not prime // 4,6,8,10,...
if (n % 2 == 0)
{
return ISPRIMEFALSE;
}
// 5 => sqrt(5)=2.1 => iMax=3 => i=3 => 5%3!=0 => prime
// 7 => sqrt(7)=2.5 => iMax=3 => i=3 => 7%3!=0 => prime
// 9 => sqrt(9)=3 => iMax=3 => i=3 => 9%3=0 => not prime
size_t iMax = static_cast(sqrt(static_cast(n))) + 1;
for (size_t i = 3; i <= iMax; i += 2)
{
if (n % i == 0)
{
return ISPRIMEFALSE;
}
}
return ISPRIMETRUE;
}
void primepow(int q, int* p, int* n, int* isit)
{
int firstfactor = 0; // maybe uninitialized otherwise
*p = *n = *isit = 0;
if (q <= 1)
{
return;
}
if (isprime(q))
{
*p = q;
*n = 1;
*isit = 1;
return;
}
for (int k = 2; k < sqrt(static_cast(q) + 1.0); k++)
{
if ((q % k) == 0)
{
firstfactor = k;
break;
}
}
if (!isprime(firstfactor)) // LCOV_EXCL_START
{
return;
} // LCOV_EXCL_STOP
while (1)
{
if (q == 1)
{
*isit = 1;
*p = firstfactor;
return;
}
if (q % firstfactor == 0)
{
*n += 1;
q /= firstfactor;
}
else
{
return;
}
}
}
int isprimepow(int q)
{
int p, n, ispp;
primepow(q, &p, &n, &ispp);
return ispp;
}
int ipow(int a, int b)
{
return (int) pow((double) a, (double) b);
}
} // end namespace
} // end namespace
lhs/src/construct.h 0000644 0001762 0000144 00000010465 13425401602 014027 0 ustar ligges users /**
* @file construct.h
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* License: This file may be freely used and shared according to the original license.
*
* Reference:
*
* These programs construct and manipulate orthogonal arrays. They were prepared by
*
* - Art Owen
* - Department of Statistics
* - Sequoia Hall
* - Stanford CA 94305
*
* They may be freely used and shared. This code comes
* with no warranty of any kind. Use it at your own
* risk.
*
* I thank the Semiconductor Research Corporation and
* the National Science Foundation for supporting this
* work.
*
*/
#ifndef CONSTRUCT_H
#define CONSTRUCT_H
/* Constructions for designs using Galois fields */
#include "OACommonDefines.h"
#include "galois.h"
#include "ak.h"
/* Glossary:
*/
namespace oacpp {
/**
* Namespace to construct Orthogonal Arrays using various algorithms
*/
namespace oaconstruct
{
/**
* Construct an orthogonal array using the bose algorithm
*
* OA( q^2, q+1, q, 2 )
* R.C. Bose (1938) Sankhya Vol 3 pp 323-338
*
* @param gf galois field
* @param A an orthogonal array
* @param ncol the number of columns
* @return an indicator of success
*/
int bose(GF & gf, bclib::matrix & A, int ncol );
/**
* Construct an orthogonal array using the bush algorithm
* @param gf
* @param A
* @param str
* @param ncol
* @return
*/
int bush(GF & gf, bclib::matrix & A, int str, int ncol );
/**
*
* Implement Addelman and Kempthorne's 1961 A.M.S. method with n=2
*
* @param gf
* @param A
* @param ncol
* @return
*/
int addelkemp(GF & gf, bclib::matrix & A, int ncol );
/**
* Construct an orthogonal array using the bosebush algorithm
*
* OA( 2q^2, 2q+1, q, 2 ), only implemented for q=2^n
* Implement Bose and Bush's 1952 A.M.S. method with p=2, u=1
*
* @param gf
* @param B
* @param ncol
* @return
*/
int bosebush(GF & gf, bclib::matrix & B, int ncol );
/**
* Construct an orthogonal array using the bose-bush algorithm
*
* @param gf
* @param lam
* @param B
* @param ncol
* @return
*/
int bosebushl(GF & gf, int lam, bclib::matrix & B, int ncol );
/**
* Check the input to the bose algorithm
* @param q the number of symbols
* @param ncol the number of columns
* @return an indicator of success
*/
int bosecheck(int q, int ncol );
/**
*
* @param n
* @param q
* @param d
* @param coef
* @return
*/
int itopoly(int n, int q, int d, std::vector & coef );
/**
* Evaluate a polynomial with coefficients, argument and result in a Galois field
* @param gf a Galois field
* @param d
* @param poly
* @param arg
* @param value
* @return
*/
int polyeval(GF & gf, int d, std::vector & poly, int arg, int* value );
/**
*
* @param q
* @param str
* @param ncol
* @return
*/
int bushcheck(int q, int str, int ncol);
/**
*
* @param q
* @param p
* @param ncol
* @return
*/
int bosebushcheck(int q, int p, int ncol );
/**
*
* @param s
* @param p
* @param lam
* @param ncol
* @return
*/
int bosebushlcheck(int s, int p, int lam, int ncol );
/**
*
* @param q
* @param p
* @param ncol
* @return
*/
int addelkempcheck(int q, int p, int ncol );
}
}// end namespace
#endif
lhs/src/optimumLHS.cpp 0000644 0001762 0000144 00000025420 13425401602 014374 0 ustar ligges users /**
* @file optimumLHS.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2014, Robert Carnell
*
* @license GNU Lesser General Public License (LGPL v3)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "LHSCommonDefines.h"
#include "utilityLHS.h"
/*
* Arrays are passed into this routine to allow R to allocate and deallocate
* memory within the wrapper function.
*
* Dimensions: oldHypercube N x K
* optimalityRecordLength = N choose 2 + 1
* Parameters:
* N: The number of points to be sampled
* K: The number of dimensions (or variables) needed
* maxSweeps: The maximum number of times the exchange algorithm
* is applied across the columns. Therefor if
* MAXSWEEPS =5 and K = 6 then 30 exchange operations
* could be used.
* eps: The minimum fraction gained in optimality that is
* desired to continue the iterations as a fraction of
* the gain from the first interchange
* References: Please see the package documentation
*
*/
namespace lhslib
{
/*
* Return an optimized hypercube according to the criteria given
*
*/
void optimumLHS(int n, int k, int maxSweeps, double eps, bclib::matrix & outlhs,
int optimalityRecordLength, bclib::CRandom & oRandom, bool bVerbose)
{
if (n < 1 || k < 1 || maxSweeps < 1 || eps <= 0)
{
throw std::runtime_error("nsamples or nparameters or maxSweeps are less than 1 or eps <= 0");
}
unsigned int nOptimalityRecordLength = static_cast(optimalityRecordLength);
msize_type nsamples = static_cast(n);
msize_type nparameters = static_cast(k);
unsigned int nMaxSweeps = static_cast(maxSweeps);
double eps_change = eps;
int extraColumns = 0;
double gOptimalityOld;
double optimalityChangeOld = 0.0;
double optimalityChange;
int test;
unsigned int iter, posit, optimalityRecordIndex;
if (outlhs.rowsize() != nsamples || outlhs.colsize() != nparameters)
{
outlhs = bclib::matrix(nsamples, nparameters);
}
//matrix_unsafe oldHypercube_new = matrix_unsafe(nsamples, nparameters, oldHypercube, true);
bclib::matrix newHypercube = bclib::matrix(nsamples, nparameters);
std::vector optimalityRecord = std::vector(nOptimalityRecordLength);
std::vector interchangeRow1 = std::vector(nOptimalityRecordLength);
std::vector interchangeRow2 = std::vector(nOptimalityRecordLength);
// fill the oldHypercube with a random lhs sample
std::vector randomUnif(nsamples);
std::vector orderedUnif(nsamples);
for (msize_type jcol = 0; jcol < nparameters; jcol++)
{
// fill a vector with a random sample to order
for (msize_type irow = 0; irow < nsamples; irow++)
{
randomUnif[irow] = oRandom.getNextRandom();
}
bclib::findorder(randomUnif, orderedUnif);
for (msize_type irow = 0; irow < nsamples; irow++)
{
outlhs(irow,jcol) = orderedUnif[irow];
}
}
/* find the initial optimality measure */
gOptimalityOld = sumInvDistance(outlhs);
if (bVerbose)
{
PRINT_MACRO << "Beginning Optimality Criterion " << gOptimalityOld << " \n"; // LCOV_EXCL_LINE
}
#if PRINT_RESULT
lhsPrint(outlhs, 1);
#endif
test = 0;
iter = 0;
while (test == 0)
{
if (iter == nMaxSweeps)
{
break;
}
iter++;
/* iterate over the columns */
for (msize_type j = 0; j < nparameters; j++)
{
optimalityRecordIndex = 0;
/* iterate over the rows for the first point from 0 to N-2 */
for (msize_type i = 0; i < (nsamples - 1); i++)
{
/* iterate over the rows for the second point from i+1 to N-1 */
for (msize_type kindex = (i + 1); kindex < nsamples; kindex++)
{
/* put the values from oldHypercube into newHypercube */
copyMatrix(newHypercube, outlhs);
/* exchange two values (from the ith and kth rows) in the jth column
* and place them in the new matrix */
newHypercube(i, j) = outlhs(kindex, j);
newHypercube(kindex, j) = outlhs(i, j);
/* store the optimality of the newly created matrix and the rows that
* were interchanged */
optimalityRecord[optimalityRecordIndex] = sumInvDistance(newHypercube);
interchangeRow1[optimalityRecordIndex] = static_cast(i);
interchangeRow2[optimalityRecordIndex] = static_cast(kindex);
optimalityRecordIndex++;
}
}
/* once all combinations of the row interchanges have been completed for
* the current column j, store the old optimality measure (the one we are
* trying to beat) */
optimalityRecord[optimalityRecordIndex] = gOptimalityOld;
interchangeRow1[optimalityRecordIndex] = 0;
interchangeRow2[optimalityRecordIndex] = 0;
/* Find which optimality measure is the lowest for the current column.
* In other words, which two row interchanges made the hypercube better in
* this column */
posit = 0;
for (vsize_type kindex = 0; kindex < nOptimalityRecordLength; kindex++)
{
if (optimalityRecord[kindex] < optimalityRecord[posit])
{
posit = static_cast(kindex);
}
}
/* If the new minimum optimality measure is better than the old measure */
if (optimalityRecord[posit] < gOptimalityOld)
{
/* put oldHypercube in newHypercube */
copyMatrix(newHypercube, outlhs);
/* Interchange the rows that were the best for this column */
newHypercube(interchangeRow1[posit], j) = outlhs(interchangeRow2[posit], j);
newHypercube(interchangeRow2[posit], j) = outlhs(interchangeRow1[posit], j);
/* put newHypercube back in oldHypercube for the next iteration */
copyMatrix(outlhs, newHypercube);
/* if this is not the first column we have used for this sweep */
if (j > 0)
{
/* check to see how much benefit we gained from this sweep */
optimalityChange = std::fabs(optimalityRecord[posit] - gOptimalityOld);
if (optimalityChange < eps_change * optimalityChangeOld)
{
test = 1;
if (bVerbose)
{
PRINT_MACRO << "Algorithm stopped when the change in the inverse distance measure was smaller than " << ((eps_change)* optimalityChangeOld) << " \n"; // LCOV_EXCL_LINE
}
}
}
/* if this is first column of the sweep, then store the benefit gained */
else
{
optimalityChangeOld = std::fabs(optimalityRecord[posit] - gOptimalityOld);
}
/* replace the old optimality measure with the current one */
gOptimalityOld = optimalityRecord[posit];
}
/* if the new and old optimality measures are equal */
else if (optimalityRecord[posit] == gOptimalityOld)
{
test = 1;
if (bVerbose)
{
PRINT_MACRO << "Algorithm stopped when changes did not improve design optimality\n"; // LCOV_EXCL_LINE
}
}
/* if the new optimality measure is worse */
else if (optimalityRecord[posit] > gOptimalityOld) // LCOV_EXCL_START
{
ERROR_MACRO << "Unexpected Result: Algorithm produced a less optimal design\n";
test = 1;
} // LCOV_EXCL_STOP
/* if there is a reason to exit... */
if (test == 1)
{
break;
}
extraColumns++;
}
}
/* if we made it through all the sweeps */
if (iter == nMaxSweeps)
{
if (bVerbose)
{
PRINT_MACRO << nMaxSweeps << " full sweeps completed\n"; // LCOV_EXCL_LINE
}
}
/* if we didn't make it through all of them */
else
{
if (bVerbose)
{
PRINT_MACRO << "Algorithm used " << (iter-1) << " sweep(s) and " << extraColumns << " extra column(s)\n"; // LCOV_EXCL_LINE
}
}
if (bVerbose)
{
PRINT_MACRO << "Final Optimality Criterion " << gOptimalityOld << " \n"; // LCOV_EXCL_LINE
}
//#if _DEBUG
bool btest = isValidLHS(outlhs);
if (!btest)
{
/* the error function should send an error message through R */
ERROR_MACRO << "Invalid Hypercube\n"; // LCOV_EXCL_LINE
}
//#endif
#if PRINT_RESULT
lhsPrint(outlhs, 1);
#endif
}
} // end namespace
lhs/src/COrthogonalArray.cpp 0000644 0001762 0000144 00000022353 13425401602 015553 0 ustar ligges users /**
* @file COrthogonalArray.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* @license GNU Lesser General Public License (LGPL v3)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Reference:
*
*/
#include "COrthogonalArray.h"
namespace oacpp {
COrthogonalArray::COrthogonalArray()
{
m_nrow = 0;
m_ncol = 0;
m_q = 0;
}
void COrthogonalArray::createGaloisField(int q)
{
bool test = galoisfield::GF_getfield(q, m_gf) == SUCCESS_CHECK ? true : false;
if (!test)
{
throw std::runtime_error("Could not construct the Galois field"); // LCOV_EXCL_TEST
}
}
void COrthogonalArray::checkDesignMemory()
{
if (m_A.isEmpty())
{
throw std::runtime_error("Could not allocate array for the design memory."); // LCOV_EXCL_TEST
}
}
int COrthogonalArray::checkMaxColumns(int k, int maxColumns)
{
if (k < 2)
{
return maxColumns;
}
else if (k > maxColumns)
{
std::ostringstream s;
s << "At most " << maxColumns << "columns are possible for the design.";
const std::string ss = s.str();
throw std::runtime_error(ss.c_str());
}
else
{
return k;
}
}
void COrthogonalArray::checkResult(int result, int nvalue, int * n)
{
if (result)
{
*n = nvalue;
}
else
{
throw std::runtime_error("Unable to construct design"); // LCOV_EXCL_TEST
}
}
void COrthogonalArray::addelkemp(int q, int k, int* n)
{
k = checkMaxColumns(k, 2*q+1);
createGaloisField(q);
int matrows = 2 * q * q;
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaconstruct::addelkemp(m_gf, m_A, k);
checkResult(result, 2*q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::addelkemp3(int q, int k, int* n)
{
k = checkMaxColumns(k, 2*q*q + 2*q + 1); /* 2(q^3-1)/(q-1) - 1 */
createGaloisField(q);
int matrows = 2 * q * q * q;
m_A = bclib::matrix(static_cast(matrows), k);
checkDesignMemory();
int result = oaaddelkemp::addelkemp3(m_gf, m_A, k);
checkResult(result, 2*q*q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::addelkempn(int akn, int q, int k, int* n) // LCOV_EXCL_START
{
k = checkMaxColumns(k, 2*(primes::ipow(q,akn)-1)/(q-1) - 1); /* 2(q^3-1)/(q-1) - 1 */
createGaloisField(q);
int matrows = 2 * primes::ipow(q, akn);
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaaddelkemp::addelkempn(m_gf, akn, m_A, k);
checkResult(result, 2*primes::ipow(q,akn), n);
m_q = q; m_ncol=k; m_nrow=*n;
} // LCOV_EXCL_STOP
void COrthogonalArray::bose(int q, int k, int* n)
{
k = checkMaxColumns(k, q+1);
createGaloisField(q);
int matrows = q * q;
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaconstruct::bose(m_gf, m_A, k);
checkResult(result, q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::bosebush(int q, int k, int *n)
{
if (q%2)
{
throw std::runtime_error("This implementation of Bose-Bush only works for a number of levels equal to a power of 2");
}
k = checkMaxColumns(k, 2*q);
createGaloisField(2*q);
int matrows = 2 * q * q;
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaconstruct::bosebush(m_gf, m_A, k);
checkResult(result, 2*q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::bosebushl(int lambda, int q, int k, int* n)
{
int pq, nq, isppq, pl, nl, isppl;
k = checkMaxColumns(k, q*lambda);
primes::primepow(lambda, &pl, &nl, &isppl);
primes::primepow(q , &pq, &nq, &isppq);
if (!isppq)
{
throw std::runtime_error("The Bose-Bush design requires that q be prime raised to a positive integral power.");
}
if (!isppl)
{
throw std::runtime_error("The Bose-Bush design requires that lambda be a prime raised to a positive integral power.");
}
if (pl != pq)
{
throw std::runtime_error("The Bose-Bush design requires that lambda and q be powers of the same prime.");
}
createGaloisField(lambda*q);
int matrows = lambda * q * q;
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaconstruct::bosebushl(m_gf, lambda, m_A, k);
checkResult(result, lambda*q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::bush(int q, int k, int* n)
{
k = checkMaxColumns(k, q+1);
createGaloisField(q);
int matrows = q * q * q;
m_A = bclib::matrix(matrows, k);
checkDesignMemory();
int result = oaconstruct::bush(m_gf, m_A, 3, k);
checkResult(result, q*q*q, n);
m_q = q; m_ncol=k; m_nrow=*n;
}
void COrthogonalArray::busht(int str, int q, int k, int* n)
{
k = checkMaxColumns(k, q+1);
if (str < 2)
{
throw std::runtime_error("Bush designs not provided for strength < 2");
}
createGaloisField(q);
m_A = bclib::matrix(primes::ipow(q,str), k);
checkDesignMemory();
int result = oaconstruct::bush(m_gf, m_A, str, k);
checkResult(result, primes::ipow(q,str), n);
m_q = q; m_ncol=k; m_nrow=*n;
}
int COrthogonalArray::oaagree(bool verbose)
{
int agree, maxagr;
int mrow1, mrow2;
maxagr = mrow1 = mrow2 = 0;
for (int i = 0; i < m_nrow; i++)
{
for (int j = i+1; j < m_nrow; j++)
{
agree = 0;
for (int k = 0; k < m_ncol; k++)
{
agree += (m_A(i,k) == m_A(j,k));
}
if (agree > maxagr)
{
maxagr = agree;
mrow1 = i;
mrow2 = j;
if (verbose)
{
PRINT_OUTPUT << "New max " << i << " " << j << " " << agree << "\n"; // LCOV_EXCL_LINE
}
}
}
if (i && i % ROWCHECK == 0 && verbose)
{
PRINT_OUTPUT << "Checked rows <= " << i << " vs all other rows.\n"; // LCOV_EXCL_LINE
}
}
if (verbose) // LCOV_EXCL_START
{
if (maxagr == 0)
{
PRINT_OUTPUT << "No two distinct rows agree in any columns.\n";
}
else
{
PRINT_OUTPUT << "Maximum number of columns matching for two distinct rows is " << maxagr << ".\n";
PRINT_OUTPUT << "This is attained by rows " << mrow1 << " and " << mrow2 << ".\n";
}
} // LCOV_EXCL_STOP
return maxagr;
}
int COrthogonalArray::oatriple(bool verbose)
{
/* Count triple agreements among rows of an array */
int a3/*, q*/;
int num3 = 0;
for (int j1 = 0; j1 < m_ncol; j1++)
{
for (int j2 = j1+1; j2 < m_ncol; j2++)
{
for (int j3 = j2+1; j3 < m_ncol; j3++)
{
a3 = 0;
for (int i1 = 0; i1 < m_nrow; i1++)
{
for (int i2 = i1+1; i2 < m_nrow; i2++)
{
a3 += ( m_A(i1,j1)==m_A(i2,j1) )&&( m_A(i1,j2)==m_A(i2,j2) )&&( m_A(i1,j3)==m_A(i2,j3) );
}
if (a3)
{
if (verbose)
{
PRINT_OUTPUT << "Cols " << j1 << " " << j2 << " " << j3 << " match in " << a3 << " distinct pairs of rows.\n"; // LCOV_EXCL_LINE
}
num3++;
}
}
}
}
}
if (verbose) // LCOV_EXCL_START
{
PRINT_OUTPUT << "There are " << num3 << " distinct triples of columns that agree\n";
PRINT_OUTPUT << "in at least two distinct rows.\n";
} // LCOV_EXCL_STOP
return num3;
}
void COrthogonalArray::oarand(int is, int js, int ks, int ls)
{
m_randomClass.seed(is, js, ks, ls);
std::vector pi = std::vector(m_q);
for (int j = 0; j < m_ncol; j++)
{
rutils::unifperm(pi, m_q, m_randomClass);
for (int i = 0; i < m_nrow; i++)
{
m_A(i,j) = pi[ m_A(i,j) ];
}
}
}
int COrthogonalArray::oastr(bool verbose)
{
int str;
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
oastrength::OA_strength(m_q, m_A, &str, bverb);
if (verbose) // LCOV_EXCL_START
{
if (str < 0)
{
PRINT_OUTPUT << "\nThe array does not even have strength 0, meaning that\n";
PRINT_OUTPUT << "it is not composed of symbols 0 through " << m_q << ".\n";
}
else
{
PRINT_OUTPUT << "\nThe array has strength " << str << " and no higher strength.\n";
}
} // LCOV_EXCL_STOP
return str;
}
bool COrthogonalArray::oastr1(bool verbose)
{
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
return (oastrength::OA_str1(m_q, m_A, bverb) == SUCCESS_CHECK);
}
bool COrthogonalArray::oastr2(bool verbose)
{
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
return (oastrength::OA_str2(m_q, m_A, bverb) == SUCCESS_CHECK);
}
bool COrthogonalArray::oastr3(bool verbose)
{
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
return (oastrength::OA_str3(m_q, m_A, bverb) == SUCCESS_CHECK);
}
bool COrthogonalArray::oastr4(bool verbose)
{
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
return (oastrength::OA_str4(m_q, m_A, bverb) == SUCCESS_CHECK);
}
bool COrthogonalArray::oastrt(int t, bool verbose)
{
int bverb = (verbose) ? ALLMESSAGES : NOMESSAGES;
return (oastrength::OA_strt(m_q, m_A, t, bverb) == SUCCESS_CHECK);
}
} // end namespace
lhs/src/utilityLHS.cpp 0000644 0001762 0000144 00000006155 13425401602 014411 0 ustar ligges users /**
* @file utilityLHS.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2014, Robert Carnell
*
* @license GNU Lesser General Public License (LGPL v3)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "utilityLHS.h"
namespace lhslib
{
bool isValidLHS(const bclib::matrix & result)
{
int total = 0;
msize_type cols = result.colsize();
msize_type rows = result.rowsize();
/*
* verify that the result is a latin hypercube. One easy check is to ensure
* that the sum of the rows is the sum of the 1st N integers. This check can
* be fooled in one unlikely way...
* if a column should be 1 2 3 4 6 8 5 7 9 10
* the sum would be 10*11/2 = 55
* the same sum could come from 5 5 5 5 5 5 5 5 5 10
* but this is unlikely
*/
// sum each column
for (msize_type jcol = 0; jcol < cols; jcol++)
{
total = 0;
for (msize_type irow = 0; irow < rows; irow++)
{
total += result(irow, jcol);
}
if (total != static_cast(rows * (rows + 1) / 2))
{
return false;
}
}
return true;
}
bool isValidLHS(const bclib::matrix & result)
{
msize_type n = result.rowsize();
msize_type k = result.colsize();
bclib::matrix resultint = bclib::matrix(n, k);
bclib::matrix::const_iterator it = result.begin();
bclib::matrix::iterator iti = resultint.begin();
for (;it != result.end(); ++it, ++iti)
{
*iti = 1 + static_cast(floor(static_cast(n) * (*it)));
}
bool ret = isValidLHS(resultint);
return ret;
}
void initializeAvailableMatrix(bclib::matrix & avail)
{
// avail is k x n
for (msize_type irow = 0; irow < avail.rowsize(); irow++)
{
for (msize_type jcol = 0; jcol < avail.colsize(); jcol++)
{
avail(irow, jcol) = static_cast(jcol + 1);
}
}
}
void runif_std(unsigned int n, std::vector & output, bclib::CRandom & oRandom)
{
if (output.size() != n)
{
output.resize(n);
}
for (unsigned int i = 0; i < n; i++)
{
output[i] = oRandom.getNextRandom();
}
}
} // end namespace
lhs/src/optSeededLHS.cpp 0000644 0001762 0000144 00000024027 13425401602 014620 0 ustar ligges users /**
* @file optSeededLHS.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2014, Robert Carnell
*
* @license GNU Lesser General Public License (LGPL v3)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include "LHSCommonDefines.h"
#include "utilityLHS.h"
/*
* Arrays are passed into this routine to allow R to allocate and deallocate
* memory within the wrapper function.
* This code uses ISO C90 comment styles and layout
*
* "oldHypercube", "newHypercube", and "matrix" are matricies but are treated as one
* dimensional arrays to facilitate passing them from R.
* Dimensions: oldHypercube N x K
* optimalityRecordLength = N choose 2 + 1
* Parameters:
* N: The number of points to be sampled
* K: The number of dimensions (or variables) needed
* maxSweeps: The maximum number of times the exchange algorithm
* is applied across the columns. Therefor if
* MAXSWEEPS =5 and K = 6 then 30 exchange operations
* could be used.
* eps: The minimum fraction gained in optimality that is
* desired to continue the iterations as a fraction of
* the gain from the first interchange
* References: Please see the package documentation
*
*/
namespace lhslib
{
/*
* Return an optimized hypercube according to the criteria given
*
*/
void optSeededLHS(int n, int k, int maxSweeps, double eps, bclib::matrix & oldHypercube,
int optimalityRecordLength, bool bVerbose)
{
if (n < 1 || k < 1 || maxSweeps < 1 || eps <= 0)
{
throw std::runtime_error("nsamples or nparameters or maxSweeps are less than 1 or eps <= 0");
}
unsigned int nOptimalityRecordLength = static_cast(optimalityRecordLength);
msize_type nsamples = static_cast(n);
msize_type nparameters = static_cast(k);
unsigned int nMaxSweeps = static_cast(maxSweeps);
double eps_change = eps;
int extraColumns = 0;
double gOptimalityOld;
double optimalityChangeOld = 0.0;
double optimalityChange;
int test;
unsigned int iter, posit, optimalityRecordIndex;
//matrix_unsafe oldHypercube_new = matrix_unsafe(nsamples, nparameters, oldHypercube, true);
bclib::matrix newHypercube = bclib::matrix(nsamples, nparameters);
std::vector optimalityRecord = std::vector(nOptimalityRecordLength);
std::vector interchangeRow1 = std::vector(nOptimalityRecordLength);
std::vector interchangeRow2 = std::vector(nOptimalityRecordLength);
/* find the initial optimality measure */
gOptimalityOld = sumInvDistance(oldHypercube);
if (bVerbose)
{
PRINT_MACRO << "Beginning Optimality Criterion " << gOptimalityOld << " \n"; // LCOV_EXCL_LINE
}
#if PRINT_RESULT
lhslib::lhsPrint(oldHypercube, false);
#endif
test = 0;
iter = 0;
while (test == 0)
{
if (iter == nMaxSweeps)
{
break;
}
iter++;
/* iterate over the columns */
for (msize_type j = 0; j < nparameters; j++)
{
optimalityRecordIndex = 0;
/* iterate over the rows for the first point from 0 to N-2 */
for (msize_type i = 0; i < nsamples - 1; i++)
{
/* iterate over the rows for the second point from i+1 to N-1 */
for (msize_type kindex = i + 1; kindex < nsamples; kindex++)
{
/* put the values from oldHypercube into newHypercube */
copyMatrix(newHypercube, oldHypercube);
/* exchange two values (from the ith and kth rows) in the jth column
* and place them in the new matrix */
newHypercube(i, j) = oldHypercube(kindex, j);
newHypercube(kindex, j) = oldHypercube(i, j);
/* store the optimality of the newly created matrix and the rows that
* were interchanged */
optimalityRecord[optimalityRecordIndex] = sumInvDistance(newHypercube);
interchangeRow1[optimalityRecordIndex] = static_cast(i);
interchangeRow2[optimalityRecordIndex] = static_cast(kindex);
optimalityRecordIndex++;
}
}
/* once all combinations of the row interchanges have been completed for
* the current column j, store the old optimality measure (the one we are
* trying to beat) */
optimalityRecord[optimalityRecordIndex] = gOptimalityOld;
interchangeRow1[optimalityRecordIndex] = 0;
interchangeRow2[optimalityRecordIndex] = 0;
/* Find which optimality measure is the lowest for the current column.
* In other words, which two row interchanges made the hypercube better in
* this column */
posit = 0;
for (vsize_type kindex = 0; kindex < nOptimalityRecordLength; kindex++)
{
if (optimalityRecord[kindex] < optimalityRecord[posit])
{
posit = static_cast(kindex);
}
}
/* If the new minimum optimality measure is better than the old measure */
if (optimalityRecord[posit] < gOptimalityOld)
{
/* put oldHypercube in newHypercube */
copyMatrix(newHypercube, oldHypercube);
/* Interchange the rows that were the best for this column */
newHypercube(interchangeRow1[posit], j) = oldHypercube(interchangeRow2[posit], j);
newHypercube(interchangeRow2[posit], j) = oldHypercube(interchangeRow1[posit], j);
/* put newHypercube back in oldHypercube for the next iteration */
copyMatrix(oldHypercube, newHypercube);
/* if this is not the first column we have used for this sweep */
if (j > 0)
{
/* check to see how much benefit we gained from this sweep */
optimalityChange = std::fabs(optimalityRecord[posit] - gOptimalityOld);
if (optimalityChange < eps_change * optimalityChangeOld)
{
test = 1;
if (bVerbose)
{
PRINT_MACRO << "Algorithm stopped when the change in the inverse distance measure was smaller than " << ((eps_change)* optimalityChangeOld) << " \n"; // LCOV_EXCL_LINE
}
}
}
/* if this is first column of the sweep, then store the benefit gained */
else
{
optimalityChangeOld = std::fabs(optimalityRecord[posit] - gOptimalityOld);
}
/* replace the old optimality measure with the current one */
gOptimalityOld = optimalityRecord[posit];
}
/* if the new and old optimality measures are equal */
else if (optimalityRecord[posit] == gOptimalityOld)
{
test = 1;
if (bVerbose)
{
PRINT_MACRO << "Algorithm stopped when changes did not impove design optimality\n"; // LCOV_EXCL_LINE
}
}
/* if the new optimality measure is worse */
else if (optimalityRecord[posit] > gOptimalityOld) // LCOV_EXCL_START
{
ERROR_MACRO << "Unexpected Result: Algorithm produced a less optimal design\n";
test = 1;
} // LCOV_EXCL_STOP
/* if there is a reason to exit... */
if (test == 1)
{
break;
}
extraColumns++;
}
}
/* if we made it through all the sweeps */
if (iter == nMaxSweeps)
{
if (bVerbose)
{
PRINT_MACRO << nMaxSweeps << " full sweeps completed\n"; // LCOV_EXCL_LINE
}
}
/* if we didn't make it through all of them */
else
{
if (bVerbose)
{
PRINT_MACRO << "Algorithm used " << (iter-1) << " sweep(s) and " << extraColumns << " extra column(s)\n"; // LCOV_EXCL_LINE
}
}
if (bVerbose)
{
PRINT_MACRO << "Final Optimality Criterion " << gOptimalityOld << " \n"; // LCOV_EXCL_LINE
}
#if PRINT_RESULT
lhsPrint(oldHypercube, false);
#endif
}
} // end namespace
lhs/src/lhs_r.cpp 0000644 0001762 0000144 00000017137 13425401602 013450 0 ustar ligges users /**
* @file lhs_r.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* @license GNU General Public License (GPL v3)
* 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 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "lhs_r.h"
RcppExport SEXP /*double matrix*/ improvedLHS_cpp(SEXP /*int*/ n, SEXP /*int*/ k,
SEXP /*int*/ dup)
{
BEGIN_RCPP
if (TYPEOF(n) != INTSXP || TYPEOF(k) != INTSXP ||
TYPEOF(dup) != INTSXP)
{
Rcpp_error("n, k, and dup should be integers");
}
Rcpp::RNGScope tempRNG;
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
int m_dup = Rcpp::as(dup);
lhs_r::checkArguments(m_n, m_k, m_dup);
lhs_r::RStandardUniform oRStandardUniform = lhs_r::RStandardUniform();
if (m_n == 1)
{
return lhs_r::degenerateCase(m_k, oRStandardUniform);
}
bclib::matrix intMat = bclib::matrix(m_n, m_k);
lhslib::improvedLHS(m_n, m_k, m_dup, intMat, oRStandardUniform);
Rcpp::NumericMatrix result = lhs_r::convertIntegerToNumericLhs(intMat);
return result;
END_RCPP
}
RcppExport SEXP /*double matrix*/ maximinLHS_cpp(SEXP /*int*/ n, SEXP /*int*/ k,
SEXP /*int*/ dup)
{
BEGIN_RCPP
if (TYPEOF(n) != INTSXP || TYPEOF(k) != INTSXP ||
TYPEOF(dup) != INTSXP)
{
Rcpp_error("n, k, and dup should be integers");
}
Rcpp::RNGScope tempRNG;
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
int m_dup = Rcpp::as(dup);
lhs_r::checkArguments(m_n, m_k, m_dup);
lhs_r::RStandardUniform oRStandardUniform = lhs_r::RStandardUniform();
if (m_n == 1)
{
return lhs_r::degenerateCase(m_k, oRStandardUniform);
}
bclib::matrix intMat = bclib::matrix(m_n, m_k);
lhslib::maximinLHS(m_n, m_k, m_dup, intMat, oRStandardUniform);
Rcpp::NumericMatrix result = lhs_r::convertIntegerToNumericLhs(intMat);
return result;
END_RCPP
}
RcppExport SEXP /*double matrix*/ optimumLHS_cpp(SEXP /*int*/ n, SEXP /*int*/ k,
SEXP /*int*/ maxsweeps, SEXP /*double*/ eps, SEXP /*bool*/ bVerbose)
{
BEGIN_RCPP
if (TYPEOF(n) != INTSXP || TYPEOF(k) != INTSXP || TYPEOF(maxsweeps) != INTSXP ||
TYPEOF(eps) != REALSXP || TYPEOF(bVerbose) != LGLSXP)
{
Rcpp_error("n, k, and maxsweeps should be integers, eps should be a real, and bVerbose should be a logical");
}
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
int m_maxsweeps = Rcpp::as(maxsweeps);
double m_eps = Rcpp::as(eps);
bool m_bVerbose = Rcpp::as(bVerbose);
lhs_r::checkArguments(m_n, m_k, m_maxsweeps, m_eps);
Rcpp::RNGScope tempRNG;
lhs_r::RStandardUniform oRStandardUniform = lhs_r::RStandardUniform();
if (m_n == 1)
{
return lhs_r::degenerateCase(m_k, oRStandardUniform);
}
int jLen = static_cast(::Rf_choose(static_cast(m_n), 2.0) + 1.0);
bclib::matrix intMat = bclib::matrix(m_n, m_k);
lhslib::optimumLHS(m_n, m_k, m_maxsweeps, m_eps, intMat,
jLen, oRStandardUniform, m_bVerbose);
Rcpp::NumericMatrix result = lhs_r::convertIntegerToNumericLhs(intMat);
return result;
END_RCPP
}
RcppExport SEXP /*double matrix*/ optSeededLHS_cpp(SEXP /*int*/ n, SEXP /*int*/ k,
SEXP /*int*/ maxsweeps, SEXP /*double*/ eps, SEXP /*numeric matrix*/ inlhs,
SEXP /*bool*/ bVerbose)
{
BEGIN_RCPP
if (TYPEOF(n) != INTSXP || TYPEOF(k) != INTSXP || TYPEOF(maxsweeps) != INTSXP ||
TYPEOF(eps) != REALSXP || TYPEOF(bVerbose) != LGLSXP)
{
Rcpp_error("n, k, and maxsweeps should be integers, eps should be a real, and bVerbose should be a logical");
}
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
int m_maxsweeps = Rcpp::as(maxsweeps);
double m_eps = Rcpp::as(eps);
bool m_bVerbose = Rcpp::as(bVerbose);
lhs_r::checkArguments(m_n, m_k, m_maxsweeps, m_eps);
Rcpp::NumericMatrix m_inlhs(inlhs);
if (m_inlhs.ncol() != m_k || m_inlhs.nrow() != m_n)
{
Rcpp_error("input matrix does not match the n and k arguments");
}
if (m_n == 1)
{
return m_inlhs;
}
int jLen = static_cast(::Rf_choose(static_cast(m_n), 2.0) + 1.0);
//std::vector mv_inlhs = Rcpp::as >(m_inlhs); // this probably unrolled the matrix columnwise
//bclib::matrix mm_inlhs = bclib::matrix(m_n, m_k, mv_inlhs); // and this was row wise
bclib::matrix mm_inlhs = bclib::matrix(m_n, m_k);
for (int i = 0; i < m_n; i++)
{
for (int j = 0; j < m_k; j++)
{
mm_inlhs(i,j) = m_inlhs(i,j);
}
}
lhslib::optSeededLHS(m_n, m_k, m_maxsweeps, m_eps, mm_inlhs,
jLen, m_bVerbose);
Rcpp::NumericMatrix result = lhs_r::convertMatrixToNumericLhs(mm_inlhs);
return result;
END_RCPP
}
RcppExport SEXP randomLHS_cpp(SEXP n, SEXP k, SEXP preserveDraw)
{
BEGIN_RCPP
if (TYPEOF(n) != INTSXP || TYPEOF(k) != INTSXP ||
TYPEOF(preserveDraw) != LGLSXP)
{
Rcpp_error("n and k should be integers, preserveDraw should be a logical");
}
Rcpp::RNGScope tempRNG;
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
bool bPreserveDraw = Rcpp::as(preserveDraw);
lhs_r::checkArguments(m_n, m_k);
lhs_r::RStandardUniform oRStandardUniform = lhs_r::RStandardUniform();
if (m_n == 1)
{
return lhs_r::degenerateCase(m_k, oRStandardUniform);
}
bclib::matrix result = bclib::matrix(m_n, m_k);
lhslib::randomLHS(m_n, m_k, bPreserveDraw, result, oRStandardUniform);
Rcpp::NumericMatrix rresult(m_n, m_k);
for (int irow = 0; irow < m_n; irow++)
{
for (int jcol = 0; jcol < m_k; jcol++)
{
rresult(irow, jcol) = result(irow, jcol);
}
}
return rresult;
END_RCPP
}
RcppExport SEXP geneticLHS_cpp(SEXP /*int*/ n, SEXP /*int*/ k, SEXP /*int*/ pop,
SEXP /*int*/ gen, SEXP /*double*/ pMut, SEXP criterium,
SEXP /*bool*/ bVerbose)
{
BEGIN_RCPP
Rcpp::RNGScope tempRNG;
int m_n = Rcpp::as(n);
int m_k = Rcpp::as(k);
int m_pop = Rcpp::as(pop);
int m_gen = Rcpp::as(gen);
double m_pMut = Rcpp::as(pMut);
std::string m_criterium = Rcpp::as(criterium);
bool m_bVerbose = Rcpp::as(bVerbose);
lhs_r::checkArguments(m_n, m_k);
lhs_r::RStandardUniform oRStandardUniform = lhs_r::RStandardUniform();
if (m_n == 1)
{
return lhs_r::degenerateCase(m_k, oRStandardUniform);
}
bclib::matrix mat = bclib::matrix(m_n, m_k);
lhslib::geneticLHS(m_n, m_k, m_pop, m_gen, m_pMut, m_criterium, m_bVerbose, mat, oRStandardUniform);
Rcpp::NumericMatrix rresult(m_n, m_k);
for (int irow = 0; irow < m_n; irow++)
{
for (int jcol = 0; jcol < m_k; jcol++)
{
rresult(irow, jcol) = mat(irow, jcol);
}
}
return rresult;
END_RCPP
}
lhs/src/construct.cpp 0000644 0001762 0000144 00000036772 13425401602 014373 0 ustar ligges users /**
* @file construct.cpp
* @author Robert Carnell
* @copyright Copyright (c) 2013, Robert Carnell
*
* License: This file may be freely used and shared according to the original license.
*
* Reference:
*
* These programs construct and manipulate orthogonal arrays. They were prepared by
*
* - Art Owen
* - Department of Statistics
* - Sequoia Hall
* - Stanford CA 94305
*
* They may be freely used and shared. This code comes
* with no warranty of any kind. Use it at your own
* risk.
*
* I thank the Semiconductor Research Corporation and
* the National Science Foundation for supporting this
* work.
*
*/
#include "construct.h"
namespace oacpp
{
namespace oaconstruct
{
int bosecheck(int q, int ncol)
{
std::ostringstream msg;
if (ncol > q + 1)
{
msg << "Bose's design must have ncol <= q+1. Had q=" << q << " and ncol=" << ncol << ".\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol <= 0)
{
msg << "Nonpositive number of columns requested for Bose's design\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
return SUCCESS_CHECK;
}
int bose(GF & gf, bclib::matrix & A, int ncol)
{
size_t icol, irow;
size_t q = static_cast(gf.q);
// bosecheck throws if it fails
bosecheck(static_cast(q), ncol);
irow = 0;
for (size_t i = 0; i < q; i++)
{
for (size_t j = 0; j < q; j++)
{
icol = 0;
A(irow, icol++) = static_cast(i);
if (ncol > 1)
{
A(irow, icol++) = static_cast(j);
}
for (icol = 2; icol < static_cast(ncol); icol++)
{
A(irow, icol) = gf.plus(j, gf.times(i, icol - 1));
}
irow++;
}
}
return SUCCESS_CHECK;
}
int itopoly(int n, int q, int d, std::vector & coef)
{
for (size_t i = 0; i <= static_cast(d); i++)
{
coef[i] = n % q;
n = n / q;
}
return UNCHECKED_RETURN;
}
/* find value = poly(arg) where poly is a polynomial of degree d
and all the arithmetic takes place in the given Galois field.*/
int polyeval(GF & gf, int d, std::vector & poly, int arg, int* value)
{
int ans = 0;
/* note: cannot decrement with a size type because it is always > 0. this needs to go < 1 to stop */
//for (size_t i = static_cast(d); i >= 0; --i) /* Horner's rule */
for (int i = d; i >= 0; i--) /* Horner's rule */
{
size_t ui = static_cast(i);
size_t uans = static_cast(ans);
size_t uarg = static_cast(arg);
#ifdef RANGE_DEBUG
size_t plusRow = static_cast(gf.times.at(uans,uarg));
size_t plusCol = static_cast(poly.at(ui));
ans = gf.plus.at(plusRow, plusCol);
#else
//ans = gf.plus(gf.times(ans,arg),poly[i]);
size_t plusRow = static_cast(gf.times(uans,uarg));
size_t plusCol = static_cast(poly[ui]);
ans = gf.plus(plusRow, plusCol);
#endif
}
*value = ans;
return UNCHECKED_RETURN;
}
int bushcheck(int q, int str, int ncol)
{
std::ostringstream msg;
if (ncol > q + 1)
{
msg << "Bush designs require ncol <= q+1. Cannot have q = " << q << " and ncol = " << ncol << ".\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (str > ncol)
{
msg << "It doesn't make sense to have an array of strength " << str << " with only " << ncol << "columns.\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (str >= q + 1) // LCOV_EXCL_START
{
PRINT_OUTPUT << "\tBush's (1952) theorem has a condition t & A, int str, int ncol)
{
int q = gf.q;
std::vector coef(str);
// bushcheck throws if it fails
bushcheck(q, str, ncol);
for (size_t i = 0; i < static_cast(primes::ipow(q, str)); i++)
{
itopoly(static_cast(i), q, str - 1, coef);
A(i, static_cast(0)) = coef[static_cast(str) - 1];
for (size_t j = 0; j < static_cast(ncol) - 1; j++)
{
polyeval(gf, str - 1, coef, static_cast(j), &(A(i, 1 + j)));
}
}
return SUCCESS_CHECK;
}
int addelkempcheck(int q, int p, int ncol)
{
std::ostringstream msg;
if (p == 2 && q > 4)
{
msg << "This Addelman-Kempthorne OA(2q^2,ncol,q,2) is only\n";
msg << "available for odd prime powers q and for even prime\n";
msg << "powers q<=4. q=" << q << " is not available, but a\n";
msg << "Bose Bush construction exists for that design.\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol > 2 * q + 1)
{
msg << "The Addelman-Kempthorne construction needs ncol <= 2q+1.\n";
msg << "Can't have ncol = " << ncol << " with q = " << q << ".\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol == 2 * q + 1) // LCOV_EXCL_START
{
PRINT_OUTPUT << "\n\tWarning: The Addelman-Kempthorne construction with ncol = 2q+1\n";
PRINT_OUTPUT << "\thas a defect. While it is still an OA(2q^2,2q+1,q,2),\n";
PRINT_OUTPUT << "\tthere exist some pairs of rows that agree in three columns.\n";
PRINT_OUTPUT << "\tThe final column in the array is involved in all of these\n";
PRINT_OUTPUT << "\ttriple coincidences.\n";
} // LCOV_EXCL_STOP
return SUCCESS_CHECK;
}
int addelkemp(GF & gf, bclib::matrix & A, int ncol)
{
int kay; /* A&K notation */
int square, ksquare, temp;
size_t row, col;
int p = gf.p;
size_t q = gf.q;
std::vector b(q);
std::vector c(q);
std::vector k(q);
// addelkempcheck throws if it fails
addelkempcheck(static_cast(q), p, ncol);
for (size_t i = 0; i < q; i++)
{ /* First q*q rows */
square = gf.times(i,i);
for (size_t j = 0; j < q; j++)
{
row = i * q + j;
col = 0;
if (col < static_cast(ncol))
{
A(row, col++) = static_cast(j);
}
for (size_t m = 1; m < q && col < static_cast(ncol); m++)
{
A(row,col++) = gf.plus(i,gf.times(m,j));
}
for (size_t m = 0; m < q && col < static_cast(ncol); m++)
{
temp = gf.plus(j,gf.times(m,i));
A(row,col++) = gf.plus(temp,square); /* Rgt cols */
}
if (col < static_cast(ncol))
{
A(row, col++) = static_cast(i);
}
}
}
if (p != 2) /* Constants kay,b,c,k for odd p */
{
oaaddelkemp::akodd(gf, &kay, b, c, k);
}
else /* Constants kay,b,c,k for even p */
{
oaaddelkemp::akeven(gf, &kay, b, c, k);
}
for (size_t i = 0; i < q; i++)
{ /* Second q*q rows */
square = gf.times(i,i);
ksquare = gf.times(kay,square);
for (size_t j = 0; j < q; j++)
{
row = q * q + i * q + j;
col = 0;
if (col < static_cast(ncol))
{
A(row, col++) = static_cast(j);
}
for (size_t m = 1; m < q && col < static_cast(ncol); m++, col++)
{
A(row,col) = gf.plus(A(row - q * q,col),b[m]);
}
if (col < static_cast(ncol))
{
A(row,col++) = gf.plus(ksquare,j); /* q+1 */
}
for (size_t m = 1; m < q && col < static_cast(ncol); m++)
{
temp = gf.times(i,k[m]);
temp = gf.plus(ksquare,temp);
temp = gf.plus(j,temp);
A(row,col++) = gf.plus(temp,c[m]);
}
if (col < static_cast(ncol))
{
A(row, col++) = static_cast(i);
}
}
}
return SUCCESS_CHECK;
}
int bosebushcheck(int q, int p, int ncol)
{
std::ostringstream msg;
if (p != 2)
{
msg << "This version of Bose and Bush needs q=2^n for some n.\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol > 2 * q + 1)
{
msg << "The Bose-Bush construction needs ncol <= 2q+1.\n";
msg << "Can't have ncol = " << ncol << " with q = " << q << ".\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol == 2 * q + 1) // LCOV_EXCL_START
{
PRINT_OUTPUT << "\n\tWarning: The Bose-Bush construction with ncol = 2q+1\n";
PRINT_OUTPUT << "\thas a defect. While it is still an OA(2q^2,2q+1,q,2),\n";
PRINT_OUTPUT << "\tthere exist some pairs of rows that agree in three columns.\n\n";
} // LCOV_EXCL_STOP
return SUCCESS_CHECK;
}
int bosebush(GF & gf, bclib::matrix & B, int ncol)
{
int p;
int mul;
size_t irow;
p = gf.p; /* GF(q) used to generate design with q/2 levels */
size_t q = static_cast(gf.q);
size_t s = q / 2; /* number of levels in design */
bclib::matrix A(s, q);
// bosebushcheck throws if it fails
bosebushcheck(static_cast(s), p, ncol);
irow = 0;
for (size_t i = 0; i < q; i++)
{
for (size_t j = 0; j < q; j++)
{
mul = gf.times(i,j);
mul = mul % s;
for (size_t k = 0; k < s; k++)
{
A(k,j) = gf.plus(mul,k);
}
}
for (size_t k = 0; k < s; k++)
{
for (size_t j = 0; j < static_cast(ncol) && j < 2 * s + 1; j++)
{
B(irow,j) = A(k,j);
}
if (static_cast(ncol) == 2 * s + 1)
{
B(irow, static_cast(ncol) - 1) = static_cast(i % s);
}
irow++;
}
}
return SUCCESS_CHECK;
}
int bosebushlcheck(int s, int p, int lam, int ncol)
{
std::ostringstream msg;
if (!primes::isprime(p))
{
msg << "Bose Bush routine given a nonprime.\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol > lam * s + 1)
{
msg << "The Bose-Bush construction needs ncol <= lambda*q+1.\n";
msg << "Can't have ncol = " << ncol << " with lam = " << lam << "\n";
msg << "and q = " << s << ".\n";
const std::string smsg = msg.str();
throw std::runtime_error(smsg.c_str());
}
if (ncol == lam * s + 1) // LCOV_EXCL_START
{
PRINT_OUTPUT << "\n\tWarning: The Bose-Bush construction with ncol = lambda*q+1\n";
PRINT_OUTPUT << "\thas a defect. While it is still an OA(lambda*q^2,lambda*q+1,q,2),\n";
PRINT_OUTPUT << "\tit may have worse coincidence properties than\n";
PRINT_OUTPUT << "\tOA(lambda*q^2,lambda*q+1,q,2).\n";
} // LCOV_EXCL_STOP
return SUCCESS_CHECK;
}
int bosebushl(GF & gf, int lam, bclib::matrix & B, int ncol)
/* Implement Bose and Bush's 1952 A.M.S. method with given lambda */
{
int p, irow;
int mul;
p = gf.p; /* GF(q) used to generate design with q/lam levels */
size_t q = static_cast