class_docstr: The multivariate Value-at-Risk as introduced in [Prekopa2012MVaR]_.
  
  MVaR is defined as the non-dominated set of points in the extended domain
  of the random variable that have multivariate CDF greater than or equal to
  `alpha`. Note that MVaR is set valued and the size of the set depends on the
  particular realizations of the random variable. [Cousin2013MVaR]_ instead
  propose to use the expectation of the set-valued MVaR as the multivariate
  VaR. We support this alternative with an `expectation` flag.
  
  This supports approximate gradients as discussed in [Daulton2022MARS]_.
docstr: Find MVaR set based on the definition in [Prekopa2012MVaR]_.
  
  This first calculates the CDF for each point on the extended domain of the
  random variable (the grid defined by the given samples), then takes the
  values with CDF equal to (rounded if necessary) `alpha`. The non-dominated
  subset of these form the MVaR set.
  
  This implementation processes each batch of `Y` in a for loop using a counting
  based implementation. It requires less memory than the vectorized implementation
  and should be used with large (>128) `n_w` values.
  
  Args:
      Y: A `batch x n_w x m`-dim tensor of outcomes.
  
  Returns:
      A `batch` length list of `k x m`-dim tensor of MVaR values, where `k`
      depends on the corresponding batch inputs. Note that MVaR values in general
      are not in-sample points.
docstr_tok: ["Find","MVaR","set","based","on","the","definition","in","[","Prekopa2012MVaR","]","_",".","This","first","calculates","the","CDF","for","each","point","on","the","extended","domain","of","the","random","variable","(","the","grid","defined","by","the","given","samples",")",",","then","takes","the","values","with","CDF","equal","to","(","rounded","if","necessary",")","`","alpha","`",".","The","non-dominated","subset","of","these","form","the","MVaR","set",".","This","implementation","processes","each","batch","of","`","Y","`","in","a","for","loop","using","a","counting","based","implementation",".","It","requires","less","memory","than","the","vectorized","implementation","and","should","be","used","with","large","(",">","128",")","`","n_w","`","values",".","Args",":","Y",":","A","`","batch","x","n_w","x","m","`","-dim","tensor","of","outcomes",".","Returns",":","A","`","batch","`","length","list","of","`","k","x","m","`","-dim","tensor","of","MVaR","values",",","where","`","k","`","depends","on","the","corresponding","batch","inputs",".","Note","that","MVaR","values","in","general","are","not","in-sample","points","."]
code: def get_mvar_set_via_counting(self, Y: Tensor) -> List[Tensor]:
      r"""Find MVaR set based on the definition in [Prekopa2012MVaR]_.
  
      This first calculates the CDF for each point on the extended domain of the
      random variable (the grid defined by the given samples), then takes the
      values with CDF equal to (rounded if necessary) `alpha`. The non-dominated
      subset of these form the MVaR set.
  
      This implementation processes each batch of `Y` in a for loop using a counting
      based implementation. It requires less memory than the vectorized implementation
      and should be used with large (>128) `n_w` values.
  
      Args:
          Y: A `batch x n_w x m`-dim tensor of outcomes.
  
      Returns:
          A `batch` length list of `k x m`-dim tensor of MVaR values, where `k`
          depends on the corresponding batch inputs. Note that MVaR values in general
          are not in-sample points.
      """
      if Y.dim() == 3:
          return sum(
              (self.get_mvar_set_via_counting(y_) for y_ in Y), []
          )
      m = Y.shape[-1]
      # Generate sets of all unique values in each output dimension.
      # Note that points in MVaR are bounded from above by the
      # independent VaR of each objective. Hence, we only need to
      # consider the unique outcomes that are less than or equal to
      # the VaR of the independent objectives
      var_alpha_idx = ceil(self.alpha * self.n_w) - 1
      Y_sorted = Y.topk(
          Y.shape[0] - var_alpha_idx, dim=0, largest=False
      ).values
      unique_outcomes_list = []
      for i in range(m):
          sorted_i = (
              Y_sorted[:, i]
              .cpu()
              .clone(memory_format=torch.contiguous_format)
          )
          unique_outcomes_list.append(sorted_i.unique().tolist()[::-1])
      # Convert this into a list of m dictionaries mapping values to indices.
      unique_outcomes = [
          dict(zip(outcomes, range(len(outcomes))))
          for outcomes in unique_outcomes_list
      ]
      # Initialize a tensor counting the number of points in Y that a given grid point
      # is dominated by. This will essentially be a non-normalized CDF.
      counter_tensor = torch.zeros(
          [len(outcomes) for outcomes in unique_outcomes],
          dtype=torch.long,
          device=Y.device,
      )
      # populate the tensor, counting the dominated points.
      # we only need to consider points in Y where at least one
      # objective is less than the max objective value in
      # unique_outcomes_list
      max_vals = torch.tensor(
          [o[0] for o in unique_outcomes_list],
          dtype=Y.dtype,
          device=Y.device,
      )
      mask = (Y < max_vals).any(dim=-1)
      counter_tensor += self.n_w - mask.sum()
      Y_pruned = Y[mask]
      for y_ in Y_pruned:
          starting_idcs = [
              unique_outcomes[i].get(y_[i].item(), 0) for i in range(m)
          ]
          slices = [slice(s_idx, None) for s_idx in starting_idcs]
          counter_tensor[slices] += 1
  
      # Get the count alpha-level points should have.
      alpha_count = ceil(self.alpha * self.n_w)
      # Get the alpha level indices.
      alpha_level_indices = (counter_tensor == alpha_count).nonzero(
          as_tuple=False
      )
      # If there are no exact alpha level points, get the smallest alpha' > alpha
      # and find the corresponding alpha level indices.
      if alpha_level_indices.numel() == 0:
          min_greater_than_alpha = counter_tensor[
              counter_tensor > alpha_count
          ].min()
          alpha_level_indices = (
              counter_tensor == min_greater_than_alpha
          ).nonzero(as_tuple=False)
      unique_outcomes = [
          torch.as_tensor(
              list(outcomes.keys()), device=Y.device, dtype=Y.dtype
          )
          for outcomes in unique_outcomes
      ]
      alpha_level_points = torch.stack(
          [
              unique_outcomes[i][alpha_level_indices[:, i]]
              for i in range(len(unique_outcomes))
          ],
          dim=-1,
      )
      # MVaR is simply the non-dominated subset of alpha level points.
      if self.filter_dominated:
          mask = is_non_dominated(alpha_level_points)
          mvar = alpha_level_points[mask]
      else:
          mvar = alpha_level_points
      return [mvar]
  
imports: import warnings
  from abc import ABC, abstractmethod
  from math import ceil
  from typing import Callable, List, Optional, Union
  import torch
  from botorch.acquisition.multi_objective.objective import IdentityMCMultiOutputObjective, MCMultiOutputObjective
  from botorch.acquisition.risk_measures import CVaR, RiskMeasureMCObjective, VaR
  from botorch.exceptions.errors import UnsupportedError
  from botorch.exceptions.warnings import BotorchWarning
  from botorch.models.model import Model
  from botorch.utils.multi_objective.pareto import is_non_dominated
  from botorch.utils.transforms import normalize
  from torch import Tensor
label_desc: Use image node_id 2 for calling the MVaR obj's underlying member method code with example usage: obj.get_mvar_set_via_counting(Y) and returns: list, sum