code: def estimate_feasible_volume(
      bounds: Tensor,
      model: model.Model,
      outcome_constraints: List[Callable[[Tensor], Tensor]],
      inequality_constraints: Optional[
          List[Tuple[Tensor, Tensor, float]]
      ] = None,
      nsample_feature: int = 1000,
      nsample_outcome: int = 1000,
      threshold: float = 0.1,
      verbose: bool = False,
      seed: Optional[int] = None,
      device: Optional[torch.device] = None,
      dtype: Optional[torch.dtype] = None,
  ) -> Tuple[float, float]:
      r"""
      Monte Carlo estimate of the feasible volume with respect
      to feature constraints and outcome constraints.
  
      Args:
          bounds: A `2 x d` tensor of lower and upper bounds
              for each column of `X`.
          model: The model used for sampling the outcomes.
          outcome_constraints: A list of callables, each mapping a Tensor of dimension
              `sample_shape x batch-shape x q x m` to a Tensor of dimension
              `sample_shape x batch-shape x q`, where negative values imply
              feasibility.
          inequality constraints: A list of tuples (indices, coefficients, rhs),
              with each tuple encoding an inequality constraint of the form
              `\sum_i (X[indices[i]] * coefficients[i]) >= rhs`.
          nsample_feature: The number of feature samples satisfying the bounds.
          nsample_outcome: The number of outcome samples from the model posterior.
          threshold: A lower limit for the probability of outcome feasibility
          seed: The seed for both feature and outcome samplers. If omitted,
              use a random seed.
          verbose: An indicator for whether to log the results.
  
      Returns:
          2-element tuple containing:
  
          - Estimated proportion of volume in feature space that is
              feasible wrt the bounds and the inequality constraints (linear).
          - Estimated proportion of feasible features for which
              posterior samples (outcome) satisfies the outcome constraints
              with probability above the given threshold.
      """
  
      seed = (
          seed
          if seed is not None
          else torch.randint(0, 1000000, (1,)).item()
      )
  
      with manual_seed(seed=seed):
          box_samples = bounds[0] + (
              bounds[1] - bounds[0]
          ) * torch.rand(
              (nsample_feature, bounds.size(1)),
              dtype=dtype,
              device=device,
          )
  
      features, p_feature = get_feasible_samples(
          samples=box_samples,
          inequality_constraints=inequality_constraints,
      )  # each new feature sample is a row
  
      p_outcome = get_outcome_feasibility_probability(
          model=model,
          X=features.unsqueeze(-2),
          outcome_constraints=outcome_constraints,
          threshold=threshold,
          nsample_outcome=nsample_outcome,
          seed=seed,
      )
  
      if verbose:  # pragma: no cover
          logger.info(
              "Proportion of volume that satisfies linear constraints: "
              + f"{p_feature:.4e}"
          )
          if p_feature <= 0.01:
              logger.warning(
                  "The proportion of satisfying volume is very low and may lead to "
                  + "very long run times. Consider making your constraints less "
                  + "restrictive."
              )
          logger.info(
              "Proportion of linear-feasible volume that also satisfies each "
              + f"outcome constraint with probability > 0.1: {p_outcome:.4e}"
          )
          if p_outcome <= 0.001:
              logger.warning(
                  "The proportion of volume that also satisfies the outcome constraint "
                  + "is very low. Consider making your parameter and outcome constraints "
                  + "less restrictive."
              )
      return p_feature, p_outcome
  
file_path: botorch/botorch/utils/feasible_volume.py
imports: from __future__ import annotations
  from typing import Callable, List, Optional, Tuple
  import botorch.models.model
  import torch
  from botorch.logging import _get_logger
  from botorch.utils.sampling import manual_seed
  from torch import Tensor
label_desc: Use image node_id 3 for calling a global function with example usage: estimate_feasible_volume(bounds, model, outcome_constraints, inequality_constraints, nsample_feature, nsample_outcome, threshold, verbose, seed, device, dtype) and returns: p_feature,  p_outcome