Properties of designs
=====================

This section shows the structural and statistical properties of the
orthogonal arrays, conference designs and D-efficient designs generated by
the Orthogonal Array package. The properties of the arrays and designs are
calculated using the :cpp:class:`array_link` 
object or functions from the package.

Definitions of arrays and designs
---------------------------------

Before introducing the structural and statistical properties,
we define orthogonal arrays, conference designs and D-efficient designs:

An orthogonal array (OA) of strength :math:`{t}`, :math:`{N}` runs and
:math:`{n}` factors at :math:`{s}` levels is an :math:`{N}\times {n}`
array of symbols :math:`0,
\ldots,({s}-1)`, such that for every subset of :math:`{t}` columns,
every :math:`{t}`-tuple occurs equally
often :cite:`Rao1947`. The set of all strength-:math:`{t}` OAs with 
:math:`{N}` runs and :math:`{n}` factors at :math:`{s}` levels 
is denoted by :math:`{\operatorname{OA}({N}; {t}; {s}^{n})}`. If :math:`{s=2}`, the OA is called a two-level OA and the set of all strength-:math:`{t}` two-level OAs with 
:math:`{N}` runs and :math:`{n}` factors is denoted as :math:`{\operatorname{OA}({N}; {t}; {2}^{n})}`.  

For :math:`{N}` even, a conference design :cite:`Schoen2018dsd` :math:`C` is 
an :math:`{N}\times {n}` array which satisfies :math:`{C}^{T}C = (n-1) I_{n}`,
with :math:`{C}_{ii} = 0` and :math:`{C}_{ij} \in \{-1,1\}`, for 
:math:`{i} \neq {j}` and :math:`{i}, {j} = 1, \ldots, n`. A :math:`{N}\times {N}` conference design :math:`E` such that :math:`E{E}^{T} = (n-1) I_{n}` is called a conference matrix; see :cite:`Elster1995`, :cite:`Colbourn2006` and :cite:`Xiao2012`. 

A D-optimal design :cite:`Donev2007` (:math:`X`) is an :math:`{N}\times {n}` array 
which maximizes the :math:`D`-efficiency, defined as :math:`{(\operatorname{det}({X}^{T}_{M}{X}^{\phantom{T}}_{M})^{1/p})/N}`,
for a given :math:`{N}\times {p}` model matrix :math:`{X}_{M}` (for details see :ref:`Model matrices`).
The Orthogonal Array package uses a coordinate-exchange algorithm to generate designs that optimize the :math:`D`-efficiency. Since there is no guarantee that the resulting designs have the largest possible :math:`D`-efficiency, we refer to them as D-efficient designs
in this documentation. An orthogonal array is called D-optimal orthogonal array if it provides the largest :math:`D`-efficiency among all comparable orthogonal arrays.

Structural properties of an array
----------------------------------

The OApackage can calculate the rank of an array, defined as the maximum number of linearly independent column
or row vectors in the array. The rank of an array is useful for several other functions in the package.
For two-level arrays, the OApackage can also check if the arrays are foldover arrays.
A two-level array is called a foldover array if half of its runs are mirror images of the other half,
in the sense that the factor levels are changed from 0 to 1 and from 1 to 0. 

For example, to calculate the rank of a two-level orthogonal array and determine whether
the array is a foldover array, one can use
the methods :cpp:func:`array_link::rank` and :cpp:func:`array_link::foldover`:

.. testsetup::
   
   import oapackage
   
.. admonition:: Calculate rank of array and test for foldover 

  .. doctest:: 
   
    >>> array = oapackage.exampleArray(1) # Select an example two-level orthogonal array
    >>> array.showarray() # Show the two-level orthogonal array
    array:
      0   0   0   0   0
      0   0   0   0   0
      0   0   0   1   1
      0   0   1   0   1
      0   1   0   1   0
      0   1   1   0   0
      0   1   1   1   1
      0   1   1   1   1
      1   0   0   1   1
      1   0   1   0   1
      1   0   1   1   0
      1   0   1   1   0
      1   1   0   0   1
      1   1   0   0   1
      1   1   0   1   0
      1   1   1   0   0
     >>> print(array.rank()) # Calculate the rank of the array
     5 
     >>> print(array.foldover()) # Determine if the array is foldover
     False

