.. _matrices:

Matrices
###########

The matrices available for use in |popy| models are shown in :numref:`table_matrices`:-

.. _table_matrices:

.. list-table:: Matrices
    :header-rows: 1
    
    * - Name
      - Syntax
      
    * - :ref:`Constant Diagonal Matrix<const_diag_mat_dist>`
      - y_mat = [[x(1,1), x(2,2)]]
      
    * - :ref:`Constant Square Matrix <const_mat_dist>`
      - y_mat = [[x(1,1)], [x(2,1), x(2,2)]]
     
    * - :ref:`SPD Diagonal Matrix<pos_diag_mat_dist>`
      - y_mat ~ diag_matrix() [[x(1,1), x(2,2)]]
     
    * - :ref:`SPD Square Matrix<spd_mat_dist>`
      - y_mat ~ spd_matrix() [[x(1,1)], [x(1,2), x(2,2)]]
      
      
..  comment
    you can add other types of matrix potentially e.g banded, + other exotic types
    maybe sparse??!?

.. _const_diag_mat_dist:
    
Constant Diagonal Matrix
=============================

A constant nxn diagonal matrix is specified using the following syntax:-

.. code-block:: pyml

    y_mat = [[x(1,1), x(2,2), ... , x(n,n)]]

Where:-

* y_mat is a multi-element |fx| matrix label
* x(1,1) is the first element of the diagonal
* x(n,n) is the last element of the diagonal

.. note::  

    A diagonal matrix definition in |popy| starts with **double** opening square brackets '[[' and ends with matching **double** closing square brackets ']]'. 
     
    This is to distinguish from a :term:`list` which uses **single** square brackets.

An example diagonal matrix is shown below:-

.. code-block:: pyml

        f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] = [
                [ 0.1, 0.03, 0.09, 0.07, 0.05]
            ]
                       
Here a 5x5 matrix called |fx_isv_mat_builtin| is created with the following values:-

.. math::

    \begin{pmatrix}
    0.1     & 0.0      & 0.0      & 0.0   & 0.0  \\
    0.0     & 0.03     & 0.0      & 0.0   & 0.0  \\
    0.0     & 0.0      & 0.09     & 0.0   & 0.0  \\
    0.0     & 0.0      & 0.0      & 0.07  & 0.0  \\
    0.0     & 0.0      & 0.0      & 0.0   & 0.05 \\
    \end{pmatrix}
      
In the definition above the number of |fx| labels on the |lhs| must correspond to the number of list elements along the diagonal on the |rhs|. |ie| in this case they both contain 5 elements.


.. _const_mat_dist:
    
Constant Square Symmetric Matrix
====================================

A constant nxn square symmetric matrix is specified using the following syntax:-

.. code-block:: pyml

    y_mat = [
        [x(1,1)],
        [x(2,1), x(2,2)],
        ...
        [x(n,1), x(n,2) ... , x(n,n)]
    ]

Where:-

* y_mat is a multi-element |fx| matrix label
* x(1,1) is the first element of the diagonal
* x(i,j) defines the element of the ith row and jth column
* x(n,n) is the last element of the diagonal

The definition above just requires the lower triangular elements to be defined, because the matrix is assumed to be square and symmetric. 

An example of defining a square symmetric matrix is shown below:-

.. code-block:: pyml

        f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] = [
                [0.1],
                [0.01, 0.03],
                [0.01, -0.01, 0.09],
                [0.01, 0.02, 0.01, 0.07],
                [0.01, 0.02, 0.01, 0.01, 0.05],
            ]
          
The number of elements in each row of the matrix definition need to be [1,2,3,4,5], because the |fx| variable contains 5 elements.
            
Therefore a 5x5 matrix called |fx_isv_mat_builtin| is created with the following values:-
            
.. math::
            
    \begin{pmatrix}
    0.1      & 0.01      & 0.01     & 0.01  & 0.01 \\
    0.01     & 0.03      & -0.01    & 0.02  & 0.02 \\
    0.01     & -0.01     & 0.09     & 0.01  & 0.01 \\
    0.01     & 0.02      & 0.01     & 0.07  & 0.01 \\
    0.01     & 0.02      & 0.01     & 0.01  & 0.05 \\
    \end{pmatrix}
            
Note you can also define the same matrix as follows:-
            
.. code-block:: pyml

        f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] = [
                [0.1 , 0.01,  0.01, 0.01, 0.01],
                [0.01, 0.03, -0.01, 0.02, 0.02],
                [0.01, -0.01, 0.09, 0.01, 0.01],
                [0.01, 0.02,  0.01, 0.07, 0.01],
                [0.01, 0.02,  0.01, 0.01, 0.05],
            ]
            
However this format risks accidentally creating a non-symmetric matrix. 

