def bootstrap_results(self, state):
    """Trains the bijector and creates initial `previous_kernel_results`.

    The supplied `state` is only used to determine the number of chains to run
    in parallel_iterations

    Args:
      state: `Tensor` or Python `list` of `Tensor`s representing the initial
        state(s) of the Markov chain(s). The first `r` dimensions index
        independent chains, `r = tf.rank(target_log_prob_fn(*state))`.

    Returns:
      kernel_results: Instance of
        `UncalibratedHamiltonianMonteCarloKernelResults` inside
        `MetropolisHastingsResults` inside `TransformedTransitionKernelResults`
        inside `SimpleStepSizeAdaptationResults`.
    """

    def loss():
      q = self._flattened_variational_distribution()
      # TODO(siege): How to seed this?
      samples = q.sample(self.train_batch_size)
      return tf.reduce_mean(
          input_tensor=q.log_prob(samples) -
          self._flattened_target_log_prob(samples),
          axis=-1)

    lr = tf.convert_to_tensor(value=self.learning_rate, dtype=self._dtype)
    dtype = lr.dtype

    learning_rate = tf.compat.v2.optimizers.schedules.PiecewiseConstantDecay(
        list(self.num_train_steps *
             np.array([0.2, 0.8]).astype(dtype.as_numpy_dtype())),
        [lr, lr * 0.1, lr * 0.01])

    opt = tf.compat.v2.optimizers.Adam(learning_rate)

    @tf.function(autograph=False)
    def train_step():
      with tf.GradientTape() as tape:
        loss_val = loss()
      vals = tape.watched_variables()
      grads = tape.gradient(loss_val, vals)
      grads_and_vals = list(zip(grads, vals))
      opt.apply_gradients(grads_and_vals)
      return loss_val

    for step in range(self.num_train_steps):
      loss_val = train_step()
      tf.debugging.assert_all_finite(
          loss_val, 'NeuTra loss is NaN at step {}'.format(step))
      if self.train_debug_fn:
        # pylint: disable=not-callable
        self.train_debug_fn(self, step, loss_val)

    state_parts = tf.nest.flatten(state)
    flat_state_shapes = tf.nest.flatten(self.state_shape)
    batch_shape = tf.shape(input=state_parts[0])[:-flat_state_shapes[0].ndims]

    return self._kernel.bootstrap_results(
        self._flattened_variational_distribution().sample(
            batch_shape, seed=self.seed))

def
bootstrap_results
(
self
,
state
)
:
def
loss
(
)
:
q
=
self
.
_flattened_variational_distribution
(
)
samples
=
q
.
sample
(
self
.
train_batch_size
)
return
tf
.
reduce_mean
(
input_tensor
=
q
.
log_prob
(
samples
)
self
.
_flattened_target_log_prob
(
samples
)
,
axis
=
1
)
lr
=
tf
.
convert_to_tensor
(
value
=
self
.
learning_rate
,
dtype
=
self
.
_dtype
)
dtype
=
lr
.
dtype
learning_rate
=
tf
.
compat
.
v2
.
optimizers
.
schedules
.
PiecewiseConstantDecay
(
list
(
self
.
num_train_steps
*
np
.
array
(
[
0.2
,
0.8
]
)
.
astype
(
dtype
.
as_numpy_dtype
(
)
)
)
,
[
lr
,
lr
*
0.1
,
lr
*
0.01
]
)
opt
=
tf
.
compat
.
v2
.
optimizers
.
Adam
(
learning_rate
)
@
tf
.
function
(
autograph
=
False
)
def
train_step
(
)
:
with
tf
.
GradientTape
(
)
as
tape
:
loss_val
=
loss
(
)
vals
=
tape
.
watched_variables
(
)
grads
=
tape
.
gradient
(
loss_val
,
vals
)
grads_and_vals
=
list
(
zip
(
grads
,
vals
)
)
opt
.
apply_gradients
(
grads_and_vals
)
return
loss_val
for
step
in
range
(
self
.
num_train_steps
)
:
loss_val
=
train_step
(
)
tf
.
debugging
.
assert_all_finite
(
loss_val
,
'NeuTra loss is NaN at step {}'
.
format
(
step
)
)
if
self
.
train_debug_fn
:
self
.
train_debug_fn
(
self
,
step
,
loss_val
)
state_parts
=
tf
.
nest
.
flatten
(
state
)
flat_state_shapes
=
tf
.
nest
.
flatten
(
self
.
state_shape
)
batch_shape
=
tf
.
shape
(
input
=
state_parts
[
0
]
)
[
:
flat_state_shapes
[
0
]
.
ndims
]
return
self
.
_kernel
.
bootstrap_results
(
self
.
_flattened_variational_distribution
(
)
.
sample
(
batch_shape
,
seed
=
self
.
seed
)
)

Trains the bijector and creates initial previous_kernel_results .


def _outer_squared_difference(x, y):
  """Convenience function analogous to tf.squared_difference."""
  z = x - y
  return z[..., tf.newaxis, :] * z[..., tf.newaxis]

def _outer_squared_difference ( x , y ) : z = x - y return z [ ... , tf . newaxis , : ] * z [ ... , tf . newaxis ]

Convenience function analogous to tf . squared_difference .


def _value_and_batch_jacobian(f, x):
  """Enables uniform interface to value and batch jacobian calculation.

  Works in both eager and graph modes.

  Arguments:
    f: The scalar function to evaluate.
    x: The value at which to compute the value and the batch jacobian.

  Returns:
    A tuple (f(x), J(x)), where J(x) is the batch jacobian.
  """
  if tf.executing_eagerly():
    with tf.GradientTape() as tape:
      tape.watch(x)
      value = f(x)
    batch_jacobian = tape.batch_jacobian(value, x)
  else:
    value = f(x)
    batch_jacobian = gradients.batch_jacobian(value, x)
  return value, batch_jacobian

def _value_and_batch_jacobian ( f , x ) : if tf . executing_eagerly ( ) : with tf . GradientTape ( ) as tape : tape . watch ( x ) value = f ( x ) batch_jacobian = tape . batch_jacobian ( value , x ) else : value = f ( x ) batch_jacobian = gradients . batch_jacobian ( value , x ) return value , batch_jacobian

Enables uniform interface to value and batch jacobian calculation .


def _prevent_2nd_derivative(x):
  """Disables computation of the second derivatives for a tensor.

  NB: you need to apply a non-identity function to the output tensor for the
  exception to be raised.

  Arguments:
    x: A tensor.

  Returns:
    A tensor with the same value and the same derivative as x, but that raises
    LookupError when trying to compute the second derivatives.
  """
  def grad(dy):
    return array_ops.prevent_gradient(
        dy, message="Second derivative is not implemented.")

  return tf.identity(x), grad

def
_prevent_2nd_derivative
(
x
)
:
def
grad
(
dy
)
:
return
array_ops
.
prevent_gradient
(
dy
,
message
=
"Second derivative is not implemented."
)
return
tf
.
identity
(
x
)
,
grad

Disables computation of the second derivatives for a tensor .


def _distributional_transform(self, x):
    """Performs distributional transform of the mixture samples.

    Distributional transform removes the parameters from samples of a
    multivariate distribution by applying conditional CDFs:
      (F(x_1), F(x_2 | x1_), ..., F(x_d | x_1, ..., x_d-1))
    (the indexing is over the "flattened" event dimensions).
    The result is a sample of product of Uniform[0, 1] distributions.

    We assume that the components are factorized, so the conditional CDFs become
      F(x_i | x_1, ..., x_i-1) = sum_k w_i^k F_k (x_i),
    where w_i^k is the posterior mixture weight: for i > 0
      w_i^k = w_k prob_k(x_1, ..., x_i-1) / sum_k' w_k' prob_k'(x_1, ..., x_i-1)
    and w_0^k = w_k is the mixture probability of the k-th component.

    Arguments:
      x: Sample of mixture distribution

    Returns:
      Result of the distributional transform
    """

    if tensorshape_util.rank(x.shape) is None:
      # tf.nn.softmax raises an error when applied to inputs of undefined rank.
      raise ValueError("Distributional transform does not support inputs of "
                       "undefined rank.")

    # Obtain factorized components distribution and assert that it's
    # a scalar distribution.
    if isinstance(self._components_distribution, independent.Independent):
      univariate_components = self._components_distribution.distribution
    else:
      univariate_components = self._components_distribution

    with tf.control_dependencies([
        assert_util.assert_equal(
            univariate_components.is_scalar_event(),
            True,
            message="`univariate_components` must have scalar event")
    ]):
      x_padded = self._pad_sample_dims(x)  # [S, B, 1, E]
      log_prob_x = univariate_components.log_prob(x_padded)  # [S, B, k, E]
      cdf_x = univariate_components.cdf(x_padded)  # [S, B, k, E]

      # log prob_k (x_1, ..., x_i-1)
      cumsum_log_prob_x = tf.reshape(
          tf.math.cumsum(
              # [S*prod(B)*k, prod(E)]
              tf.reshape(log_prob_x, [-1, self._event_size]),
              exclusive=True,
              axis=-1),
          tf.shape(input=log_prob_x))  # [S, B, k, E]

      logits_mix_prob = distribution_utils.pad_mixture_dimensions(
          self.mixture_distribution.logits, self, self.mixture_distribution,
          self._event_ndims)  # [B, k, 1]

      # Logits of the posterior weights: log w_k + log prob_k (x_1, ..., x_i-1)
      log_posterior_weights_x = logits_mix_prob + cumsum_log_prob_x

      component_axis = tensorshape_util.rank(x.shape) - self._event_ndims
      posterior_weights_x = tf.nn.softmax(log_posterior_weights_x,
                                          axis=component_axis)
      return tf.reduce_sum(
          input_tensor=posterior_weights_x * cdf_x, axis=component_axis)

Performs distributional transform of the mixture samples .


def _split_covariance_into_marginals(covariance, block_sizes):
  """Split a covariance matrix into block-diagonal marginals of given sizes."""
  start_dim = 0
  marginals = []
  for size in block_sizes:
    end_dim = start_dim + size
    marginals.append(covariance[..., start_dim:end_dim, start_dim:end_dim])
    start_dim = end_dim
  return marginals

def _split_covariance_into_marginals ( covariance , block_sizes ) : start_dim = 0 marginals = [ ] for size in block_sizes : end_dim = start_dim + size marginals . append ( covariance [ ... , start_dim : end_dim , start_dim : end_dim ] ) start_dim = end_dim return marginals

Split a covariance matrix into block - diagonal marginals of given sizes .


def _decompose_from_posterior_marginals(
    model, posterior_means, posterior_covs, parameter_samples):
  """Utility method to decompose a joint posterior into components.

  Args:
    model: `tfp.sts.Sum` instance defining an additive STS model.
    posterior_means: float `Tensor` of shape `concat(
      [[num_posterior_draws], batch_shape, num_timesteps, latent_size])`
      representing the posterior mean over latents in an
      `AdditiveStateSpaceModel`.
    posterior_covs: float `Tensor` of shape `concat(
      [[num_posterior_draws], batch_shape, num_timesteps,
      latent_size, latent_size])`
      representing the posterior marginal covariances over latents in an
      `AdditiveStateSpaceModel`.
    parameter_samples: Python `list` of `Tensors` representing posterior
      samples of model parameters, with shapes `[concat([
      [num_posterior_draws], param.prior.batch_shape,
      param.prior.event_shape]) for param in model.parameters]`. This may
      optionally also be a map (Python `dict`) of parameter names to
      `Tensor` values.

  Returns:
    component_dists: A `collections.OrderedDict` instance mapping
      component StructuralTimeSeries instances (elements of `model.components`)
      to `tfd.Distribution` instances representing the posterior marginal
      distributions on the process modeled by each component. Each distribution
      has batch shape matching that of `posterior_means`/`posterior_covs`, and
      event shape of `[num_timesteps]`.
  """

  try:
    model.components
  except AttributeError:
    raise ValueError('Model decomposed into components must be an instance of'
                     '`tfp.sts.Sum` (passed model {})'.format(model))

  with tf.compat.v1.name_scope('decompose_from_posterior_marginals'):

    # Extract the component means/covs from the joint latent posterior.
    latent_sizes = [component.latent_size for component in model.components]
    component_means = tf.split(posterior_means, latent_sizes, axis=-1)
    component_covs = _split_covariance_into_marginals(
        posterior_covs, latent_sizes)

    # Instantiate per-component state space models, and use them to push the
    # posterior means/covs through the observation model for each component.
    num_timesteps = dist_util.prefer_static_value(
        tf.shape(input=posterior_means))[-2]
    component_ssms = model.make_component_state_space_models(
        num_timesteps=num_timesteps,
        param_vals=parameter_samples)
    component_predictive_dists = collections.OrderedDict()
    for (component, component_ssm,
         component_mean, component_cov) in zip(model.components, component_ssms,
                                               component_means, component_covs):
      component_obs_mean, component_obs_cov = (
          component_ssm.latents_to_observations(
              latent_means=component_mean,
              latent_covs=component_cov))

      # Using the observation means and covs, build a mixture distribution
      # that integrates over the posterior draws.
      component_predictive_dists[component] = sts_util.mix_over_posterior_draws(
          means=component_obs_mean[..., 0],
          variances=component_obs_cov[..., 0, 0])
  return component_predictive_dists

