class_docstr: Hypervolume computation dimension sweep algorithm from [Fonseca2006]_.
  
  Adapted from Simon Wessing's implementation of the algorithm
  (Variant 3, Version 1.2) in [Fonseca2006]_ in PyMOO:
  https://github.com/msu-coinlab/pymoo/blob/master/pymoo/vendor/hv.py
  
  Maximization is assumed.
  
  TODO: write this in C++ for faster looping.
class_docstr_tok: ["Hypervolume","computation","dimension","sweep","algorithm","from","[","Fonseca2006","]","_",".","Adapted","from","Simon","Wessing","'s","implementation","of","the","algorithm","(","Variant","3",",","Version","1.2",")","in","[","Fonseca2006","]","_","in","PyMOO",":","https",":","\/\/github.com\/msu-coinlab\/pymoo\/blob\/master\/pymoo\/vendor\/hv.py","Maximization","is","assumed",".","TODO",":","write","this","in","C++","for","faster","looping","."]
docstr: Recursive method for hypervolume calculation.
  
  This assumes minimization (internally).
  
  In contrast to the paper, this code assumes that the reference point
  is the origin. This enables pruning a few operations.
  
  Args:
      i: objective index
      n_pareto: number of pareto points
      bounds: objective bounds
  
  Returns:
      The hypervolume.
docstr_tok: ["Recursive","method","for","hypervolume","calculation",".","This","assumes","minimization","(","internally",")",".","In","contrast","to","the","paper",",","this","code","assumes","that","the","reference","point","is","the","origin",".","This","enables","pruning","a","few","operations",".","Args",":","i",":","objective","index","n_pareto",":","number","of","pareto","points","bounds",":","objective","bounds","Returns",":","The","hypervolume","."]
code: def _hv_recursive(
      self, i: int, n_pareto: int, bounds: Tensor
  ) -> float:
      r"""Recursive method for hypervolume calculation.
  
      This assumes minimization (internally).
  
      In contrast to the paper, this code assumes that the reference point
      is the origin. This enables pruning a few operations.
  
      Args:
          i: objective index
          n_pareto: number of pareto points
          bounds: objective bounds
  
      Returns:
          The hypervolume.
      """
      hvol = torch.tensor(0.0, dtype=bounds.dtype, device=bounds.device)
      sentinel = self.list.sentinel
      if n_pareto == 0:
          # base case: one dimension
          return hvol.item()
      elif i == 0:
          # base case: one dimension
          return -sentinel.next[0].data[0].item()
      elif i == 1:
          # two dimensions, end recursion
          q = sentinel.next[1]
          h = q.data[0]
          p = q.next[1]
          while p is not sentinel:
              hvol += h * (q.data[1] - p.data[1])
              if p.data[0] < h:
                  h = p.data[0]
              q = p
              p = q.next[1]
          hvol += h * q.data[1]
          return hvol.item()
      else:
          p = sentinel
          q = p.prev[i]
          while q.data is not None:
              if q.ignore < i:
                  q.ignore = 0
              q = q.prev[i]
          q = p.prev[i]
          while n_pareto > 1 and (
              q.data[i] > bounds[i] or q.prev[i].data[i] >= bounds[i]
          ):
              p = q
              self.list.remove(p, i, bounds)
              q = p.prev[i]
              n_pareto -= 1
          q_prev = q.prev[i]
          if n_pareto > 1:
              hvol = q_prev.volume[i] + q_prev.area[i] * (
                  q.data[i] - q_prev.data[i]
              )
          else:
              q.area[0] = 1
              q.area[1 : i + 1] = q.area[:i] * -(q.data[:i])
          q.volume[i] = hvol
          if q.ignore >= i:
              q.area[i] = q_prev.area[i]
          else:
              q.area[i] = self._hv_recursive(i - 1, n_pareto, bounds)
              if q.area[i] <= q_prev.area[i]:
                  q.ignore = i
          while p is not sentinel:
              p_data = p.data[i]
              hvol += q.area[i] * (p_data - q.data[i])
              bounds[i] = p_data
              self.list.reinsert(p, i, bounds)
              n_pareto += 1
              q = p
              p = p.next[i]
              q.volume[i] = hvol
              if q.ignore >= i:
                  q.area[i] = q.prev[i].area[i]
              else:
                  q.area[i] = self._hv_recursive(
                      i - 1, n_pareto, bounds
                  )
                  if q.area[i] <= q.prev[i].area[i]:
                      q.ignore = i
          hvol -= q.area[i] * q.data[i]
          return hvol.item()
  
imports: from __future__ import annotations
  import warnings
  from copy import deepcopy
  from itertools import combinations
  from typing import Callable, List, Optional, Union
  import torch
  from botorch.acquisition.cached_cholesky import CachedCholeskyMCSamplerMixin
  from botorch.acquisition.multi_objective.objective import MCMultiOutputObjective
  from botorch.acquisition.multi_objective.utils import prune_inferior_points_multi_objective
  from botorch.exceptions.errors import BotorchError, BotorchTensorDimensionError, UnsupportedError
  from botorch.exceptions.warnings import BotorchWarning
  from botorch.models.model import Model
  from botorch.sampling.base import MCSampler
  from botorch.utils.multi_objective.box_decompositions.box_decomposition_list import BoxDecompositionList
  from botorch.utils.multi_objective.box_decompositions.dominated import DominatedPartitioning
  from botorch.utils.multi_objective.box_decompositions.non_dominated import FastNondominatedPartitioning, NondominatedPartitioning
  from botorch.utils.multi_objective.box_decompositions.utils import _pad_batch_pareto_frontier
  from botorch.utils.objective import compute_feasibility_indicator
  from botorch.utils.torch import BufferDict
  from torch import Tensor
label_desc: Use image node_id 5 for calling the Hypervolume obj's underlying member method code with example usage: obj._hv_recursive(i, n_pareto, bounds) and returns: hvol, unknown, hvol, hvol