class_docstr: Customizable multi-objective model.
  
  By default, this uses an Expected Hypervolume Improvment function to find the
  pareto frontier of a function with multiple outcomes. This behavior
  can be modified by providing custom implementations of the following
  components:
  
  - a `model_constructor` that instantiates and fits a model on data
  - a `model_predictor` that predicts outcomes using the fitted model
  - a `acqf_constructor` that creates an acquisition function from a fitted model
  - a `acqf_optimizer` that optimizes the acquisition function
  
  Args:
      model_constructor: A callable that instantiates and fits a model on data,
          with signature as described below.
      model_predictor: A callable that predicts using the fitted model, with
          signature as described below.
      acqf_constructor: A callable that creates an acquisition function from a
          fitted model, with signature as described below.
      acqf_optimizer: A callable that optimizes an acquisition
          function, with signature as described below.
  
  
  
  Call signatures:
  
  ::
  
      model_constructor(
          Xs,
          Ys,
          Yvars,
          task_features,
          fidelity_features,
          metric_names,
          state_dict,
          **kwargs,
      ) -> model
  
  Here `Xs`, `Ys`, `Yvars` are lists of tensors (one element per outcome),
  `task_features` identifies columns of Xs that should be modeled as a task,
  `fidelity_features` is a list of ints that specify the positions of fidelity
  parameters in 'Xs', `metric_names` provides the names of each `Y` in `Ys`,
  `state_dict` is a pytorch module state dict, and `model` is a BoTorch `Model`.
  Optional kwargs are being passed through from the `BotorchModel` constructor.
  This callable is assumed to return a fitted BoTorch model that has the same
  dtype and lives on the same device as the input tensors.
  
  ::
  
      model_predictor(model, X) -> [mean, cov]
  
  Here `model` is a fitted botorch model, `X` is a tensor of candidate points,
  and `mean` and `cov` are the posterior mean and covariance, respectively.
  
  ::
  
      acqf_constructor(
          model,
          objective_weights,
          outcome_constraints,
          X_observed,
          X_pending,
          **kwargs,
      ) -> acq_function
  
  
  Here `model` is a botorch `Model`, `objective_weights` is a tensor of weights
  for the model outputs, `outcome_constraints` is a tuple of tensors describing
  the (linear) outcome constraints, `X_observed` are previously observed points,
  and `X_pending` are points whose evaluation is pending. `acq_function` is a
  BoTorch acquisition function crafted from these inputs. For additional
  details on the arguments, see `get_qLogNEHVI`.
  
  ::
  
      acqf_optimizer(
          acq_function,
          bounds,
          n,
          inequality_constraints,
          fixed_features,
          rounding_func,
          **kwargs,
      ) -> candidates
  
  Here `acq_function` is a BoTorch `AcquisitionFunction`, `bounds` is a tensor
  containing bounds on the parameters, `n` is the number of candidates to be
  generated, `inequality_constraints` are inequality constraints on parameter
  values, `fixed_features` specifies features that should be fixed during
  generation, and `rounding_func` is a callback that rounds an optimization
  result appropriately. `candidates` is a tensor of generated candidates.
  For additional details on the arguments, see `scipy_optimizer`.
  
  ::
  
      frontier_evaluator(
          model,
          objective_weights,
          objective_thresholds,
          X,
          Y,
          Yvar,
          outcome_constraints,
      )
  
  Here `model` is a botorch `Model`, `objective_thresholds` is used in hypervolume
  evaluations, `objective_weights` is a tensor of weights applied to the  objectives
  (sign represents direction), `X`, `Y`, `Yvar` are tensors, `outcome_constraints` is
  a tuple of tensors describing the (linear) outcome constraints.
