B
    `m                 @   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ZddlZddl	m
  mZ ddlm
  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dddddddddddddddddd d!d"gZd#ZG d$d deZ dAd%dZ!dBd&dZ"dCd'dZ#dDd(dZ$dEd)dZ%d*d Z&d+d Z'd,d Z(dFd-d.Z)dGd/dZ*d0d  Z+ej, dfd1dZ-d2d Z.dHd3d4Z/dId6dZ0dJd8dZ1dKd9d!Z2d:d Z3d;d Z4d<d Z5d=d> Z6d?d" Z7dLd@dZ8dS )Mz>Internal utility functions for implementing TransitionKernels.    )absolute_import)division)print_functionN)distribution_util)
dtype_util)prefer_static)tensorshape_util)value_and_gradient)control_flow_util)deprecationchoose"enable_store_parameters_in_resultsleft_justified_expand_dims_likeleft_justified_expand_dims_toleft_justified_broadcast_likeleft_justified_broadcast_toindex_remapping_gatheris_list_likeis_namedtuple_likemake_innermost_gettermake_innermost_setter	make_namemaybe_call_fn_and_gradsprepare_state_partsPrettyNamedTupleMixinsafe_sumSEED_CTOR_ARG_DEPRECATION_MSGset_docsmart_for_loopstrip_seeds
trace_scan)warn_if_parameters_are_not_simple_tensorszSeeding `tfp.mcmc.TransitionKernel` instances by constructor argument is deprecated. Use the `seed` argument to `tfp.mcmc.sample_chain` or directly on `one_step`. The legacy behavior is still supported and should be through 2020-09-20.c               @   s   e Zd ZdZdZdd ZdS )r   z2Mixin adding a nicer `__repr__` for `namedtuple`s. c             C   s*   d t| jddd |   D S )Nz{}(
{}
)z,
c             s   s*   | ]"\}}d  |t|ddV  qdS )z  {}={}
z
    N)formatreprreplace).0kvr"   r"   _/home/dcms/DCMS/lib/python3.7/site-packages/tensorflow_probability/python/mcmc/internal/util.py	<genexpr>N   s   z1PrettyNamedTupleMixin.__repr__.<locals>.<genexpr>)r$   type__name__join_asdictitems)selfr"   r"   r*   __repr__K   s    
zPrettyNamedTupleMixin.__repr__N)r-   
__module____qualname____doc__	__slots__r2   r"   r"   r"   r*   r   G   s   c          	   C   s*   t |p
d t| t|S Q R X dS )z5Right pads `x` with `rank(reference) - rank(x)` ones.r   N)tf
name_scoper   r   rank)x	referencenamer"   r"   r*   r   R   s    c          	   C   sr   t |p
dZ t j|t jd}t|t|  d}tjt| tj	|gt jdgdd}t
| |S Q R X dS )z*Right pads `x` with `rank - rank(x)` ones.r   )dtyper   )shaper=   )axisN)r7   r8   convert_to_tensorint32r   maximumr9   concatr>   ZonesZreshape)r:   r9   r<   Zexpand_ndimsZexpand_shaper"   r"   r*   r   X   s    c          	   C   s*   t |p
d t| t|S Q R X dS )zABroadcasts `x` to shape of reference, in a left-justified manner.r   N)r7   r8   r   r   r>   )r:   r;   r<   r"   r"   r*   r   d   s    c          	   C   s2   t |p
d t t| t||S Q R X dS )z4Broadcasts `x` to shape, in a left-justified manner.r   N)r7   r8   broadcast_tor   r   size)r:   r>   r<   r"   r"   r*   r   j   s    c                s2   t | }|r| n| g} fdd|D }||fS )zHCalls c2t on each element or the entirety if not iterable; returns list.c                s   g | ]}t j| d qS ))r=   r<   )r7   r@   )r'   r:   )r=   r<   r"   r*   
<listcomp>v   s   z'prepare_state_parts.<locals>.<listcomp>)r   )Zstate_or_state_partr=   r<   is_multipartZstate_partsr"   )r=   r<   r*   r   q   s
    c             C   s   t | ttfS )z4Helper which returns `True` if input is `list`-like.)
isinstancetuplelist)r:   r"   r"   r*   r   {   s    c             C   s8   yx| j D ]}t| |}q
W dS  tk
r2   dS X dS )zFHelper which returns `True` if input is `collections.namedtuple`-like.TFN)_fieldsgetattrAttributeError)r:   fn_r"   r"   r*   r      s    c             C   s(   | dk	r| n|}|dk	r$|d| 7 }|S )z:Helper which makes a `str` name; useful for tf.name_scope.NrO   r"   )Z
super_nameZdefault_super_nameZsub_namer<   r"   r"   r*   r      s    c          	      sT   fdd t pd0 t|s. ||S  fddt||D S Q R X dS )zHHelper to `choose` which expand_dims `is_accepted` and applies tf.where.c                sR   | |kr| S t |dd}|dk	r:|dd ddd }tjt | | ||dS )	zWraps `tf.where`.r<   N/   :   r   )r<   )rL   
rpartitionrsplitr7   wherer   )proposedcurrentr<   )is_acceptedr"   r*   _where   s    z!_choose_base_case.<locals>._wherer   c                s2   g | ]*\}}t |r$t||d n ||qS ))r<   )r   r   )r'   pc)rZ   rY   r<   r"   r*   rF      s   z%_choose_base_case.<locals>.<listcomp>N)r7   r8   r   zip)rY   rW   rX   r<   r"   )rZ   rY   r<   r*   _choose_base_case   s    
r^   c          	   C   s   t |p
d t|s(t| |||dS t|t|sRtdt|jt|ji }x.|j	D ]$}t
| t||t|||d||< q^W t|f |S Q R X dS )z=Helper which expand_dims `is_accepted` then applies tf.where.r   )r<   zCType of `proposed` ({}) must be identical to type of `current` ({})N)r7   r8   r   r^   rH   r,   	TypeErrorr$   r-   rK   r   rL   )rY   rW   rX   r<   r0   rN   r"   r"   r*   r      s    c             C   s,   t | s| S t| f dd |   D S )Nc             S   s&   i | ]\}}|d krt |ng |qS )seed)r   )r'   rN   Zfvr"   r"   r*   
<dictcomp>   s   zstrip_seeds.<locals>.<dictcomp>)r   r,   r/   r0   )objr"   r"   r*   r      s    c          	   C   s|   t |p
dd t| s td| s,td| d j}t | } t t j	| | t j
|| jd} t| | | S Q R X dS )a  Elementwise adds list members, replacing non-finite results with alt_value.

  Typically the `alt_value` is chosen so the `MetropolisHastings`
  `TransitionKernel` always rejects the proposal.

  Args:
    x: Python `list` of `Tensors` to elementwise add.
    alt_value: Python scalar used to replace any elementwise sums which would
      otherwise be non-finite.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., "safe_sum").

  Returns:
    safe_sum: `Tensor` representing the elementwise sum of list of `Tensor`s
      `x` or `alt_value` where sums are non-finite.

  Raises:
    TypeError: if `x` is not list-like.
    ValueError: if `x` is empty.
  r   zExpected list input.zInput should not be empty.r   )r=   N)r7   r8   r   r_   
ValueErrorr>   Zadd_nrV   math	is_finiteZconstantr=   r   	set_shape)r:   Z	alt_valuer<   Zin_shaper"   r"   r*   r      s    

"c                s    fdd}|S )z7Decorator to programmatically set a function docstring.c                s
    | _ | S )N)r5   )func)valuer"   r*   _doc   s    zset_doc.<locals>._docr"   )rh   ri   r"   )rh   r*   r      s    c          	      s   t |p
d dd }tr(tng|d|dkrf  }|dkrft  rfdd D ||d}|dk	r||d	}||fS t|rt|tkr· fd
dfddtt|D }nt \}}||fS Q R X dS )z$Helper to `maybe_call_fn_and_grads`.Zvalue_and_gradientsc                s.   fdd t | r& fdd| D S  | S )Nc                s   | d krd S t j|  dS )N)r<   )r7   r@   )x_)r<   r"   r*   <lambda>   s   zB_value_and_gradients.<locals>._convert_to_tensor.<locals>.<lambda>c                s   g | ]} |qS r"   r"   )r'   rj   )cttr"   r*   rF      s    zD_value_and_gradients.<locals>._convert_to_tensor.<locals>.<listcomp>)r   )r:   r<   r"   )rl   r<   r*   _convert_to_tensor   s    z0_value_and_gradients.<locals>._convert_to_tensorZfn_argNc             S   s   g | ]}d | qS )r   r"   )r'   r:   r"   r"   r*   rF     s    z(_value_and_gradients.<locals>.<listcomp>Z	fn_resultZfn_gradc                s    fddS )z6Needed to prevent `cell-var-from-loop` pylint warning.c                s&    d  | g d d    S )NrS   r"   )r:   )rN   fn_arg_listir"   r*   rk         z8_value_and_gradients.<locals>.fn_slice.<locals>.<lambda>r"   )ro   )rN   rn   )ro   r*   fn_slice  s    z&_value_and_gradients.<locals>.fn_slicec                s"   g | ]}t | | d  qS )rS   )tfp_math_value_and_gradients)r'   ro   )rn   rq   r"   r*   rF     s   )r7   r8   r   rJ   executing_eagerlylenrangerr   )rN   rn   resultgradsr<   rm   rO   r"   )rN   rn   rq   r*   _value_and_gradients   s&    


rx   Tc          	   C   s   t |p
d t|r t|n|g}t| |||\}}tdd t|rL|n|gD s`tdt|t|krxtd|rt	dd |D rtd
||||fS Q R X dS )	zCCalls `fn` and computes the gradient of the result wrt `args_list`.r   c             s   s   | ]}t |jV  qd S )N)r   Zis_floatingr=   )r'   rr"   r"   r*   r+   &  s   z*maybe_call_fn_and_grads.<locals>.<genexpr>z8Function result must be a `Tensor` with `float` `dtype`.z>Function args must be in one-to-one correspondence with grads.c             s   s   | ]}|d kV  qd S )Nr"   )r'   gr"   r"   r*   r+   -  s    z:Encountered `None` gradient.
  fn_arg_list: {}
  grads: {}N)r7   r8   r   rJ   rx   allr_   rt   rc   anyr$   )rN   rn   rv   rw   Zcheck_non_none_gradsr<   r"   r"   r*   r     s    

   c          	      s   t |p
d t }|dks8t  s8tt r~t jt j	dt j
fdd fddt	dg| |dd	d S |}xt|D ]} | }qW |S Q R X dS )
a  Construct a for loop, preferring a python loop if `n` is staticaly known.

  Given `loop_num_iter` and `body_fn`, return an op corresponding to executing
  `body_fn` `loop_num_iter` times, feeding previous outputs of `body_fn` into
  the next iteration.

  If `loop_num_iter` is statically known, the op is constructed via python for
  loop, and otherwise a `tf.while_loop` is used.

  Args:
    loop_num_iter: `Integer` `Tensor` representing the number of loop
      iterations.
    body_fn: Callable to be executed `loop_num_iter` times.
    initial_loop_vars: Listlike object of `Tensors` to be passed in to
      `body_fn`'s first execution.
    parallel_iterations: The number of iterations allowed to run in parallel.
      It must be a positive integer. See `tf.while_loop` for more details.
      Default value: `10`.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., "smart_for_loop").
  Returns:
    result: `Tensor` representing applying `body_fn` iteratively `n` times.
  r   N)r=   c                s   |  k S )Nr"   )ro   args)loop_num_iterr"   r*   rk   V  rp   z smart_for_loop.<locals>.<lambda>c                s   | d gt  |  S )NrS   )rJ   )ro   r~   )body_fnr"   r*   rk   W  rp   r   )condbody	loop_varsparallel_iterationsrS   )r7   r8   get_static_valuers   r
   ZGraphOrParentsInXlaContexttf1Zget_default_graphcastrA   
while_loopnpru   )r   r   Zinitial_loop_varsr   r<   Zloop_num_iter_rv   rO   r"   )r   r   r*   r   4  s     




c          
      s  t |p
dj tt N}|jdkrDt  sD|dd  t j	dd |}t j
|dd}t|t j|j|jdd d	|d
\ dkrt   n|rd|  t j	 fdd|}	fddfdd}
t jfdd|
d|d|	f|d\}}}}	t j	dd |	}t  rHdnfdd}t j	||}||fS Q R X W dQ R X dS )a  A simplified version of `tf.scan` that has configurable tracing.

  This function repeatedly calls `loop_fn(state, elem)`, where `state` is the
  `initial_state` during the first iteration, and the return value of `loop_fn`
  for every iteration thereafter. `elem` is a slice of `elements` along the
  first dimension, accessed in order. Additionally, it calls `trace_fn` on the
  return value of `loop_fn`. The `Tensor`s in return values of `trace_fn` are
  stacked and returned from this function, such that the first dimension of
  those `Tensor`s matches the size of `elems`.

  Args:
    loop_fn: A callable that takes in a `Tensor` or a nested collection of
      `Tensor`s with the same structure as `initial_state`, a slice of `elems`
      and returns the same structure as `initial_state`.
    initial_state: A `Tensor` or a nested collection of `Tensor`s passed to
      `loop_fn` in the first iteration.
    elems: A `Tensor` that is split along the first dimension and each element
      of which is passed to `loop_fn`.
    trace_fn: A callable that takes in the return value of `loop_fn` and returns
      a `Tensor` or a nested collection of `Tensor`s.
    trace_criterion_fn: Optional callable that takes in the return value of
      `loop_fn` and returns a boolean `Tensor` indicating whether to trace it.
      If `None`, all steps are traced.
      Default value: `None`.
    static_trace_allocation_size: Optional Python `int` size of trace to
      allocate statically. This should be an upper bound on the number of steps
      traced and is used only when the length cannot be
      statically inferred (for example, if a `trace_criterion_fn` is specified).
      It is primarily intended for contexts where static shapes are required,
      such as in XLA-compiled code.
      Default value: `None`.
    parallel_iterations: Passed to the internal `tf.while_loop`.
    name: Name scope used in this function. Default: 'trace_scan'.

  Returns:
    final_state: The final return value of `loop_fn`.
    trace: The same structure as the return value of `trace_fn`, but with each
      `Tensor` being a stack of the corresponding `Tensors` in the return value
      of `trace_fn` for each slice of `elems`.
  r    Nc             S   s   | j S )N)Zdevice)opr"   r"   r*   rk     rp   ztrace_scan.<locals>.<lambda>c             S   s   t j| ddS )Ninitial_state)r<   )r7   r@   )r:   r"   r"   r*   rk     rp   elems)r<   rS   )rE   element_shape)Tr   Fc                s   t j| j | jdS )N)rE   dynamic_sizer   )r7   TensorArrayr=   r>   )r:   )r   initial_sizer"   r*   rk     s   c                s   t j fdd||S )Nc                s   |   |S )N)write)tar:   )num_steps_tracedr"   r*   rk     rp   z4trace_scan.<locals>.trace_one_step.<locals>.<lambda>)r7   nestmap_structure)r   trace_arraysstate)trace_fn)r   r*   trace_one_step  s    
z"trace_scan.<locals>.trace_one_stepc                sZ    | }|tr$nd fdd fdd\ | d  fS )NTc                  s     d fS )NrS   r"   r"   )r   r   r   r   r"   r*   rk     s   
z+trace_scan.<locals>._body.<locals>.<lambda>c                  s    fS )Nr"   r"   )r   r   r"   r*   rk     rp   rS   )readr   r   )ro   r   r   r   elem)elems_arrayloop_fntrace_criterion_fnr   )r   r   r   r*   _body  s    

ztrace_scan.<locals>._bodyc                s   |  k S )Nr"   )ro   rO   )lengthr"   r*   rk     rp   r   )r   r   r   r   c             S   s   |   S )N)stack)r:   r"   r"   r*   rk     rp   c                s    t |  | jdd   | S )NrS   )r   rf   Zconcatenater>   )r:   )static_lengthr"   r*   _merge_static_length  s    z(trace_scan.<locals>._merge_static_length)r7   r8   r   Zvariable_scopeZget_variable_scopeZcaching_devicers   Zset_caching_devicer   r   r@   r   Zsize0r   r=   r>   ZunstackZ	is_tensorr   ZTensorShape)r   r   r   r   r   Zstatic_trace_allocation_sizer   r<   vsr   r   rO   Zfinal_stateZstacked_tracer   r"   )	r   r   r   r   r   r   r   r   r   r*   r    a  s@    0





c                s   t   fdd}|S )a  Wraps a setter so it applies to the inner-most results in `kernel_results`.

  The wrapped setter unwraps `kernel_results` and applies `setter` to the first
  results without an `inner_results` attribute.

  Args:
    setter: A callable that takes the kernel results as well as some `*args` and
      `**kwargs` and returns a modified copy of those kernel results.

  Returns:
    new_setter: A wrapped `setter`.
  c                sV   g }xt | dr"||  | j} qW  | f||}xt|D ]}|j|d}q>W |S )zWrapped setter.inner_results)r   )hasattrappendr   reversed_replace)kernel_resultsr~   kwargsresults_stackZnew_kernel_resultsZouter_results)setterr"   r*   _new_setter  s    

z*make_innermost_setter.<locals>._new_setter)	functoolswraps)r   r   r"   )r   r*   r     s    c                s   t   fdd}|S )an  Wraps a getter so it applies to the inner-most results in `kernel_results`.

  The wrapped getter unwraps `kernel_results` and returns the return value of
  `getter` called with the first results without an `inner_results` attribute.

  Args:
    getter: A callable that takes Kernel results and returns some value.

  Returns:
    new_getter: A wrapped `getter`.
  c                s4   g }xt | dr"||  | j} qW  | f||S )zWrapped getter.r   )r   r   r   )r   r~   r   r   )getterr"   r*   _new_getter  s
    

z*make_innermost_getter.<locals>._new_getter)r   r   )r   r   r"   )r   r*   r     s    
c             C   sv   g }x,t | dr0d| jkr0||  | jd } qW dd }t | drN|| i } x"t|D ]}||d| i}|} qXW | S )a  Enables the `store_parameters_in_results` parameter in a chain of kernels.

  This is a temporary utility for use during the transition period of the
  parameter storage methods.

  Args:
    kernel: A TransitionKernel.

  Returns:
    kernel: The same kernel, but recreated with `store_parameters_in_results`
        recursively set to `True` in its parameters and its inner kernels (as
        appropriate).
  
parametersZinner_kernelc          	   S   sF   | j  }|| d|kr$d|d< t  t| f |S Q R X d S )NZstore_parameters_in_resultsT)r   copyupdater   Zsilencer,   )kernelr   Znew_parametersr"   r"   r*   _recreate_kernel#  s    


z<enable_store_parameters_in_results.<locals>._recreate_kernel)r   r   r   r   )r   Zkernel_stackr   Zouter_kernelr"   r"   r*   r     s    


c             C   s8   t | rtdd | D S t| tjp6t| jtjkS )Nc             S   s   g | ]}t |qS r"   )_is_tensor_like)r'   r[   r"   r"   r*   rF   7  s    z#_is_tensor_like.<locals>.<listcomp>)	r   r{   rH   r7   ZTensorr   arrayr=   object)paramr"   r"   r*   r   5  s    r   c             C   s2   x,|   D ] \}}t|s
td| q
W d S )NaY  `{}` is not a `tf.Tensor`, Python number, or Numpy array. If this parameter is mutable (e.g., a `tf.Variable`), then the behavior implied by `store_parameters_in_results` will silently change on 2019-08-01. Please consult the docstring for `store_parameters_in_results` details and use `store_parameters_in_results=True` to silence this warning.)r0   r   warningswarnr$   )params_dict
param_namer   r"   r"   r*   r!   ;  s
    c          	   C   s  t | t j| dd} t j|dd}t| j}t|j}t t j|t jd}t t j|t jd}|dkr~td|dkrtd|dkrtd||krtd		|||d
k rtd	||dk	r|d
k rtd	||dk	r|| || krtd	||||t
j||dd}|||  }t
j| ||d
 d}	t|	}
tj|
d|d
  t|||d
  |
|d gdd}t||d| }t j|	||d
 |d
 d}t
j||d
 |dS Q R X dS )ad  Gather values from `axis` of `params` using `indices_axis` of `indices`.

  The shape of `indices` must broadcast to that of `params` when
  their `indices_axis` and `axis` (respectively) are aligned:

  ```python
  # params.shape:
  [p[0],  ..., ...,         p[axis], ..., ..., p[rank(params)] - 1])
  # indices.shape:
        [i[0], ..., i[indices_axis], ..., i[rank(indices)] - 1])
  ```

  In particular, `params` must have at least as many
  leading dimensions as `indices` (`axis >= indices_axis`), and at least as many
  trailing dimensions (`rank(params) - axis >= rank(indices) - indices_axis`).

  The `result` has the same shape as `params`, except that the dimension
  of size `p[axis]` is replaced by one of size `i[indices_axis]`:

  ```python
  # result.shape:
  [p[0],  ..., ..., i[indices_axis], ..., ..., p[rank(params) - 1]]
  ```

  In the case where `rank(params) == 5`, `rank(indices) == 3`, `axis = 2`, and
  `indices_axis = 1`, the result is given by

   ```python
   # alignment is:                       v axis
   # params.shape    ==   [p[0], p[1], p[2], p[3], p[4]]
   # indices.shape   ==         [i[0], i[1], i[2]]
   #                                     ^ indices_axis
   result[i, j, k, l, m] = params[i, j, indices[j, k, l], l, m]
  ```

  Args:
    params:  `N-D` `Tensor` (`N > 0`) from which to gather values.
      Number of dimensions must be known statically.
    indices: `Tensor` with values in `{0, ..., params.shape[axis] - 1}`, whose
      shape broadcasts to that of `params` as described above.
    axis: Python `int` axis of `params` from which to gather.
    indices_axis: Python `int` axis of `indices` to align with the `axis`
      over which `params` is gathered.
    name: String name for scoping created ops.

  Returns:
    `Tensor` composed of elements of `params`.

  Raises:
    ValueError: If shape/rank requirements are not met.
  params)r<   indices)r=   NzoRank of `params`, must be known statically. This is due to tf.gather not accepting a `Tensor` for `batch_dims`.zd`axis` must be known statically. This is due to tf.gather not accepting a `Tensor` for `batch_dims`.zl`indices_axis` must be known statically. This is due to tf.gather not accepting a `Tensor` for `batch_dims`.z3`indices_axis` should be <= `axis`, but was {} > {}rS   z*Rank of params should be `> 0`, but was {}z+Rank of indices should be `> 0`, but was {}za`rank(params) - axis` ({} - {}) must be >= `rank(indices) - indices_axis` ({} - {}), but was not.)Z
source_idxZdest_idxr   )r?   )Z
batch_dimsr?   )r7   r8   r@   r   r9   r>   r   int64rc   r$   	dist_utilZmove_dimensionr   rC   rD   Zgather)r   r   r?   Zindices_axisr<   Zparams_ndimsZindices_ndimsZtransposed_indicesZbroadcast_indices_ndimsZtransposed_paramsZtransposed_params_shapeZresult_shapeZbroadcast_indicesZresult_tr"   r"   r*   r   H  sn    8


)N)N)N)N)NN)N)N)NNN)NNTN)r}   N)NNr}   N)r   r   r   )9r5   
__future__r   r   r   r   r   numpyr   Ztensorflow.compat.v1compatZv1r   Ztensorflow.compat.v2Zv2r7   Z&tensorflow_probability.python.internalr   r   r   r   r   Z+tensorflow_probability.python.math.gradientr	   rr   Ztensorflow.python.opsr
   Ztensorflow.python.utilr   __all__r   r   r   r   r   r   r   r   r   r   r   r^   r   r   infr   r   rx   r   r   r    r   r   r   r   r!   r   r"   r"   r"   r*   <module>   s   








!
.   

0   
n %  