Other structural properties such as whether an array involves two levels or is symetric can be found in the
documentation of :cpp:class:`array_link`, which shows the full set of methods available.

Model matrices
--------------

For orthogonal arrays and conference designs, the OApackage can 
calculate different model matrices. The model matrices available depend
on the type of array and design.

.. topic:: Model matrices for two-level orthogonal arrays
  :name: modelmattwoleveloa

   For two-level orthogonal arrays, the levels of the array are first coded 
   according to the map :math:`{0 \rightarrow -1}` and :math:`{1 \rightarrow +1}`. 
   The coded matrix is referred to as the design matrix.
   The main effect contrast vectors are given by the columns in the design 
   matrix. The contrast vectors associated to the two-factor interactions 
   are calculated by taking products between two different columns in the 
   design matrix. The model matrix consists of the intercept column 
   (i.e. a columns of ones) and the contrast vectors associated to 
   the main effects and, optionally, the two-factor interactions. 

.. topic:: Model matrices for conference designs
  :name: modelmatconference

   The model matrix for a conference design consists 
   of the intercept column (i.e. a columns of ones) and the contrast 
   vectors associated to the main effects and, optionally, the 
   second-order effects (two-factor interactions and quadratic effects). 
   The main effect contrast vectors are given by the columns in the
   conference design. The contrast vectors associated to the second-order 
   effects are calculated by taking products between two columns in the 
   conference design. 

.. topic:: Model matrices for mixed-level orthogonal arrays
  :name: modelmatmixedleveloa

   For mixed-level orthogonal arrays, the main effect contrast vectors
   are defined by the Helmert contrasts. The contrast vectors associated 
   to the two-factor interactions 
   are calculated by taking products between two different columns in the 
   matrix containing the Helmert contrasts of the array; see 
   :ref:`Model matrices for mixed-level orthogonal arrays` for details. 
   The model matrix consists of the intercept column 
   (i.e. a columns of ones) and the contrast vectors associated to 
   the main effects and, optionally, the two-factor interactions.    

An example on how to generate an interaction model matrix for a 
two-level orthogonal array is shown below.

.. admonition:: Calculate interaction effects model matrix 

  .. doctest:: 

    >>> array=oapackage.exampleArray(0,1)
    exampleArray 0: array in OA(8,2, 2^2)
    >>> array.showarray()
    array:
      0   0
      0   0
      0   1
      0   1
      1   0
      1   0
      1   1
      1   1
    >>> M=oapackage.array2modelmatrix(array, 'i')
    >>> print(M)
    [[ 1. -1. -1.  1.]
     [ 1. -1. -1.  1.]
     [ 1. -1.  1. -1.]
     [ 1. -1.  1. -1.]
     [ 1.  1. -1. -1.]
     [ 1.  1. -1. -1.]
     [ 1.  1.  1.  1.]
     [ 1.  1.  1.  1.]]    


Statistical properties of orthogonal arrays
-------------------------------------------

Orthogonal arrays are commonly evaluated in terms of their generalized wordlength pattern :cite:`Xu2001` (GWLP).
Two-level OAs are also commonly evaluated in terms of their :math:`{J}_{k}`-characteristics
and :math:`F`-vectors :cite:`Deng1999`. The OApackage can calculate all these statistical criteria: :cpp:func:`array_link::GWLP`, :cpp:func:`array_link::Fvalues`, :cpp:func:`array_link::Jcharacteristics`.

The following example shows how to calculate the GWLP, :math:`{F}_{k}`-values and
:math:`{J}_{k}`-characteristics from an :cpp:class:`array_link` object:

.. admonition:: Calculate GWLP and F-values 

  .. doctest:: 
     
     >>> al=oapackage.exampleArray(1,1) # Select an example array
     exampleArray 1: array 3 in OA(16, 2, 2^5)
     >>> gwlp = al.GWLP() # Calculate its generalized word length pattern
     >>> print('GWLP: %s'% str(gwlp) )
     GWLP: (1.0, 0.0, 0.0, 1.0, 1.0, 0.0)
     >>> print('F3-value: %s' % str(al.Fvalues(3))) # Calculate the F_3 values
     F3-value: (4, 6)
     >>> print('F4-value: %s' % str(al.Fvalues(4))) # Calculate the F_3 values
     F4-value: (1, 4)
     >>> print('J3-characteristics: %s' % str(al.Jcharacteristics(3))) # Calculate the J_3-characteristics
     J3-characteristics: (-8, -8, 0, 0, 0, -8, 0, -8, 0, 0)

