a
    ;gha                     @   s&  d dl mZ d dlmZmZ d dlZd dlZd dlZd dlmZm	Z	 d dl
mZ d dlZd dlmZmZ g Zedg dZdd	 Zd
d ZG dd deZdd Zd,ddZd-ddZdd Zd.ddZdd Zdd Zd/ddZd0dd ZG d!d" d"eZ ed1d$d%Z!d&d' Z"d(d) Z#d*d+ Z$dS )2    )
namedtuple)	ExitStackcontextmanagerN)laxrandom)find_stack_levelidentityCondIndepStackFrame)namedimsizec                 C   sf   | d d u rb| d dkrD| d | d ddi| d \| d< | d	< n| d | d i | d | d< d S )
NvaluetypesamplefnargsZsample_intermediatesTkwargsintermediates msgr   r   b/mounts/lovelace/software/anaconda3/envs/metaDMG/lib/python3.9/site-packages/numpyro/primitives.pydefault_process_message   s    r   c                 C   s`   d}t ttD ] \}}||  | dr q2qt|  t| d d D ]}||  qL| S )al  
    Execute the effect stack at a single site according to the following scheme:

        1. For each ``Messenger`` in the stack from bottom to top,
           execute ``Messenger.process_message`` with the message;
           if the message field "stop" is True, stop;
           otherwise, continue
        2. Apply default behavior (``default_process_message``) to finish remaining
           site execution
        3. For each ``Messenger`` in the stack from top to bottom,
           execute ``Messenger.postprocess_message`` to update the message
           and internal messenger state with the site results
    r   stop   N)	enumeratereversed_PYRO_STACKprocess_messagegetr   postprocess_message)r   Zpointerhandlerr   r   r   apply_stack   s    

r"   c                   @   s>   e Zd ZdddZdd Zdd Zdd	 Zd
d Zdd ZdS )	MessengerNc                 C   s<   |d ur"t |s"tdt||| _tj| |g d d S )NzJExpected `fn` to be a Python callable object; instead found type(fn) = {}.)updated)callable
ValueErrorformatr   r   	functoolsupdate_wrapper)selfr   r   r   r   __init__@   s    zMessenger.__init__c                 C   s   t |  d S N)r   appendr*   r   r   r   	__enter__I   s    zMessenger.__enter__c                 C   sT   |d u r"t d | u sJ t   n.| t v rPt | }t|tt D ]}t   qBd S )N)r   popindexrangelen)r*   exc_type	exc_value	tracebacklocir   r   r   __exit__L   s    

zMessenger.__exit__c                 C   s   d S r,   r   r*   r   r   r   r   r   \   s    zMessenger.process_messagec                 C   s   d S r,   r   r;   r   r   r   r    _   s    zMessenger.postprocess_messagec                 O   sd   | j d u r,t|dkr|rJ |d | _ | S |   | j |i |W  d    S 1 sV0    Y  d S )Nr   r   )r   r4   )r*   r   r   r   r   r   __call__b   s    

zMessenger.__call__)N)	__name__
__module____qualname__r+   r/   r:   r   r    r<   r   r   r   r   r#   ?   s   
	r#   c           
      K   s   t jj|d0 t|  d|fi |d|i}W d    n1 sD0    Y  t jj|dA d( t|  d|fi |}W d    n1 s0    Y  t|d|j  }t||}t|||}	t	| |	S )N)maskZ	_observedobsTZ_unobserved)r   )
numpyrohandlersr@   r   jnpshape	event_dimZreshapewheredeterministic)
r
   r   rA   obs_maskr   ZobservedZ
unobservedrE   Z
batch_maskr   r   r   r   _masked_observel   s    >6rJ   r   c                 C   sx  t |tsJ dt |tjjstd}zddlm} W n tyN   d}Y n0 |du sbt ||szddl	m}	 ddl
m}
 W n ty   |Y n0 t ||	jrt 4 tjdtd	 |
|j f i |j}W d   q1 s0    Y  n|ts|du r|||d
S |S |dur4t| ||||d|dS d| |d||d
|d|dug g |du r`i n|d}t|}|d S )a  
    Returns a random sample from the stochastic function `fn`. This can have
    additional side effects when wrapped inside effect handlers like
    :class:`~numpyro.handlers.substitute`.

    .. note::
        By design, `sample` primitive is meant to be used inside a NumPyro model.
        Then :class:`~numpyro.handlers.seed` handler is used to inject a random
        state to `fn`. In those situations, `rng_key` keyword will take no
        effect.

    :param str name: name of the sample site.
    :param fn: a stochastic function that returns a sample.
    :param numpy.ndarray obs: observed value
    :param jax.random.PRNGKey rng_key: an optional random key for `fn`.
    :param sample_shape: Shape of samples to be drawn.
    :param dict infer: an optional dictionary containing additional information
        for inference algorithms. For example, if `fn` is a discrete distribution,
        setting `infer={'enumerate': 'parallel'}` to tell MCMC marginalize
        this discrete latent site.
    :param numpy.ndarray obs_mask: Optional boolean array mask of shape
        broadcastable with ``fn.batch_shape``. If provided, events with
        mask=True will be conditioned on ``obs`` and remaining events will be
        imputed by sampling. This introduces a latent sample site named ``name
        + "_unobserved"`` which should be used by guides in SVI. Note that this
        argument is not intended to be used with MCMC.
    :return: sample from the stochastic `fn`.
    z,sample_shape needs to be a tuple of integerszIt looks like you tried to use a fn that isn't an instance of numpyro.distributions.Distribution, funsor.Funsor or tensorflow_probability.distributions.Distribution. If you're using funsor or tensorflow_probability, make sure they are correctly installed.r   )FunsorN)distributions)TFPDistributionignore)category)rng_keysample_shaper   )rP   rQ   inferr   )r   r
   r   r   r   r   scaleZis_observedr   cond_indep_stackrR   r   )
