def gen(
        self,
        n: int,
        bounds: List[Tuple[float, float]],
        linear_constraints: Optional[Tuple[np.ndarray, np.ndarray]] = None,
        fixed_features: Optional[Dict[int, float]] = None,
        model_gen_options: Optional[TConfig] = None,
        rounding_func: Optional[Callable[[np.ndarray], np.ndarray]] = None,
    ) -> Tuple[np.ndarray, np.ndarray]:
        """Generate new candidates.

        Args:
            n: Number of candidates to generate.
            bounds: A list of (lower, upper) tuples for each column of X.
                Defined on [0, 1]^d.
            linear_constraints: A tuple of (A, b). For k linear constraints on
                d-dimensional x, A is (k x d) and b is (k x 1) such that
                A x <= b.
            fixed_features: A map {feature_index: value} for features that
                should be fixed to a particular value during generation.
            model_gen_options: A config dictionary that is passed along to the
                model.
            rounding_func: A function that rounds an optimization result
                appropriately (e.g., according to `round-trip` transformations).

        Returns:
            2-element tuple containing

            - (n x d) array of generated points.
            - Uniform weights, an n-array of ones for each point.

        """
        tf_indices = tunable_feature_indices(bounds=bounds,
                                             fixed_features=fixed_features)
        if fixed_features:
            fixed_feature_indices = np.array(list(fixed_features.keys()))
        else:
            fixed_feature_indices = np.array([])

        validate_bounds(bounds=bounds,
                        fixed_feature_indices=fixed_feature_indices)
        attempted_draws = 0
        max_draws = None
        if model_gen_options:
            max_draws = model_gen_options.get("max_rs_draws")
            if max_draws is not None:
                max_draws: int = int(max_draws)
        # Always rejection sample, but this only rejects if there are
        # constraints or actual duplicates and deduplicate is specified.
        points, attempted_draws = rejection_sample(
            gen_unconstrained=self._gen_unconstrained,
            n=n,
            d=len(bounds),
            tunable_feature_indices=tf_indices,
            linear_constraints=linear_constraints,
            deduplicate=self.deduplicate,
            max_draws=max_draws,
            fixed_features=fixed_features,
            rounding_func=rounding_func,
            existing_points=self.generated_points,
        )
        # pyre-fixme[16]: `RandomModel` has no attribute `attempted_draws`.
        self.attempted_draws = attempted_draws
        if self.deduplicate:
            if self.generated_points is None:
                self.generated_points = points
            else:
                # pyre-fixme[6]: Expected `Collection[ndarray]` for 1st param but
                #  got `List[Optional[ndarray]]`.
                self.generated_points = np.vstack(
                    [self.generated_points, points])
        return (points, np.ones(len(points)))
Exemple #2
0
    def gen(
        self,
        n: int,
        bounds: List[Tuple[float, float]],
        linear_constraints: Optional[Tuple[np.ndarray, np.ndarray]] = None,
        fixed_features: Optional[Dict[int, float]] = None,
        model_gen_options: Optional[TConfig] = None,
        rounding_func: Optional[Callable[[np.ndarray], np.ndarray]] = None,
    ) -> Tuple[np.ndarray, np.ndarray]:
        """Generate new candidates.

        Args:
            n: Number of candidates to generate.
            bounds: A list of (lower, upper) tuples for each column of X.
                Defined on [0, 1]^d.
            linear_constraints: A tuple of (A, b). For k linear constraints on
                d-dimensional x, A is (k x d) and b is (k x 1) such that
                A x <= b.
            fixed_features: A map {feature_index: value} for features that
                should be fixed to a particular value during generation.
            model_gen_options: A config dictionary that is passed along to the
                model.
            rounding_func: A function that rounds an optimization result
                appropriately (e.g., according to `round-trip` transformations).

        Returns:
            2-element tuple containing

            - (n x d) array of generated points.
            - Uniform weights, an n-array of ones for each point.

        """
        tf_indices = tunable_feature_indices(
            bounds=bounds, fixed_features=fixed_features
        )
        if fixed_features:
            fixed_feature_indices = np.array(list(fixed_features.keys()))
        else:
            fixed_feature_indices = np.array([])

        validate_bounds(bounds=bounds, fixed_feature_indices=fixed_feature_indices)
        attempted_draws = 0
        max_draws = None
        if model_gen_options:
            max_draws = model_gen_options.get("max_rs_draws")
            if max_draws is not None:
                # pyre-fixme[6]: Expected `Union[bytes, str, typing.SupportsInt]`
                #  for 1st param but got
                #  `Union[botorch.acquisition.acquisition.AcquisitionFunction, float,
                #  int, str]`.
                # pyre-fixme[35]: Target cannot be annotated.
                max_draws: int = int(max_draws)
        try:
            # Always rejection sample, but this only rejects if there are
            # constraints or actual duplicates and deduplicate is specified.
            # If rejection sampling fails, fall back to polytope sampling
            points, attempted_draws = rejection_sample(
                gen_unconstrained=self._gen_unconstrained,
                n=n,
                d=len(bounds),
                tunable_feature_indices=tf_indices,
                linear_constraints=linear_constraints,
                deduplicate=self.deduplicate,
                max_draws=max_draws,
                fixed_features=fixed_features,
                rounding_func=rounding_func,
                existing_points=self.generated_points,
            )
        except SearchSpaceExhausted as e:
            if self.fallback_to_sample_polytope:
                logger.info(
                    "Rejection sampling exceeded specified maximum draws."
                    "Falling back on polytope sampler"
                )
                # If rejection sampling fails, try polytope sampler.
                polytope_sampler = HitAndRunPolytopeSampler(
                    inequality_constraints=self._convert_inequality_constraints(
                        linear_constraints
                    ),
                    equality_constraints=self._convert_equality_constraints(
                        len(bounds), fixed_features
                    ),
                    interior_point=self._get_last_point(),
                    bounds=self._convert_bounds(bounds),
                )
                points = polytope_sampler.draw(n).numpy()
            else:
                raise e

        # pyre-fixme[16]: `RandomModel` has no attribute `attempted_draws`.
        self.attempted_draws = attempted_draws
        if self.deduplicate:
            if self.generated_points is None:
                self.generated_points = points
            else:
                self.generated_points = np.vstack([self.generated_points, points])
        return points, np.ones(len(points))