In |popy|, you can only specify square symmetric matrices. If the input matrix is non-square |popy| will report an error. If the input matrix is square, but non-symmetric then |popy| will average the upper and lower triangles and output a warning. 

           
.. _pos_diag_mat_dist:
    
Positive Definite Diagonal Matrix
========================================

A positive definite nxn diagonal matrix for estimation is specified using the following syntax:-

.. code-block:: pyml

    y_mat ~ diag_matrix() [[x(1,1), x(2,2), ... , x(n,n)]]

Where:-

* y_mat is a multi-element |fx| matrix label
* x(1,1) is the first element of the diagonal
* x(n,n) is the last element of the diagonal

.. note::  

    The '~' notation means that 'y_mat' is a diagonal matrix to be estimated, and only initialised with the diagonal matrix specified in square brackets.


An example diagonal matrix for estimation in a :ref:`fit_script` is shown below:-

.. code-block:: pyml

        f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] ~ diag_matrix() [
                [ 0.1, 0.03, 0.09, 0.07, 0.05]
            ]
                       
Here a 5x5 matrix variable |fx_isv_mat_builtin| is initialised with the following values:-

.. math::

    \begin{pmatrix}
    0.1     & 0.0      & 0.0      & 0.0   & 0.0  \\
    0.0     & 0.03     & 0.0      & 0.0   & 0.0  \\
    0.0     & 0.0      & 0.09     & 0.0   & 0.0  \\
    0.0     & 0.0      & 0.0      & 0.07  & 0.0  \\
    0.0     & 0.0      & 0.0      & 0.0   & 0.05 \\
    \end{pmatrix}
      
In the fitting process the diagonal elements are constrained to be positive and the off diagonal terms are fixed to zero.

.. note::  

    This matrix is defined using the '~' notation, however it is |not| a true distribution in the sense that if you sample from the matrix distribution, only the current value is returned. This fact is only relevant for a :ref:`gen_script` or :ref:`mgen_script`. 
    
    
.. _spd_mat_dist:
    
Symmetric Positive Definite Matrix
================================================

A |spd| nxn square symmetric matrix for estimation is specified using the following syntax:-

.. code-block:: pyml

    y_mat ~ spd_matrix() [
        [x(1,1)],
        [x(2,1), x(2,2)],
        ...
        [x(n,1), x(n,2) ... , x(n,n)]
    ]

Where:-

* y_mat is a multi-element |fx| matrix label
* x(1,1) is the first element of the diagonal
* x(i,j) defines the element of the ith row and jth column
* x(n,n) is the last element of the diagonal

An example of defining a square symmetric matrix to be estimated is shown below:-

.. code-block:: pyml

        f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] ~ spd_matrix() [
                [0.1],
                [0.01, 0.03],
                [0.01, -0.01, 0.09],
                [0.01, 0.02, 0.01, 0.07],
                [0.01, 0.02, 0.01, 0.01, 0.05],
            ]
          
Here we are only defining the lower triangle elements, as the upper triangle elements are the same in a symmetric matrix. The number of elements in each row of the initial matrix definition needs to be [1,2,3,4,5], because the |fx| variable contains 5 elements.
            
We are declaring a 5x5 matrix called |fx_isv_mat_builtin|, that will be estimated by a :ref:`fit_script` and is constrained to be |spd|. The matrix is initialised with the following values:-
            
.. math::
            
    \begin{pmatrix}
    0.1      & 0.01      & 0.01     & 0.01  & 0.01 \\
    0.01     & 0.03      & -0.01    & 0.02  & 0.02 \\
    0.01     & -0.01     & 0.09     & 0.01  & 0.01 \\
    0.01     & 0.02      & 0.01     & 0.07  & 0.01 \\
    0.01     & 0.02      & 0.01     & 0.01  & 0.05 \\
    \end{pmatrix}

Note that the symmetric |spd_mat_dist| is initialised here by defining just the lower triangular elements (without loss of generality), but it can also be initialised by carefully defining all 5*5 elements. 
    
.. _mat_usage:

Covariance Matrix Usage
=================================

The most common use for matrices as defined above is to define a covariance matrix for a :ref:`mnorm_dist`. For example in a |effects| section:-

.. code-block:: pyml

    EFFECTS:
        ID: |
            r[KA,CL,V1,Q,V2] ~ mnorm(
                [0, 0, 0, 0, 0], f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] )

Here the matrix |fx_isv_mat_builtin|, can be defined in any of the ways specified above, |ie| constant/variable or square/diagonal. 

In the definition of |rx_vec_builtin|, the size of the variance matrix must agree with the size of the random vector and the mean vector of the |mnorm_dist|. See :ref:`mnorm_dist_re_example` for more information.
                    
