Model matrices for mixed-level orthogonal arrays
================================================


For mixed-level orthogonal arrays, we generate model matrices using Helmert contrasts.
First, we define an example array and calculate the Helmert contrasts for 2 and 3 levels.

The Helmert contrasts for a given number of levels can be generated with :meth:`~oapackage.oahelper.helmert_contrasts`.
The rows of the array generated by the method correspond to the levels.
:meth:`oapackage.oahelper.helmert_contrasts`. The contrasts are orthogonal and normalized.

.. testsetup::
   
    import numpy as np
    np.set_printoptions(precision=3, suppress=True)
    import oapackage
   
.. admonition:: Define mixed-level array and calculate Helmert contrasts 


  .. doctest:: 
   
    >>> array = oapackage.exampleArray(54,1)
    exampleArray 54: root array in OA(6,2,3^1 2^1)
    >>> array.showarray()
    array:
      0   0
      0   1
      1   0
      1   1
      2   0
      2   1
    >>> hc3 = oapackage.oahelper.helmert_contrasts(3)
    >>> hc2 = oapackage.oahelper.helmert_contrasts(2)
    >>> print(hc3)
    [[-1.225 -0.707]
     [ 1.225 -0.707]
     [ 0.     1.414]]
    >>> print(hc2)
    [[-1.]
     [ 1.]]    
    >>> # the Helmert contrasts are orthognal and normalized 
    >>> print(hc3.T.dot(hc3))
    [[3. 0.]
     [0. 3.]]
    >>> print(hc2.T.dot(hc2))
    [[2.]]


The main effects for a mixel-level array are created by replacing each level in the array by the corresponding Helmert contrast

.. admonition:: Intercept and main effects 

  .. doctest:: 
  
    >>> X=oapackage.array2modelmatrix(array, 'main')
    >>> print(X)
    [[ 1.    -1.225 -0.707 -1.   ]
     [ 1.    -1.225 -0.707  1.   ]
     [ 1.     1.225 -0.707 -1.   ]
     [ 1.     1.225 -0.707  1.   ]
     [ 1.     0.     1.414 -1.   ]
     [ 1.     0.     1.414  1.   ]]
         
Note the Helmert contrasts for the 2-level column are +1 and -1. So the calculation for mixel-level arrays for a 2-level array
yields the same results as the direct calculation for a 2-level design.
         
The interaction effects are created by determining for all pair-wise combinations of columns the 
product between the elements of the contrast vectors.
For this array, there is only one column combination: one 3-level factor and one 2-level factor.
The number of columns in the interaction part is therefore :math:`(3-1)(2-1) = 2`.
         
.. admonition:: Interaction model and information matrix

  .. doctest::    

    >>> X=oapackage.array2modelmatrix(array, 'interaction')
    >>> print(X)
    [[ 1.    -1.225 -0.707 -1.     1.225  0.707]
     [ 1.    -1.225 -0.707  1.    -1.225 -0.707]
     [ 1.     1.225 -0.707 -1.    -1.225  0.707]
     [ 1.     1.225 -0.707  1.     1.225 -0.707]
     [ 1.     0.     1.414 -1.    -0.    -1.414]
     [ 1.     0.     1.414  1.     0.     1.414]]    
    >>> M=X.T.dot(X)
    >>> print(M)
    [[6. 0. 0. 0. 0. 0.]
     [0. 6. 0. 0. 0. 0.]
     [0. 0. 6. 0. 0. 0.]
     [0. 0. 0. 6. 0. 0.]
     [0. 0. 0. 0. 6. 0.]
     [0. 0. 0. 0. 0. 6.]]    
