Type Hierarchy
===============

All samplers and distributions provided in this package are organized into a type hierarchy described as follows.

.. _sampleable:

Sampleable
-----------

The root of this type hierarchy is ``Sampleable``. The abstract type ``Sampleable`` subsumes any types of objects from which one can draw samples, which particularly includes *samplers* and *distributions*. Formally, ``Sampleable`` is defined as

.. code-block:: julia

    abstract Sampleable{F<:VariateForm,S<:ValueSupport}

It has two type parameters that define the kind of samples that can be drawn therefrom. 

- ``F <: VariateForm`` specifies the form of the variate, which can be one of the following:

    ================== ========================= ======================================
       **Type**           **A single sample**       **Multiple samples**
    ------------------ ------------------------- --------------------------------------
    ``Univariate``      a scalar number           A numeric array of arbitrary shape, 
                                                  each element being a sample
    ``Multivariate``    a numeric vector          A matrix, each column being a sample
    ``Matrixvariate``   a numeric matrix          An array of matrices, each element
                                                  being a sample matrix
    ================== ========================= ======================================  

- ``S <: ValueSupport`` specifies the support of sample elements, which can be either of the following:

    ================== ========================= ======================================
       **Type**           **Element type**           **Descriptions**
    ------------------ ------------------------- --------------------------------------
     ``Discrete``         ``Int``                   Samples take discrete values
     ``Continuous``       ``Float64``               Samples take continuous real values
    ================== ========================= ======================================

Multiple samples are often organized into an array, depending on the variate form.

The basic functionalities that a sampleable object provides is to *retrieve information about the samples it generates* and to *draw samples*. Particularly, the following functions are provided for sampleable objects:

.. function:: length(s)

    The length of each sample. 

    It always returns ``1`` when ``s`` is univariate.

.. function:: size(s)

    The size (i.e. shape) of each sample. 

    It always returns ``()`` when ``s`` is univariate, and ``(length(s),)`` when ``s`` is multivariate.

.. function:: nsamples(s, A)

    The number of samples contained in ``A``.

    See above for how multiple samples may be organized into a single array.

.. function:: eltype(s)

    The default element type of a sample. 

    This is the type of elements of the samples generated by the ``rand`` method. However, one can provide an array of different element types to store the samples using ``rand!``.

.. function:: rand(s)

    Generate one sample from ``s``.

.. function:: rand(s, n)

    Generate ``n`` samples from ``s``. The form of the returned object depends on the variate form of ``s``:

    - When ``s`` is univariate, it returns a vector of length ``n``.
    - When ``s`` is multivariate, it returns a matrix with ``n`` columns.
    - When ``s`` is matrix-variate, it returns an array, where each element is a sample matrix.

.. function:: rand!(s, A)

    Generate one or multiple samples from ``s`` to a pre-allocated array ``A``.

    ``A`` should be in the form as specified above. The rules are summarized as below:

    - When ``s`` is univariate, ``A`` can be an array of arbitrary shape. Each element of ``A`` will be overriden by one sample.

    - When ``s`` is multivariate, ``A`` can be a vector to store one sample, or a matrix with each column for a sample.

    - When ``s`` is matrix-variate, ``A`` can be a matrix to store one sample, or an array of matrices with each element for a sample matrix.


Distributions
--------------

We use `Distribution`, a subtype of `Sampleable` as defined below, to capture probabilistic distributions. In addition to being sampleable, a *distribution* typically comes with an explicit way to combine its domain, probability density functions, among many other quantities. 

.. code-block:: julia

    abstract Distribution{F<:VariateForm,S<:ValueSupport} <: Sampleable{F,S}

To simplify the use in practice, we introduce a series of type alias as follows:

.. code-block:: julia

    const UnivariateDistribution{S<:ValueSupport}   = Distribution{Univariate,S}
    const MultivariateDistribution{S<:ValueSupport} = Distribution{Multivariate,S}
    const MatrixDistribution{S<:ValueSupport}       = Distribution{Matrixvariate,S}
    const NonMatrixDistribution = Union{UnivariateDistribution, MultivariateDistribution}

    const DiscreteDistribution{F<:VariateForm}   = Distribution{F,Discrete}
    const ContinuousDistribution{F<:VariateForm} = Distribution{F,Continuous}

    const DiscreteUnivariateDistribution     = Distribution{Univariate,    Discrete}
    const ContinuousUnivariateDistribution   = Distribution{Univariate,    Continuous}
    const DiscreteMultivariateDistribution   = Distribution{Multivariate,  Discrete}
    const ContinuousMultivariateDistribution = Distribution{Multivariate,  Continuous}
    const DiscreteMatrixDistribution         = Distribution{Matrixvariate, Discrete}
    const ContinuousMatrixDistribution       = Distribution{Matrixvariate, Continuous}

All methods applicable to `Sampleable` also applies to `Distribution`. The API for distributions of different variate forms are different (refer to :ref:`univariates`, :ref:`multivariates`, and :ref:`matrix` for details).









