Source code for deephyper.skopt.moo._pf

import numpy as np


[docs]def is_pareto_efficient(new_obj, objvals): """Check if the new objective vector is pareto efficient with respect to previously computed values. Args: new_obj (array or list): Array or list of size (n_objectives, ) objvals (array or list): Array or list of size (n_points, n_objectives) Returns: bool: True if the vector is pareto efficient and false otherwise. """ return np.all(np.any(np.asarray(new_obj) < objvals, axis=1))
[docs]def pareto_front(y, sort=False, return_idx=False): """Extract the pareto front (actual objective values of the non-dominated set). Args: y (array or list): Array or list of size (n_points, n_objectives). sort (bool, optional): Whether to sort the pareto front (practical to plot in 2D or 3D). Defaults to False. return_idx (bool, optional): Whether to return the indices of the pareto front in the original array. Defaults to False. Returns: array: Subarray of y representing the pareto front. """ nds_idx = non_dominated_set(y, return_mask=False) pf = y[nds_idx] if sort: n_objectives = y.shape[1] pf = np.array( [tuple(row) for row in pf], dtype=[(f"objective_{i}", float) for i in range(n_objectives)], ) sorted_idx = pf.argsort(order=[f"objective_{i}" for i in range(n_objectives)]) pf = pf[sorted_idx] pf = np.array([list(row) for row in pf]) nds_idx = nds_idx[sorted_idx] if return_idx: return pf, nds_idx return pf
[docs]def non_dominated_set_ranked(y, fraction, return_mask=True): """Find the set of top-``fraction x 100%`` of non-dominated points. The number of points returned is ``min(n_points, ceil(fraction * n_points))`` where ``n_points`` is the number of points in the input array. Function assumes minimization. Args: y (array or list): Array or list of size (n_points, n_objectives) fraction (float or int): Fraction of points to return. return_mask (bool, optional): Whether to return a mask or the actual indices of the non-dominated set. Defaults to True. Raises: ValueError: Raised if ``fraction`` is not a non-negative number or if y is not an array of size (n_points, n_objectives). Returns: array: If return_mask is True, this will be an (n_points, ) boolean array. Else it will be a 1-d integer array of indices indicating which points are in the top non-dominated set. """ if not isinstance(fraction, (float, int)) or fraction < 0: raise ValueError("Expected 'fraction' to be a non-negative scalar") if np.ndim(y) == 0: return np.asarray([fraction > 0.0]) n_points = np.shape(y)[0] req_number = min(np.ceil(fraction * n_points).astype(int), n_points) if req_number <= 0: return np.zeros(n_points, dtype=bool) if req_number >= n_points: return np.ones(n_points, dtype=bool) chosen_indices = [] map_indices = np.arange(n_points) while len(chosen_indices) < req_number: nds = non_dominated_set(y, return_mask=True) chosen_indices.extend(map_indices[nds]) if len(chosen_indices) > req_number: del chosen_indices[req_number:] break y = y[~nds] map_indices = map_indices[~nds] if return_mask: chosen = np.zeros(n_points, dtype=bool) chosen[chosen_indices] = True return chosen return chosen_indices
[docs]def non_dominated_set(y, return_mask=True): """Find the set of non-dominated points. If there are multiple duplicate non-dominated points, then only one will be included. The function assumes minimization and is adapted from https://stackoverflow.com/a/40239615 by adding a sorting step to improve efficiency. Args: y (array or list): Array or list of size (n_points, n_objectives) return_mask (bool, optional): Whether to return a mask or the actual indices of the non-dominated set. Defaults to True. Returns: array: If return_mask is True, this will be an (n_points, ) boolean array. Else it will be a 1-d integer array of indices indicating which points are non-dominated. """ if np.ndim(y) == 1: y = np.asarray_chkfinite(y)[:, np.newaxis] elif np.ndim(y) == 2: y = np.asarray_chkfinite(y) else: raise ValueError("Expected y to be an array of size (n_points, n_objectives)") order = np.argsort(y.sum(axis=1)) costs = y[order] n_points = y.shape[0] is_efficient = np.arange(n_points) idx = 0 while idx < len(costs): nondominated_point_mask = np.any(costs < costs[idx], axis=1) nondominated_point_mask[idx] = True is_efficient = is_efficient[nondominated_point_mask] # Remove dominated points costs = costs[nondominated_point_mask] idx = np.sum(nondominated_point_mask[:idx], dtype=int) + 1 if return_mask: is_efficient_mask = np.zeros(n_points, dtype=bool) is_efficient_mask[is_efficient] = True is_efficient_mask[order] = is_efficient_mask.copy() return is_efficient_mask return order[is_efficient]
def non_dominated_set_dumb(y): """For debugging purposes.""" is_efficient = np.ones(y.shape[0], dtype=bool) for i, c in enumerate(y): is_efficient[i] = np.all(np.any(y[:i] > c, axis=1)) and np.all( np.any(y[i + 1 :] > c, axis=1) ) return is_efficient if __name__ == "__main__": rng = np.random.RandomState(42) npoints = 1000 nobj = 2 for it in range(100): y = rng.rand(npoints, nobj) pf = non_dominated_set(y, return_mask=True) assert np.array_equal(non_dominated_set_dumb(y), pf) assert np.array_equal( pf.nonzero()[0], np.sort(non_dominated_set(y, return_mask=False)) )