We now briefly mention some technical details of the :math:`{J}_{k}`-characteristics, the :math:`{F}_{k}`-values and the GWLP.

.. topic:: :math:`{J}_{k}`-characteristics
 :name: Jcharacteristics

   To calculate :math:`{J}_{k}`-characteristics of a two-level OA, the OApackage codes the levels of the array as :math:`-1` and :math:`+1`. To this end, the package uses the mapping :math:`{0 \rightarrow -1}` and :math:`{1 \rightarrow +1}`. Let :math:`D` be an :math:`{N}\times {n}` with coded levels :math:`-1` and :math:`+1`. For :math:`{S} = \{l_1, \ldots, l_k\}`, a subset of :math:`k` different factors of :math:`D = (d_{il})`, define 

   .. math::
       j_k (S; D) = \sum_{i = 1}^{N} d_{i l_1} \cdots d_{i l_k}. 

   The :math:`{|{j}_{k} (S; D)|}` values are called the :math:`{J}_{k}`-characteristics, which necessarily equal :math:`N - 4q` :cite:`Deng2002`, where :math:`{q} \leq N/4` is a non-negative integer. 

.. topic:: :math:`{F}_{k}`-values
  :name: Fvalues

    The :math:`{F}_{k}`-vector collects the frequencies of all the :math:`{J}_{k}`-characteristics.
    More specifically, the vector :math:`{F}_{k} = (f_{k1}, \ldots, f_{kv})`, where :math:`v = N/4` and
    :math:`f_{ku}` denotes the frequency of the :math:`{J}_{k}`-characteristics which are equal
    to :math:`4(v + 1 - u)`. When calculating an :math:`{F}_{k}`-vector, the OApackage shows only
    the vector :math:`(f_{k1}, \ldots, f_{kv})`, whose elements are referred to
    as the :math:`{F}_{k}`-values. 

.. topic:: Generalized word length pattern
 :name: GWLPname

   Consider an OA, :math:`{D}`, of strength :math:`{t}` with :math:`{N}` runs and :math:`{n}` factors at :math:`{s}` levels. Let :math:`{X_0}` be a column of ones, :math:`{X_1}` the matrix involving the contrast vectors associated with the main effects, and :math:`{X_j}` the matrix involving the contrast vectors associated with the :math:`{j}`-factor interactions, :math:`{j \geq 2}`. We assume that the column vectors in :math:`{X_1}` are normalized so that they have the same length :math:`{\sqrt{N}}`. For :math:`{j = 0, \ldots, n}`, let 

   .. math::
       A_j (D) = N^{-2} 1_{N}^{T} X_{j} X_{j}^{T} 1_{N}^{\phantom{T}}, 

   where :math:`1_{N}` denotes the :math:`N \times 1` column of ones. The value of :math:`{A}_{j}(D)` is invariant to the choice of the orthonormal contrasts used; see :cite:`Xu2001` for details. The vector :math:`{(A_0(D), \ldots, A_n (D) )}` is called the generalized word length pattern (GWLP). To increase the speed of the computations for the GWLP, the OApackage uses the distance distribution and the MacWilliams identities as in :cite:`Xu2001` and :cite:`Xu09algorithmicconstruction`.



Optimality criteria for D-efficient designs
-------------------------------------------

In :cite:`EendebakSO`, D-efficient designs for the model including the intercept, all main effects and all two-factor interactions are generated. The OApackage provides functionality to compute the optimality criteria used to generate the D-efficient designs in :cite:`EendebakSO`.
Moreover, the package can calculate the well-known :math:`A`- and :math:`E`-optimality criteria from the literature
on Optimal Experimental Design :cite:`Donev2007`.
The functions to perform the calulcations are
:cpp:func:`array_link::Defficiency`,
:cpp:func:`array_link::DsEfficiency`,
:cpp:func:`array_link::Aefficiency`,
:cpp:func:`array_link::Eefficiency`.

The following example shows how to calculate the :math:`D`-, :math:`{D}_{s}`-, :math:`A`- and :math:`E`-efficiency for a design that permits the estimation of the interaction model.