Utility method to decompose a joint posterior into components .


def decompose_by_component(model, observed_time_series, parameter_samples):
  """Decompose an observed time series into contributions from each component.

  This method decomposes a time series according to the posterior represention
  of a structural time series model. In particular, it:
    - Computes the posterior marginal mean and covariances over the additive
      model's latent space.
    - Decomposes the latent posterior into the marginal blocks for each
      model component.
    - Maps the per-component latent posteriors back through each component's
      observation model, to generate the time series modeled by that component.

  Args:
    model: An instance of `tfp.sts.Sum` representing a structural time series
      model.
    observed_time_series: `float` `Tensor` of shape
      `batch_shape + [num_timesteps, 1]` (omitting the trailing unit dimension
      is also supported when `num_timesteps > 1`), specifying an observed time
      series. May optionally be an instance of `tfp.sts.MaskedTimeSeries`, which
      includes a mask `Tensor` to specify timesteps with missing observations.
    parameter_samples: Python `list` of `Tensors` representing posterior
      samples of model parameters, with shapes `[concat([
      [num_posterior_draws], param.prior.batch_shape,
      param.prior.event_shape]) for param in model.parameters]`. This may
      optionally also be a map (Python `dict`) of parameter names to
      `Tensor` values.
  Returns:
    component_dists: A `collections.OrderedDict` instance mapping
      component StructuralTimeSeries instances (elements of `model.components`)
      to `tfd.Distribution` instances representing the posterior marginal
      distributions on the process modeled by each component. Each distribution
      has batch shape matching that of `posterior_means`/`posterior_covs`, and
      event shape of `[num_timesteps]`.

  #### Examples

  Suppose we've built a model and fit it to data:

  ```python
    day_of_week = tfp.sts.Seasonal(
        num_seasons=7,
        observed_time_series=observed_time_series,
        name='day_of_week')
    local_linear_trend = tfp.sts.LocalLinearTrend(
        observed_time_series=observed_time_series,
        name='local_linear_trend')
    model = tfp.sts.Sum(components=[day_of_week, local_linear_trend],
                        observed_time_series=observed_time_series)

    num_steps_forecast = 50
    samples, kernel_results = tfp.sts.fit_with_hmc(model, observed_time_series)
  ```

  To extract the contributions of individual components, pass the time series
  and sampled parameters into `decompose_by_component`:

  ```python
    component_dists = decompose_by_component(
      model,
      observed_time_series=observed_time_series,
      parameter_samples=samples)

    # Component mean and stddev have shape `[len(observed_time_series)]`.
    day_of_week_effect_mean = component_dists[day_of_week].mean()
    day_of_week_effect_stddev = component_dists[day_of_week].stddev()
  ```

  Using the component distributions, we can visualize the uncertainty for
  each component:

  ```
  from matplotlib import pylab as plt
  num_components = len(component_dists)
  xs = np.arange(len(observed_time_series))
  fig = plt.figure(figsize=(12, 3 * num_components))
  for i, (component, component_dist) in enumerate(component_dists.items()):

    # If in graph mode, replace `.numpy()` with `.eval()` or `sess.run()`.
    component_mean = component_dist.mean().numpy()
    component_stddev = component_dist.stddev().numpy()

    ax = fig.add_subplot(num_components, 1, 1 + i)
    ax.plot(xs, component_mean, lw=2)
    ax.fill_between(xs,
                    component_mean - 2 * component_stddev,
                    component_mean + 2 * component_stddev,
                    alpha=0.5)
    ax.set_title(component.name)
  ```

  """

  with tf.compat.v1.name_scope('decompose_by_component',
                               values=[observed_time_series]):
    [
        observed_time_series,
        is_missing
    ] = sts_util.canonicalize_observed_time_series_with_mask(
        observed_time_series)

    # Run smoothing over the training timesteps to extract the
    # posterior on latents.
    num_timesteps = dist_util.prefer_static_value(
        tf.shape(input=observed_time_series))[-2]
    ssm = model.make_state_space_model(num_timesteps=num_timesteps,
                                       param_vals=parameter_samples)
    posterior_means, posterior_covs = ssm.posterior_marginals(
        observed_time_series, mask=is_missing)

    return _decompose_from_posterior_marginals(
        model, posterior_means, posterior_covs, parameter_samples)

Decompose an observed time series into contributions from each component .


def decompose_forecast_by_component(model, forecast_dist, parameter_samples):
  """Decompose a forecast distribution into contributions from each component.

  Args:
    model: An instance of `tfp.sts.Sum` representing a structural time series
      model.
    forecast_dist: A `Distribution` instance returned by `tfp.sts.forecast()`.
      (specifically, must be a `tfd.MixtureSameFamily` over a
      `tfd.LinearGaussianStateSpaceModel` parameterized by posterior samples).
    parameter_samples: Python `list` of `Tensors` representing posterior samples
      of model parameters, with shapes `[concat([[num_posterior_draws],
      param.prior.batch_shape, param.prior.event_shape]) for param in
      model.parameters]`. This may optionally also be a map (Python `dict`) of
      parameter names to `Tensor` values.
  Returns:
    component_forecasts: A `collections.OrderedDict` instance mapping
      component StructuralTimeSeries instances (elements of `model.components`)
      to `tfd.Distribution` instances representing the marginal forecast for
      each component. Each distribution has batch and event shape matching
      `forecast_dist` (specifically, the event shape is
      `[num_steps_forecast]`).

  #### Examples

  Suppose we've built a model, fit it to data, and constructed a forecast
  distribution:

  ```python
    day_of_week = tfp.sts.Seasonal(
        num_seasons=7,
        observed_time_series=observed_time_series,
        name='day_of_week')
    local_linear_trend = tfp.sts.LocalLinearTrend(
        observed_time_series=observed_time_series,
        name='local_linear_trend')
    model = tfp.sts.Sum(components=[day_of_week, local_linear_trend],
                        observed_time_series=observed_time_series)

    num_steps_forecast = 50
    samples, kernel_results = tfp.sts.fit_with_hmc(model, observed_time_series)
    forecast_dist = tfp.sts.forecast(model, observed_time_series,
                                 parameter_samples=samples,
                                 num_steps_forecast=num_steps_forecast)
  ```

  To extract the forecast for individual components, pass the forecast
  distribution into `decompose_forecast_by_components`:

  ```python
    component_forecasts = decompose_forecast_by_component(
      model, forecast_dist, samples)

    # Component mean and stddev have shape `[num_steps_forecast]`.
    day_of_week_effect_mean = forecast_components[day_of_week].mean()
    day_of_week_effect_stddev = forecast_components[day_of_week].stddev()
  ```

  Using the component forecasts, we can visualize the uncertainty for each
  component:

  ```
  from matplotlib import pylab as plt
  num_components = len(component_forecasts)
  xs = np.arange(num_steps_forecast)
  fig = plt.figure(figsize=(12, 3 * num_components))
  for i, (component, component_dist) in enumerate(component_forecasts.items()):

    # If in graph mode, replace `.numpy()` with `.eval()` or `sess.run()`.
    component_mean = component_dist.mean().numpy()
    component_stddev = component_dist.stddev().numpy()

    ax = fig.add_subplot(num_components, 1, 1 + i)
    ax.plot(xs, component_mean, lw=2)
    ax.fill_between(xs,
                    component_mean - 2 * component_stddev,
                    component_mean + 2 * component_stddev,
                    alpha=0.5)
    ax.set_title(component.name)
  ```

  """

  with tf.compat.v1.name_scope('decompose_forecast_by_component'):
    try:
      forecast_lgssm = forecast_dist.components_distribution
      forecast_latent_mean, _ = forecast_lgssm._joint_mean()  # pylint: disable=protected-access
      forecast_latent_covs, _ = forecast_lgssm._joint_covariances()  # pylint: disable=protected-access
    except AttributeError as e:
      raise ValueError(
          'Forecast distribution must be a MixtureSameFamily of'
          'LinearGaussianStateSpaceModel distributions, such as returned by'
          '`tfp.sts.forecast()`. (saw exception: {})'.format(e))

    # Since `parameter_samples` will have sample shape `[num_posterior_draws]`,
    # we need to move the `num_posterior_draws` dimension of the forecast
    # moments from the trailing batch dimension, where it's currently put by
    # `sts.forecast`, back to the leading (sample shape) dimension.
    forecast_latent_mean = dist_util.move_dimension(
        forecast_latent_mean, source_idx=-3, dest_idx=0)
    forecast_latent_covs = dist_util.move_dimension(
        forecast_latent_covs, source_idx=-4, dest_idx=0)

    return _decompose_from_posterior_marginals(
        model, forecast_latent_mean, forecast_latent_covs, parameter_samples)

Decompose a forecast distribution into contributions from each component .


def dense_to_sparse(x, ignore_value=None, name=None):
  """Converts dense `Tensor` to `SparseTensor`, dropping `ignore_value` cells.

  Args:
    x: A `Tensor`.
    ignore_value: Entries in `x` equal to this value will be
      absent from the return `SparseTensor`. If `None`, default value of
      `x` dtype will be used (e.g. '' for `str`, 0 for `int`).
    name: Python `str` prefix for ops created by this function.

  Returns:
    sparse_x: A `tf.SparseTensor` with the same shape as `x`.

  Raises:
    ValueError: when `x`'s rank is `None`.
  """
  # Copied (with modifications) from:
  # tensorflow/contrib/layers/python/ops/sparse_ops.py.
  with tf.compat.v1.name_scope(name, 'dense_to_sparse', [x, ignore_value]):
    x = tf.convert_to_tensor(value=x, name='x')
    if ignore_value is None:
      if x.dtype.base_dtype == tf.string:
        # Exception due to TF strings are converted to numpy objects by default.
        ignore_value = ''
      else:
        ignore_value = x.dtype.as_numpy_dtype(0)
      ignore_value = tf.cast(ignore_value, x.dtype, name='ignore_value')
    indices = tf.where(tf.not_equal(x, ignore_value), name='indices')
    return tf.SparseTensor(
        indices=indices,
        values=tf.gather_nd(x, indices, name='values'),
        dense_shape=tf.shape(input=x, out_type=tf.int64, name='dense_shape'))

def dense_to_sparse ( x , ignore_value = None , name = None ) : with tf . compat . v1 . name_scope ( name , 'dense_to_sparse' , [ x , ignore_value ] ) : x = tf . convert_to_tensor ( value = x , name = 'x' ) if ignore_value is None : if x . dtype . base_dtype == tf . string : ignore_value = '' else : ignore_value = x . dtype . as_numpy_dtype ( 0 ) ignore_value = tf . cast ( ignore_value , x . dtype , name = 'ignore_value' ) indices = tf . where ( tf . not_equal ( x , ignore_value ) , name = 'indices' ) return tf . SparseTensor ( indices = indices , values = tf . gather_nd ( x , indices , name = 'values' ) , dense_shape = tf . shape ( input = x , out_type = tf . int64 , name = 'dense_shape' ) )