isinstancetuplerB   rL   Distribution	TypeErrorZfunsorrK   ImportErrorZ%tensorflow_probability.substrates.jaxZ!numpyro.contrib.tfp.distributionsrM   warningscatch_warningssimplefilterFutureWarning	__class__
parametersr   rJ   r"   )r
   r   rA   rP   rQ   rR   rI   Z
type_errorrK   ZtfdrM   initial_msgr   r   r   r   r   z   s\    


6

r   c              	   K   sV   t st|rJ d|S t|r*dd }nt}d| ||f|ddg d}t|}|d S )as  
    Annotate the given site as an optimizable parameter for use with
    :mod:`jax.example_libraries.optimizers`. For an example of how `param` statements
    can be used in inference algorithms, refer to :class:`~numpyro.infer.SVI`.

    :param str name: name of site.
    :param init_value: initial value specified by the user or a lazy callable
        that accepts a JAX random PRNGKey and returns an array.
        Note that the onus of using this to initialize the optimizer is
        on the user inference algorithm, since there is no global parameter
        store in NumPyro.
    :type init_value: numpy.ndarray or callable
    :param constraint: NumPyro constraint, defaults to ``constraints.real``.
    :type constraint: numpyro.distributions.constraints.Constraint
    :param int event_dim: (optional) number of rightmost dimensions unrelated
        to batching. Dimension to the left of this will be considered batch
        dimensions; if the param statement is inside a subsampled plate, then
        corresponding batch dimensions of the parameter will be correspondingly
        subsampled. If unspecified, all dimensions will be considered event
        dims and no subsampling will be performed.
    :return: value for the parameter. Unless wrapped inside a
        handler like :class:`~numpyro.handlers.substitute`, this will simply
        return the initial value.
    zMA callable init_value needs to be put inside a numpyro.handlers.seed handler.c                 _   s
   | t  S r,   )prng_key)Zinit_fnr   r   r   r   r   r     s    zparam.<locals>.fnparamN)r   r
   r   r   r   r   rS   rT   r   )r   r%   r   r"   )r
   