.. admonition:: Calculate optimality criteria for D-efficient designs 

  .. doctest:: 
     
     # Select an array that can estimate the interaction model
     >>> al = oapackage.exampleArray(11, 1)
     exampleArray 11: D-optimal array in OA(44, 2^8)
     >>> print('D-efficiency: %.4f' % al.Defficiency())
     D-efficiency: 0.8879
     >>> print('Ds-efficiency (Eendebak and Schoen, 2017): %.4f' % al.DsEfficiency()) 
     Ds-efficiency (Eendebak and Schoen, 2017): 0.8059
     >>> print('A-efficiency for the interaction model: %.4f' % al.Aefficiency())
     A-efficiency for the interaction model: 0.7906
     >>> print('E-efficiency for the interaction model: %.4f' % al.Eefficiency())  
     E-efficiency for the interaction model: 0.3602

.. topic:: Calculation of :math:`D`-, :math:`A`- and :math:`E`-efficiency
  :name: DAE

   Let :math:`{X}` be again the :math:`{N}\times {p}` interaction model matrix (see section `Model matrices`_) consisting of a column of ones and the contrast vectors associated to the main and two-factor interactions of :math:`{n}` factors, where :math:`{p = 1 + n + (n)(n-1)/2}`. The :math:`D`-, :math:`A`- and :math:`E`-efficiency are calculated using the eigenvalues of the singular-value decomposition (SVD) of :math:`{X}`. To calculate the rank of a matrix, the lower-upper (LU) decomposition, as implemented in the Eigen package :cite:`eigenweb`, is used.

   Let :math:`\lambda_1, \ldots, \lambda_p` be the eigenvalues of the SVD of :math:`{X}`. The OApackage calculates the :math:`D`-, :math:`A`- and :math:`E`-efficiency of a design :math:`D` as follows:

   .. math::
    
       {D_{\text{eff}}(D)} = (\prod_j \lambda_j)^{1/p} / N \label{formula:Defficiency}, \\
       {A_{\text{eff}}(D)} = N (\sum_j \lambda_j^{-1})/m \label{formula:VIF}, \\ 
       {E_{\text{eff}}(D)} = \min_j \lambda_j. \label{formula:E-efficiency}

