约束多目标优化#
违反量约定#
CMOP.evaluate(population) 返回 (fitness, cv):目标形状为 (N, M),
违反量形状为 (N, n_iq + n_eq),每列对应一个约束。
不等式按
g(x) <= 0定义,违反量为max(g(x), 0)。等式违反量为
max(abs(h(x)) - constr_eq_eps, 0),默认容差为1e-4。所有违反量均为零时解可行;当前选择逻辑按总违反量进行比较。
原始负数不等式值表示满足约束,不应直接作为违反量使用。
当前 CMOP.evaluate 始终返回二元组;构造参数 return_cv 不改变这个返回约定。
NSGA-II 求解 DOC1#
"""Keep constraint violations when solving DOC1 with NSGA-II."""
import torch
from evomo.algorithms import NSGA2
from evomo.operators.selection import non_dominate_rank
from evomo.problems.constrained import DOC1
from evomo.workflows import UnifiedWorkflow
def main():
device = "cuda" if torch.cuda.is_available() else "cpu"
torch.set_default_device(device)
torch.manual_seed(42)
problem = DOC1()
algorithm = NSGA2(pop_size=100, n_objs=problem.m, lb=problem.lb, ub=problem.ub, device=device)
workflow = UnifiedWorkflow(algorithm, problem, device=device)
workflow.init_step()
for _ in range(50):
workflow.step()
fitness = workflow.algorithm.fit
cv = workflow.algorithm.cv
feasible = (cv.sum(dim=1) <= 0) & torch.isfinite(fitness).all(dim=1)
feasible_fitness = fitness[feasible]
print(f"Fitness: {tuple(fitness.shape)}, CV: {tuple(cv.shape)}")
print(f"Finite feasible solutions: {feasible.sum().item()}/{feasible.numel()}")
if feasible_fitness.shape[0] > 0:
print(feasible_fitness[non_dominate_rank(feasible_fitness) == 0])
else:
print("No feasible solution in the current population; do not report a feasible-front metric.")
if __name__ == "__main__":
main()
python docs/examples/constrained.py
NSGA-II 在初始化和每代选择中保留违反量。可行解优先于不可行解;不可行解之间优先比较 总违反量;可行解之间或总违反量相等时,再比较目标支配关系。
提取可行前沿#
先按违反量和有限目标值筛选,再对剩余目标计算零等级前沿。 不要用仅按目标提取的前沿替代可行前沿,也不要只查看某个目标的最小值来判断是否成功。
当没有可行解时,应报告可行率或违反量统计;GD、IGD 需要非空的输入解集, 此时不能报告可行前沿的距离指标。
接入其他算法#
必须同时满足三个条件:
问题返回目标值及违反量。
工作流保留二者,例如使用
UnifiedWorkflow。算法在比较、选择及状态更新中使用违反量。
仅满足前两个条件还不足以构成约束优化。部分算法直接将 evaluate 当成目标张量,
另一些实现虽然拆开返回值,但仍需逐一检查约束是否参与选择。
本文约束示例使用 evomo.algorithms.NSGA2;不能据此推断全部算法支持约束。