code: def _mcmc_sample(
      self,
      mcmc_steps,
      step_size=0.0,
      dynamic_step_size=False,
      num_chains=1,
      init_samples=None,
      init_loglikes=None,
      init_derived=None,
      loglstar=None,
      show_progress=False,
      max_start_tries=100,
      output_interval=None,
      stats_interval=None,
      plot_trace=True,
      prior_volume_steps=1,
  ):
      self.trainer.netG.eval()
  
      if step_size <= 0.0:
          step_size = 2 / self.x_dim**0.5
  
      samples = []
      latent_samples = []
      derived_samples = []
      loglikes = []
  
      iters = (
          tqdm(range(1, mcmc_steps + 1))
          if show_progress
          else range(1, mcmc_steps + 1)
      )
      scale = step_size
      accept = 0
      reject = 0
      ncall = 0
  
      if init_samples is not None:
          num_chains = init_samples.shape[0]
          z, _ = self.trainer.forward(init_samples)
          # Add the inverse version of x rather than init_samples due to numerical precision
          x = self.trainer.get_samples(z, to_numpy=True)
          if init_loglikes is None or init_derived is None:
              logl, derived = self.loglike(x)
              ncall += num_chains
          else:
              logl = init_loglikes
              derived = init_derived
          logl_prior = self.prior(x)
      else:
          for i in range(max_start_tries):
              z = self.trainer.get_prior_samples(num_chains)
              x = self.trainer.get_samples(z, to_numpy=True)
              logl, derived = self.loglike(x)
              ncall += num_chains
              logl_prior = self.prior(x)
              if np.all(logl > -1e30) and np.all(logl_prior) > -1e30:
                  break
              if i == max_start_tries - 1:
                  raise Exception("Could not find starting value")
  
      samples.append(x)
      latent_samples.append(z.cpu().numpy())
      derived_samples.append(derived)
      loglikes.append(logl)
  
      for it in iters:
          self.logger.debug("z={}".format(z))
  
          x, log_det_J = self.trainer.inverse(z)
          x = x.cpu().numpy()
          self.logger.debug("x={}".format(x))
  
          if loglstar is not None:
              # Sampling from a hard likelihood constraint logl > loglstar
              x_prime = x
              z_prime = z
              mask_prior_volume = torch.zeros(num_chains)
  
              # Find a move that satisfies prior and Jacobian
              for i in range(prior_volume_steps):
                  # Propose a move in latent space
                  dz = torch.randn_like(z) * scale
                  if (
                      self.num_slow > 0
                      and np.random.uniform() < self.oversample_rate
                  ):
                      fast = True
                      dz[:, 0 : self.num_slow] = 0.0
                  else:
                      fast = False
                  z_propose = z + dz
  
                  self.logger.debug("z_propose={}".format(z_propose))
  
                  try:
                      (
                          x_propose,
                          log_det_J_propose,
                      ) = self.trainer.inverse(z_propose)
                  except ValueError:
                      # self.logger.error('Could not find inverse', z_propose)
                      continue
                  x_propose = x_propose.cpu().numpy()
                  log_ratio = log_det_J_propose - log_det_J
  
                  self.logger.debug("x_propose={}".format(x_propose))
  
                  logl_prior = self.prior(x_propose)
                  log_ratio[np.where(logl_prior < -1e30)] = -np.inf
  
                  # Check prior and Jacobian is accepted
                  rnd_u = torch.rand(
                      log_ratio.shape, device=self.trainer.device
                  )
                  ratio = log_ratio.exp().clamp(max=1)
                  mask = (rnd_u < ratio).int()
  
                  self.logger.debug("Mask={}".format(mask))
  
                  m = mask[:, None].float()
                  z_prime = (z_propose * m + z_prime * (1 - m)).detach()
                  x_prime = x_propose * m.cpu().numpy() + x_prime * (
                      1 - m.cpu().numpy()
                  )
                  mask_prior_volume += mask
  
              mask = mask_prior_volume
              mask[mask > 1] = 1
  
              self.logger.debug("z_prime={}".format(z_prime))
              self.logger.debug("x_prime={}".format(x_prime))
  
              self.logger.debug("Pre-likelihood mask={}".format(mask))
  
              logl_prior_prime = self.prior(x_prime)
              derived_prime = np.copy(derived)
              # Only evaluate likelihood if prior and Jacobian is accepted
              logl_prime = np.full(num_chains, logl)
  
              mask_idx = np.where(mask.cpu().numpy() == 1)[0]
              if len(mask_idx) > 0:
                  lp, der = self.loglike(x_prime[mask_idx])
                  accept_idx = np.where(
                      (np.isfinite(lp)) & (lp > loglstar)
                  )[0]
                  non_accept_idx = np.where(
                      np.logical_not(
                          (np.isfinite(lp)) & (lp > loglstar)
                      )
                  )[0]
                  ncall += len(mask_idx)
                  if fast:
                      self.total_fast_calls += len(mask_idx)
                  logl_prime[mask_idx[accept_idx]] = lp[accept_idx]
                  derived_prime[mask_idx[accept_idx]] = der[accept_idx]
                  mask[mask_idx[non_accept_idx]] = 0
  
              self.logger.debug("Post-likelihood mask={}".format(mask))
  
          else:
              # Use likelihood and prior in proposal ratio
  
              # Propose a move in latent space
              dz = torch.randn_like(z) * scale
              if (
                  self.num_slow > 0
                  and np.random.uniform() < self.oversample_rate
              ):
                  fast = True
                  dz[:, 0 : self.num_slow] = 0.0
              else:
                  fast = False
              z_prime = z + dz
  
              self.logger.debug("z={}".format(z))
              self.logger.debug("z_prime={}".format(z_prime))
  
              try:
                  x_prime, log_det_J_prime = self.trainer.inverse(
                      z_prime
                  )
              except ValueError:
                  # self.logger.error('Could not find inverse', z_prime)
                  continue
  
              x_prime = x_prime.cpu().numpy()
              self.logger.debug("x_prime={}".format(x_prime))
  
              ncall += num_chains
              if fast:
                  self.total_fast_calls += num_chains
              logl_prime, derived_prime = self.loglike(x_prime)
              logl_prior_prime = self.prior(x_prime)
              log_ratio_1 = log_det_J_prime - log_det_J
              log_ratio_2 = torch.tensor(logl_prime - logl)
              log_ratio_3 = torch.tensor(logl_prior_prime - logl_prior)
              log_ratio = log_ratio_1 + log_ratio_2 + log_ratio_3
  
              self.logger.debug("log ratio 1={}".format(log_ratio_1))
              self.logger.debug("log ratio 2={}".format(log_ratio_2))
              self.logger.debug("log ratio 3={}".format(log_ratio_3))
              self.logger.debug("log ratio={}".format(log_ratio))
  
              rnd_u = torch.rand(
                  log_ratio.shape, device=self.trainer.device
              )
              ratio = log_ratio.exp().clamp(max=1)
              mask = (rnd_u < ratio).int()
  
              self.logger.debug("Mask={}".format(mask))
  
          num_accepted = torch.sum(mask).cpu().numpy()
          self.total_accepted += num_accepted
          self.total_rejected += num_chains - num_accepted
  
          if dynamic_step_size:
              if 2 * num_accepted > num_chains:
                  accept += 1
              else:
                  reject += 1
              if accept > reject:
                  scale *= np.exp(1.0 / (1 + accept))
              if accept < reject:
                  scale /= np.exp(1.0 / (1 + reject))
              self.logger.debug(
                  "scale=%5.4f accept=%d reject=%d"
                  % (scale, accept, reject)
              )
  
          logl = logl_prime * mask.cpu().numpy() + logl * (
              1 - mask.cpu().numpy()
          )
          # Avoid multiplying due to -np.inf
          logl_prior[
              np.where(mask.cpu().numpy() == 1)
          ] = logl_prior_prime[np.where(mask.cpu().numpy() == 1)]
          m = mask[:, None].float()
          z = (z_prime * m + z * (1 - m)).detach()
          x = x_prime * m.cpu().numpy() + x * (1 - m.cpu().numpy())
          derived = derived_prime * m.cpu().numpy() + derived * (
              1 - m.cpu().numpy()
          )
  
          samples.append(x)
          latent_samples.append(z.cpu().numpy())
          derived_samples.append(derived)
          loglikes.append(logl)
  
          if output_interval is not None and it % output_interval == 0:
              self._save_samples(
                  np.transpose(
                      np.array(self.transform(samples)), axes=[1, 0, 2]
                  ),
                  np.transpose(np.array(loglikes), axes=[1, 0]),
                  derived_samples=np.transpose(
                      np.array(derived_samples), axes=[1, 0, 2]
                  ),
              )
  
          if stats_interval is not None and it % stats_interval == 0:
              self._chain_stats(
                  np.transpose(
                      np.array(self.transform(samples)), axes=[1, 0, 2]
                  ),
                  step=it,
              )
  
      # Transpose samples so shape is (chain_num, iteration, dim)
      samples = np.transpose(np.array(samples), axes=[1, 0, 2])
      latent_samples = np.transpose(
          np.array(latent_samples), axes=[1, 0, 2]
      )
      derived_samples = np.transpose(
          np.array(derived_samples), axes=[1, 0, 2]
      )
      loglikes = np.transpose(np.array(loglikes), axes=[1, 0])
  
      if plot_trace:
          self._plot_trace(samples, latent_samples)
  
      return (
          samples,
          latent_samples,
          derived_samples,
          loglikes,
          scale,
          ncall,
      )
  
imports: from __future__ import print_function
  from __future__ import division
  import os
  import json
  import logging
  import time
  import torch
  import numpy
  from tqdm import tqdm
  import matplotlib.pyplot
  from nnest.trainer import Trainer
  from nnest.utils.evaluation import acceptance_rate, effective_sample_size, mean_jump_distance, gelman_rubin_diagnostic
  from nnest.utils.logger import create_logger, get_or_create_run_dir
label_desc: Use image node_id 3 for calling the Sampler obj's underlying member method code with example usage: obj._mcmc_sample(mcmc_steps, step_size, dynamic_step_size, num_chains, init_samples, init_loglikes, init_derived, loglstar, show_progress, max_start_tries, output_interval, stats_interval, plot_trace, prior_volume_steps) and returns: samples,  latent_samples,  derived_samples,  loglikes,  scale,  ncall