U
    d                     @   sh   d dl Z d dlmZ d dlmZ d dlmZ d dlmZm	Z	 d dl
mZ d dlmZ G dd	 d	eZdS )
    N)constraints)Distribution)Independent)ComposeTransform	Transform)_sum_rightmost)Dictc                       s   e Zd ZU dZi Zeeejf e	d< d fdd	Z
d fdd	Zejdd	d
d Zedd Ze fddZe fddZdd Zdd Zdd Zdd Z  ZS )TransformedDistributiona  
    Extension of the Distribution class, which applies a sequence of Transforms
    to a base distribution.  Let f be the composition of transforms applied::

        X ~ BaseDistribution
        Y = f(X) ~ TransformedDistribution(BaseDistribution, f)
        log p(Y) = log p(X) + log |det (dX/dY)|

    Note that the ``.event_shape`` of a :class:`TransformedDistribution` is the
    maximum shape of its base distribution and its transforms, since transforms
    can introduce correlations among events.

    An example for the usage of :class:`TransformedDistribution` would be::

        # Building a Logistic Distribution
        # X ~ Uniform(0, 1)
        # f = a + b * logit(X)
        # Y ~ f(X) ~ Logistic(a, b)
        base_distribution = Uniform(0, 1)
        transforms = [SigmoidTransform().inv, AffineTransform(loc=a, scale=b)]
        logistic = TransformedDistribution(base_distribution, transforms)

    For more examples, please look at the implementations of
    :class:`~torch.distributions.gumbel.Gumbel`,
    :class:`~torch.distributions.half_cauchy.HalfCauchy`,
    :class:`~torch.distributions.half_normal.HalfNormal`,
    :class:`~torch.distributions.log_normal.LogNormal`,
    :class:`~torch.distributions.pareto.Pareto`,
    :class:`~torch.distributions.weibull.Weibull`,
    :class:`~torch.distributions.relaxed_bernoulli.RelaxedBernoulli` and
    :class:`~torch.distributions.relaxed_categorical.RelaxedOneHotCategorical`
    arg_constraintsNc                    sR  t |tr|g| _n:t |tr@tdd |D s8td|| _ntd||j|j }t	|j}t
| j}|jj}t	||k rtd||||}||}	||	kr|	d t	|	|  }
||
}|| }|dkrt||}|| _|jjt|| d }t	||kstt	|| }|d | }||d  }tt| j|||d d S )Nc                 s   s   | ]}t |tV  qd S N)
isinstancer   ).0t r   P/tmp/pip-unpacked-wheel-ua33x9lu/torch/distributions/transformed_distribution.py	<genexpr>1   s     z3TransformedDistribution.__init__.<locals>.<genexpr>z6transforms must be a Transform or a list of Transformsz2transforms must be a Transform or list, but was {}zHbase_distribution needs to have shape with size at least {}, but got {}.r   validate_args)r   r   
transformslistall
ValueErrorformatbatch_shapeevent_shapelenr   domain	event_dimZforward_shapeinverse_shapeexpandr   	base_distcodomainmaxAssertionErrorsuperr	   __init__)selfZbase_distributionr   r   Z
base_shapeZbase_event_dim	transformZdomain_event_dimshapeZexpanded_base_shapebase_batch_shapeZreinterpreted_batch_ndimsr   Zcutr   r   	__class__r   r   r%   -   s>    




 



z TransformedDistribution.__init__c                    s   |  t|}t|}|| j }t| jD ]}||}q*|d t|t| j	j  }| j	
||_	| j|_tt|j|| jdd | j|_|S )NFr   )Z_get_checked_instancer	   torchSizer   reversedr   r   r   r    r   r$   r%   _validate_args)r&   r   Z	_instancenewr(   r   r)   r*   r   r   r   Q   s    

zTransformedDistribution.expandF)Zis_discretec                 C   sF   | j s| jjS | j d j}t| j|jkrBt|t| j|j }|S )N)	r   r    supportr!   r   r   r   r   Zindependent)r&   r2   r   r   r   r2   ^   s    zTransformedDistribution.supportc                 C   s   | j jS r   )r    has_rsample)r&   r   r   r   r3   g   s    z#TransformedDistribution.has_rsamplec              
   C   sD   t  2 | j|}| jD ]}||}q|W  5 Q R  S Q R X dS )a  
        Generates a sample_shape shaped sample or sample_shape shaped batch of
        samples if the distribution parameters are batched. Samples first from
        base distribution and applies `transform()` for every transform in the
        list.
        N)r,   Zno_gradr    sampler   r&   Zsample_shapexr'   r   r   r   r4   k   s
    


zTransformedDistribution.samplec                 C   s$   | j |}| jD ]}||}q|S )a$  
        Generates a sample_shape shaped reparameterized sample or sample_shape
        shaped batch of reparameterized samples if the distribution parameters
        are batched. Samples first from base distribution and applies
        `transform()` for every transform in the list.
        )r    rsampler   r5   r   r   r   r7   x   s    

zTransformedDistribution.rsamplec                 C   s   | j r| | t| j}d}|}t| jD ]D}||}||jj|j	j 7 }|t
|||||jj  }|}q,|t
| j||t| jj  }|S )z
        Scores the sample by inverting the transform(s) and computing the score
        using the score of the base distribution and the log abs det jacobian.
        g        )r/   _validate_sampler   r   r.   r   invr   r   r!   r   Zlog_abs_det_jacobianr    log_prob)r&   valuer   r:   yr'   r6   r   r   r   r:      s     



z TransformedDistribution.log_probc                 C   s@   d}| j D ]}||j }q
t|tr0|dkr0|S ||d  d S )zu
        This conditionally flips ``value -> 1-value`` to ensure :meth:`cdf` is
        monotone increasing.
           g      ?)r   signr   int)r&   r;   r>   r'   r   r   r   _monotonize_cdf   s    
z'TransformedDistribution._monotonize_cdfc                 C   sL   | j ddd D ]}||}q| jr2| j| | j|}| |}|S )z
        Computes the cumulative distribution function by inverting the
        transform(s) and computing the score of the base distribution.
        Nr1   )r   r9   r/   r    r8   cdfr@   r&   r;   r'   r   r   r   rA      s    
zTransformedDistribution.cdfc                 C   s.   |  |}| j|}| jD ]}||}q|S )z
        Computes the inverse cumulative distribution function using
        transform(s) and computing the score of the base distribution.
        )r@   r    icdfr   rB   r   r   r   rC      s
    


zTransformedDistribution.icdf)N)N)__name__
__module____qualname____doc__r
   r   strr   Z
Constraint__annotations__r%   r   Zdependent_propertyr2   propertyr3   r,   r-   r4   r7   r:   r@   rA   rC   __classcell__r   r   r*   r   r	   
   s   
 $


r	   )r,   Ztorch.distributionsr   Z torch.distributions.distributionr   Ztorch.distributions.independentr   Ztorch.distributions.transformsr   r   Ztorch.distributions.utilsr   typingr   r	   r   r   r   r   <module>   s   