a
    ;ghqe                     @   s  d dl mZ d dlmZ d dlZd dlmZ d dlZd dlZd dl	Z	d dl
Z
d dlZd dlZd dlmZ d dlZd dlmZmZmZ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mZ da e	!dZ"dd Z#d=ddZ$d>ddZ%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# Z-d$d% Z.d&d' Z/e-dddd(fd)d*Z0d?d+d,Z1dd-dd.d/d0Z2d@d2d3Z3d4d5 Z4d6d7 Z5d8d9 Z6e7d:d;d<Z8dS )A    )OrderedDict)contextmanagerN)zip_longest)tqdm)
device_putjitlaxvmap)Tracer)host_callback)ravel_pytree)tree_flattentree_mapFz\d+$c                 C   s   t |  tj |  dS )z
    Initializes internal state for the Python and NumPy random number generators.

    :param int rng_seed: seed for Python and NumPy random states.
    N)randomseednp)Zrng_seed r   \/mounts/lovelace/software/anaconda3/envs/metaDMG/lib/python3.9/site-packages/numpyro/util.pyset_rng_seed   s    
r   Tc                 C   s"   | st dd} tjd|  dS )z
    Changes the default array type to use 64 bit precision as in NumPy.

    :param bool use_x64: when `True`, JAX arrays will use 64 bits by default;
        else 32 bits.
    ZJAX_ENABLE_X64r   Zjax_enable_x64Nosgetenvjaxconfigupdate)Zuse_x64r   r   r   
enable_x64'   s    r   c                 C   s&   | du rt dd} tjd|  dS )z
    Changes platform to CPU, GPU, or TPU. This utility only takes
    effect at the beginning of your program.

    :param str platform: either 'cpu', 'gpu', or 'tpu'.
    NZJAX_PLATFORM_NAMEcpuZjax_platform_namer   )platformr   r   r   set_platform3   s    r   c                 C   s>   t dd}tdd| }dd| g| t jd< dS )a  
    By default, XLA considers all CPU cores as one device. This utility tells XLA
    that there are `n` host (CPU) devices available to use. As a consequence, this
    allows parallel mapping in JAX :func:`jax.pmap` to work in CPU platform.

    .. note:: This utility only takes effect at the beginning of your program.
        Under the hood, this sets the environment variable
        `XLA_FLAGS=--xla_force_host_platform_device_count=[num_devices]`, where
        `[num_device]` is the desired number of CPU devices `n`.

    .. warning:: Our understanding of the side effects of using the
        `xla_force_host_platform_device_count` flag in XLA is incomplete. If you
        observe some strange phenomenon when using this utility, please let us
        know through our issue or forum page. More information is available in this
        `JAX issue <https://github.com/google/jax/issues/1408>`_.

    :param int n: number of CPU devices to use.
    Z	XLA_FLAGS z*--xla_force_host_platform_device_count=\S+ z)--xla_force_host_platform_device_count={}N)r   r   resubsplitjoinformatenviron)nZ	xla_flagsr   r   r   set_host_device_count?   s    r(   c                 c   s:   | r0| dV  W d   q61 s$0    Y  ndV  dS )zJ
    Optionally wrap inside `context_manager` if condition is `True`.
    Nr   )	conditionZcontext_managerr   r   r   optional[   s    &r*   c                  c   s"   t } zda d V  W | a n| a 0 d S )NT)_DISABLE_CONTROL_FLOW_PRIM)Zstored_flagr   r   r   control_flow_prims_disabledg   s
    r,   c                 C   s0   t r| r||S ||S nt| ||||S d S N)r+   r   cond)predZtrue_operandZtrue_funZfalse_operandZ	false_funr   r   r   r.   r   s
    
r.   c                 C   s0   t r|}| |r||}q|S t| ||S d S r-   )r+   r   
while_loop)Zcond_funbody_funinit_valvalr   r   r   r0   |   s    
r0   c                 C   sB   t r.|}tt| t|D ]}|||}q|S t| |||S d S r-   )r+   rangeintr   	fori_loop)lowerupperr1   r2   r3   ir   r   r   r6      s    r6   c                 C   s   t | t S )zf
    Checks if `x` is not an array generated inside `jit`, `pmap`, `vmap`, or `lax_control_flow`.
    )