Converts dense Tensor to SparseTensor dropping ignore_value cells .


def _operator(attr):
  """Defers an operator overload to `attr`.

  Args:
    attr: Operator attribute to use.

  Returns:
    Function calling operator attribute.
  """
  @functools.wraps(attr)
  def func(a, *args):
    return attr(a.value, *args)
  return func

def _operator ( attr ) : @ functools . wraps ( attr ) def func ( a , * args ) : return attr ( a . value , * args ) return func


def _numpy_text(tensor, is_repr=False):
  """Human-readable representation of a tensor's numpy value."""
  if tensor.dtype.is_numpy_compatible:
    text = repr(tensor.numpy()) if is_repr else str(tensor.numpy())
  else:
    text = "<unprintable>"
  if "\n" in text:
    text = "\n" + text
  return text

def _numpy_text ( tensor , is_repr = False ) : if tensor . dtype . is_numpy_compatible : text = repr ( tensor . numpy ( ) ) if is_repr else str ( tensor . numpy ( ) ) else : text = "<unprintable>" if "\n" in text : text = "\n" + text return text

Human - readable representation of a tensor s numpy value .


def sample_shape(self):
    """Sample shape of random variable as a `TensorShape`."""
    if isinstance(self._sample_shape, tf.Tensor):
      return tf.TensorShape(tf.get_static_value(self._sample_shape))
    return tf.TensorShape(self._sample_shape)

def sample_shape ( self ) : if isinstance ( self . _sample_shape , tf . Tensor ) : return tf . TensorShape ( tf . get_static_value ( self . _sample_shape ) ) return tf . TensorShape ( self . _sample_shape )

Sample shape of random variable as a TensorShape .


def sample_shape_tensor(self, name="sample_shape_tensor"):
    """Sample shape of random variable as a 1-D `Tensor`.

    Args:
      name: name to give to the op

    Returns:
      sample_shape: `Tensor`.
    """
    with tf.compat.v1.name_scope(name):
      if isinstance(self._sample_shape, tf.Tensor):
        return self._sample_shape
      return tf.convert_to_tensor(
          value=self.sample_shape.as_list(), dtype=tf.int32)

def sample_shape_tensor ( self , name = "sample_shape_tensor" ) : with tf . compat . v1 . name_scope ( name ) : if isinstance ( self . _sample_shape , tf . Tensor ) : return self . _sample_shape return tf . convert_to_tensor ( value = self . sample_shape . as_list ( ) , dtype = tf . int32 )

Sample shape of random variable as a 1 - D Tensor .


def value(self):
    """Get tensor that the random variable corresponds to."""
    if self._value is None:
      try:
        self._value = self.distribution.sample(self.sample_shape_tensor())
      except NotImplementedError:
        raise NotImplementedError(
            "sample is not implemented for {0}. You must either pass in the "
            "value argument or implement sample for {0}."
            .format(self.distribution.__class__.__name__))
    return self._value

Get tensor that the random variable corresponds to .


def eval(self, session=None, feed_dict=None):
    """In a session, computes and returns the value of this random variable.

    This is not a graph construction method, it does not add ops to the graph.

    This convenience method requires a session where the graph
    containing this variable has been launched. If no session is
    passed, the default session is used.

    Args:
      session: tf.BaseSession.
        The `tf.Session` to use to evaluate this random variable. If
        none, the default session is used.
      feed_dict: dict.
        A dictionary that maps `tf.Tensor` objects to feed values. See
        `tf.Session.run()` for a description of the valid feed values.

    Returns:
      Value of the random variable.

    #### Examples

    ```python
    x = Normal(0.0, 1.0)
    with tf.Session() as sess:
      # Usage passing the session explicitly.
      print(x.eval(sess))
      # Usage with the default session.  The 'with' block
      # above makes 'sess' the default session.
      print(x.eval())
    ```
    """
    return self.value.eval(session=session, feed_dict=feed_dict)

def eval ( self , session = None , feed_dict = None ) : return self . value . eval ( session = session , feed_dict = feed_dict )

In a session computes and returns the value of this random variable .


def numpy(self):
    """Value as NumPy array, only available for TF Eager."""
    if not isinstance(self.value, ops.EagerTensor):
      raise NotImplementedError("value argument must be a EagerTensor.")

    return self.value.numpy()

def
numpy
(
self
)
:
if
not
isinstance
(
self
.
value
,
ops
.
EagerTensor
)
:
raise
NotImplementedError
(
"value argument must be a EagerTensor."
)
return
self
.
value
.
numpy
(
)

Value as NumPy array only available for TF Eager .


def normal_conjugates_known_scale_posterior(prior, scale, s, n):
  """Posterior Normal distribution with conjugate prior on the mean.

  This model assumes that `n` observations (with sum `s`) come from a
  Normal with unknown mean `loc` (described by the Normal `prior`)
  and known variance `scale**2`. The "known scale posterior" is
  the distribution of the unknown `loc`.

  Accepts a prior Normal distribution object, having parameters
  `loc0` and `scale0`, as well as known `scale` values of the predictive
  distribution(s) (also assumed Normal),
  and statistical estimates `s` (the sum(s) of the observations) and
  `n` (the number(s) of observations).

  Returns a posterior (also Normal) distribution object, with parameters
  `(loc', scale'**2)`, where:

  ```
  mu ~ N(mu', sigma'**2)
  sigma'**2 = 1/(1/sigma0**2 + n/sigma**2),
  mu' = (mu0/sigma0**2 + s/sigma**2) * sigma'**2.
  ```

  Distribution parameters from `prior`, as well as `scale`, `s`, and `n`.
  will broadcast in the case of multidimensional sets of parameters.

  Args:
    prior: `Normal` object of type `dtype`:
      the prior distribution having parameters `(loc0, scale0)`.
    scale: tensor of type `dtype`, taking values `scale > 0`.
      The known stddev parameter(s).
    s: Tensor of type `dtype`. The sum(s) of observations.
    n: Tensor of type `int`. The number(s) of observations.

  Returns:
    A new Normal posterior distribution object for the unknown observation
    mean `loc`.

  Raises:
    TypeError: if dtype of `s` does not match `dtype`, or `prior` is not a
      Normal object.
  """
  if not isinstance(prior, normal.Normal):
    raise TypeError("Expected prior to be an instance of type Normal")

  if s.dtype != prior.dtype:
    raise TypeError(
        "Observation sum s.dtype does not match prior dtype: %s vs. %s"
        % (s.dtype, prior.dtype))

  n = tf.cast(n, prior.dtype)
  scale0_2 = tf.square(prior.scale)
  scale_2 = tf.square(scale)
  scalep_2 = 1.0/(1/scale0_2 + n/scale_2)
  return normal.Normal(
      loc=(prior.loc / scale0_2 + s / scale_2) * scalep_2,
      scale=tf.sqrt(scalep_2))

Posterior Normal distribution with conjugate prior on the mean .


def real_nvp_default_template(hidden_layers,
                              shift_only=False,
                              activation=tf.nn.relu,
                              name=None,
                              *args,  # pylint: disable=keyword-arg-before-vararg
                              **kwargs):
  """Build a scale-and-shift function using a multi-layer neural network.

  This will be wrapped in a make_template to ensure the variables are only
  created once. It takes the `d`-dimensional input x[0:d] and returns the `D-d`
  dimensional outputs `loc` ("mu") and `log_scale` ("alpha").

  The default template does not support conditioning and will raise an
  exception if `condition_kwargs` are passed to it. To use conditioning in
  real nvp bijector, implement a conditioned shift/scale template that
  handles the `condition_kwargs`.

  Arguments:
    hidden_layers: Python `list`-like of non-negative integer, scalars
      indicating the number of units in each hidden layer. Default: `[512, 512].
    shift_only: Python `bool` indicating if only the `shift` term shall be
      computed (i.e. NICE bijector). Default: `False`.
    activation: Activation function (callable). Explicitly setting to `None`
      implies a linear activation.
    name: A name for ops managed by this function. Default:
      "real_nvp_default_template".
    *args: `tf.layers.dense` arguments.
    **kwargs: `tf.layers.dense` keyword arguments.

  Returns:
    shift: `Float`-like `Tensor` of shift terms ("mu" in
      [Papamakarios et al.  (2016)][1]).
    log_scale: `Float`-like `Tensor` of log(scale) terms ("alpha" in
      [Papamakarios et al. (2016)][1]).

  Raises:
    NotImplementedError: if rightmost dimension of `inputs` is unknown prior to
      graph execution, or if `condition_kwargs` is not empty.

  #### References

  [1]: George Papamakarios, Theo Pavlakou, and Iain Murray. Masked
       Autoregressive Flow for Density Estimation. In _Neural Information
       Processing Systems_, 2017. https://arxiv.org/abs/1705.07057
  """

  with tf.compat.v2.name_scope(name or "real_nvp_default_template"):

    def _fn(x, output_units, **condition_kwargs):
      """Fully connected MLP parameterized via `real_nvp_template`."""
      if condition_kwargs:
        raise NotImplementedError(
            "Conditioning not implemented in the default template.")

      if tensorshape_util.rank(x.shape) == 1:
        x = x[tf.newaxis, ...]
        reshape_output = lambda x: x[0]
      else:
        reshape_output = lambda x: x
      for units in hidden_layers:
        x = tf.compat.v1.layers.dense(
            inputs=x,
            units=units,
            activation=activation,
            *args,  # pylint: disable=keyword-arg-before-vararg
            **kwargs)
      x = tf.compat.v1.layers.dense(
          inputs=x,
          units=(1 if shift_only else 2) * output_units,
          activation=None,
          *args,  # pylint: disable=keyword-arg-before-vararg
          **kwargs)
      if shift_only:
        return reshape_output(x), None
      shift, log_scale = tf.split(x, 2, axis=-1)
      return reshape_output(shift), reshape_output(log_scale)

    return tf.compat.v1.make_template("real_nvp_default_template", _fn)

Build a scale - and - shift function using a multi - layer neural network .


def _uniform_unit_norm(dimension, shape, dtype, seed):
  """Returns a batch of points chosen uniformly from the unit hypersphere."""
  # This works because the Gaussian distribution is spherically symmetric.
  # raw shape: shape + [dimension]
  raw = normal.Normal(
      loc=dtype_util.as_numpy_dtype(dtype)(0),
      scale=dtype_util.as_numpy_dtype(dtype)(1)).sample(
          tf.concat([shape, [dimension]], axis=0), seed=seed())
  unit_norm = raw / tf.norm(tensor=raw, ord=2, axis=-1)[..., tf.newaxis]
  return unit_norm

def _uniform_unit_norm ( dimension , shape , dtype , seed ) : raw = normal . Normal ( loc = dtype_util . as_numpy_dtype ( dtype ) ( 0 ) , scale = dtype_util . as_numpy_dtype ( dtype ) ( 1 ) ) . sample ( tf . concat ( [ shape , [ dimension ] ] , axis = 0 ) , seed = seed ( ) ) unit_norm = raw / tf . norm ( tensor = raw , ord = 2 , axis = - 1 ) [ ... , tf . newaxis ] return unit_norm

Returns a batch of points chosen uniformly from the unit hypersphere .


def _log_unnorm_prob(self, x, name=None):
    """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`.
      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`.
    """
    with tf.name_scope(name or 'log_unnorm_prob_lkj'):
      x = tf.convert_to_tensor(value=x, name='x')
      # The density is det(matrix) ** (concentration - 1).
      # Computing the determinant with `logdet` is usually fine, since
      # correlation matrices are Hermitian and PSD. But in some cases, for a
      # PSD matrix whose eigenvalues are close to zero, `logdet` raises an error
      # complaining that it is not PSD. The root cause is the computation of the
      # cholesky decomposition in `logdet`. Hence, we use the less efficient but
      # more robust `slogdet` which does not use `cholesky`.
      #
      # An alternative would have been to check allow_nan_stats and use
      #   eigenvalues = tf.linalg.self_adjoint_eigvals(x)
      #   psd_mask = tf.cast(
      #     tf.reduce_min(eigenvalues, axis=-1) >= 0, dtype=x.dtype)
      #   tf.where(psd_mask, answer, float('-inf'))
      # to emit probability 0 for inputs that are not PSD, without ever raising
      # an error. More care must be taken, as due to numerical stability issues,
      # self_adjoint_eigvals can return slightly negative eigenvalues even for
      # a PSD matrix.
      if self.input_output_cholesky:
        logdet = 2.0 * tf.reduce_sum(
            input_tensor=tf.math.log(tf.linalg.diag_part(x)), axis=[-1])
      else:
        _, logdet = tf.linalg.slogdet(x)
      answer = (self.concentration - 1.) * logdet
      return answer

def _log_unnorm_prob ( self , x , name = None ) : with tf . name_scope ( name or 'log_unnorm_prob_lkj' ) : x = tf . convert_to_tensor ( value = x , name = 'x' ) if self . input_output_cholesky : logdet = 2.0 * tf . reduce_sum ( input_tensor = tf . math . log ( tf . linalg . diag_part ( x ) ) , axis = [ - 1 ] ) else : _ , logdet = tf . linalg . slogdet ( x ) answer = ( self . concentration - 1. ) * logdet return answer

Returns the unnormalized log density of an LKJ distribution .


def _log_normalization(self, name='log_normalization'):
    """Returns the log normalization of an LKJ distribution.

    Args:
      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.
    """
    # The formula is from D. Lewandowski et al [1], p. 1999, from the
    # proof that eqs 16 and 17 are equivalent.
    with tf.name_scope(name or 'log_normalization_lkj'):
      logpi = np.log(np.pi)
      ans = tf.zeros_like(self.concentration)
      for k in range(1, self.dimension):
        ans += logpi * (k / 2.)
        ans += tf.math.lgamma(self.concentration +
                              (self.dimension - 1 - k) / 2.)
        ans -= tf.math.lgamma(self.concentration + (self.dimension - 1) / 2.)
      return ans

def _log_normalization ( self , name = 'log_normalization' ) : with tf . name_scope ( name or 'log_normalization_lkj' ) : logpi = np . log ( np . pi ) ans = tf . zeros_like ( self . concentration ) for k in range ( 1 , self . dimension ) : ans += logpi * ( k / 2. ) ans += tf . math . lgamma ( self . concentration + ( self . dimension - 1 - k ) / 2. ) ans -= tf . math . lgamma ( self . concentration + ( self . dimension - 1 ) / 2. ) return ans

Returns the log normalization of an LKJ distribution .


def common_dtype(args_list, preferred_dtype=None):
  """Returns explict dtype from `args_list` if exists, else preferred_dtype."""
  dtype = None
  preferred_dtype = (None if preferred_dtype is None
                     else tf.as_dtype(preferred_dtype))
  for a in tf.nest.flatten(args_list):
    if hasattr(a, 'dtype'):
      dt = tf.as_dtype(a.dtype)
    else:
      continue
    if dtype is None:
      dtype = dt
    elif dtype != dt:
      raise TypeError('Found incompatible dtypes, {} and {}.'.format(dtype, dt))
  if dtype is None and preferred_dtype is None:
    return None
  return (preferred_dtype if dtype is None else dtype).as_numpy_dtype

def
common_dtype
(
args_list
,
preferred_dtype
=
)
:
dtype
=
preferred_dtype
=
(
if
preferred_dtype
is
else
tf
.
as_dtype
(
preferred_dtype
)
)
for
a
in
tf
.
nest
.
flatten
(
args_list
)
:
if
hasattr
(
a
,
'dtype'
)
:
dt
=
tf
.
as_dtype
(
a
.
dtype
)
else
:
continue
if
dtype
is
:
dtype
=
dt
elif
dtype
!=
dt
:
raise
TypeError
(
'Found incompatible dtypes, {} and {}.'
.
format
(
dtype
,
dt
)
)
if
dtype
is
and
preferred_dtype
is
:
return
return
(
preferred_dtype
if
dtype
is
else
dtype
)
.
as_numpy_dtype

Returns explict dtype from args_list if exists else preferred_dtype .


def _make_summary_statistic(attr):
  """Factory for implementing summary statistics, eg, mean, stddev, mode."""
  def _fn(self, **kwargs):
    """Implements summary statistic, eg, mean, stddev, mode."""
    x = getattr(self.distribution, attr)(**kwargs)
    shape = prefer_static.concat([
        self.distribution.batch_shape_tensor(),
        prefer_static.ones(prefer_static.rank_from_shape(self.sample_shape),
                           dtype=self.sample_shape.dtype),
        self.distribution.event_shape_tensor(),
    ], axis=0)
    x = tf.reshape(x, shape=shape)
    shape = prefer_static.concat([
        self.distribution.batch_shape_tensor(),
        self.sample_shape,
        self.distribution.event_shape_tensor(),
    ], axis=0)
    return tf.broadcast_to(x, shape)
  return _fn

def _make_summary_statistic ( attr ) : def _fn ( self , ** kwargs ) : x = getattr ( self . distribution , attr ) ( ** kwargs ) shape = prefer_static . concat ( [ self . distribution . batch_shape_tensor ( ) , prefer_static . ones ( prefer_static . rank_from_shape ( self . sample_shape ) , dtype = self . sample_shape . dtype ) , self . distribution . event_shape_tensor ( ) , ] , axis = 0 ) x = tf . reshape ( x , shape = shape ) shape = prefer_static . concat ( [ self . distribution . batch_shape_tensor ( ) , self . sample_shape , self . distribution . event_shape_tensor ( ) , ] , axis = 0 ) return tf . broadcast_to ( x , shape ) return _fn

Factory for implementing summary statistics eg mean stddev mode .


def _broadcast_to(tensor_to_broadcast, target_tensors):
  """Helper to broadcast a tensor using a list of target tensors."""
  output = tensor_to_broadcast
  for tensor in target_tensors:
    output += tf.zeros_like(tensor)
  return output

def _broadcast_to ( tensor_to_broadcast , target_tensors ) : output = tensor_to_broadcast for tensor in target_tensors : output += tf . zeros_like ( tensor ) return output

Helper to broadcast a tensor using a list of target tensors .


def _pdf_at_peak(self):
    """Pdf evaluated at the peak."""
    return (self.peak - self.low) / (self.high - self.low)

def _pdf_at_peak ( self ) : return ( self . peak - self . low ) / ( self . high - self . low )


def effective_sample_size(states,
                          filter_threshold=0.,
                          filter_beyond_lag=None,
                          name=None):
  """Estimate a lower bound on effective sample size for each independent chain.

  Roughly speaking, "effective sample size" (ESS) is the size of an iid sample
  with the same variance as `state`.

  More precisely, given a stationary sequence of possibly correlated random
  variables `X_1, X_2,...,X_N`, each identically distributed ESS is the number
  such that

  ```Variance{ N**-1 * Sum{X_i} } = ESS**-1 * Variance{ X_1 }.```

  If the sequence is uncorrelated, `ESS = N`.  In general, one should expect
  `ESS <= N`, with more highly correlated sequences having smaller `ESS`.

  Args:
    states:  `Tensor` or list of `Tensor` objects.  Dimension zero should index
      identically distributed states.
    filter_threshold:  `Tensor` or list of `Tensor` objects.
      Must broadcast with `state`.  The auto-correlation sequence is truncated
      after the first appearance of a term less than `filter_threshold`.
      Setting to `None` means we use no threshold filter.  Since `|R_k| <= 1`,
      setting to any number less than `-1` has the same effect.
    filter_beyond_lag:  `Tensor` or list of `Tensor` objects.  Must be
      `int`-like and scalar valued.  The auto-correlation sequence is truncated
      to this length.  Setting to `None` means we do not filter based on number
      of lags.
    name:  `String` name to prepend to created ops.

  Returns:
    ess:  `Tensor` or list of `Tensor` objects.  The effective sample size of
      each component of `states`.  Shape will be `states.shape[1:]`.

  Raises:
    ValueError:  If `states` and `filter_threshold` or `states` and
      `filter_beyond_lag` are both lists with different lengths.

  #### Examples

  We use ESS to estimate standard error.

  ```
  import tensorflow as tf
  import tensorflow_probability as tfp
  tfd = tfp.distributions

  target = tfd.MultivariateNormalDiag(scale_diag=[1., 2.])

  # Get 1000 states from one chain.
  states = tfp.mcmc.sample_chain(
      num_burnin_steps=200,
      num_results=1000,
      current_state=tf.constant([0., 0.]),
      kernel=tfp.mcmc.HamiltonianMonteCarlo(
        target_log_prob_fn=target.log_prob,
        step_size=0.05,
        num_leapfrog_steps=20))
  states.shape
  ==> (1000, 2)

  ess = effective_sample_size(states)
  ==> Shape (2,) Tensor

  mean, variance = tf.nn.moments(states, axis=0)
  standard_error = tf.sqrt(variance / ess)
  ```

  Some math shows that, with `R_k` the auto-correlation sequence,
  `R_k := Covariance{X_1, X_{1+k}} / Variance{X_1}`, we have

  ```ESS(N) =  N / [ 1 + 2 * ( (N - 1) / N * R_1 + ... + 1 / N * R_{N-1}  ) ]```

  This function estimates the above by first estimating the auto-correlation.
  Since `R_k` must be estimated using only `N - k` samples, it becomes
  progressively noisier for larger `k`.  For this reason, the summation over
  `R_k` should be truncated at some number `filter_beyond_lag < N`.  Since many
  MCMC methods generate chains where `R_k > 0`, a reasonable criteria is to
  truncate at the first index where the estimated auto-correlation becomes
  negative.

  The arguments `filter_beyond_lag`, `filter_threshold` are filters intended to
  remove noisy tail terms from `R_k`.  They combine in an "OR" manner meaning
  terms are removed if they were to be filtered under the `filter_beyond_lag` OR
  `filter_threshold` criteria.
  """
  states_was_list = _is_list_like(states)

  # Convert all args to lists.
  if not states_was_list:
    states = [states]

  filter_beyond_lag = _broadcast_maybelist_arg(states, filter_beyond_lag,
                                               'filter_beyond_lag')
  filter_threshold = _broadcast_maybelist_arg(states, filter_threshold,
                                              'filter_threshold')

  # Process items, one at a time.
  with tf.compat.v1.name_scope(name, 'effective_sample_size'):
    ess_list = [
        _effective_sample_size_single_state(s, ml, mlt)
        for (s, ml, mlt) in zip(states, filter_beyond_lag, filter_threshold)
    ]

  if states_was_list:
    return ess_list
  return ess_list[0]

def effective_sample_size ( states , filter_threshold = 0. , filter_beyond_lag = None , name = None ) : states_was_list = _is_list_like ( states ) if not states_was_list : states = [ states ] filter_beyond_lag = _broadcast_maybelist_arg ( states , filter_beyond_lag , 'filter_beyond_lag' ) filter_threshold = _broadcast_maybelist_arg ( states , filter_threshold , 'filter_threshold' ) with tf . compat . v1 . name_scope ( name , 'effective_sample_size' ) : ess_list = [ _effective_sample_size_single_state ( s , ml , mlt ) for ( s , ml , mlt ) in zip ( states , filter_beyond_lag , filter_threshold ) ] if states_was_list : return ess_list return ess_list [ 0 ]

Estimate a lower bound on effective sample size for each independent chain .


def _effective_sample_size_single_state(states, filter_beyond_lag,
                                        filter_threshold):
  """ESS computation for one single Tensor argument."""

  with tf.compat.v1.name_scope(
      'effective_sample_size_single_state',
      values=[states, filter_beyond_lag, filter_threshold]):

    states = tf.convert_to_tensor(value=states, name='states')
    dt = states.dtype

    # filter_beyond_lag == None ==> auto_corr is the full sequence.
    auto_corr = stats.auto_correlation(
        states, axis=0, max_lags=filter_beyond_lag)
    if filter_threshold is not None:
      filter_threshold = tf.convert_to_tensor(
          value=filter_threshold, dtype=dt, name='filter_threshold')
      # Get a binary mask to zero out values of auto_corr below the threshold.
      #   mask[i, ...] = 1 if auto_corr[j, ...] > threshold for all j <= i,
      #   mask[i, ...] = 0, otherwise.
      # So, along dimension zero, the mask will look like [1, 1, ..., 0, 0,...]
      # Building step by step,
      #   Assume auto_corr = [1, 0.5, 0.0, 0.3], and filter_threshold = 0.2.
      # Step 1:  mask = [False, False, True, False]
      mask = auto_corr < filter_threshold
      # Step 2:  mask = [0, 0, 1, 1]
      mask = tf.cast(mask, dtype=dt)
      # Step 3:  mask = [0, 0, 1, 2]
      mask = tf.cumsum(mask, axis=0)
      # Step 4:  mask = [1, 1, 0, 0]
      mask = tf.maximum(1. - mask, 0.)
      auto_corr *= mask

    # With R[k] := auto_corr[k, ...],
    # ESS = N / {1 + 2 * Sum_{k=1}^N (N - k) / N * R[k]}
    #     = N / {-1 + 2 * Sum_{k=0}^N (N - k) / N * R[k]} (since R[0] = 1)
    #     approx N / {-1 + 2 * Sum_{k=0}^M (N - k) / N * R[k]}
    # where M is the filter_beyond_lag truncation point chosen above.

    # Get the factor (N - k) / N, and give it shape [M, 1,...,1], having total
    # ndims the same as auto_corr
    n = _axis_size(states, axis=0)
    k = tf.range(0., _axis_size(auto_corr, axis=0))
    nk_factor = (n - k) / n
    if auto_corr.shape.ndims is not None:
      new_shape = [-1] + [1] * (auto_corr.shape.ndims - 1)
    else:
      new_shape = tf.concat(
          ([-1],
           tf.ones([tf.rank(auto_corr) - 1], dtype=tf.int32)),
          axis=0)
    nk_factor = tf.reshape(nk_factor, new_shape)

    return n / (-1 +
                2 * tf.reduce_sum(input_tensor=nk_factor * auto_corr, axis=0))

def _effective_sample_size_single_state ( states , filter_beyond_lag , filter_threshold ) : with tf . compat . v1 . name_scope ( 'effective_sample_size_single_state' , values = [ states , filter_beyond_lag , filter_threshold ] ) : states = tf . convert_to_tensor ( value = states , name = 'states' ) dt = states . dtype auto_corr = stats . auto_correlation ( states , axis = 0 , max_lags = filter_beyond_lag ) if filter_threshold is not None : filter_threshold = tf . convert_to_tensor ( value = filter_threshold , dtype = dt , name = 'filter_threshold' ) mask = auto_corr < filter_threshold mask = tf . cast ( mask , dtype = dt ) mask = tf . cumsum ( mask , axis = 0 ) mask = tf . maximum ( 1. - mask , 0. ) auto_corr *= mask n = _axis_size ( states , axis = 0 ) k = tf . range ( 0. , _axis_size ( auto_corr , axis = 0 ) ) nk_factor = ( n - k ) / n if auto_corr . shape . ndims is not None : new_shape = [ - 1 ] + [ 1 ] * ( auto_corr . shape . ndims - 1 ) else : new_shape = tf . concat ( ( [ - 1 ] , tf . ones ( [ tf . rank ( auto_corr ) - 1 ] , dtype = tf . int32 ) ) , axis = 0 ) nk_factor = tf . reshape ( nk_factor , new_shape ) return n / ( - 1 + 2 * tf . reduce_sum ( input_tensor = nk_factor * auto_corr , axis = 0 ) )

ESS computation for one single Tensor argument .


def _potential_scale_reduction_single_state(state, independent_chain_ndims):
  """potential_scale_reduction for one single state `Tensor`."""
  with tf.compat.v1.name_scope(
      'potential_scale_reduction_single_state',
      values=[state, independent_chain_ndims]):
    # We assume exactly one leading dimension indexes e.g. correlated samples
    # from each Markov chain.
    state = tf.convert_to_tensor(value=state, name='state')
    sample_ndims = 1

    sample_axis = tf.range(0, sample_ndims)
    chain_axis = tf.range(sample_ndims,
                          sample_ndims + independent_chain_ndims)
    sample_and_chain_axis = tf.range(
        0, sample_ndims + independent_chain_ndims)

    n = _axis_size(state, sample_axis)
    m = _axis_size(state, chain_axis)

    # In the language of Brooks and Gelman (1998),
    # B / n is the between chain variance, the variance of the chain means.
    # W is the within sequence variance, the mean of the chain variances.
    b_div_n = _reduce_variance(
        tf.reduce_mean(input_tensor=state, axis=sample_axis, keepdims=True),
        sample_and_chain_axis,
        biased=False)
    w = tf.reduce_mean(
        input_tensor=_reduce_variance(
            state, sample_axis, keepdims=True, biased=True),
        axis=sample_and_chain_axis)

    # sigma^2_+ is an estimate of the true variance, which would be unbiased if
    # each chain was drawn from the target.  c.f. "law of total variance."
    sigma_2_plus = w + b_div_n

    return ((m + 1.) / m) * sigma_2_plus / w - (n - 1.) / (m * n)

potential_scale_reduction for one single state Tensor .


def _axis_size(x, axis=None):
  """Get number of elements of `x` in `axis`, as type `x.dtype`."""
  if axis is None:
    return tf.cast(tf.size(input=x), x.dtype)
  return tf.cast(
      tf.reduce_prod(input_tensor=tf.gather(tf.shape(input=x), axis)), x.dtype)

def _axis_size ( x , axis = None ) : if axis is None : return tf . cast ( tf . size ( input = x ) , x . dtype ) return tf . cast ( tf . reduce_prod ( input_tensor = tf . gather ( tf . shape ( input = x ) , axis ) ) , x . dtype )

Get number of elements of x in axis as type x . dtype .


def _broadcast_maybelist_arg(states, secondary_arg, name):
  """Broadcast a listable secondary_arg to that of states."""
  if _is_list_like(secondary_arg):
    if len(secondary_arg) != len(states):
      raise ValueError('Argument `%s` was a list of different length ({}) than '
                       '`states` ({})'.format(name, len(states)))
  else:
    secondary_arg = [secondary_arg] * len(states)

  return secondary_arg

Broadcast a listable secondary_arg to that of states .


def quadrature_scheme_lognormal_gauss_hermite(
    loc, scale, quadrature_size,
    validate_args=False, name=None):  # pylint: disable=unused-argument
  """Use Gauss-Hermite quadrature to form quadrature on positive-reals.

  Note: for a given `quadrature_size`, this method is generally less accurate
  than `quadrature_scheme_lognormal_quantiles`.

  Args:
    loc: `float`-like (batch of) scalar `Tensor`; the location parameter of
      the LogNormal prior.
    scale: `float`-like (batch of) scalar `Tensor`; the scale parameter of
      the LogNormal prior.
    quadrature_size: Python `int` scalar representing the number of quadrature
      points.
    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.
    name: Python `str` name prefixed to Ops created by this class.

  Returns:
    grid: (Batch of) length-`quadrature_size` vectors representing the
      `log_rate` parameters of a `Poisson`.
    probs: (Batch of) length-`quadrature_size` vectors representing the
      weight associate with each `grid` value.
  """
  with tf.name_scope(
      name or "vector_diffeomixture_quadrature_gauss_hermite"):
    grid, probs = np.polynomial.hermite.hermgauss(deg=quadrature_size)
    npdt = dtype_util.as_numpy_dtype(loc.dtype)
    grid = grid.astype(npdt)
    probs = probs.astype(npdt)
    probs /= np.linalg.norm(probs, ord=1, keepdims=True)
    probs = tf.convert_to_tensor(value=probs, name="probs", dtype=loc.dtype)
    # The following maps the broadcast of `loc` and `scale` to each grid
    # point, i.e., we are creating several log-rates that correspond to the
    # different Gauss-Hermite quadrature points and (possible) batches of
    # `loc` and `scale`.
    grid = (loc[..., tf.newaxis] + np.sqrt(2.) * scale[..., tf.newaxis] * grid)
    return grid, probs

Use Gauss - Hermite quadrature to form quadrature on positive - reals .


def quadrature_scheme_lognormal_quantiles(
    loc, scale, quadrature_size,
    validate_args=False, name=None):
  """Use LogNormal quantiles to form quadrature on positive-reals.

  Args:
    loc: `float`-like (batch of) scalar `Tensor`; the location parameter of
      the LogNormal prior.
    scale: `float`-like (batch of) scalar `Tensor`; the scale parameter of
      the LogNormal prior.
    quadrature_size: Python `int` scalar representing the number of quadrature
      points.
    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.
    name: Python `str` name prefixed to Ops created by this class.

  Returns:
    grid: (Batch of) length-`quadrature_size` vectors representing the
      `log_rate` parameters of a `Poisson`.
    probs: (Batch of) length-`quadrature_size` vectors representing the
      weight associate with each `grid` value.
  """
  with tf.name_scope(name or "quadrature_scheme_lognormal_quantiles"):
    # Create a LogNormal distribution.
    dist = transformed_distribution.TransformedDistribution(
        distribution=normal.Normal(loc=loc, scale=scale),
        bijector=exp_bijector.Exp(),
        validate_args=validate_args)
    batch_ndims = tensorshape_util.rank(dist.batch_shape)
    if batch_ndims is None:
      batch_ndims = tf.shape(input=dist.batch_shape_tensor())[0]

    def _compute_quantiles():
      """Helper to build quantiles."""
      # Omit {0, 1} since they might lead to Inf/NaN.
      zero = tf.zeros([], dtype=dist.dtype)
      edges = tf.linspace(zero, 1., quadrature_size + 3)[1:-1]
      # Expand edges so its broadcast across batch dims.
      edges = tf.reshape(
          edges,
          shape=tf.concat(
              [[-1], tf.ones([batch_ndims], dtype=tf.int32)], axis=0))
      quantiles = dist.quantile(edges)
      # Cyclically permute left by one.
      perm = tf.concat([tf.range(1, 1 + batch_ndims), [0]], axis=0)
      quantiles = tf.transpose(a=quantiles, perm=perm)
      return quantiles
    quantiles = _compute_quantiles()

    # Compute grid as quantile midpoints.
    grid = (quantiles[..., :-1] + quantiles[..., 1:]) / 2.
    # Set shape hints.
    new_shape = tensorshape_util.concatenate(dist.batch_shape,
                                             [quadrature_size])
    tensorshape_util.set_shape(grid, new_shape)

    # By construction probs is constant, i.e., `1 / quadrature_size`. This is
    # important, because non-constant probs leads to non-reparameterizable
    # samples.
    probs = tf.fill(
        dims=[quadrature_size], value=1. / tf.cast(quadrature_size, dist.dtype))

    return grid, probs

def quadrature_scheme_lognormal_quantiles ( loc , scale , quadrature_size , validate_args = False , name = None ) : with tf . name_scope ( name or "quadrature_scheme_lognormal_quantiles" ) : dist = transformed_distribution . TransformedDistribution ( distribution = normal . Normal ( loc = loc , scale = scale ) , bijector = exp_bijector . Exp ( ) , validate_args = validate_args ) batch_ndims = tensorshape_util . rank ( dist . batch_shape ) if batch_ndims is None : batch_ndims = tf . shape ( input = dist . batch_shape_tensor ( ) ) [ 0 ] def _compute_quantiles ( ) : zero = tf . zeros ( [ ] , dtype = dist . dtype ) edges = tf . linspace ( zero , 1. , quadrature_size + 3 ) [ 1 : - 1 ] edges = tf . reshape ( edges , shape = tf . concat ( [ [ - 1 ] , tf . ones ( [ batch_ndims ] , dtype = tf . int32 ) ] , axis = 0 ) ) quantiles = dist . quantile ( edges ) perm = tf . concat ( [ tf . range ( 1 , 1 + batch_ndims ) , [ 0 ] ] , axis = 0 ) quantiles = tf . transpose ( a = quantiles , perm = perm ) return quantiles quantiles = _compute_quantiles ( ) grid = ( quantiles [ ... , : - 1 ] + quantiles [ ... , 1 : ] ) / 2. new_shape = tensorshape_util . concatenate ( dist . batch_shape , [ quadrature_size ] ) tensorshape_util . set_shape ( grid , new_shape ) probs = tf . fill ( dims = [ quadrature_size ] , value = 1. / tf . cast ( quadrature_size , dist . dtype ) ) return grid , probs

Use LogNormal quantiles to form quadrature on positive - reals .


def merge(self, x=None, y=None, ildj=None, kwargs=None, mapping=None):
    """Returns new _Mapping with args merged with self.

    Args:
      x: `Tensor` or None. Input to forward; output of inverse.
      y: `Tensor` or None. Input to inverse; output of forward.
      ildj: `Tensor`. This is the (un-reduce_sum'ed) inverse log det jacobian.
      kwargs: Python dictionary. Extra args supplied to forward/inverse/etc
        functions.
      mapping: Instance of _Mapping to merge. Can only be specified if no other
        arg is specified.

    Returns:
      mapping: New instance of `_Mapping` which has inputs merged with self.

    Raises:
      ValueError: if mapping and any other arg is not `None`.
    """
    if mapping is None:
      mapping = _Mapping(x=x, y=y, ildj=ildj, kwargs=kwargs)
    elif any(arg is not None for arg in [x, y, ildj, kwargs]):
      raise ValueError("Cannot simultaneously specify mapping and individual "
                       "arguments.")

    return _Mapping(
        x=self._merge(self.x, mapping.x),
        y=self._merge(self.y, mapping.y),
        ildj=self._merge(self.ildj, mapping.ildj),
        kwargs=self._merge(self.kwargs, mapping.kwargs, use_equals=True))

Returns new _Mapping with args merged with self .


def remove(self, field):
    """To support weak referencing, removes cache key from the cache value."""
    return _Mapping(
        x=None if field == "x" else self.x,
        y=None if field == "y" else self.y,
        ildj=self.ildj,
        kwargs=self.kwargs)

def remove ( self , field ) : return _Mapping ( x = None if field == "x" else self . x , y = None if field == "y" else self . y , ildj = self . ildj , kwargs = self . kwargs )

To support weak referencing removes cache key from the cache value .


def _merge(self, old, new, use_equals=False):
    """Helper to merge which handles merging one value."""
    if old is None:
      return new
    if new is None:
      return old
    if (old == new) if use_equals else (old is new):
      return old
    raise ValueError("Incompatible values: %s != %s" % (old, new))

def
_merge
(
self
,
old
,
new
,
use_equals
=
False
)
:
if
old
is
:
return
new
if
new
is
:
return
old
if
(
old
==
new
)
if
use_equals
else
(
old
is
new
)
:
return
old
raise
ValueError
(
"Incompatible values: %s != %s"
%
(
old
,
new
)
)

Helper to merge which handles merging one value .


def _deep_tuple(self, x):
    """Converts nested `tuple`, `list`, or `dict` to nested `tuple`."""
    if isinstance(x, dict):
      return self._deep_tuple(tuple(sorted(x.items())))
    elif isinstance(x, (list, tuple)):
      return tuple(map(self._deep_tuple, x))

    return x

def _deep_tuple ( self , x ) : if isinstance ( x , dict ) : return self . _deep_tuple ( tuple ( sorted ( x . items ( ) ) ) ) elif isinstance ( x , ( list , tuple ) ) : return tuple ( map ( self . _deep_tuple , x ) ) return x

Converts nested tuple list or dict to nested tuple .


def _left_doubling_increments(batch_shape, max_doublings, step_size, seed=None,
                              name=None):
  """Computes the doubling increments for the left end point.

  The doubling procedure expands an initial interval to find a superset of the
  true slice. At each doubling iteration, the interval width is doubled to
  either the left or the right hand side with equal probability.
  If, initially, the left end point is at `L(0)` and the width of the
  interval is `w(0)`, then the left end point and the width at the
  k-th iteration (denoted L(k) and w(k) respectively) are given by the following
  recursions:

  ```none
  w(k) = 2 * w(k-1)
  L(k) = L(k-1) - w(k-1) * X_k, X_k ~ Bernoulli(0.5)
  or, L(0) - L(k) = w(0) Sum(2^i * X(i+1), 0 <= i < k)
  ```

  This function computes the sequence of `L(0)-L(k)` and `w(k)` for k between 0
  and `max_doublings` independently for each chain.

  Args:
    batch_shape: Positive int32 `tf.Tensor`. The batch shape.
    max_doublings: Scalar positive int32 `tf.Tensor`. The maximum number of
      doublings to consider.
    step_size: A real `tf.Tensor` with shape compatible with [num_chains].
      The size of the initial interval.
    seed: (Optional) positive int. The random seed. If None, no seed is set.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., 'find_slice_bounds').

  Returns:
    left_increments: A tensor of shape (max_doublings+1, batch_shape). The
      relative position of the left end point after the doublings.
    widths: A tensor of shape (max_doublings+1, ones_like(batch_shape)). The
      widths of the intervals at each stage of the doubling.
  """
  with tf.compat.v1.name_scope(name, 'left_doubling_increments',
                               [batch_shape, max_doublings, step_size]):

    step_size = tf.convert_to_tensor(value=step_size)
    dtype = step_size.dtype.base_dtype
    # Output shape of the left increments tensor.
    output_shape = tf.concat(([max_doublings + 1], batch_shape), axis=0)
    # A sample realization of X_k.
    expand_left = distributions.Bernoulli(0.5, dtype=dtype).sample(
        sample_shape=output_shape, seed=seed)

    # The widths of the successive intervals. Starts with 1.0 and ends with
    # 2^max_doublings.
    width_multipliers = tf.cast(2 ** tf.range(0, max_doublings+1), dtype=dtype)
    # Output shape of the `widths` tensor.
    widths_shape = tf.concat(([max_doublings + 1],
                              tf.ones_like(batch_shape)), axis=0)
    width_multipliers = tf.reshape(width_multipliers, shape=widths_shape)
    # Widths shape is [max_doublings + 1, 1, 1, 1...].
    widths = width_multipliers * step_size

    # Take the cumulative sum of the left side increments in slice width to give
    # the resulting distance from the inital lower bound.
    left_increments = tf.cumsum(widths * expand_left, exclusive=True, axis=0)
    return left_increments, widths

def _left_doubling_increments ( batch_shape , max_doublings , step_size , seed = None , name = None ) : with tf . compat . v1 . name_scope ( name , 'left_doubling_increments' , [ batch_shape , max_doublings , step_size ] ) : step_size = tf . convert_to_tensor ( value = step_size ) dtype = step_size . dtype . base_dtype output_shape = tf . concat ( ( [ max_doublings + 1 ] , batch_shape ) , axis = 0 ) expand_left = distributions . Bernoulli ( 0.5 , dtype = dtype ) . sample ( sample_shape = output_shape , seed = seed ) width_multipliers = tf . cast ( 2 ** tf . range ( 0 , max_doublings + 1 ) , dtype = dtype ) widths_shape = tf . concat ( ( [ max_doublings + 1 ] , tf . ones_like ( batch_shape ) ) , axis = 0 ) width_multipliers = tf . reshape ( width_multipliers , shape = widths_shape ) widths = width_multipliers * step_size left_increments = tf . cumsum ( widths * expand_left , exclusive = True , axis = 0 ) return left_increments , widths

Computes the doubling increments for the left end point .


def _find_best_interval_idx(x, name=None):
  """Finds the index of the optimal set of bounds for each chain.

  For each chain, finds the smallest set of bounds for which both edges lie
  outside the slice. This is equivalent to the point at which a for loop
  implementation (P715 of Neal (2003)) of the algorithm would terminate.

  Performs the following calculation, where i is the number of doublings that
  have been performed and k is the max number of doublings:

  (2 * k - i) * flag + i

  The argmax of the above returns the earliest index where the bounds were
  outside the slice and if there is no such point, the widest bounds.

  Args:
    x: A tensor of shape (max_doublings+1, batch_shape). Type int32, with value
      0 or 1. Indicates if this set of bounds is outside the slice.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., 'find_slice_bounds').

  Returns:
    indices: A tensor of shape batch_shape. Type int32, with the index of the
      first set of bounds outside the slice and if there are none, the index of
      the widest set.
  """
  with tf.compat.v1.name_scope(name, 'find_best_interval_idx', [x]):
    # Returns max_doublings + 1. Positive int32.
    k = tf.shape(input=x)[0]
    dtype = x.dtype.base_dtype
    # Factors by which to multiply the flag. Corresponds to (2 * k - i) above.
    mults = tf.range(2 * k, k, -1, dtype=dtype)[:, tf.newaxis]
    # Factors by which to shift the flag. Corresponds to i above. Ensures the
    # widest bounds are selected if there are no bounds outside the slice.
    shifts = tf.range(k, dtype=dtype)[:, tf.newaxis]
    indices = tf.argmax(input=mults * x + shifts, axis=0, output_type=dtype)
    return indices

def _find_best_interval_idx ( x , name = None ) : with tf . compat . v1 . name_scope ( name , 'find_best_interval_idx' , [ x ] ) : k = tf . shape ( input = x ) [ 0 ] dtype = x . dtype . base_dtype mults = tf . range ( 2 * k , k , - 1 , dtype = dtype ) [ : , tf . newaxis ] shifts = tf . range ( k , dtype = dtype ) [ : , tf . newaxis ] indices = tf . argmax ( input = mults * x + shifts , axis = 0 , output_type = dtype ) return indices

Finds the index of the optimal set of bounds for each chain .


