def select_new_region_of_interest( factorial_importance_analysis: pd.DataFrame, space: Space, threshold: float, n_levels: int, ) -> Tuple[Space, dict]: """Select new region of interest and frozen parameter values based on factorial analysis. Parameters ---------- factorial_importance_analysis: dict Marginal variance ratios on best levels of the factorial performance analysis. Should have format {'dim-name': <marginal variance ratio>, ...} space: ``orion.algo.space.Space`` Space object representing the current region of interest. threshold: float Threshold of marginal variance ratio below which we should freeze a dimension. """ frozen_param_values = {} new_space = Space() for key, dim in space.items(): dim_importance_analysis = factorial_importance_analysis[ factorial_importance_analysis["param"] == key] if float(dim_importance_analysis["importance"]) < threshold: frozen_param_values[key] = sum(dim.interval()) / 2.0 else: level = int(dim_importance_analysis["best_level"]) low, high = dim.interval() intervals = (high - low) / n_levels new_low = low + intervals * (level - 1) new_space.register(Real(dim.name, "uniform", new_low, intervals)) return new_space, frozen_param_values
def test_order(self): """Test that the same space built twice will have the same ordering.""" space1 = Space() space1.register(Integer('yolo1', 'uniform', -3, 6, shape=(2, ))) space1.register(Integer('yolo2', 'uniform', -3, 6, shape=(2, ))) space1.register(Real('yolo3', 'norm', 0.9)) space1.register(Categorical('yolo4', ('asdfa', 2))) space2 = Space() space2.register(Integer('yolo1', 'uniform', -3, 6, shape=(2, ))) space2.register(Real('yolo3', 'norm', 0.9)) space2.register(Categorical('yolo4', ('asdfa', 2))) space2.register(Integer('yolo2', 'uniform', -3, 6, shape=(2, ))) assert list(space1) == list(space1.keys()) assert list(space2) == list(space2.keys()) assert list(space1.values()) == list(space2.values()) assert list(space1.items()) == list(space2.items()) assert list(space1.keys()) == list(space2.keys()) assert list(space1.values()) == list(space2.values()) assert list(space1.items()) == list(space2.items())
def test_order(self): """Test that the same space built twice will have the same ordering.""" space1 = Space() space1.register(Integer("yolo1", "uniform", -3, 6, shape=(2,))) space1.register(Integer("yolo2", "uniform", -3, 6, shape=(2,))) space1.register(Real("yolo3", "norm", 0.9)) space1.register(Categorical("yolo4", ("asdfa", 2))) space2 = Space() space2.register(Integer("yolo1", "uniform", -3, 6, shape=(2,))) space2.register(Real("yolo3", "norm", 0.9)) space2.register(Categorical("yolo4", ("asdfa", 2))) space2.register(Integer("yolo2", "uniform", -3, 6, shape=(2,))) assert list(space1) == list(space1.keys()) assert list(space2) == list(space2.keys()) assert list(space1.values()) == list(space2.values()) assert list(space1.items()) == list(space2.items()) assert list(space1.keys()) == list(space2.keys()) assert list(space1.values()) == list(space2.values()) assert list(space1.items()) == list(space2.items())
def orion_space_to_hebo_space(space: Space) -> DesignSpace: """Get the HEBO-equivalent space for the `Space` `space`. Parameters ---------- :param space: `Space` instance. Returns ------- a `DesignSpace` from the `hebo` package. Raises ------ NotImplementedError If there is an unsupported dimension or prior type in `space`. """ specs = [] ds = DesignSpace() name: str dimension: Dimension for name, dimension in space.items(): spec: dict[str, Any] = {"name": name} prior_name = dimension.prior_name bounds = dimension.interval() if dimension.shape: raise NotImplementedError( f"HEBO algorithm doesn't support dimension {dimension} since it has a shape." ) if dimension.type == "fidelity": # Ignore that dimension: Don't include it in the space for Hebo to optimize. continue # BUG: https://github.com/Epistimio/orion/issues/800 bounds = tuple(b.item() if isinstance(b, np.ndarray) else b for b in bounds) if prior_name == "choices": categories = [str(b) for b in bounds] spec.update(type="cat", categories=categories) elif prior_name == "uniform": spec.update(type="num", lb=bounds[0], ub=bounds[1]) elif prior_name == "reciprocal": spec.update(type="pow", lb=bounds[0], ub=bounds[1]) elif prior_name == "int_uniform": spec.update(type="int", lb=bounds[0], ub=bounds[1]) elif prior_name == "int_reciprocal": spec.update(type="pow_int", lb=bounds[0], ub=bounds[1]) else: raise NotImplementedError(prior_name, dimension) specs.append(spec) ds.parse(specs) return ds
def to_ng_space(orion_space: Space) -> Instrumentation: """Convert an orion space to a nevergrad space.""" if IMPORT_ERROR: raise IMPORT_ERROR converted_dimensions: dict[str, Parameter] = {} for name, dim in orion_space.items(): try: converted_dimensions[name] = registry[dim.type, dim.prior_name](dim) except KeyError as exc: raise RuntimeError( f"Dimension with type and prior: {exc.args[0]} cannot be converted to nevergrad." ) from exc return ng.p.Instrumentation(**converted_dimensions)
def generate_olh_samples(space: Space, n_levels: int, strength: int, index: int, rng: RandomState) -> Tuple[np.array, int]: """ Generates samples from an orthogonal Latin hypercube (OLH) Parameters ---------- space: `orion.core.worker.Space` Parameter space n_levels: int Number of levels strength: {1,2} Strength parameter for an orthogonal Latin hypercube rng: None or ``numpy.random.RandomState`` Random number generator Returns ------- (numpy.array, int) A tuple of the samples array from the normalized parameter space, and the n_levels parameter, which may be changed to suit the OLH requirements. """ dimensions = len(space.items()) if not _is_prime(n_levels): n_levels = _find_n_levels(dimensions) logger.warning( """WARNING: n_levels specified is not a prime number. Changing n_levels to %d""", n_levels, ) elif n_levels < dimensions - 1: n_levels = _find_n_levels(dimensions) logger.warning( """WARNING: n_levels specified is less than the number of hyperparameters minus 1. Changing n_levels to %d""", n_levels, ) n_rows = n_levels**strength logger.debug("MOFA: setting number of trials in this iteration to %d", n_rows * index) all_samples = [] for _ in range(index): lhc = LatinHypercube(d=dimensions, strength=strength, seed=rng) samples = lhc.random(n_rows).tolist() all_samples.extend(samples) return np.array(all_samples), n_levels
def build_grid(space: Space, n_values: dict[str, int], max_trials: int = 10000): """Build a grid of trials Parameters ---------- n_values: int or dict Dictionary specifying number of trials for each dimension independently (name, n_values). For categorical dimensions, n_values will not be used, and all categories will be used to build the grid. max_trials: int Maximum number of trials for the grid. If n_values lead to more trials than max_trials, the n_values will be adjusted down. Will raise ValueError if it is impossible to build a grid smaller than max_trials (for instance if choices are too large). """ adjust = 0 n_trials = float("inf") coordinates: list[list] = [] while n_trials > max_trials: coordinates = [] capped_values = [] for name, dim in space.items(): capped_value = max(n_values[name] - adjust, 1) capped_values.append(capped_value) coordinates.append(list(grid(dim, capped_value))) if all(value <= 1 for value in capped_values): raise ValueError( f"Cannot build a grid smaller than {max_trials}. " "Try reducing the number of choices in categorical dimensions." ) n_trials = numpy.prod( [len(dim_values) for dim_values in coordinates]) # TODO: Use binary search instead of incrementing by one. adjust += 1 if adjust > 1: log.warning( f"`n_values` reduced by {adjust-1} to limit number of trials below {max_trials}." ) return list(itertools.product(*coordinates))