isinstancer
   xr   r   r   not_jax_tracer   s    r=   c                 O   s   | S r-   r   )r<   argskwargsr   r   r   identity   s    r@   c                    s(   dt dt _ fdd}|S )N   _cachec                    sJ   j } |v r&|  } | = | | < n| | < t|krF|jdd | S )NF)last)rB   lenpopitem)fnZfn_cachekeysmax_sizeouter_fnr   r   _wrapped   s    
zcached_by.<locals>._wrapped)getattrr   rB   )rJ   rH   rK   r   rG   r   	cached_by   s    rM   c                    s   dkrt d nd i g tD ]*}tt|d|< | jddd q2fddfd	d
  fddfdd}|S )zvFactory that builds a progress bar decorator along
    with the `set_tqdm_description` and `close_tqdm` functions
          )positionzCompiling.. Trefreshc                    sL   t t|}|sJ t| } | jd| dd  | |  d S )NzRunning chain FrQ   )	_CHAIN_REsearchstrr5   groupset_descriptionr   arg	transformZdeviceZchain_matchchain)	tqdm_barsr   r   _update_tqdm   s
    z*progress_bar_factory.<locals>._update_tqdmc                    sd   t t|}|sJ t| }| |   | t kr`tD ]}| 	  qNd S r-   )
rS   rT   rU   r5   rV   r   appendrD   r4   closerX   )finished_chains
num_chainsr\   r   r   _close_tqdm   s    
z)progress_bar_factory.<locals>._close_tqdmc                    s   t j dk fdd fdddd}t j  dk fdd fd	ddd}t j k fd
d fdddd}dS )zUpdates tqdm progress bar of a JAX loop only if the iteration number is a multiple of the print_rate
        Usage: carry = progress_bar((iter_num, print_rate), carry)
        rO   c                    s   t j dddS )Nr   TresultZtap_with_devicer   Zid_tap_)r]   iter_numr   r   <lambda>   s   zDprogress_bar_factory.<locals>._update_progress_bar.<locals>.<lambda>c                    s    S r-   r   rf   rh   r   r   ri          N)operandr   c                    s   t j ddS NTrc   re   rf   )r]   rh   
print_rater   r   ri      s   c                    s    S r-   r   rf   rj   r   r   ri      rk   c                    s   t j ddS rm   re   rf   )rb   rh   	remainderr   r   ri      s   c                    s    S r-   r   rf   rj   r   r   ri      rk   )r   r.   )rh   rg   )rb   r]   num_samplesrn   ro   rj   r   _update_progress_bar   s$    



z2progress_bar_factory.<locals>._update_progress_barc                    s    fdd}|S )a   Decorator that adds a progress bar to `body_fun` used in `lax.fori_loop`.
        Note that `body_fun` must be looping over a tuple who's first element is `np.arange(num_samples)`.
        This means that `iter_num` is the current iteration number
        c                    s   | |} | d  |S NrO   r   )r9   valsrd   )rq   funcr   r   wrapper_progress_bar   s    
zRprogress_bar_factory.<locals>.progress_bar_fori_loop.<locals>.wrapper_progress_barr   )rt   ru   )rq   )rt   r   progress_bar_fori_loop   s    z4progress_bar_factory.<locals>.progress_bar_fori_loop)r5   r4   	tqdm_autorW   )rp   ra   r[   rv   r   )	rb   rq   r]   r`   ra   rp   rn   ro   r\   r   progress_bar_factory   s    
rx   rO   c	                    s  | |ksJ |dksJ |du r,||  | n|}|||  | ksDJ t |\}
}| ||  |  }|	dd}|dkrt dkrtjdt d d}tt  fdd	}t	j
|f|
j |
jd
}|std||||||f\}}}}n|dkr,t||}||}td||||||f\}}}}n|	dd}|	ddd }||t|t|f}|dkrvt|d| ntt|X}|D ]B}t|||}|j||dd |r|j||d dd qW d   n1 s0    Y  |\}}}}t||}|r||fS |S )a  
    This looping construct works like :func:`~jax.lax.fori_loop` but with the additional
    effect of collecting values from the loop body. In addition, this allows for
    post-processing of these samples via `transform`, and progress bar updates.
    Note that, `progbar=False` will be faster, especially when collecting a
    lot of samples. Refer to example usage in :func:`~numpyro.infer.mcmc.hmc`.

    :param int lower: the index to start the collective work. In other words,
        we will skip collecting the first `lower` values.
    :param int upper: number of times to run the loop body.
    :param body_fun: a callable that takes a collection of
        `np.ndarray` and returns a collection with the same shape and
        `dtype`.
    :param init_val: initial value to pass as argument to `body_fun`. Can
        be any Python collection type containing `np.ndarray` objects.
    :param transform: a callable to post-process the values returned by `body_fn`.
    :param progbar: whether to post progress bar updates.
    :param bool return_last_val: If `True`, the last value is also returned.
        This has the same type as `init_val`.
    :param thinning: Positive integer that controls the thinning ratio for retained
        values. Defaults to 1, i.e. no thinning.
    :param int collection_size: Size of the returned collection. If not
        specified, the size will be ``(upper - lower) // thinning``. If the
        size is larger than ``(upper - lower) // thinning``, only the top
        ``(upper - lower) // thinning`` entries will be non-zero.
    :param `**progbar_opts`: optional additional progress bar arguments. A
        `diagnostics_fn` can be supplied which when passed the current value
        from `body_fun` returns a string that is used to update the progress
        bar postfix. Also a `progbar_desc` keyword argument can be supplied
        which is used to label the progress bar.
    :return: collection with the same type as `init_val` with values
        collected along the leading axis of `np.ndarray` objects.
    rO   Nra   ZgpuzRWe will disable progress bar because it does not work yet on multi-GPUs platforms.