.. topic:: :math:`D_s`-efficiency and :math:`D_1`-efficiency
  :name: DS
   
   In :cite:`EendebakSO`, the :math:`D_s`-efficiency is used to assess the joint precision of the main effects in the
   interaction model. Let the interaction model matrix :math:`{X}` be split into :math:`{X_{1}}`, containing the contrast
   vectors associated with the main effects only, and :math:`{X_{02}}`, containing the intercept column and the contrast
   vectors associated to the two-factor interactions. The :math:`D_{s}`-criterion of a design :math:`D` is defined as 

   .. math::
    
       {D_{s,\text{crit}}(D)} = \operatorname{det}(X^{T}X) / \operatorname{det}(X_{02}^{T} X_{02}^{\phantom{T}}), \label{formula:Dsefficiency}

   where :math:`{X_{02}}` is necessarily of full rank. Similar to the calculations of the :math:`D`-efficiency, the
   OApackage calculates the :math:`D_{s}`-criterion using the eigenvalues of the SVD of the matrices :math:`{X}` and :math:`{X_{01}}`.
   Finally, the package calculates the :math:`D_{s}`-efficiency of :math:`D` as :math:`D_{s,\text{eff}}(A) = D_{s,\text{crit}}(A)^{1/m}`, where :math:`m` is the number of factors. 
   
   In a similar way the :math:`D_{1}`-efficiency of a design :math:`{A}` with :math:`n` factors and model matrix of intercept and main effects :math:`{X_{01}}`,  is defined as

   .. math::
    
       D_{1,\text{eff}}(A) = ( \operatorname{det}((X_{01})^{T}(X_{01}) )^{1/(n+1)}  \label{formula:D1efficiency}
   
Projection capacities
---------------------

Other relevant statistical criteria to evaluate a two-level design with :math:`N` runs and :math:`k` factors
include the so-called projection estimation capacity (PEC) and
projection information capacity (PIC) :cite:`Loeppky2007`. These criteria focus on the projections of the two-level design onto a smaller number of factors. More specifically, the PEC and PIC summarize the performance of all the :math:`N`-run subdesigns with :math:`l \leq k` factors in terms of the capacity to estimate the interaction model and the :math:`D`-efficiency for this model, respectively. 

The PEC and PIC are based on the so-called PEC and PIC sequences, which are formally defined as follows.
Let :math:`PEC_{l}` denote the proportion of :math:`N`-run :math:`l`-factor subdesigns that permit the estimation of
the interaction model in :math:`l` factors, that is, the model including the
intercept, all :math:`l` main effects and all :math:`l(l-1)/2` two-factor
interactions. The PEC sequence is the vector :math:`(PEC_{1}, PEC_{2}, \ldots, PEC_{k})`. Now, let :math:`PIC_{l}` denote the average :math:`D`-efficiency for the interaction model in :math:`l` factors accross all :math:`N`-run :math:`l`-factor subdesigns. The PIC sequence is the vector :math:`(PIC_{1}, PIC_{2}, \ldots, PIC_{k})`.
The OApackage can calculate the PEC and PIC sequences of two-level designs with
:cpp:func:`PECsequence` and :cpp:func:`PICsequence`, respectively.
    

The following example shows how to compute the PEC and PIC sequences of a two-level orthogonal array using the OApackage.

.. admonition:: Calculate the PEC and PIC sequences

  .. doctest:: 
     
     >>> al=oapackage.exampleArray(1,1) 
     exampleArray 1: array 3 in OA(16, 2, 2^5)
     >>> PEC = al.PECsequence() 
     >>> print('PEC sequence: %s'% ','.join(['%.2f' % x for x in PEC]) )
     PEC sequence: 1.00,1.00,1.00,0.80,0.00
     >>> PIC = al.PICsequence() 
     >>> print('PIC sequence: %s'% ','.join(['%.2f' % x for x in PIC]) )
     PIC sequence: 1.00,1.00,0.95,0.66,0.00

Properties of conference designs
--------------------------------

In :cite:`Schoen2018dsd`, it is shown that the :math:`F_4` vector is useful for
classifying definitive screening designs :cite:`Xiao2012` that are generated by folding over
a conference design. To calculate the :math:`F_4` vector, we first need to compute
the :math:`J_4`-characteristics of the conference design. The calculations for 
the :math:`J_k`-characteristics of conference designs are similar as 
for orthogonal arrays; see :ref:`Statistical properties of orthogonal arrays`. 
Consider a definitive screening design constructed from an :math:`N`-run conference design with at least
four factors. The :math:`F_4` vector of this design collects 
the frequencies of the :math:`J_4`-characteristics of 
:math:`{2N − 8\lambda}` for :math:`{\lambda = 1,\ldots,N/4}` 
when :math:`N` is a multiple of 4, or :math:`{\lambda = 1, \ldots, (N − 2)/4}`
when :math:`N` is an odd multiple of 2.

Note: the :math:`J_4`-values of a definitive screening design generated by folding over a conference design
are twice the value of the :math:`J_4`-values of the conference design. The :math:`F_4` vector of
a conference design and the corresponding definitive screening design are equal.

.. admonition:: Calculate the F4 vector for a conference design

  .. doctest:: 

    >>> import oapackage
    >>> array=oapackage.exampleArray(47,1)
    exampleArray 47: third conference design in C(20,8)
    >>> F4=array.FvaluesConference(4)
    >>> print(F4)
    (0, 2, 4, 51, 13)
    >>> definitive_screening_design = oapackage.conference2DSD(array)

The individual :math:`J_k`-characteristics can be calculated with the method :cpp:func:`Jcharacteristics_conference`.
For conference designs, we can calculate the projection statistics using
:meth:`~oalib.conference.conferenceProjectionStatistics`.

.. admonition:: Calculate projection statistics for conference designs

  .. doctest:: 
   
    >>> array = oapackage.exampleArray(46, 1) 
    exampleArray 46: second conference design in C(20,8)
    >>> pec, pic, ppc = oapackage.conference.conferenceProjectionStatistics(array)
    >>> print('Projection estimation capacity for 4 columns: %.3f'  % pec)
    Projection estimation capacity for 4 columns: 0.986
    >>> J3 = oapackage.Jcharacteristics_conference(array, number_of_columns = 3)






    

    
