约束多目标优化#

违反量约定#

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 需要非空的输入解集, 此时不能报告可行前沿的距离指标。

接入其他算法#

必须同时满足三个条件:

  1. 问题返回目标值及违反量。

  2. 工作流保留二者,例如使用 UnifiedWorkflow。

  3. 算法在比较、选择及状态更新中使用违反量。

仅满足前两个条件还不足以构成约束优化。部分算法直接将 evaluate 当成目标张量, 另一些实现虽然拆开返回值,但仍需逐一检查约束是否参与选择。 本文约束示例使用 evomo.algorithms.NSGA2;不能据此推断全部算法支持约束。