stacklevelFc                    sL   |\}}}| | |  t  dk| fdd|t}|||fS )Nr   c                    s   | j   td S )Nr   )atsetr   r;   )idxrZ   r3   r   r   ri   H  rk   z0fori_collect.<locals>._body_fn.<locals>.<lambda>)r.   r@   )r9   rs   
collection	start_idxthinningr1   rZ   )r}   r3   r   _body_fn@  s    zfori_collect.<locals>._body_fn)dtyper   diagnostics_fnprogbar_descc                 S   s   dS )Nr   r   r;   r   r   r   ri   ]  rk   zfori_collect.<locals>.<lambda>rQ   )r   popr   Zdefault_backendwarningswarnfind_stack_levelrM   fori_collectjnpZzerosshaper   r6   rx   r   r   r   ZtrangerW   Zset_postfix_strr	   )r7   r8   r1   r2   rZ   ZprogbarZreturn_last_valZcollection_sizer   Zprogbar_optsZinit_val_flatZ
unravel_fnr   ra   r   r~   Zlast_valrg   rv   Z_body_fn_pbarr   r   rs   tr9   Zunravel_collectionr   r   r   r     sV    -



:r   c                    s`  t |d }t|d d  |dd D ]}t|d  ks.J q.tt }dkrnfndt fdd|}du rntdkr  tfdd|} tdk }|dkrdndtfd	d|}t| } |dkrt	| |n| |}t|dktdk tfd
d|}tfdd|S )a>  
    Vectorizing map that maps a function `fn` over `batch_ndims` leading axes
    of `xs`. This uses jax.vmap over smaller chunks of the batch dimensions
    to keep memory usage constant.

    :param callable fn: The function to map over.
    :param xs: JAX pytree (e.g. an array, a list/tuple/dict of arrays,...)
    :param int batch_ndims: The number of leading dimensions of `xs`
        to apply `fn` element-wise over them.
    :param int chunk_size: Size of each chunk of `xs`.
        Defaults to the size of batch dimensions.
    :returns: output of `fn(xs)`.
    r   NrO   r   c                    s   t | t |  d   S r-   r   reshaper   r;   )batch_ndimsprepend_shaper   r   ri     rk   zsoft_vmap.<locals>.<lambda>c                    s$   t | d ffdt| d   S )Nr   ))r   r   rO   )r   padr   ndimr;   )r   r   r   ri     rk   )c                    s$   t |  f t | dd   S rr   r   r;   )
chunk_sizer   r   r   ri     rk   c              	      s@   t | ttt | d  ft | d   d   S r-   )r   r   r5   r   prodr   y)
batch_size	map_ndimsr   r   ri     s   0c                    s   t |  t | dd   S rr   r   r   )batch_shaper   r   ri     rk   )
r   r   r   r5   r   r   minr	   r   map)rF   Zxsr   r   Z
flatten_xsr<   Z
num_chunksZysr   )r   r   r   r   r   r   r   r   	soft_vmapq  s:    
r   zTrace Shapes:)compute_log_probtitle	last_sitec             	   C   s,  |   s|S |gg}|dg |  D ]H\}}|d dkrd||dgdd t|d dd	D   ||kr( qrq(|d
g |  D ]\}}|d dkrt|d dd	}t|d dd	}|| ddgdd |D  ddg dd |D   t|}	t|d dd	}
|
dt|
|	  }|
t|
|	 d }|ddgdd |D  ddg dd |D   t|sx|st|r||rt|d |d dd	}|ddgdd |D  ddg  nH|d dkrt|d dd	}
|| ddgdd |
D  ddg  ||kr q$qt|S )a  
    Given the trace of a function, returns a string showing a table of the shapes of
    all sites in the trace.

    Use :class:`~numpyro.handlers.trace` handler (or funsor
    :class:`~numpyro.contrib.funsor.enum_messenger.trace` handler for enumeration) to
    produce the trace.

    :param dict trace: The model trace to format.
    :param compute_log_prob: Compute log probabilities and display the shapes in the
        table. Accepts True / False or a function which when given a dictionary
        containing site-level metadata returns whether the log probability should be
        calculated and included in the table.
    :param str title: Title for the table of shapes.
    :param str last_site: Name of a site in the model. If supplied, subsequent sites
        are not displayed in the table.

    Usage::

        def model(*args, **kwargs):
            ...

        with numpyro.handlers.seed(rng_seed=1):
            trace = numpyro.handlers.trace(model).get_trace(*args, **kwargs)
        print(numpyro.util.format_shapes(trace))
    zParam Sites:typeparamNc                 S   s   g | ]}t |qS r   rU   .0sizer   r   r   
<listcomp>  rk   z!format_shapes.<locals>.<listcomp>valuer   r   zSample Sites:samplerF   r   event_shapez distc                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   |c                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   c                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   c                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   log_probc                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   platez platec                 S   s   g | ]}t |qS r   r   r   r   r   r   r     rk   )rH   r^   itemsrL   rD   callabler   _format_table)tracer   r   r   rowsnamesiter   r   	event_dimr   r   r   r   format_shapes  sz    !"r   loosec              
   C   s   |dv sJ |   D ]\}}|d dkrt|d }tt|d jt|d d |t|d j	  }t
dd |d D }t|}t|D ]}| d	 }	||	 d	kr|	|vr|	| kr|d
drqd|	 d| d}
|dkrt|
q|dkst|dkrtj|
t d qqd S )N)r   stricterrorr   r   r   rF   c                 s   s   | ]}|j V  qd S r-   )dim)r   fr   r   r   	<genexpr>  rk   z"_validate_model.<locals>.<genexpr>Zcond_indep_stackrO   Z_control_flow_doneFz.Missing a plate statement for batch dimension z
 at site 'z_'. You can use `numpyro.util.format_shapes` utility to check shapes at all sites of your model.r   r   r   ry   )r   r   r   r   Zbroadcast_shapestupler   r   rD   r   r|   r4   get
ValueErrorr   r   r   )model_traceZplate_warningr   r   Z
value_ndimr   Z
plate_dimsZ
batch_ndimr9   r   messager   r   r   _validate_model  s.    "

r   c                 C   s  t dd | D }t dd | D }t dd |  D }t  }||@ rjtjd||@ t d |||B kstjd|| | t d |||B kstjd|| | t d ||@ D ]}| | }|| }t|d	 d
r.t|d	 d
r.|d	 j|d	 jkr.td||d	 j|d	 jt|d	 drt|d	 dr|d	 	|d d }	|d	 	|d d }
|	|
krqt
t|	t|
ddD ]&\}}||krtd||	|
qqt dd |  D }t dd | D }||kstjd|| t d dS )a  
    Checks the following assumptions:

    1. Each sample site in the model also appears in the guide and is not
        marked auxiliary.
    2. Each sample site in the guide either appears in the model or is marked,
        auxiliary via ``infer={'is_auxiliary': True}``.
    3. Each :class:`~numpyro.primitives.plate` statement in the guide also
        appears in the model.
    4. At each sample site that appears in both the model and guide, the model
        and guide agree on sample shape.

    :param dict model_trace: The model trace to check.
    :param dict guide_trace: The guide trace to check.
    :raises: RuntimeWarning, ValueError
    c                 s   s.   | ]&\}}|d  dkr| dds|V  qdS r   r   Zis_observedFNr   r   r   r   r   r   r   r   5  s   z*check_model_guide_match.<locals>.<genexpr>c                 s   s0   | ](\}}|d  dkr|d  dr|V  qdS )r   r   ZinferZis_auxiliaryNr   r   r   r   r   r   :  s   c                 s   s.   | ]&\}}|d  dkr| dds|V  qdS r   r   r   r   r   r   r   @  s   z%Found auxiliary vars in the model: {}ry   zjFound non-auxiliary vars in guide but not model, consider marking these infer={{'is_auxiliary': True}}:
{}z%Found vars in model but not guide: {}rF   r   z:Model and guide event_dims disagree at site '{}': {} vs {}r   r?   Zsample_shaperO   )	fillvaluez6Model and guide shapes disagree at site '{}': {} vs {}c                 s   s"   | ]\}}|d  dkr|V  qdS r   r   Nr   r   r   r   r   r   }  s   c                 s   s"   | ]\}}|d  dkr|V  qdS r   r   r   r   r   r   r     s   z1Found plate statements in guide but not model: {}N)r|   r   r   r   r%   r   hasattrr   r   r   r   reversed)r   Zguide_traceZ
guide_varsZaux_varsZ
model_varsZ	enum_varsr   Z
model_siteZ
guide_siteZmodel_shapeZguide_shapeZ
model_sizeZ
guide_sizer   r   r   check_model_guide_match#  s    





r   c                    s8  g d}| D ]`}g d}d}|D ]&}|du r6|d7 }q ||  d7  < q t dD ]}t|| || ||< qPqt| D ]d\}}g g g g}d}|D ]$}|du r|d7 }q|| | qdd t||dD }t|g | |< qvdgt| d   | D ],}t|D ]\}}t | t| |< qqd	 fd
d| D S )zI
    Formats a right justified table using None as column separator.
    )r   r   r   r   NrO      c                 S   sF   g | ]>\}}}|d kr,dg|t |  | n|dg|t |   qS )rr   )rD   )r   widthcol	directionr   r   r   r     s   z!_format_table.<locals>.<listcomp>Zrrl
