Skip to content

Instantly share code, notes, and snippets.

@toshihikoyanase
Last active January 19, 2021 08:02
Show Gist options
  • Save toshihikoyanase/c79a944377c4b1d5b8f448d9ffe339e9 to your computer and use it in GitHub Desktop.
Save toshihikoyanase/c79a944377c4b1d5b8f448d9ffe339e9 to your computer and use it in GitHub Desktop.
A prototypical implementation of MOTPE with constraints
def _dominates_both_feasible(extended: np.ndarray, const_extended: np.ndarray) -> np.ndarray:
return np.logical_and(
np.logical_and(
np.all(const_extended <= 0, axis=2),
np.all(np.swapaxes(const_extended, 0, 1) <= 0, axis=2),
), np.logical_and(
np.all(extended <= np.swapaxes(extended, 0, 1), axis=2),
np.any(extended < np.swapaxes(extended, 0, 1), axis=2),
)
)
def _dominates_by_feasibility(const_extended: np.ndarray) -> np.ndarray:
return np.logical_and(
np.all(const_extended <= 0, axis=2),
np.any(np.swapaxes(const_extended, 0, 1) > 0, axis=2),
)
def _dominates_by_total_violation(const_extended: np.ndarray) -> np.ndarray:
inv_const_extended = np.swapaxes(const_extended, 0, 1)
return np.logical_and(
np.logical_and(
np.any(const_extended > 0, axis=2),
np.any(inv_const_extended > 0, axis=2),
),
np.sum(const_extended, axis=2) < np.sum(inv_const_extended, axis=2)
)
def _constrained_dominates(extended: np.ndarray, const_extended: np.ndarray) -> np.ndarray:
return np.logical_or(
np.logical_or(
_dominates_both_feasible(extended, const_extended),
_dominates_by_feasibility(const_extended),
),
_dominates_by_total_violation(const_extended)
)
def _calculate_constrained_nondomination_rank(loss_vals: np.ndarray, constraint_vals: np.ndarray) -> np.ndarray:
const_vecs = constraint_vals.copy()
const_vecs[const_vecs < 0] = 0
const_max = max(np.max(const_vecs), 1)
vecs = loss_vals.copy()
# Normalize values
lb = vecs.min(axis=0, keepdims=True)
ub = vecs.max(axis=0, keepdims=True)
vecs = (vecs - lb) / (ub - lb)
ranks = np.zeros(len(vecs))
num_unranked = len(vecs)
rank = 0
while num_unranked > 0:
extended = np.tile(vecs, (vecs.shape[0], 1, 1))
const_extended = np.tile(const_vecs, (const_vecs.shape[0], 1, 1))
counts = np.sum(
_constrained_dominates(extended, const_extended),
axis=1,
)
vecs[counts == 0] = 1.1 # mark as ranked
const_vecs[counts == 0] = const_max # marked as ranked
ranks[counts == 0] = rank
rank += 1
num_unranked -= np.sum(counts == 0)
return ranks
def _calculate_nondomination_rank(loss_vals: np.ndarray) -> np.ndarray:
vecs = loss_vals.copy()
# Normalize values
lb = vecs.min(axis=0, keepdims=True)
ub = vecs.max(axis=0, keepdims=True)
vecs = (vecs - lb) / (ub - lb)
ranks = np.zeros(len(vecs))
num_unranked = len(vecs)
rank = 0
while num_unranked > 0:
extended = np.tile(vecs, (vecs.shape[0], 1, 1))
counts = np.sum(
np.logical_and(
np.all(extended <= np.swapaxes(extended, 0, 1), axis=2),
np.any(extended < np.swapaxes(extended, 0, 1), axis=2),
),
axis=1,
)
vecs[counts == 0] = 1.1 # mark as ranked
ranks[counts == 0] = rank
rank += 1
num_unranked -= np.sum(counts == 0)
return ranks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment