B
    `AU                 @   s   d Z ddlmZ ddlmZ ddlmZ ddlZddlmZ	 ddl
mZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ dgZdd Zdd ZdddZG dd dejZdS )a4  Lewandowski-Kurowicka-Joe distribution on correlation matrices.

The sampler follows the 'onion' method from
[1] Daniel Lewandowski, Dorota Kurowicka, and Harry Joe,
'Generating random correlation matrices based on vines and extended
onion method,' Journal of Multivariate Analysis 100 (2009), pp
1989-2001.
    )absolute_import)division)print_functionN)v2)_numpy)beta)distribution)assert_util)
dtype_util)prefer_static)reparameterization)samplers)tensor_util)tensorshape_utilLKJc             C   sB   t jtj|| ggdd||d}|tj|ddddtjf  }|S )zEReturns a batch of points chosen uniformly from the unit hypersphere.r   )axis)shapeseeddtype   )ordr   .)r   normaltfconcatnormnewaxis)	dimensionr   r   r   rawZ	unit_norm r   e/home/dcms/DCMS/lib/python3.7/site-packages/tensorflow_probability/python/distributions/_numpy/lkj.py_uniform_unit_norm1   s    r!   c             C   s:   t j| gt jt |g| jdgdd}t |t j |S )zAReplicate the input tensor n times along a new (major) dimension.)r   r   )r   )r   r   onesZrankr   Ztiler   )nZtensorZ	multiplesr   r   r    
_replicate;   s     r$   Fc          
   C   s  |dk rt dddtd|d   }ttj||dd}tdpD|v t|}t	|j
sxtdt|j
t| |}t|}|dkrtj|||ggdd}	tj|	|j
d	S ||d
 d
  }
tj|
|
d}d
|j| d d }tjt|dtjf t|dtjf gdd}tj|dtjf td|d  dtjf gdd}tj|dtjddf |dtjddf gdd}xtd|D ]}|
d }
tj|d
 |
dj| d}t|dtjf }t|||j
| d}|| }tj|td|dtjf  gdd}tj|t|d dtjf gdd}tj||dtjddf gdd}qW |rztdt| |r|S tj||dd}tj |tjt|dd |j
d	}|S Q R X dS )a!  Returns a Tensor of samples from an LKJ distribution.

  Args:
    num_samples: Python `int`. The number of samples to draw.
    dimension: Python `int`. The dimension of correlation matrices.
    concentration: `Tensor` representing the concentration of the LKJ
      distribution.
    cholesky_space: Python `bool`. Whether to take samples from LKJ or
      Chol(LKJ).
    seed: Python integer seed for RNG
    name: Python `str` name prefixed to Ops created by this function.

  Returns:
    samples: A Tensor of correlation matrices (or Cholesky factors of
      correlation matrices if `cholesky_space = True`) with shape
      `[n] + B + [D, D]`, where `B` is the shape of the `concentration`
      parameter, and `D` is the `dimension`.

  Raises:
    ValueError: If `dimension` is negative.
  r   z6Cannot sample negative-dimension correlation matrices.   r   
sample_lkj)r#   Zsaltz<The concentration argument should have floating type, not {})r   )r   r   g       @)Zconcentration1Zconcentration0)r   g      ?.r   Ng      ?).r   zDid not use all seeds: T)Ztranspose_b)!
ValueErrormaxlistr   Z
split_seedr   
name_scopeconvert_to_tensorr
   Zis_floatingr   	TypeErrorformatnamer$   r   r   r"   r   BetasamplepopZ	ones_liker   
zeros_likesqrtranger!   AssertionErrorlenmatmullinalgZset_diag)num_samplesr   concentrationcholesky_spacer   r/   Z	num_seedsZseedsZconcentration_shaper   Z	beta_concZ	beta_distZcorr12Z	first_rowZ
second_rowZchol_resultr#   r   Zdistance	directionZraw_correlationZnew_rowresultr   r   r    r&   C   sl    


$" $"r&   c                   s   e Zd ZdZd( fdd	Zedd Zedd	 Zed
d Z	edd Z
dd Zdd Zdd Zdd Zd)ddZdd Zd*ddZd+ddZd d! Zd"d# Zd$d% Zd&d' Z  ZS ),r   a>  The LKJ distribution on correlation matrices.

  This is a one-parameter family of distributions on correlation matrices.  The
  probability density is proportional to the determinant raised to the power of
  the parameter: `pdf(X; eta) = Z(eta) * det(X) ** (eta - 1)`, where `Z(eta)` is
  a normalization constant.  The uniform distribution on correlation matrices is
  the special case `eta = 1`.

  The distribution is named after Lewandowski, Kurowicka, and Joe, who gave a
  sampler for the distribution in [(Lewandowski, Kurowicka, Joe, 2009)][1].

  Note: For better numerical stability, it is recommended that you use
  `CholeskyLKJ` instead.

  #### Examples

  ```python
  # Initialize a single 3x3 LKJ with concentration parameter 1.5
  dist = tfp.distributions.LKJ(dimension=3, concentration=1.5)

  # Evaluate this at a batch of two observations, each in R^{3x3}.
  x = ...  # Shape is [2, 3, 3].
  dist.prob(x)  # Shape is [2].

  # Draw 6 LKJ-distributed 3x3 correlation matrices
  ans = dist.sample(sample_shape=[2, 3], seed=42)
  # shape of ans is [2, 3, 3, 3]
  ```
  FTc       	   	      s   |dk rt dtt }|| _t|P t|gtj}t	j
|d|d| _|| _tt| j| jj||tj||d W dQ R X dS )ay  Construct LKJ distributions.

    Args:
      dimension: Python `int`. The dimension of the correlation matrices
        to sample.
      concentration: `float` or `double` `Tensor`. The positive concentration
        parameter of the LKJ distributions. The pdf of a sample matrix `X` is
        proportional to `det(X) ** (concentration - 1)`.
      input_output_cholesky: Python `bool`. If `True`, functions whose input or
        output have the semantics of samples assume inputs are in Cholesky form
        and return outputs in Cholesky form. In particular, if this flag is
        `True`, input to `log_prob` is presumed of Cholesky form and output from
        `sample` is of Cholesky form.  Setting this argument to `True` is purely
        a computational optimization and does not change the underlying
        distribution. Additionally, validation checks which are only defined on
        the multiplied-out form are omitted, even if `validate_args` is `True`.
        Default value: `False` (i.e., input/output does not have Cholesky
        semantics). WARNING: Do not set this boolean to true, when using
        `tfp.mcmc`. The density is not the density of Cholesky factors of
        correlation matrices drawn via LKJ.
      validate_args: Python `bool`, default `False`. When `True` distribution
        parameters are checked for validity despite possibly degrading runtime
        performance. When `False` invalid inputs may silently render incorrect
        outputs.
      allow_nan_stats: Python `bool`, default `True`. When `True`, statistics
        (e.g., mean, mode, variance) use the value `NaN` to indicate the
        result is undefined. When `False`, an exception is raised if one or
        more of the statistic's batch members are undefined.
      name: Python `str` name prefixed to Ops created by this class.

    Raises:
      ValueError: If `dimension` is negative.
    r   z5There are no negative-dimension correlation matrices.r;   )r/   r   )r   validate_argsallow_nan_statsZreparameterization_type
parametersr/   N)r(   dictlocals_input_output_choleskyr   r+   r
   Zcommon_dtypefloat32r   Zconvert_nonref_to_tensor_concentration
_dimensionsuperr   __init__r   r   ZNOT_REPARAMETERIZED)	selfr   r;   input_output_choleskyr?   r@   r/   rA   r   )	__class__r   r    rI      s"    (

zLKJ.__init__c             C   s
   t ddS )Nr   )r;   )rB   )clsr   r   r    _params_event_ndims+  s    zLKJ._params_event_ndimsc             C   s   | j S )z+Dimension of returned correlation matrices.)rG   )rJ   r   r   r    r   /  s    zLKJ.dimensionc             C   s   | j S )zConcentration parameter.)rF   )rJ   r   r   r    r;   4  s    zLKJ.concentrationc             C   s   | j S )zEBoolean indicating if `Tensor` input/outputs are Cholesky factorized.)rD   )rJ   r   r   r    rK   9  s    zLKJ.input_output_choleskyc             C   s   t | jS )N)r   r   r;   )rJ   r   r   r    _batch_shape_tensor>  s    zLKJ._batch_shape_tensorc             C   s   | j jS )N)r;   r   )rJ   r   r   r    _batch_shapeA  s    zLKJ._batch_shapec             C   s   t j| j| jgt jdS )N)r   )r   Zconstantr   int32)rJ   r   r   r    _event_shape_tensorD  s    zLKJ._event_shape_tensorc             C   s   t | j| jgS )N)r   ZTensorShaper   )rJ   r   r   r    _event_shapeG  s    zLKJ._event_shapeNc             C   s   t || j| j| j||dS )a  Returns a Tensor of samples from an LKJ distribution.

    Args:
      num_samples: Python `int`. The number of samples to draw.
      seed: Python integer seed for RNG
      name: Python `str` name prefixed to Ops created by this function.

    Returns:
      samples: A Tensor of correlation matrices with shape `[n, B, D, D]`,
        where `B` is the shape of the `concentration` parameter, and `D`
        is the `dimension`.

    Raises:
      ValueError: If `dimension` is negative.
    )r:   r   r;   r<   r   r/   )r&   r   r;   rK   )rJ   r:   r   r/   r   r   r    	_sample_nJ  s    zLKJ._sample_nc             C   s(   t | j}| j|d}| ||| S )N)r;   )r   r,   r;   _log_normalization_log_unnorm_prob)rJ   xr;   Z
normalizerr   r   r    	_log_probb  s    zLKJ._log_probc          	   C   st   t |p
d\ t j|dd}| jrJdt jt jt j|dgd }nt j	|\}}|d | }|S Q R X dS )	a  Returns the unnormalized log density of an LKJ distribution.

    Args:
      x: `float` or `double` `Tensor` of correlation matrices.  The shape of `x`
        must be `B + [D, D]`, where `B` broadcasts with the shape of
        `concentration`.
      concentration: `float` or `double` `Tensor`. The positive concentration
        parameter of the LKJ distributions.
      name: Python `str` name prefixed to Ops created by this function.

    Returns:
      log_p: A Tensor of the unnormalized log density of each matrix element of
        `x`, with respect to an LKJ distribution with parameter the
        corresponding element of `concentration`.
    Zlog_unnorm_prob_lkjrW   )r/   g       @r   )r   g      ?N)
r   r+   r,   rK   Z
reduce_summathlogr9   	diag_partZslogdet)rJ   rW   r;   r/   Zlogdet_answerr   r   r    rV   j  s     zLKJ._log_unnorm_problog_normalizationc          	   C   s   t |p
d t |dkr"| jn|}tttj}t |}xNt	d| j
D ]>}|||d   }|| j
d | d  }|t|d | }qPW |S Q R X dS )a  Returns the log normalization of an LKJ distribution.

    Args:
      concentration: `float` or `double` `Tensor`. The positive concentration
        parameter of the LKJ distributions.
      name: Python `str` name prefixed to Ops created by this function.

    Returns:
      log_z: A Tensor of the same shape and dtype as `concentration`, containing
        the corresponding log normalizers.
    Zlog_normalization_lkjNr%   g       @)r   r+   r,   r;   floatnprZ   pir3   r5   r   tfp_mathZlog_gamma_difference)rJ   r;   r/   ZlogpiZanskZeffective_concentrationr   r   r    rU     s    
zLKJ._log_normalizationc             C   s.   t | j}t |}t j| j||jd}|S )N)Znum_rowsZbatch_shaper   )r   r,   r;   r   Zeyer   r   )rJ   r;   batchr]   r   r   r    _mean  s    

z	LKJ._meanc             C   s   d S )Nr   )rJ   r   r   r    _default_event_space_bijector  s    z!LKJ._default_event_space_bijectorc             C   s<   g }| j s|S |t| jkr8|tj| jd dd |S )Nr%   z&Argument `concentration` must be >= 1.)message)r?   r   Zis_refr;   appendr	   Zassert_non_negative)rJ   Zis_init
assertionsr   r   r    _parameter_control_dependencies  s    
z#LKJ._parameter_control_dependenciesc             C   sr  g }t |jdd  rht |jd t |jd   krF| jksn td| j| jt |jnb| jrd| j| jt|}|	t
jt|d | j|d |	t
jt|d | j|d | jrn| jsn|	t
jt|jd|ddd |	t
j|t|jdd	dd |	t
jtj|t|jdd
dd |	t
j|tj|ddd |S )Nr'   r   z8Input dimension mismatch: expected [..., {}, {}], got {})rg   zCorrelations must be >= -1.   )rg   Z	summarizer%   zCorrelations must be <= 1.zSelf-correlations must be = 1.z'Correlation matrices must be symmetric.)r   Zis_fully_definedr   Zdimsr   r(   r.   r?   r   rh   r	   Zassert_equalrK   Zassert_less_equalr
   Zas_numpy_dtyper   Zassert_nearr9   r[   Zmatrix_transpose)rJ   rW   ri   msgr   r   r    _sample_control_dependencies  sJ    





z LKJ._sample_control_dependencies)FFTr   )NN)N)Nr^   )__name__
__module____qualname____doc__rI   classmethodrN   propertyr   r;   rK   rO   rP   rR   rS   rT   rX   rV   rU   re   rf   rj   rm   __classcell__r   r   )rL   r    r      s*      4

+
)FNN)rq   
__future__r   r   r   numpyr`   Z;tensorflow_probability.python.internal.backend.numpy.compatr   r   Z"tensorflow_probability.python.mathr   rb   Z2tensorflow_probability.python.distributions._numpyr   r   Z-tensorflow_probability.python.internal._numpyr	   r
   r   Z&tensorflow_probability.python.internalr   r   r   r   __all__r!   r$   r&   Distributionr   r   r   r   r    <module>   s.   
  
 