def slice_bounds_by_doubling(x_initial,
                             target_log_prob,
                             log_slice_heights,
                             max_doublings,
                             step_size,
                             seed=None,
                             name=None):
  """Returns the bounds of the slice at each stage of doubling procedure.

  Precomputes the x coordinates of the left (L) and right (R) endpoints of the
  interval `I` produced in the "doubling" algorithm [Neal 2003][1] P713. Note
  that we simultaneously compute all possible doubling values for each chain,
  for the reason that at small-medium densities, the gains from parallel
  evaluation might cause a speed-up, but this will be benchmarked against the
  while loop implementation.

  Args:
    x_initial: `tf.Tensor` of any shape and any real dtype consumable by
      `target_log_prob`. The initial points.
    target_log_prob: A callable taking a `tf.Tensor` of shape and dtype as
      `x_initial` and returning a tensor of the same shape. The log density of
      the target distribution.
    log_slice_heights: `tf.Tensor` with the same shape as `x_initial` and the
      same dtype as returned by `target_log_prob`. The log of the height of the
      slice for each chain. The values must be bounded above by
      `target_log_prob(x_initial)`.
    max_doublings: Scalar positive int32 `tf.Tensor`. The maximum number of
      doublings to consider.
    step_size: `tf.Tensor` with same dtype as and shape compatible with
      `x_initial`. The size of the initial interval.
    seed: (Optional) positive int. The random seed. If None, no seed is set.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., 'find_slice_bounds').

  Returns:
    upper_bounds: A tensor of same shape and dtype as `x_initial`. Slice upper
      bounds for each chain.
    lower_bounds: A tensor of same shape and dtype as `x_initial`. Slice lower
      bounds for each chain.
    both_ok: A tensor of shape `x_initial` and boolean dtype. Indicates if both
      the chosen upper and lower bound lie outside of the slice.

  #### References

  [1]: Radford M. Neal. Slice Sampling. The Annals of Statistics. 2003, Vol 31,
       No. 3 , 705-767.
       https://projecteuclid.org/download/pdf_1/euclid.aos/1056562461
  """
  with tf.compat.v1.name_scope(
      name, 'slice_bounds_by_doubling',
      [x_initial, log_slice_heights, max_doublings, step_size]):
    seed_gen = distributions.SeedStream(seed, salt='slice_bounds_by_doubling')
    x_initial = tf.convert_to_tensor(value=x_initial)
    batch_shape = tf.shape(input=x_initial)
    dtype = step_size.dtype.base_dtype
    left_endpoints = x_initial + step_size * tf.random.uniform(
        batch_shape, minval=-1.0, maxval=0.0, dtype=dtype, seed=seed_gen())

    # Compute the increments by which we need to step the upper and lower bounds
    # part of the doubling procedure.
    left_increments, widths = _left_doubling_increments(
        batch_shape, max_doublings, step_size, seed=seed_gen())
    # The left and right end points. Shape (max_doublings+1,) + batch_shape.
    left_endpoints -= left_increments
    right_endpoints = left_endpoints + widths

    # Test if these end points lie outside of the slice.
    # Checks if the end points of the slice are outside the graph of the pdf.
    left_ep_values = tf.map_fn(target_log_prob, left_endpoints)
    right_ep_values = tf.map_fn(target_log_prob, right_endpoints)
    left_ok = left_ep_values < log_slice_heights
    right_ok = right_ep_values < log_slice_heights
    both_ok = left_ok & right_ok

    both_ok_f = tf.reshape(both_ok, [max_doublings + 1, -1])

    best_interval_idx = _find_best_interval_idx(
        tf.cast(both_ok_f, dtype=tf.int32))

    # Formats the above index as required to use with gather_nd.
    point_index_gather = tf.stack(
        [best_interval_idx,
         tf.range(tf.size(input=best_interval_idx))],
        axis=1,
        name='point_index_gather')
    left_ep_f = tf.reshape(left_endpoints, [max_doublings + 1, -1])
    right_ep_f = tf.reshape(right_endpoints, [max_doublings + 1, -1])
    # The x values of the uppper and lower bounds of the slices for each chain.
    lower_bounds = tf.reshape(tf.gather_nd(left_ep_f, point_index_gather),
                              batch_shape)
    upper_bounds = tf.reshape(tf.gather_nd(right_ep_f, point_index_gather),
                              batch_shape)
    both_ok = tf.reduce_any(input_tensor=both_ok, axis=0)
    return upper_bounds, lower_bounds, both_ok

def slice_bounds_by_doubling ( x_initial , target_log_prob , log_slice_heights , max_doublings , step_size , seed = None , name = None ) : with tf . compat . v1 . name_scope ( name , 'slice_bounds_by_doubling' , [ x_initial , log_slice_heights , max_doublings , step_size ] ) : seed_gen = distributions . SeedStream ( seed , salt = 'slice_bounds_by_doubling' ) x_initial = tf . convert_to_tensor ( value = x_initial ) batch_shape = tf . shape ( input = x_initial ) dtype = step_size . dtype . base_dtype left_endpoints = x_initial + step_size * tf . random . uniform ( batch_shape , minval = - 1.0 , maxval = 0.0 , dtype = dtype , seed = seed_gen ( ) ) left_increments , widths = _left_doubling_increments ( batch_shape , max_doublings , step_size , seed = seed_gen ( ) ) left_endpoints -= left_increments right_endpoints = left_endpoints + widths left_ep_values = tf . map_fn ( target_log_prob , left_endpoints ) right_ep_values = tf . map_fn ( target_log_prob , right_endpoints ) left_ok = left_ep_values < log_slice_heights right_ok = right_ep_values < log_slice_heights both_ok = left_ok & right_ok both_ok_f = tf . reshape ( both_ok , [ max_doublings + 1 , - 1 ] ) best_interval_idx = _find_best_interval_idx ( tf . cast ( both_ok_f , dtype = tf . int32 ) ) point_index_gather = tf . stack ( [ best_interval_idx , tf . range ( tf . size ( input = best_interval_idx ) ) ] , axis = 1 , name = 'point_index_gather' ) left_ep_f = tf . reshape ( left_endpoints , [ max_doublings + 1 , - 1 ] ) right_ep_f = tf . reshape ( right_endpoints , [ max_doublings + 1 , - 1 ] ) lower_bounds = tf . reshape ( tf . gather_nd ( left_ep_f , point_index_gather ) , batch_shape ) upper_bounds = tf . reshape ( tf . gather_nd ( right_ep_f , point_index_gather ) , batch_shape ) both_ok = tf . reduce_any ( input_tensor = both_ok , axis = 0 ) return upper_bounds , lower_bounds , both_ok

Returns the bounds of the slice at each stage of doubling procedure .


def _sample_with_shrinkage(x_initial, target_log_prob, log_slice_heights,
                           step_size, lower_bounds, upper_bounds, seed=None,
                           name=None):
  """Samples from the slice by applying shrinkage for rejected points.

  Implements the one dimensional slice sampling algorithm of Neal (2003), with a
  doubling algorithm (Neal 2003 P715 Fig. 4), which doubles the size of the
  interval at each iteration and shrinkage (Neal 2003 P716 Fig. 5), which
  reduces the width of the slice when a selected point is rejected, by setting
  the relevant bound that that value. Randomly sampled points are checked for
  two criteria: that they lie within the slice and that they pass the
  acceptability check (Neal 2003 P717 Fig. 6), which tests that the new state
  could have generated the previous one.

  Args:
    x_initial: A tensor of any shape. The initial positions of the chains. This
      function assumes that all the dimensions of `x_initial` are batch
      dimensions (i.e. the event shape is `[]`).
    target_log_prob: Callable accepting a tensor like `x_initial` and returning
      a tensor containing the log density at that point of the same shape.
    log_slice_heights: Tensor of the same shape and dtype as the return value
      of `target_log_prob` when applied to `x_initial`. The log of the height of
      the chosen slice.
    step_size: A tensor of shape and dtype compatible with `x_initial`. The min
      interval size in the doubling algorithm.
    lower_bounds: Tensor of same shape and dtype as `x_initial`. Slice lower
      bounds for each chain.
    upper_bounds: Tensor of same shape and dtype as `x_initial`. Slice upper
      bounds for each chain.
    seed: (Optional) positive int. The random seed. If None, no seed is set.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., 'find_slice_bounds').

  Returns:
    x_proposed: A tensor of the same shape and dtype as `x_initial`. The next
      proposed state of the chain.
  """
  with tf.compat.v1.name_scope(
      name, 'sample_with_shrinkage',
      [x_initial, log_slice_heights, step_size, lower_bounds, upper_bounds]):
    seed_gen = distributions.SeedStream(seed, salt='_sample_with_shrinkage')
    # Keeps track of whether an acceptable sample has been found for the chain.
    found = tf.zeros_like(x_initial, dtype=tf.bool)
    cond = lambda found, *ignored_args: ~tf.reduce_all(input_tensor=found)
    x_next = tf.identity(x_initial)
    x_initial_shape = tf.shape(input=x_initial)
    x_initial_dtype = x_initial.dtype.base_dtype
    def _body(found, left, right, x_next):
      """Iterates until every chain has found a suitable next state."""
      proportions = tf.random.uniform(
          x_initial_shape, dtype=x_initial_dtype, seed=seed_gen())
      x_proposed = tf.where(~found, left + proportions * (right - left), x_next)
      accept_res = _test_acceptance(x_initial, target_log_prob=target_log_prob,
                                    decided=found,
                                    log_slice_heights=log_slice_heights,
                                    x_proposed=x_proposed, step_size=step_size,
                                    lower_bounds=left, upper_bounds=right)
      boundary_test = log_slice_heights < target_log_prob(x_proposed)
      can_accept = boundary_test & accept_res
      next_found = found | can_accept
      # Note that it might seem that we are moving the left and right end points
      # even if the point has been accepted (which is contrary to the stated
      # algorithm in Neal). However, this does not matter because the endpoints
      # for points that have been already accepted are not used again so it
      # doesn't matter what we do with them.
      next_left = tf.where(x_proposed < x_initial, x_proposed, left)
      next_right = tf.where(x_proposed >= x_initial, x_proposed, right)
      return next_found, next_left, next_right, x_proposed

    return tf.while_loop(
        cond=cond,
        body=_body,
        loop_vars=(found, lower_bounds, upper_bounds, x_next))[-1]

def _sample_with_shrinkage ( x_initial , target_log_prob , log_slice_heights , step_size , lower_bounds , upper_bounds , seed = None , name = None ) : with tf . compat . v1 . name_scope ( name , 'sample_with_shrinkage' , [ x_initial , log_slice_heights , step_size , lower_bounds , upper_bounds ] ) : seed_gen = distributions . SeedStream ( seed , salt = '_sample_with_shrinkage' ) found = tf . zeros_like ( x_initial , dtype = tf . bool ) cond = lambda found , * ignored_args : ~ tf . reduce_all ( input_tensor = found ) x_next = tf . identity ( x_initial ) x_initial_shape = tf . shape ( input = x_initial ) x_initial_dtype = x_initial . dtype . base_dtype def _body ( found , left , right , x_next ) : proportions = tf . random . uniform ( x_initial_shape , dtype = x_initial_dtype , seed = seed_gen ( ) ) x_proposed = tf . where ( ~ found , left + proportions * ( right - left ) , x_next ) accept_res = _test_acceptance ( x_initial , target_log_prob = target_log_prob , decided = found , log_slice_heights = log_slice_heights , x_proposed = x_proposed , step_size = step_size , lower_bounds = left , upper_bounds = right ) boundary_test = log_slice_heights < target_log_prob ( x_proposed ) can_accept = boundary_test & accept_res next_found = found | can_accept next_left = tf . where ( x_proposed < x_initial , x_proposed , left ) next_right = tf . where ( x_proposed >= x_initial , x_proposed , right ) return next_found , next_left , next_right , x_proposed return tf . while_loop ( cond = cond , body = _body , loop_vars = ( found , lower_bounds , upper_bounds , x_next ) ) [ - 1 ]

Samples from the slice by applying shrinkage for rejected points .


def slice_sampler_one_dim(target_log_prob, x_initial, step_size=0.01,
                          max_doublings=30, seed=None, name=None):
  """For a given x position in each Markov chain, returns the next x.

  Applies the one dimensional slice sampling algorithm as defined in Neal (2003)
  to an input tensor x of shape (num_chains,) where num_chains is the number of
  simulataneous Markov chains, and returns the next tensor x of shape
  (num_chains,) when these chains are evolved by the slice sampling algorithm.

  Args:
    target_log_prob: Callable accepting a tensor like `x_initial` and returning
      a tensor containing the log density at that point of the same shape.
    x_initial: A tensor of any shape. The initial positions of the chains. This
      function assumes that all the dimensions of `x_initial` are batch
      dimensions (i.e. the event shape is `[]`).
    step_size: A tensor of shape and dtype compatible with `x_initial`. The min
      interval size in the doubling algorithm.
    max_doublings: Scalar tensor of dtype `tf.int32`. The maximum number of
      doublings to try to find the slice bounds.
    seed: (Optional) positive int. The random seed. If None, no seed is set.
    name: Python `str` name prefixed to Ops created by this function.
      Default value: `None` (i.e., 'find_slice_bounds').

  Returns:
    retval: A tensor of the same shape and dtype as `x_initial`. The next state
      of the Markov chain.
    next_target_log_prob: The target log density evaluated at `retval`.
    bounds_satisfied: A tensor of bool dtype and shape batch dimensions.
    upper_bounds: Tensor of the same shape and dtype as `x_initial`. The upper
      bounds for the slice found.
    lower_bounds: Tensor of the same shape and dtype as `x_initial`. The lower
      bounds for the slice found.
  """
  with tf.compat.v1.name_scope(name, 'slice_sampler_one_dim',
                               [x_initial, step_size, max_doublings]):
    x_initial = tf.convert_to_tensor(value=x_initial)
    # Obtain the input dtype of the array.
    dtype = x_initial.dtype.base_dtype
    # Select the height of the slice. Tensor of shape x_initial.shape.
    log_slice_heights = target_log_prob(x_initial) - tf.random.gamma(
        tf.shape(input=x_initial), alpha=1, dtype=dtype, seed=seed)
    # Given the above x and slice heights, compute the bounds of the slice for
    # each chain.
    upper_bounds, lower_bounds, bounds_satisfied = slice_bounds_by_doubling(
        x_initial, target_log_prob, log_slice_heights, max_doublings, step_size,
        seed=seed)
    retval = _sample_with_shrinkage(x_initial, target_log_prob=target_log_prob,
                                    log_slice_heights=log_slice_heights,
                                    step_size=step_size,
                                    lower_bounds=lower_bounds,
                                    upper_bounds=upper_bounds, seed=seed)
    return (retval, target_log_prob(retval), bounds_satisfied,
            upper_bounds, lower_bounds)

