class_docstr: Nested sampler with reactive exploration strategy.
  
  Storage & resume capable, optionally MPI parallelised.
class_docstr_tok: ["Nested","sampler","with","reactive","exploration","strategy",".","Storage","&","resume","capable",",","optionally","MPI","parallelised","."]
docstr: Ask each strategy which log-likelihood interval needs more exploration.
  
  Returns
  -------
  (Llo_Z, Lhi_Z): floats
      interval where dlogz strategy requires more samples.
  (Llo_KL, Lhi_KL): floats
      interval where posterior uncertainty strategy requires more samples.
  (Llo_ess, Lhi_ess): floats
      interval where effective sample strategy requires more samples.
  
  Parameters
  ----------
  saved_logl: array of float
      loglikelihood values in integration
  main_iterator: BreadthFirstIterator
      current tree exploration iterator
  dlogz: float
      required logZ accuracy (smaller is stricter)
  dKL: float
      required Kulback-Leibler information gain between bootstrapped
      nested sampling incarnations (smaller is stricter).
  min_ess: float
      required number of effective samples (higher is stricter).
docstr_tok: ["Ask","each","strategy","which","log-likelihood","interval","needs","more","exploration",".","Returns","--","--","--","-","(","Llo_Z",",","Lhi_Z",")",":","floats","interval","where","dlogz","strategy","requires","more","samples",".","(","Llo_KL",",","Lhi_KL",")",":","floats","interval","where","posterior","uncertainty","strategy","requires","more","samples",".","(","Llo_ess",",","Lhi_ess",")",":","floats","interval","where","effective","sample","strategy","requires","more","samples",".","Parameters","--","--","--","--","--","saved_logl",":","array","of","float","loglikelihood","values","in","integration","main_iterator",":","BreadthFirstIterator","current","tree","exploration","iterator","dlogz",":","float","required","logZ","accuracy","(","smaller","is","stricter",")","dKL",":","float","required","Kulback-Leibler","information","gain","between","bootstrapped","nested","sampling","incarnations","(","smaller","is","stricter",")",".","min_ess",":","float","required","number","of","effective","samples","(","higher","is","stricter",")","."]
code: def _find_strategy(
      self, saved_logl, main_iterator, dlogz, dKL, min_ess
  ):
      """Ask each strategy which log-likelihood interval needs more exploration.
  
      Returns
      -------
      (Llo_Z, Lhi_Z): floats
          interval where dlogz strategy requires more samples.
      (Llo_KL, Lhi_KL): floats
          interval where posterior uncertainty strategy requires more samples.
      (Llo_ess, Lhi_ess): floats
          interval where effective sample strategy requires more samples.
  
      Parameters
      ----------
      saved_logl: array of float
          loglikelihood values in integration
      main_iterator: BreadthFirstIterator
          current tree exploration iterator
      dlogz: float
          required logZ accuracy (smaller is stricter)
      dKL: float
          required Kulback-Leibler information gain between bootstrapped
          nested sampling incarnations (smaller is stricter).
      min_ess: float
          required number of effective samples (higher is stricter).
  
      """
      saved_logl = np.asarray(saved_logl)
      logw = (
          np.asarray(main_iterator.logweights)
          + saved_logl.reshape((-1, 1))
          - main_iterator.all_logZ
      )
      ref_logw = logw[:, 0].reshape((-1, 1))
      other_logw = logw[:, 1:]
  
      Llo_ess = np.inf
      Lhi_ess = -np.inf
      w = exp(ref_logw.flatten())
      w /= w.sum()
      ess = len(w) / (1.0 + ((len(w) * w - 1) ** 2).sum() / len(w))
      if ess < min_ess:
          samples = np.random.choice(len(w), p=w, size=min_ess)
          Llo_ess = saved_logl[samples].min()
          Lhi_ess = saved_logl[samples].max()
      if self.log and Lhi_ess > Llo_ess:
          self.logger.info(
              "Effective samples strategy wants to improve: %.2f..%.2f (ESS = %.1f, need >%d)",
              Llo_ess,
              Lhi_ess,
              ess,
              min_ess,
          )
      elif self.log and min_ess > 0:
          self.logger.info(
              "Effective samples strategy satisfied (ESS = %.1f, need >%d)",
              ess,
              min_ess,
          )
  
      # compute KL divergence
      with np.errstate(invalid="ignore"):
          KL = np.where(
              np.isfinite(other_logw),
              exp(other_logw) * (other_logw - ref_logw),
              0,
          )
      KLtot = KL.sum(axis=0)
      dKLtot = np.abs(KLtot - KLtot.mean())
      p = np.where(KL > 0, KL, 0)
      p /= p.sum(axis=0).reshape((1, -1))
  
      Llo_KL = np.inf
      Lhi_KL = -np.inf
      for i, (pi, dKLi, logwi) in enumerate(
          zip(p.transpose(), dKLtot, other_logw)
      ):
          if dKLi > dKL:
              ilo, ihi = _get_cumsum_range(pi, 1.0 / 400)
              # ilo and ihi are most likely missing in this iterator
              # --> select the one before/after in this iterator
              ilos = np.where(np.isfinite(logwi[:ilo]))[0]
              ihis = np.where(np.isfinite(logwi[ihi:]))[0]
              ilo2 = ilos[-1] if len(ilos) > 0 else 0
              ihi2 = (ihi + ihis[0]) if len(ihis) > 0 else -1
              # self.logger.info('   - KL[%d] = %.2f: need to improve near %.2f..%.2f --> %.2f..%.2f' % (
              #  i, dKLi, saved_logl[ilo], saved_logl[ihi], saved_logl[ilo2], saved_logl[ihi2]))
              Llo_KL = min(Llo_KL, saved_logl[ilo2])
              Lhi_KL = max(Lhi_KL, saved_logl[ihi2])
  
      if self.log and Lhi_KL > Llo_KL:
          self.logger.info(
              "Posterior uncertainty strategy wants to improve: %.2f..%.2f (KL: %.2f+-%.2f nat, need <%.2f nat)",
              Llo_KL,
              Lhi_KL,
              KLtot.mean(),
              dKLtot.max(),
              dKL,
          )
      elif self.log:
          self.logger.info(
              "Posterior uncertainty strategy is satisfied (KL: %.2f+-%.2f nat, need <%.2f nat)",
              KLtot.mean(),
              dKLtot.max(),
              dKL,
          )
  
      Nlive_min = 0
      p = exp(logw)
      p /= p.sum(axis=0).reshape((1, -1))
      deltalogZ = np.abs(
          main_iterator.all_logZ[1:] - main_iterator.logZ
      )
  
      tail_fraction = (
          w[np.asarray(main_iterator.istail)].sum() / w.sum()
      )
      logzerr_tail = (
          logaddexp(
              log(tail_fraction) + main_iterator.logZ,
              main_iterator.logZ,
          )
          - main_iterator.logZ
      )
      maxlogzerr = max(
          main_iterator.logZerr,
          deltalogZ.max(),
          main_iterator.logZerr_bs,
      )
      if maxlogzerr > dlogz:
          if logzerr_tail > maxlogzerr:
              if self.log:
                  self.logger.info(
                      "logz error is dominated by tail. Decrease frac_remain to make progress."
                  )
          # very convervative estimation using all iterations
          # this punishes short intervals with many live points
          niter_max = len(saved_logl)
          Nlive_min = int(np.ceil(niter_max**0.5 / dlogz))
          if self.log:
              self.logger.debug(
                  "  conservative estimate says at least %d live points are needed to reach dlogz goal",
                  Nlive_min,
              )
  
          # better estimation:
  
          # get only until where logz bulk is (random sample here)
          itmax = np.random.choice(len(w), p=w)
          # back out nlive sequence (width changed by (1 - exp(-1/N))*(exp(-1/N)) )
          logweights = np.array(main_iterator.logweights[:itmax])
          with np.errstate(divide="ignore", invalid="ignore"):
              widthratio = 1 - np.exp(
                  logweights[1:, 0] - logweights[:-1, 0]
              )
              nlive = 1.0 / np.log(
                  (1 - np.sqrt(1 - 4 * widthratio)) / (2 * widthratio)
              )
              nlive[~(nlive > 1)] = 1
  
          # build iteration groups
          nlive_sets, niter = np.unique(
              nlive.astype(int), return_counts=True
          )
          if self.log:
              self.logger.debug(
                  "  number of live points vary between %.0f and %.0f, most (%d/%d iterations) have %d",
                  nlive.min(),
                  nlive.max(),
                  niter.max(),
                  itmax,
                  nlive_sets[niter.argmax()],
              )
          for nlive_floor in nlive_sets:
              # estimate error if this was the minimum nlive applied
              nlive_adjusted = np.where(
                  nlive_sets < nlive_floor, nlive_floor, nlive_sets
              )
              deltalogZ_expected = (
                  niter / nlive_adjusted**2.0
              ).sum() ** 0.5
              if deltalogZ_expected < dlogz:
                  # achievable with Nlive_min
                  Nlive_min = int(nlive_floor)
                  if self.log:
                      self.logger.debug(
                          "  at least %d live points are needed to reach dlogz goal",
                          Nlive_min,
                      )
                  break
  
      if self.log and Nlive_min > 0:
          self.logger.info(
              "Evidency uncertainty strategy wants %d minimum live points (dlogz from %.2f to %.2f, need <%s)",
              Nlive_min,
              deltalogZ.mean(),
              deltalogZ.max(),
              dlogz,
          )
      elif self.log:
          self.logger.info(
              "Evidency uncertainty strategy is satisfied (dlogz=%.2f, need <%s)",
              (main_iterator.logZerr_bs**2 + logzerr_tail**2)
              ** 0.5,
              dlogz,
          )
      if self.log:
          self.logger.info(
              "  logZ error budget: single: %.2f bs:%.2f tail:%.2f total:%.2f required:<%.2f",
              main_iterator.logZerr,
              main_iterator.logZerr_bs,
              logzerr_tail,
              (main_iterator.logZerr_bs**2 + logzerr_tail**2)
              ** 0.5,
              dlogz,
          )
  
      return Nlive_min, (Llo_KL, Lhi_KL), (Llo_ess, Lhi_ess)
  
imports: from __future__ import print_function, division
  import os
  import sys
  import csv
  import json
  import operator
  import time
  import warnings
  from numpy import log, exp, logaddexp
  import numpy
  from .utils import create_logger, make_run_dir, resample_equal, vol_prefactor, vectorize, listify
  from .utils import is_affine_transform, normalised_kendall_tau_distance, distributed_work_chunk_size
  from ultranest.mlfriends import MLFriends, AffineLayer, ScalingLayer, find_nearby, WrappingEllipsoid, RobustEllipsoidRegion
  from .store import HDF5PointStore, TextPointStore, NullPointStore
  from .viz import get_default_viz_callback
  from .ordertest import UniformOrderAccumulator
  from .netiter import PointPile, SingleCounter, MultiCounter, BreadthFirstIterator, TreeNode, count_tree_between, find_nodes_before, logz_sequence
  from .netiter import dump_tree, combine_results
  from .hotstart import get_auxiliary_contbox_parameterization
label_desc: Use image node_id 9 for calling the ReactiveNestedSampler obj's underlying member method code with example usage: obj._find_strategy(saved_logl, main_iterator, dlogz, dKL, min_ess) and returns: Nlive_min,  Llo_KL,  Lhi_KL,  Llo_ess,  Lhi_ess