LHS-MDU
--------

Installation
============

You can install lhsmdu using pip::

    $ pip install lhsmdu
    
Alternatively, you can clone on github and then install the package locally::

    $ git clone https://github.com/sahilm89/lhsmdu
    $ cd lhsmdu
    $ python setup.py install --user   # for this user only.
    
or::
    
    $ pip install git+https://github.com/sahilm89/lhsmdu --user  
    
    
Basics
======

This is a package for generating latin hypercube samples with multi-dimensional uniformity.

To use, simply do::

    >>> import lhsmdu 
    >>> k = lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity 

This will generate a nested list with 2 variables, with 20 samples each.

To plot and see the difference between Monte Carlo and LHS-MDU sampling for a 2 dimensional system::

    >>> l = lhsmdu.createRandomStandardUniformMatrix(2, 20) # Monte Carlo sampling
    >>> k = lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity
    >>> k = np.array(k)
    >>> l = np.array(l)
    >>> import matplotlib.pyplot as plt 
    >>> fig = plt.figure() 
    >>> ax = fig.gca()
    >>> ax.set_xticks(numpy.arange(0,1,0.1))
    >>> ax.set_yticks(numpy.arange(0,1,0.1))
    >>> plt.scatter(k[0], k[1], color="g", label="LHS-MDU") 
    >>> plt.scatter(l[0], l[1], color="r", label="MC") 
    >>> plt.grid()
    >>> plt.show() 

You can use the strata generated by the algorithm to sample again, if you so desire. For this, you can do::

    >>> m = lhsmdu.resample()
    >>> n = lhsmdu.resample()
    >>> o = lhsmdu.resample()

This will again generate the same number of samples as before, a nested list with 2 variables, with 20 samples each.

You can plot these together and see the sampling from the strata::
    >>> m = np.array(m)
    >>> n = np.array(n)
    >>> o = np.array(o)
    
    >>> fig = plt.figure() 
    >>> ax = fig.gca()
    >>> ax.set_xticks(numpy.arange(0,1,0.1))
    >>> ax.set_yticks(numpy.arange(0,1,0.1))
    >>> plt.title("LHS-MDU") 
    >>> plt.scatter(k[0], k[1], c="g", label="sample 1") 
    >>> plt.scatter(m[0], m[1], c="r", label="resample 2") 
    >>> plt.scatter(n[0], n[1], c="b", label="resample 3") 
    >>> plt.scatter(o[0], o[1], c="y", label="resample 4") 
    >>> plt.grid()
    >>> plt.show() 

Alternatively, you can choose to get new strata each time, and see the sampling hence::

    >>> p = lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity 
    >>> q = lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity 
    >>> r = lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity 
    
    >>> p = np.array(p)
    >>> q = np.array(q)
    >>> r = np.array(r)
    
    >>> fig = plt.figure() 
    >>> ax = fig.gca()
    >>> ax.set_xticks(numpy.arange(0,1,0.1))
    >>> ax.set_yticks(numpy.arange(0,1,0.1))
    >>> plt.title("LHS-MDU") 
    >>> plt.scatter(k[0], k[1], c="g", label="sample 1") 
    >>> plt.scatter(p[0], p[1], c="r", label="sample 2") 
    >>> plt.scatter(q[0], q[1], c="b", label="sample 3") 
    >>> plt.scatter(r[0], r[1], c="y", label="sample 4") 
    >>> plt.grid()
    >>> plt.show() 

===========================================================================================

Changing the random seed
=========================

You will notice that the strata generated are the same each time you run the program again. This is because the random seed is a global constant set to a default value by design, so that simulations can be replicated. In order to change this behavior, you can set a new random seed using the following code::


    >>> randSeed = 11 # random number of choice 
    >>> lhsmdu.setRandomSeed(randSeed) # Latin Hypercube Sampling with multi-dimensional uniformity 
    >>> lhsmdu.sample(2, 20) # Latin Hypercube Sampling with multi-dimensional uniformity 

Alternatively, you can also set the seed by using sample with a new seed::

    >>> lhsmdu.sample(2, 20, randomSeed=randSeed) # Latin Hypercube Sampling with multi-dimensional uniformity 

To change the random seed in every run, you can set on top of the program::

    >>> lhsmdu.setRandomSeed(None) 

Sampling from arbitrary CDFs
=============================

After uniformly distributed samples have been generated from LHSMDU, you can convert these to samples from arbitrary distributions using inverse tranform sampling. In this, the CDF [0,1] of the distribution of interest is inverted, and then data points corresponding to the uniformly sampled points are picked up. To do this, you must have a `rv_contiuous` or `rv_discrete` distribution instance taken from scipy.stats. You can also use frozen distributions (after setting loc and scale parameters). Following is an example for normal distribution.::

    >>> import scipy.stats.distributions as ssd
    >>> p = ssd.norm
    >>> new_samples = lhsmdu.inverseTransformSample(p, k[0])
    >>> plt.hist(new_samples[0])
    >>> plt.show()

Citing this repository
=======================
To cite, please cite both the original paper from Deutsch and Deutsch: http://dx.doi.org/10.1016%2Fj.jspi.2011.09.016.
and the repository: https://doi.org/10.5281/zenodo.3929531