init_valuer   r   r`   r   r   r   r   rb      s*    
rb   c                 C   s$   t s|S d| |d}t|}|d S )a  
    Used to designate deterministic sites in the model. Note that most effect
    handlers will not operate on deterministic sites (except
    :func:`~numpyro.handlers.trace`), so deterministic sites should be
    side-effect free. The use case for deterministic nodes is to record any
    values in the model execution trace.

    :param str name: name of the deterministic site.
    :param numpy.ndarray value: deterministic value to record in the trace.
    rH   )r   r
   r   r   r   r"   )r
   r   r`   r   r   r   r   rH     s
    rH   c                 C   s,   t s|S d| t|fi |d}t|}|d S )a  
    This primitive is used to store a mutable value that can be changed
    during model execution::

        a = numpyro.mutable("a", {"value": 1.})
        a["value"] = 2.
        assert numpyro.mutable("a")["value"] == 2.

    For example, this can be used to store and update information like
    running mean/variance in a neural network batch normalization layer.

    :param str name: name of the mutable site.
    :param init_value: mutable value to record in the trace.
    mutable)r   r
   r   r   r   r   r   )r   r   r"   )r
   rc   r`   r   r   r   r   re   0  s    
re   c                  C   s"   ddd di ddd} t |  | S )z
    EXPERIMENTAL Inspect the Pyro stack.

    .. warning:: The format of the returned message may change at any time and
        does not guarantee backwards compatibility.

    :returns: A message with mask effects applied.
    :rtype: dict
    inspectc                   S   s   dS )NTr   r   r   r   r   <lambda>^      z_inspect.<locals>.<lambda>r   N)r   r   r   r   r   r@   )r"   r   r   r   r   _inspectP  s    ri   c                   C   s
   t  d S )a  
    Records the effects of enclosing ``handlers.mask`` handlers.
    This is useful for avoiding expensive ``numpyro.factor()`` computations during
    prediction, when the log density need not be computed, e.g.::

        def model():
            # ...
            if numpyro.get_mask() is not False:
                log_density = my_expensive_computation()
                numpyro.factor("foo", log_density)
            # ...

    :returns: The mask.
    :rtype: None, bool, or numpy.ndarray
    r@   )ri   r   r   r   r   get_maskh  s    rj   c           	      C   sZ   | d }|\}}t |}|du rN|du r0tdt }|||\}}t || t||S )az  
    Declare a :mod:`~jax.example_libraries.stax` style neural network inside a
    model so that its parameters are registered for optimization via
    :func:`~numpyro.primitives.param` statements.

    :param str name: name of the module to be registered.
    :param tuple nn: a tuple of `(init_fn, apply_fn)` obtained by a :mod:`~jax.example_libraries.stax`
        constructor function.
    :param tuple input_shape: shape of the input taken by the
        neural network.
    :return: a `apply_fn` with bound parameters that takes an array
        as an input and returns the neural network transformed output
        array.
    z$paramsNz3Valid value for `input_shape` needed to initialize.)rb   r&   ra   r(   partial)	r
   nnZinput_shapeZ
module_keyZnn_initZnn_applyZ	nn_paramsrP   _r   r   r   module{  s    
rn   c                    sz   |d u rt dt dkrbt||  fdd}t|tt|\}}|| d  S tj	||fddS d S )NzMissing random key to generate subsample indices. Algorithms like HMC/NUTS do not support subsampling. You might want to use SVI or HMCECS instead.cpuc                    sT   | }|d }t  | dd|}| jt||g | t||g } | d fS )Nr   r   r   )r   randintatrD   arrayset)validxZi_p1r9   jZrng_keysr   r   r   body_fn  s
    (z_subsample_fn.<locals>.body_fnF)replace)
r&   jaxZdefault_backendr   splitr   scanrD   arangechoice)r   subsample_sizerP   rx   rt   rm   r   rw   r   _subsample_fn  s    r   c                       sV   e Zd ZdZd fdd	Zedd Z fddZed	d
 Zdd Z	dd Z
  ZS )platea  
    Construct for annotating conditionally independent variables. Within a
    `plate` context manager, `sample` sites will be automatically broadcasted to
    the size of the plate. Additionally, a scale factor might be applied by
    certain inference algorithms if `subsample_size` is specified.

    .. note:: This can be used to subsample minibatches of data:

        .. code-block:: python

            with plate("data", len(data), subsample_size=100) as ind:
                batch = data[ind]
                assert len(batch) == 100

    :param str name: Name of the plate.
    :param int size: Size of the plate.
    :param int subsample_size: Optional argument denoting the size of the mini-batch.
        This can be used to apply a scaling factor by inference algorithms. e.g.
        when computing ELBO using a mini-batch.
    :param int dim: Optional argument to specify which dimension in the tensor
        is used as the plate dim. If `None` (default), the rightmost available dim
        is allocated.
    Nc                    sp   || _ |dksJ d|| _|d ur4|dkr4td| | j | j||\| _| _| jjd | _tt	| 
  d S )Nr   z size of plate should be positivezdim arg must be negative.)r
   r   r&   
_subsampler   _indicesrE   r   superr   r+   )r*   r
   r   r   r   r^   r   r   r+     s    zplate.__init__c           	   	   C   s   dt | ||fdd i|d ur&||kr&d nt|dg d}t| |d }|d d }|d ur||jd krtjd	|t|d
 t	 d |d }dd |D }|d u rd}||v r|d8 }q|}n||vsJ ||fS )Nr   rP         ?)r   r   r
   r   r   r   rS   rT   r   r   r   r   z7subsample_size does not match len(subsample), {} vs {}.zJ Did you accidentally use different subsample_size in the model and guide?)
stacklevelrT   c                 S   s   h | ]
}|j qS r   r   .0fr   r   r   	<setcomp>  rh   z#plate._subsample.<locals>.<setcomp>r0   )
r   rD   r}   r"   rE   rZ   warnr'   r4   r   )	r
   r   r   r   r   	subsamplerT   Zoccupied_dimsZnew_dimr   r   r   r     sD    
zplate._subsamplec                    s   t    | jS r,   )r   r/   r   r.   r   r   r   r/     s    
zplate.__enter__c                 C   s:   t dd | D }dg| }| D ]}|j||j< q t|S )Nc                 s   s   | ]}|j  V  qd S r,   r   r   r   r   r   	<genexpr>   rh   z)plate._get_batch_shape.<locals>.<genexpr>r   )maxr   r   rV   )rT   Zn_dimsbatch_shaper   r   r   r   _get_batch_shape  s
    
zplate._get_batch_shapec                 C   s0  |d dvr$|d dkr t dd S |d }t| j| j| j}|| |d dkr| |}|d j}d|d	 v r|d	 d | }d
|d	 d< tt	|t	| d}||d  }t
|t|}|d | | }	|d |	|d< | j| jkr,|d d u rdn|d }
|
| jr"| j| j nd |d< d S )Nr   )rb   r   r   Zcontrol_flowzCannot use control flow primitive under a `plate` primitive. Please move those `plate` statements into the control flow body function. See `scan` documentation for more information.rT   r   r   rQ   r   r   r   rS   r   r   )NotImplementedErrorr	   r
   r   r   r-   r   r   r   r4   r   Zbroadcast_shapesrV   expandr   )r*   r   rT   frameZexpected_shapeZdist_batch_shapeZoverlap_idxZtrailing_shapeZbroadcast_shaper   rS   r   r   r   r     s6    


zplate.process_messagec                 C   s   |d dv r| j d ur|d d}|d ur|dks8J | j | }t|d }t|| kr|| dkr|| | jkr|d dkrd	|d
 |}n
d|}td| j| j| j ||| j	| jk r|d }t
|| j|}||d< d S )Nr   )r   rb   r   rF   r   r   r   rb   z$numpyro.param({}, ..., event_dim={})r
   z$numpyro.subsample(..., event_dim={})z<Inside numpyro.plate({}, {}, dim={}) invalid shape of {}: {})r   r   rD   rE   r4   r   r'   r&   r
   r   Ztaker   )r*   r   rF   r   rE   Z	statementr   	new_valuer   r   r   r    &  s0    
zplate.postprocess_message)NN)r=   r>   r?   __doc__r+   staticmethodr   r/   r   r   r    __classcell__r   r   r   r   r     s   
%
 r   r0   c                 c   sv   |dk sJ t  P}tt|D ],\}}td| |||| d}|| q dV  W d   n1 sh0    Y  dS )a2  
    Create a contiguous stack of :class:`plate` s with dimensions::

        rightmost_dim - len(sizes), ..., rightmost_dim

    :param str prefix: Name prefix for plates.
    :param iterable sizes: An iterable of plate sizes.
    :param int rightmost_dim: The rightmost dim, counting from the right.
    r   z{}_{}r   N)r   r   r   r   r'   enter_context)prefixsizesZrightmost_dimstackr9   r   Zplate_ir   r   r   plate_stackB  s    r   c                 C   s0   t jj|}|d}t| ||ddid dS )z
    Factor statement to add arbitrary log probability factor to a
    probabilistic model.

    :param str name: Name of the trivial sample.
    :param numpy.ndarray log_factor: A possibly batched log probability factor.
    NZis_auxiliaryT)rA   rR   )rB   rL   distributionZUnitr   )r
   Z
log_factorZ	unit_distZ
unit_valuer   r   r   factorU  s    
r   c                  C   s0   t sdS ddd dddidd} t| }|d S )	z
    A statement to draw a pseudo-random number generator key
    :func:`~jax.random.PRNGKey` under :class:`~numpyro.handlers.seed` handler.

    :return: a PRNG key of shape (2,) and dtype unit32.
    Nra   c                 S   s   | S r,   r   )rP   r   r   r   rg   n  rh   zprng_key.<locals>.<lambda>r   rP   )r   r   r   r   r   r   rd   )r`   r   r   r   r   ra   b  s    ra   c                 C   s>   t s| S t|tr|dksJ d| d|id}t|}|d S )a,  
    EXPERIMENTAL Subsampling statement to subsample data based on enclosing
    :class:`~numpyro.primitives.plate` s.

    This is typically called on arguments to ``model()`` when subsampling is
    performed automatically by :class:`~numpyro.primitives.plate` s by passing
    ``subsample_size`` kwarg. For example the following are equivalent::

        # Version 1. using indexing
        def model(data):
            with numpyro.plate("data", len(data), subsample_size=10, dim=-data.dim()) as ind:
                data = data[ind]
                # ...

        # Version 2. using numpyro.subsample()
        def model(data):
            with numpyro.plate("data", len(data), subsample_size=10, dim=-data.dim()):
                data = numpyro.subsample(data, event_dim=0)
                # ...

    :param numpy.ndarray data: A tensor of batched data.
    :param int event_dim: The event dimension of the data tensor. Dimensions to
        the left are considered batch dimensions.
    :returns: A subsampled version of ``data``
    :rtype: ~numpy.ndarray
    r   r   rF   )r   r   r   r   )r   rU   intr"   )datarF   r`   r   r   r   r   r   x  s    r   )NNr   NN)N)N)N)N)r0   )%collectionsr   
contextlibr   r   r(   rZ   rz   r   r   Z	jax.numpynumpyrD   rB   Znumpyro.utilr   r   r   r	   r   r"   objectr#   rJ   r   rb   rH   re   ri   rj   rn   r   r   r   r   ra   r   r   r   r   r   <module>   s<   
 - 
h
9
 

 