code: def __init__(
      self,
      model_constructor: TModelConstructor = get_and_fit_model,
      model_predictor: TModelPredictor = predict_from_model,
      # pyre-fixme[9]: acqf_constructor has type `Callable[[Model, Tensor,
      #  Optional[Tuple[Tensor, Tensor]], Optional[Tensor], Optional[Tensor], Any],
      #  AcquisitionFunction]`; used as `Callable[[Model, Tensor,
      #  Optional[Tuple[Tensor, Tensor]], Optional[Tensor], Optional[Tensor],
      #  **(Any)], AcquisitionFunction]`.
      acqf_constructor: TAcqfConstructor = get_qLogNEHVI,
      # pyre-fixme[9]: acqf_optimizer has type `Callable[[AcquisitionFunction,
      #  Tensor, int, Optional[Dict[int, float]], Optional[Callable[[Tensor],
      #  Tensor]], Any], Tensor]`; used as `Callable[[AcquisitionFunction, Tensor,
      #  int, Optional[Dict[int, float]], Optional[Callable[[Tensor], Tensor]],
      #  **(Any)], Tensor]`.
      acqf_optimizer: TOptimizer = scipy_optimizer,
      # TODO: Remove best_point_recommender for botorch_moo. Used in modelbridge._gen.
      best_point_recommender: TBestPointRecommender = recommend_best_observed_point,
      frontier_evaluator: TFrontierEvaluator = pareto_frontier_evaluator,
      refit_on_cv: bool = False,
      refit_on_update: bool = True,
      warm_start_refitting: bool = False,
      use_input_warping: bool = False,
      use_loocv_pseudo_likelihood: bool = False,
      prior: Optional[Dict[str, Any]] = None,
      **kwargs: Any,
  ) -> None:
      self.model_constructor = model_constructor
      self.model_predictor = model_predictor
      self.acqf_constructor = acqf_constructor
      self.acqf_optimizer = acqf_optimizer
      self.best_point_recommender = best_point_recommender
      self.frontier_evaluator = frontier_evaluator
      # pyre-fixme[4]: Attribute must be annotated.
      self._kwargs = kwargs
      self.refit_on_cv = refit_on_cv
      self.refit_on_update = refit_on_update
      self.warm_start_refitting = warm_start_refitting
      self.use_input_warping = use_input_warping
      self.use_loocv_pseudo_likelihood = use_loocv_pseudo_likelihood
      self.prior = prior
      self.model: Optional[Model] = None
      self.Xs = []
      self.Ys = []
      self.Yvars = []
      self.dtype = None
      self.device = None
      self.task_features: List[int] = []
      self.fidelity_features: List[int] = []
      self.metric_names: List[str] = []
  
imports: from logging import Logger
  from typing import Any, Callable, Dict, List, Optional, Tuple
  import torch
  from ax.core.search_space import SearchSpaceDigest
  from ax.exceptions.core import AxError
  from ax.models.torch.botorch import BotorchModel, get_rounding_func, TBestPointRecommender, TModelConstructor, TModelPredictor, TOptimizer
  from ax.models.torch.botorch_defaults import get_and_fit_model, recommend_best_observed_point, scipy_optimizer, TAcqfConstructor
  from ax.models.torch.botorch_moo_defaults import get_qLogNEHVI, infer_objective_thresholds, pareto_frontier_evaluator, scipy_optimizer_list
  from ax.models.torch.frontier_utils import TFrontierEvaluator
  from ax.models.torch.utils import _get_X_pending_and_observed, _to_inequality_constraints, predict_from_model, randomize_objective_weights, subset_model
  from ax.models.torch_base import TorchGenResults, TorchModel, TorchOptConfig
  from ax.utils.common.constants import Keys
  from ax.utils.common.docutils import copy_doc
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast, not_none
  from botorch.acquisition.acquisition import AcquisitionFunction
  from botorch.models.model import Model
  from torch import Tensor
label_desc: Use image node_id 1 to create a new MultiObjectiveBotorchModel object from inherited base classes: BotorchModel with example: obj = MultiObjectiveBotorchModel(model_constructor, model_predictor, acqf_constructor, acqf_optimizer, best_point_recommender, frontier_evaluator, refit_on_cv, refit_on_update, warm_start_refitting, use_input_warping, use_loocv_pseudo_likelihood, prior)