class_docstr: A class for partitioning the non-dominated space into hyper-cells.
  
  Note: this assumes maximization. Internally, it multiplies outcomes by -1 and
  performs the decomposition under minimization. TODO: use maximization
  internally as well.
  
  Note: it is only feasible to use this algorithm to compute an exact
  decomposition of the non-dominated space for `m<5` objectives (alpha=0.0).
  
  The alpha parameter can be increased to obtain an approximate partitioning
  faster. The `alpha` is a fraction of the total hypervolume encapsuling the
  entire Pareto set. When a hypercell's volume divided by the total hypervolume
  is less than `alpha`, we discard the hypercell. See Figure 2 in
  [Couckuyt2012]_ for a visual representation.
  
  This PyTorch implementation of the binary partitioning algorithm ([Couckuyt2012]_)
  is adapted from numpy/tensorflow implementation at:
  https://github.com/GPflow/GPflowOpt/blob/master/gpflowopt/pareto.py.
  
  TODO: replace this with a more efficient decomposition. E.g.
  https://link.springer.com/content/pdf/10.1007/s10898-019-00798-7.pdf
docstr: Partition the non-dominated space into disjoint hypercells.
  
  This direct method works for `m=2` outcomes.
docstr_tok: ["Partition","the","non-dominated","space","into","disjoint","hypercells",".","This","direct","method","works","for","`","m=2","`","outcomes","."]
code: def _partition_space_2d(self) -> None:
      r"""Partition the non-dominated space into disjoint hypercells.
  
      This direct method works for `m=2` outcomes.
      """
      pf_ext_idx = self._get_augmented_pareto_front_indices()
      n_pf_plus_1 = self._neg_pareto_Y.shape[-2] + 1
      view_shape = torch.Size(
          [1] * len(self.batch_shape) + [n_pf_plus_1]
      )
      expand_shape = self.batch_shape + torch.Size([n_pf_plus_1])
      range_pf_plus1 = torch.arange(
          n_pf_plus_1,
          dtype=torch.long,
          device=self._neg_pareto_Y.device,
      )
      range_pf_plus1_expanded = range_pf_plus1.view(view_shape).expand(
          expand_shape
      )
  
      lower = torch.stack(
          [
              range_pf_plus1_expanded,
              torch.zeros_like(range_pf_plus1_expanded),
          ],
          dim=-1,
      )
      upper = torch.stack(
          [
              1 + range_pf_plus1_expanded,
              pf_ext_idx[..., -range_pf_plus1 - 1, -1],
          ],
          dim=-1,
      )
      # 2 x batch_shape x n_cells x 2
      self.hypercells = torch.stack([lower, upper], dim=0)
  
imports: from __future__ import annotations
  from typing import Optional
  import torch
  from botorch.utils.multi_objective.box_decompositions.box_decomposition import BoxDecomposition, FastPartitioning
  from botorch.utils.multi_objective.box_decompositions.utils import _expand_ref_point, compute_non_dominated_hypercell_bounds_2d, get_partition_bounds, update_local_upper_bounds_incremental
  from torch import Tensor
label_desc: Use image node_id 3 for calling the NondominatedPartitioning obj's underlying member method code with example usage: obj._partition_space_2d() without return types