class_docstr: This helper makes it easy to read from variables being set with a prior and
  writes values back to the same variables.
  
  Example::
  
      model = ...  # Create a GPflow model
      hmc_helper = SamplingHelper(model.log_posterior_density, model.trainable_parameters)
  
      target_log_prob_fn = hmc_helper.target_log_prob_fn
      current_state = hmc_helper.current_state
  
      hmc = tfp.mcmc.HamiltonianMonteCarlo(target_log_prob_fn=target_log_prob_fn, ...)
      adaptive_hmc = tfp.mcmc.SimpleStepSizeAdaptation(hmc, ...)
  
      @tf.function
      def run_chain_fn():
          return mcmc.sample_chain(
              num_samples, num_burnin_steps, current_state, kernel=adaptive_hmc)
  
      hmc_samples = run_chain_fn()
      parameter_samples = hmc_helper.convert_to_constrained_values(hmc_samples)
docstr: The target log probability, adjusted to allow for optimisation to occur on the tracked
  unconstrained underlying variables.
docstr_tok: ["The","target","log","probability",",","adjusted","to","allow","for","optimisation","to","occur","on","the","tracked","unconstrained","underlying","variables","."]
code: def target_log_prob_fn(
      self,
  ) -> Callable[
      ...,
      Tuple[tf.Tensor, Callable[..., Tuple[tf.Tensor, Sequence[None]]]],
  ]:
      """
      The target log probability, adjusted to allow for optimisation to occur on the tracked
      unconstrained underlying variables.
      """
      variables_list = self.current_state
  
      @tf.custom_gradient
      def _target_log_prob_fn_closure(
          *variables: tf.Variable,
      ) -> Tuple[
          tf.Tensor, Callable[..., Tuple[tf.Tensor, Sequence[None]]]
      ]:
          for v_old, v_new in zip(variables_list, variables):
              v_old.assign(v_new)
  
          with tf.GradientTape(watch_accessed_variables=False) as tape:
              tape.watch(variables_list)
              log_prob = self._target_log_prob_fn()
              # Now need to correct for the fact that the prob fn is evaluated on the
              # constrained space while we wish to evaluate it in the unconstrained space
              for param in self._parameters:
                  if param.transform is not None:
                      x = param.unconstrained_variable
                      log_det_jacobian = (
                          param.transform.forward_log_det_jacobian(
                              x, x.shape.ndims
                          )
                      )
                      log_prob += tf.reduce_sum(log_det_jacobian)
  
          @tf.function
          def grad_fn(
              dy: tf.Tensor, variables: Optional[tf.Tensor] = None
          ) -> Tuple[tf.Tensor, Sequence[None]]:
              grad = tape.gradient(log_prob, variables_list)
              return grad, [None] * len(variables_list)
  
          return log_prob, grad_fn
  
      return _target_log_prob_fn_closure
  
file_path: GPflow/gpflow/optimizers/mcmc.py
imports: from typing import Callable, Optional, Sequence, Tuple
  import tensorflow
  from gpflow.base import Parameter
label_desc: Use image node_id 3 for calling the SamplingHelper obj's underlying member method code with example usage: obj.target_log_prob_fn() and returns: _target_log_prob_fn_closure, log_prob,  grad_fn, grad,  unknown