def hypervolume( modelbridge: modelbridge_module.array.ArrayModelBridge, observation_features: List[ObservationFeatures], objective_thresholds: Optional[TRefPoint] = None, observation_data: Optional[List[ObservationData]] = None, optimization_config: Optional[MultiObjectiveOptimizationConfig] = None, use_model_predictions: bool = True, ) -> float: """Helper function that computes hypervolume of a given list of outcomes.""" # Get Pareto front observations, f, obj_w, obj_t = get_pareto_frontier_and_transformed_configs( modelbridge=modelbridge, objective_thresholds=objective_thresholds, observation_features=observation_features, observation_data=observation_data, optimization_config=optimization_config, use_model_predictions=use_model_predictions, ) if obj_t is None: raise ValueError( "Cannot compute hypervolume without having objective thresholds specified." ) # Apply appropriate weights and thresholds obj, obj_t = get_weighted_mc_objective_and_objective_thresholds( objective_weights=obj_w, objective_thresholds=obj_t) f_t = obj(f) hv = Hypervolume(ref_point=obj_t) return hv.compute(f_t)
def hypervolume( modelbridge: modelbridge_module.array.ArrayModelBridge, observation_features: List[ObservationFeatures], objective_thresholds: Optional[TRefPoint] = None, observation_data: Optional[List[ObservationData]] = None, optimization_config: Optional[MultiObjectiveOptimizationConfig] = None, use_model_predictions: bool = True, ) -> float: """Helper function that computes hypervolume of a given list of outcomes.""" array_to_tensor = partial(_array_to_tensor, modelbridge=modelbridge) # Extract a tensor of outcome means from observation data. observations = pareto_frontier( modelbridge=modelbridge, objective_thresholds=objective_thresholds, observation_features=observation_features, observation_data=observation_data, optimization_config=optimization_config, use_model_predictions=use_model_predictions, ) observation_data = [obs.data for obs in observations] if not observation_data: # The hypervolume of an empty set is always 0. return 0 means, _ = modelbridge.transform_observation_data(observation_data) # Extract objective_weights and objective_thresholds if optimization_config is None: optimization_config = ( # pyre-ignore[16]: Optional has undefined attr `_optimization_config` modelbridge._optimization_config.clone() if modelbridge._optimization_config is not None else None ) else: # pyre-ignore[9]: declared as type `Optional[...]` but used as type `...` optimization_config = optimization_config.clone() if objective_thresholds is not None: optimization_config = optimization_config.clone_with_args( objective_thresholds=objective_thresholds ) obj_w = extract_objective_weights( objective=optimization_config.objective, outcomes=modelbridge.outcomes ) obj_w = array_to_tensor(obj_w) objective_thresholds_arr = extract_objective_thresholds( objective_thresholds=optimization_config.objective_thresholds, outcomes=modelbridge.outcomes, ) obj_t = array_to_tensor(objective_thresholds_arr) obj, obj_t = get_weighted_mc_objective_and_objective_thresholds( objective_weights=obj_w, objective_thresholds=obj_t ) means = obj(means) hv = Hypervolume(ref_point=obj_t) return hv.compute(means)
def _hypervolume( self, objective_thresholds: Optional[TRefPoint] = None, observation_features: Optional[List[ObservationFeatures]] = None, observation_data: Optional[List[ObservationData]] = None, optimization_config: Optional[OptimizationConfig] = None, ) -> float: # Extract a tensor of outcome means from observation data. observation_data = self._pareto_frontier( objective_thresholds=objective_thresholds, observation_features=observation_features, observation_data=observation_data, # pyre-fixme[6]: Expected `Optional[MultiObjectiveOptimizationConfig]` # for 4th param but got `Optional[OptimizationConfig]`. optimization_config=optimization_config, ) if not observation_data: # The hypervolume of an empty set is always 0. return 0 means, _ = self._transform_observation_data(observation_data) # Extract objective_weights and objective_thresholds if optimization_config is None: optimization_config = ( # pyre-fixme[16]: `Optional` has no attribute `clone`. self._optimization_config.clone() if self._optimization_config is not None else None ) else: optimization_config = optimization_config.clone() if objective_thresholds is not None: optimization_config = optimization_config.clone_with_args( objective_thresholds=objective_thresholds ) obj_w = extract_objective_weights( objective=optimization_config.objective, outcomes=self.outcomes ) obj_w = self._array_to_tensor(obj_w) # TODO: can I get rid of these tensors? objective_thresholds_arr = extract_objective_thresholds( objective_thresholds=optimization_config.objective_thresholds, outcomes=self.outcomes, ) obj_t = self._array_to_tensor( objective_thresholds_arr ) # TODO: can I get rid of these tensors? obj, obj_t = get_weighted_mc_objective_and_objective_thresholds( objective_weights=obj_w, objective_thresholds=obj_t ) means = obj(means) hv = Hypervolume(ref_point=obj_t) return hv.compute(means)
def hypervolume( modelbridge: modelbridge_module.array.ArrayModelBridge, observation_features: List[ObservationFeatures], objective_thresholds: Optional[TRefPoint] = None, observation_data: Optional[List[ObservationData]] = None, optimization_config: Optional[MultiObjectiveOptimizationConfig] = None, selected_metrics: Optional[List[str]] = None, use_model_predictions: bool = True, ) -> float: """Helper function that computes (feasible) hypervolume. Args: modelbridge: The modelbridge. observation_features: The observation features for the in-sample arms. objective_thresholds: The objective thresholds to be used for computing the hypervolume. If None, these are extracted from the optimization config. observation_data: The observed outcomes for the in-sample arms. optimization_config: The optimization config specifying the objectives, objectives thresholds, and outcome constraints. selected_metrics: A list of objective metric names specifying which objectives to use in hypervolume computation. By default, all objectives are used. use_model_predictions: A boolean indicating whether to use model predictions for determining the in-sample Pareto frontier instead of the raw observed values. Returns: The (feasible) hypervolume. """ frontier_observations, f, obj_w, obj_t = get_pareto_frontier_and_configs( modelbridge=modelbridge, observation_features=observation_features, observation_data=observation_data, objective_thresholds=objective_thresholds, optimization_config=optimization_config, use_model_predictions=use_model_predictions, transform_outcomes_and_configs=False, ) if obj_t is None: raise ValueError( "Cannot compute hypervolume without having objective thresholds specified." ) oc = _get_multiobjective_optimization_config( modelbridge=modelbridge, optimization_config=optimization_config, objective_thresholds=objective_thresholds, ) # Set to all metrics if unspecified if selected_metrics is None: selected_metrics = oc.objective.metric_names # filter to only include objectives else: if any(m not in oc.objective.metric_names for m in selected_metrics): raise ValueError("All selected metrics must be objectives.") # Create a mask indicating selected metrics selected_metrics_mask = torch.tensor( [metric in selected_metrics for metric in modelbridge.outcomes], dtype=torch.bool, device=f.device, ) # Apply appropriate weights and thresholds obj, obj_t = get_weighted_mc_objective_and_objective_thresholds( objective_weights=obj_w, objective_thresholds=not_none(obj_t)) f_t = obj(f) # pyre-ignore [16] obj_mask = obj_w.nonzero().view(-1) selected_metrics_mask = selected_metrics_mask[obj_mask] f_t = f_t[:, selected_metrics_mask] obj_t = obj_t[selected_metrics_mask] bd = DominatedPartitioning(ref_point=obj_t, Y=f_t) return bd.compute_hypervolume().item()