def slice_sampler_one_dim ( target_log_prob , x_initial , step_size = 0.01 , max_doublings = 30 , seed = None , name = None ) : with tf . compat . v1 . name_scope ( name , 'slice_sampler_one_dim' , [ x_initial , step_size , max_doublings ] ) : x_initial = tf . convert_to_tensor ( value = x_initial ) dtype = x_initial . dtype . base_dtype log_slice_heights = target_log_prob ( x_initial ) - tf . random . gamma ( tf . shape ( input = x_initial ) , alpha = 1 , dtype = dtype , seed = seed ) upper_bounds , lower_bounds , bounds_satisfied = slice_bounds_by_doubling ( x_initial , target_log_prob , log_slice_heights , max_doublings , step_size , seed = seed ) retval = _sample_with_shrinkage ( x_initial , target_log_prob = target_log_prob , log_slice_heights = log_slice_heights , step_size = step_size , lower_bounds = lower_bounds , upper_bounds = upper_bounds , seed = seed ) return ( retval , target_log_prob ( retval ) , bounds_satisfied , upper_bounds , lower_bounds )

For a given x position in each Markov chain returns the next x .


def make_value_setter(**model_kwargs):
  """Creates a value-setting interceptor.

  This function creates an interceptor that sets values of Edward2 random
  variable objects. This is useful for a range of tasks, including conditioning
  on observed data, sampling from posterior predictive distributions, and as a
  building block of inference primitives such as computing log joint
  probabilities (see examples below).

  Args:
    **model_kwargs: dict of str to Tensor. Keys are the names of random
      variables in the model to which this interceptor is being applied. Values
      are Tensors to set their value to. Variables not included in this dict
      will not be set and will maintain their existing value semantics (by
      default, a sample from the parent-conditional distribution).

  Returns:
    set_values: function that sets the value of intercepted ops.

  #### Examples

  Consider for illustration a model with latent `z` and
  observed `x`, and a corresponding trainable posterior model:

  ```python
  num_observations = 10
  def model():
    z = ed.Normal(loc=0, scale=1., name='z')  # log rate
    x = ed.Poisson(rate=tf.exp(z) * tf.ones(num_observations), name='x')
    return x

  def variational_model():
    return ed.Normal(loc=tf.Variable(0.),
                     scale=tf.nn.softplus(tf.Variable(-4.)),
                     name='z')  # for simplicity, match name of the model RV.
  ```

  We can use a value-setting interceptor to condition the model on observed
  data. This approach is slightly more cumbersome than that of partially
  evaluating the complete log-joint function, but has the potential advantage
  that it returns a new model callable, which may be used to sample downstream
  variables, passed into additional transformations, etc.

  ```python
  x_observed = np.array([6, 3, 1, 8, 7, 0, 6, 4, 7, 5])
  def observed_model():
    with ed.interception(make_value_setter(x=x_observed)):
      model()
  observed_log_joint_fn = ed.make_log_joint_fn(observed_model)

  # After fixing 'x', the observed log joint is now only a function of 'z'.
  # This enables us to define a variational lower bound,
  # `E_q[ log p(x, z) - log q(z)]`, simply by evaluating the observed and
  # variational log joints at variational samples.
  variational_log_joint_fn = ed.make_log_joint_fn(variational_model)
  with ed.tape() as variational_sample:  # Sample trace from variational model.
    variational_model()
  elbo_loss = -(observed_log_joint_fn(**variational_sample) -
                variational_log_joint_fn(**variational_sample))
  ```

  After performing inference by minimizing the variational loss, a value-setting
  interceptor enables simulation from the posterior predictive distribution:

  ```python
  with ed.tape() as posterior_samples:  # tape is a map {rv.name : rv}
    variational_model()
  with ed.interception(ed.make_value_setter(**posterior_samples)):
    x = model()
  # x is a sample from p(X | Z = z') where z' ~ q(z) (the variational model)
  ```

  As another example, using a value setter inside of `ed.tape` enables
  computing the log joint probability, by setting all variables to
  posterior values and then accumulating the log probs of those values under
  the induced parent-conditional distributions. This is one way that we could
  have implemented `ed.make_log_joint_fn`:

  ```python
  def make_log_joint_fn_demo(model):
    def log_joint_fn(**model_kwargs):
      with ed.tape() as model_tape:
        with ed.make_value_setter(**model_kwargs):
          model()

      # accumulate sum_i log p(X_i = x_i | X_{:i-1} = x_{:i-1})
      log_prob = 0.
      for rv in model_tape.values():
        log_prob += tf.reduce_sum(rv.log_prob(rv.value))

      return log_prob
    return log_joint_fn
  ```

  """
  def set_values(f, *args, **kwargs):
    """Sets random variable values to its aligned value."""
    name = kwargs.get("name")
    if name in model_kwargs:
      kwargs["value"] = model_kwargs[name]
    return interceptable(f)(*args, **kwargs)
  return set_values

def make_value_setter ( ** model_kwargs ) : def set_values ( f , * args , ** kwargs ) : name = kwargs . get ( "name" ) if name in model_kwargs : kwargs [ "value" ] = model_kwargs [ name ] return interceptable ( f ) ( * args , ** kwargs ) return set_values


def make_log_joint_fn(model):
  """Takes Edward probabilistic program and returns its log joint function.

  Args:
    model: Python callable which executes the generative process of a
      computable probability distribution using `ed.RandomVariable`s.

  Returns:
    A log-joint probability function. Its inputs are `model`'s original inputs
    and random variables which appear during the program execution. Its output
    is a scalar tf.Tensor.

  #### Examples

  Below we define Bayesian logistic regression as an Edward program,
  representing the model's generative process. We apply `make_log_joint_fn` in
  order to represent the model in terms of its joint probability function.

  ```python
  from tensorflow_probability import edward2 as ed

  def logistic_regression(features):
    coeffs = ed.Normal(loc=0., scale=1.,
                       sample_shape=features.shape[1], name="coeffs")
    outcomes = ed.Bernoulli(logits=tf.tensordot(features, coeffs, [[1], [0]]),
                            name="outcomes")
    return outcomes

  log_joint = ed.make_log_joint_fn(logistic_regression)

  features = tf.random_normal([3, 2])
  coeffs_value = tf.random_normal([2])
  outcomes_value = tf.round(tf.random_uniform([3]))
  output = log_joint(features, coeffs=coeffs_value, outcomes=outcomes_value)
  ```

  """
  def log_joint_fn(*args, **kwargs):
    """Log-probability of inputs according to a joint probability distribution.

    Args:
      *args: Positional arguments. They are the model's original inputs and can
        alternatively be specified as part of `kwargs`.
      **kwargs: Keyword arguments, where for each key-value pair `k` and `v`,
        `v` is passed as a `value` to the random variable(s) whose keyword
        argument `name` during construction is equal to `k`.

    Returns:
      Scalar tf.Tensor, which represents the model's log-probability summed
      over all Edward random variables and their dimensions.

    Raises:
      TypeError: If a random variable in the model has no specified value in
        `**kwargs`.
    """
    log_probs = []

    def interceptor(rv_constructor, *rv_args, **rv_kwargs):
      """Overrides a random variable's `value` and accumulates its log-prob."""
      # Set value to keyword argument indexed by `name` (an input tensor).
      rv_name = rv_kwargs.get("name")
      if rv_name is None:
        raise KeyError("Random variable constructor {} has no name "
                       "in its arguments.".format(rv_constructor.__name__))

      # If no value is explicitly passed in for an RV, default to the value
      # from the RV constructor. This may have been set explicitly by the user
      # or forwarded from a lower-level interceptor.
      previously_specified_value = rv_kwargs.get("value")
      value = kwargs.get(rv_name, previously_specified_value)
      if value is None:
        raise LookupError("Keyword argument specifying value for {} is "
                          "missing.".format(rv_name))
      rv_kwargs["value"] = value

      rv = rv_constructor(*rv_args, **rv_kwargs)
      log_prob = tf.reduce_sum(input_tensor=rv.distribution.log_prob(rv.value))
      log_probs.append(log_prob)
      return rv

    model_kwargs = _get_function_inputs(model, kwargs)
    with interception(interceptor):
      model(*args, **model_kwargs)
    log_prob = sum(log_probs)
    return log_prob
  return log_joint_fn

Takes Edward probabilistic program and returns its log joint function .


def _get_function_inputs(f, src_kwargs):
  """Filters inputs to be compatible with function `f`'s signature.

  Args:
    f: Function according to whose input signature we filter arguments.
    src_kwargs: Keyword arguments to filter according to `f`.

  Returns:
    kwargs: Dict of key-value pairs in `src_kwargs` which exist in `f`'s
      signature.
  """
  if hasattr(f, "_func"):  # functions returned by tf.make_template
    f = f._func  # pylint: disable=protected-access

  try:  # getargspec was deprecated in Python 3.6
    argspec = inspect.getfullargspec(f)
  except AttributeError:
    argspec = inspect.getargspec(f)

  fkwargs = {k: v for k, v in six.iteritems(src_kwargs) if k in argspec.args}
  return fkwargs

def _get_function_inputs ( f , src_kwargs ) : if hasattr ( f , "_func" ) : f = f . _func try : argspec = inspect . getfullargspec ( f ) except AttributeError : argspec = inspect . getargspec ( f ) fkwargs = { k : v for k , v in six . iteritems ( src_kwargs ) if k in argspec . args } return fkwargs

Filters inputs to be compatible with function f s signature .


def _vggconv_block(x, filters, kernel, stride, kernel_posterior_fn):
  """Network block for VGG."""
  out = tfp.layers.Convolution2DFlipout(
      filters,
      kernel,
      padding='same',
      kernel_posterior_fn=kernel_posterior_fn)(x)
  out = tf.keras.layers.BatchNormalization()(out)
  out = tf.keras.layers.Activation('relu')(out)

  out = tfp.layers.Convolution2DFlipout(
      filters,
      kernel,
      padding='same',
      kernel_posterior_fn=kernel_posterior_fn)(out)
  out = tf.keras.layers.BatchNormalization()(out)
  out = tf.keras.layers.Activation('relu')(out)

  out = tf.keras.layers.MaxPooling2D(
      pool_size=(2, 2), strides=stride)(out)
  return out

def _vggconv_block ( x , filters , kernel , stride , kernel_posterior_fn ) : out = tfp . layers . Convolution2DFlipout ( filters , kernel , padding = 'same' , kernel_posterior_fn = kernel_posterior_fn ) ( x ) out = tf . keras . layers . BatchNormalization ( ) ( out ) out = tf . keras . layers . Activation ( 'relu' ) ( out ) out = tfp . layers . Convolution2DFlipout ( filters , kernel , padding = 'same' , kernel_posterior_fn = kernel_posterior_fn ) ( out ) out = tf . keras . layers . BatchNormalization ( ) ( out ) out = tf . keras . layers . Activation ( 'relu' ) ( out ) out = tf . keras . layers . MaxPooling2D ( pool_size = ( 2 , 2 ) , strides = stride ) ( out ) return out