import pyLHD1. Introduction to pyLHD
pyLHD is a python implementation of the R package LHD by Hongzhi Wang, Qian Xiao, Abhyuday Mandal. As of now, only the algebraic construction of Latin hypercube designs (LHD) are implemented in this package. For search algorithms to construct LHDs such as: Simulated annealing, particle swarm optimization, and genetic algorithms refer to the R package.
In section 2 algebraic construction methods for LHDs are discussed
To evalute the generated LHDs we consider the following criteria
Maximin distance Criterion
Let \(X\) denote an LHD matrix. Define the \(L_q\)-distance between two run \(x_i\) and \(x_j\) of \(X\) as \(d_q(x_i,x_j) = \left( \sum_{k=1}^m |x_{ik}-x_{jk}|^q \right)^{1/q}\) where \(q\) is an integer. Define the \(L_q\)-distance of design \(X\) as \(d_q(X) = \min \{ d_q(x_i,x_j), 1 \leq i\leq j \leq n \}\). If \(q=1\), we are considering the Manhattan \((L_1)\) distance. If \(q=2\), the Euclidean \((L_2)\) distance is considered. A design \(X\) is called a maximim \(L_q\)-distance if it has the unique largest \(d_q(X)\) value.
Morris and Mitch (1995) and Jin et al. (2005) proposed the \(\phi_p\) criterion which is defined as \[ \phi_p = \left( \sum_{i=1}^{n-1} \sum_{j=i+1}^n d_q (x_i,x_j)^{-p} \right)^{1/p} \]
The \(\phi_p\) criterion is asymptotically equivalent to the Maximin distance criterion as \(p \rightarrow \infty\). In practice \(p=15\) often suffices.
Maximum Projection Criterion
Joseph et al (2015) proposed the maximum projection LHDs that consider designs’ space-filling properties in all possible dimensional spaces. Such designs minimize the maximum projection criterion, which is defined as
\[ \underset{X}{\min} \psi(X) = \left( \frac{1}{{n \choose 2}} \sum_{i=1}^{n-1} \sum_{j=i+1}^n \frac{1}{ \prod_{l=1}^k (x_{il}-x_{jl})^2} \right)^{1/k} \]
We can wee that any two design points should be apart from each other in any projection to minimize the value of \(\psi(x)\)
Orthogonality Criteria
Two major correlation-based criteria to measure designs’ orthogonality is the average absolute correlation criterion and the maximum absolute correlation
\[ ave(|q|) = \frac{2 \sum_{i=1}^{k-1} \sum_{j=i+1}^k |q_{ij}|}{k(k-1)} \quad \text{and} \quad \max |q| = \underset{i,j}{\max} |q_{ij}| \]
where \(q_{ij}\) is the correlation between the \(i\)th and \(j\)th columns of the design matrix \(X\). Orthogonal design have \(ave(|q|)=0\) and \(\max|q|=0\), which may not exist for all design sizes. Designs with smaller \(ave(|q|)\) or \(\max|q|\) are generally preferred in practice.
Lets start by generating a random centered LHD with 5 rows and 3 columns
X = pyLHD.LatinHypercube(size = (5,3),scramble=False)
Xarray([[0.1, 0.5, 0.5],
[0.5, 0.1, 0.9],
[0.9, 0.3, 0.1],
[0.3, 0.9, 0.7],
[0.7, 0.7, 0.3]])
We evaluate the above design with the different optimamlity criteria described earlier:
The maximin distance criterion (Manhattan)
pyLHD.phi_p(X,p=15,q=1) # using default parameters1.312360649138747
The maximin distance criterion (Euclidean)
pyLHD.phi_p(X,p=10,q=2) # different p used than above2.210980529249712
The average absolute correlation
pyLHD.AvgAbsCor(X)0.3333333333333334
The maximum absolute correlation
pyLHD.MaxAbsCor(X)0.6
The maximum projection criterion
pyLHD.MaxProCriterion(X)10.757357196557185
We can apply Williams transformation on X defined as: \[ W(x) = \begin{cases} 2x & 0 \leq x \leq N/2 \\ 2(N-x)-1 & N/2 \leq x < N \end{cases} \]
W_x = pyLHD.WilliamsTransform(X)
W_xarray([[0. , 0.8, 0.8],
[0.8, 0. , 1.6],
[1.6, 0.4, 0. ],
[0.4, 1.6, 1.2],
[1.2, 1.2, 0.4]])
Lets evaluate the new transformed design
pyLHD.phi_p(W_x)0.6561803245693735
The \(\phi_p\) value of transformed \(W_x\) is smaller than the original design \(X\)
2. Algebraic Construction Functions
The algebraic construction methods are demonstrated in the table below
| Ye98 | Cioppa07 | Sun10 | Tang93 | Lin09 | Butler01 | |
|---|---|---|---|---|---|---|
| Run # \(n\) | \(2^m +1\) | \(2^m +1\) | \(r2^{m +1}\) or \(r2^{m +1} +1\) | \(n\) | \(n^2\) | \(n\) |
| Factor # \(k\) | \(2m-2\) | \(m + {m-1 \choose 2}\) | \(2^c\) | \(m\) | \(2fp\) | \(k \leq n-1\) |
| Note | \(m\) is a positive integer \(m\geq 2\) | \(m\) is a positive integer \(m\geq 2\) | \(r\) and \(c\) are positive integers | \(n\) and \(m\) are from \(OA(n,m,s,r)\) | \(n^2,2f\) and \(p\) are from \(OA(n^2,2f,n,2)\) and \(OLHD(n,p)\) | \(n\) is an odd prime number |
For theoretical details on the construction methods, a good overview is Section 4.2: Algebraic Constuctions for Orthogonal LHDs from Musings about Constructions of Efficient Latin Hypercube Designs with Flexible Run-sizes
We start by implementing Ye 1998 construction, the resulting desig will have \(2^m+1\) runs and \(2m-2\) factors
Ye98 = pyLHD.OLHD_Ye98(m=4)
Ye98array([[ 2., -6., -5., -1., 8., 3.],
[ 6., 2., -7., -8., -1., -4.],
[ 7., -5., 6., -4., -3., 8.],
[ 5., 7., 2., -3., 4., -1.],
[ 3., -4., -1., 5., 7., -2.],
[ 4., 3., -8., 7., -5., 6.],
[ 8., -1., 4., 6., -2., -7.],
[ 1., 8., 3., 2., 6., 5.],
[ 0., 0., 0., 0., 0., 0.],
[-2., 6., 5., 1., -8., -3.],
[-6., -2., 7., 8., 1., 4.],
[-7., 5., -6., 4., 3., -8.],
[-5., -7., -2., 3., -4., 1.],
[-3., 4., 1., -5., -7., 2.],
[-4., -3., 8., -7., 5., -6.],
[-8., 1., -4., -6., 2., 7.],
[-1., -8., -3., -2., -6., -5.]])
pyLHD.MaxAbsCor(Ye98) # column-wise correlation are 00.0
Cioppa and Lucas 2007 construction, the resulting design will be a \(2^m+1\) by \(m+ {m-1 \choose 2}\) orthogonal LHD. Note \(m \geq 2\)
Cioppa07 = pyLHD.OLHD_Cioppa07(m=3)
Cioppa07array([[ 1., -2., -4., 3.],
[ 2., 1., -3., -4.],
[ 3., -4., 2., -1.],
[ 4., 3., 1., 2.],
[ 0., 0., 0., 0.],
[-1., 2., 4., -3.],
[-2., -1., 3., 4.],
[-3., 4., -2., 1.],
[-4., -3., -1., -2.]])
pyLHD.MaxAbsCor(Cioppa07) # column-wise correlation are 00.0
Sun et al. 2010 construction, the resulting design will be \(r2^{c+1}\) by \(2^c\) if type=‘even’. If type=‘odd’ the resulting design will be \(r2^{c+1} + 1\) by \(2^c\), where \(r\) and \(c\) are positive integers.
Sun10_odd = pyLHD.OLHD_Sun10(C=2,r=2,type='odd')
Sun10_oddarray([[ 1., 2., 3., 4.],
[ 2., -1., -4., 3.],
[ 3., 4., -1., -2.],
[ 4., -3., 2., -1.],
[ 5., 6., 7., 8.],
[ 6., -5., -8., 7.],
[ 7., 8., -5., -6.],
[ 8., -7., 6., -5.],
[ 0., 0., 0., 0.],
[-1., -2., -3., -4.],
[-2., 1., 4., -3.],
[-3., -4., 1., 2.],
[-4., 3., -2., 1.],
[-5., -6., -7., -8.],
[-6., 5., 8., -7.],
[-7., -8., 5., 6.],
[-8., 7., -6., 5.]])
Sun10_even = pyLHD.OLHD_Sun10(C=2,r=2,type='even')
Sun10_evenarray([[ 0.5, 1.5, 2.5, 3.5],
[ 1.5, -0.5, -3.5, 2.5],
[ 2.5, 3.5, -0.5, -1.5],
[ 3.5, -2.5, 1.5, -0.5],
[ 4.5, 5.5, 6.5, 7.5],
[ 5.5, -4.5, -7.5, 6.5],
[ 6.5, 7.5, -4.5, -5.5],
[ 7.5, -6.5, 5.5, -4.5],
[-0.5, -1.5, -2.5, -3.5],
[-1.5, 0.5, 3.5, -2.5],
[-2.5, -3.5, 0.5, 1.5],
[-3.5, 2.5, -1.5, 0.5],
[-4.5, -5.5, -6.5, -7.5],
[-5.5, 4.5, 7.5, -6.5],
[-6.5, -7.5, 4.5, 5.5],
[-7.5, 6.5, -5.5, 4.5]])
Line et al. 2009 construction, the resulting design will be \(n^2\) by \(2fp\). This is obtained by using a \(n\) by \(p\) orthogonal LHD with a \(n^2\) by \(2f\) strength 2 and level \(n\) orthogonal array.
Start by generating an orthogonal LHD
OLHD_example = pyLHD.OLHD_Cioppa07(m=2)Next, create an orthogonal array with 25 rows, 6 columns, 5 levels, and strength 2 OA(25,6,5,2)
import numpy as np
OA_example = np.array([[2,2,2,2,2,1],[2,1,5,4,3,5],
[3,2,1,5,4,5],[1,5,4,3,2,5],
[4,1,3,5,2,3],[1,2,3,4,5,2],
[1,3,5,2,4,3],[1,1,1,1,1,1],
[4,3,2,1,5,5],[5,5,5,5,5,1],
[4,4,4,4,4,1],[3,1,4,2,5,4],
[3,3,3,3,3,1],[3,5,2,4,1,3],
[3,4,5,1,2,2],[5,4,3,2,1,5],
[2,3,4,5,1,2],[2,5,3,1,4,4],
[1,4,2,5,3,4],[4,2,5,3,1,4],
[2,4,1,3,5,3],[5,3,1,4,2,4],
[5,2,4,1,3,3],[5,1,2,3,4,2],
[4,5,1,2,3,2] ])Now using Lin at al. 2009 construction, we couple OLHD and OA to obtain
Lin09 = pyLHD.OLHD_Lin09(OLHD=OLHD_example,OA=OA_example)
Lin09array([[ 12., -8., 12., -8., 7., -9., 6., -4., 6., -4., -9.,
-7.],
[ 7., -9., -7., 9., -10., -2., -9., -7., 9., 7., -5.,
-1.],
[ 10., 2., -9., -7., -11., 3., 5., 1., -7., 9., -3.,
-11.],
[ -9., -7., -1., 5., -8., -12., -7., 9., 2., -10., -4.,
-6.],
[ 4., 6., -10., -2., 2., -10., -8., -12., -5., -1., 1.,
-5.],
[ 11., -3., -5., -1., 8., 12., 3., 11., 10., 2., 4.,
6.],
[ 1., -5., 8., 12., -1., 5., -2., 10., 4., 6., 2.,
-10.],
[ 6., -4., 6., -4., 6., -4., -12., 8., -12., 8., -12.,
8.],
[ -1., 5., 7., -9., -12., 8., 2., -10., -9., -7., -6.,
4.],
[-12., 8., -12., 8., 3., 11., -6., 4., -6., 4., -11.,
3.],
[ -6., 4., -6., 4., 4., 6., 12., -8., 12., -8., -8.,
-12.],
[ 5., 1., 9., 7., -7., 9., -10., -2., 7., -9., 9.,
7.],
[ 0., 0., 0., 0., 5., 1., 0., 0., 0., 0., -10.,
-2.],
[-10., -2., -3., -11., 1., -5., -5., -1., 11., -3., -2.,
10.],
[ -5., -1., 3., 11., 12., -8., 10., 2., -11., 3., 6.,
-4.],
[ -7., 9., 10., 2., -9., -7., 9., 7., 5., 1., -7.,
9.],
[ 2., -10., -11., 3., 11., -3., 1., -5., -3., -11., 3.,
11.],
[ -8., -12., 5., 1., -6., 4., -4., -6., -10., -2., 12.,
-8.],
[ -4., -6., -8., -12., -5., -1., 8., 12., -4., -6., 10.,
2.],
[ 9., 7., -2., 10., -4., -6., 7., -9., -1., 5., 8.,
12.],
[ -3., -11., 1., -5., -2., 10., 11., -3., -2., 10., -1.,
5.],
[ -2., 10., -4., -6., -3., -11., -1., 5., 8., 12., 11.,
-3.],
[ 8., 12., 4., 6., 0., 0., 4., 6., -8., -12., 0.,
0.],
[ 3., 11., 2., -10., 9., 7., -11., 3., 1., -5., 7.,
-9.],
[-11., 3., 11., -3., 10., 2., -3., -11., 3., 11., 5.,
1.]])
We can convert an orthogonal array into a LHD using the function OA2LHD. Consider the earlier OA_example with 25 rows and 6 columns.
pyLHD.OA2LHD(OA_example)array([[10, 8, 7, 8, 6, 1],
[ 9, 1, 22, 17, 14, 24],
[11, 6, 4, 23, 18, 22],
[ 4, 21, 17, 11, 9, 21],
[19, 2, 13, 25, 7, 13],
[ 5, 9, 11, 16, 23, 8],
[ 3, 11, 24, 10, 17, 14],
[ 2, 5, 1, 4, 2, 2],
[20, 13, 9, 5, 22, 23],
[25, 25, 25, 22, 21, 5],
[17, 18, 19, 20, 20, 4],
[13, 4, 16, 7, 24, 20],
[15, 12, 15, 14, 13, 3],
[14, 22, 8, 18, 5, 11],
[12, 19, 23, 2, 8, 6],
[23, 16, 14, 6, 4, 25],
[ 7, 15, 20, 21, 3, 10],
[ 6, 24, 12, 1, 19, 19],
[ 1, 20, 6, 24, 15, 18],
[16, 10, 21, 12, 1, 17],
[ 8, 17, 2, 13, 25, 12],
[22, 14, 5, 19, 10, 16],
[21, 7, 18, 3, 11, 15],
[24, 3, 10, 15, 16, 9],
[18, 23, 3, 9, 12, 7]])
Lastly, we consider Butler 2001 construction by generating a \(n\) by \(k\) OLHD
Butler01 = pyLHD.OLHD_Butler01(size = (11,5))
Butler01 array([[ 5., 1., 3., 2., 4.],
[ 8., 7., 11., 3., 2.],
[ 3., 10., 4., 7., 1.],
[10., 4., 5., 11., 3.],
[ 1., 3., 10., 8., 5.],
[11., 9., 2., 4., 7.],
[ 2., 8., 7., 1., 9.],
[ 9., 2., 8., 5., 11.],
[ 4., 5., 1., 9., 10.],
[ 7., 11., 9., 10., 8.],
[ 6., 6., 6., 6., 6.]])