c                 3   s(   | ] }d  dd t| D V  qdS )r    c                 s   s   | ]\}}| |V  qd S r-   )rjust)r   cellr   r   r   r   r     rk   z*_format_table.<locals>.<genexpr>.<genexpr>N)r$   zip)r   rowZcell_widthsr   r   r     s   z _format_table.<locals>.<genexpr>)r4   max	enumerater^   r   sumrD   r$   )r   Zcolumn_widthsr   widthsjr   r9   colsr   r   r   r     s8    



r   c                 C   s   t dd | dD S )z
    :param str version: Version, in string format.
    Parse version string into tuple of ints.

    Only to be used for the standard 'major.minor.patch' format,
    such as ``'0.2.13'``.

    Source: https://stackoverflow.com/a/11887825/4451315
    c                 S   s   g | ]}t |qS r   )r5   )r   numberr   r   r   r     rk   z!_versiontuple.<locals>.<listcomp>.)r   r#   )versionr   r   r   _versiontuple  s    
r   )returnc                  C   sR   ddl } tj| j}t }d}|rNt|}||rN|j	}|d7 }q"qNq"|S )z
    Find the first place in the stack that is not inside numpyro
    (tests notwithstanding).

    Source:
    https://github.com/pandas-dev/pandas/blob/ccb25ab1d24c4fb9691270706a59c8d319750870/pandas/util/_exceptions.py#L27-L48
    r   NrO   )
numpyror   pathdirname__file__inspectcurrentframegetfile
startswithf_back)r   Zpkg_dirframer'   fnamer   r   r   r     s    


r   )T)N)rO   N)r   )9collectionsr   
contextlibr   r   	itertoolsr   r   r   r!   r   numpyr   r   Z	tqdm.autorw   r   r   r   r   r	   Zjax.corer
   Zjax.experimentalr   Zjax.flatten_utilr   Z	jax.numpyr   Zjax.tree_utilr   r   r+   compilerS   r   r   r   r(   r*   r,   r.   r0   r6   r=   r@   rM   rx   r   r   r   r   r   r   r   r5   r   r   r   r   r   <module>   sd   









V
p
7_
i/