制約付き最適化#

Layer 1: Use

設計変数に制約がある問題を、saealibで解きます。

minimize()では、アルゴリズム、代理モデル、評価戦略に文字列またはインスタンスを指定できます。 このページの例では問題固有の設定だけを追加し、それ以外にはデフォルトを使います。

このページでできるようになること

このページを終えると、Problemに制約を定義し、実行可能性を確認しながらConstraintHandlerを選べます。

単純な目的関数の最適化には単目的最適化を使い、独自の制約処理を実装する場合は独自コンポーネントを参照してください。このページでは、Problemへの制約定義、実行可能性の確認、ConstraintHandlerの選択を扱います。

Problemを準備する#

目的関数に加えて、解が満たすべき不等式制約g(x) <= 0を持つ問題を想定します。

import numpy as np


def expensive_func(x):
    return np.sum(x**2)


def g1(x):
    # require the sum of the design variables to be at least 1
    return 1.0 - np.sum(x)


DIM = 5
LB = [-5.0] * DIM
UB = [5.0] * DIM

g1(x) <= 0を満たす解だけが実行可能解です。

制約を定義する#

制約はInequalityConstraint(func, threshold=0.0)で定義し、Problemのconstraints引数に渡します。

from saealib import InequalityConstraint, Problem, minimize

constraint = InequalityConstraint(g1, threshold=0.0)

problem = Problem(
    func=expensive_func,
    dim=DIM,
    n_obj=1,
    direction=np.array([-1.0]),
    lb=LB,
    ub=UB,
    constraints=[constraint],
)

result = minimize(problem, max_fe=1000, seed=0)
print(result.x, result.f)
print(constraint.violation(result.x))  # 0.0 means the constraint is satisfied

g(x) >= thresholdの形で制約を課したい場合は、funcの符号を反転させて渡します。

等式制約h(x) = 0は、InequalityConstraintを符号反転で組み合わせる代わりにEqualityConstraint(func, tolerance=1e-6)を使います。

from saealib import EqualityConstraint


def h1(x):
    # require the sum of the design variables to be exactly 1
    return np.sum(x) - 1.0


equality = EqualityConstraint(h1, tolerance=1e-6)

EqualityConstraintは、|h(x)| <= toleranceを満たす解を実行可能解として扱います。

実行可能性を確認する#

Problemのeps_cv(既定値1e-6)以下の制約違反量cvを持つ解が実行可能解です。

archive_cv = result.ctx.archive.get_array("cv")
feasible = archive_cv <= problem.eps_cv
print(f"feasible: {feasible.sum()} / {len(archive_cv)}")

ConstraintHandlerで制約処理をカスタマイズする#

複数の制約からcvを集約する方法と、その値をもとに実行可能性を判定する方法は、ConstraintHandlerが決めます。

Problemのhandler引数を省略すると、StaticToleranceHandler(eps_cv=problem.eps_cv)が使われ、cv <= eps_cvかどうかで実行可能性を判定します。

クラス

動作

StaticToleranceHandler

固定の許容誤差eps_cvで実行可能性を判定する(既定)

EpsilonConstraintHandler

許容誤差を世代とともに0へ近づけ、徐々に実行可能領域へ解を追い込む

GradientRepairHandler

制約の勾配を使って実行不可能解を修復する

EpsilonConstraintHandlerには、世代番号を受け取り許容誤差を返す関数を渡します。

from saealib import EpsilonConstraintHandler


def schedule(gen):
    return max(0.0, 1.0 - gen * 0.05)


problem = Problem(
    func=expensive_func,
    dim=DIM,
    n_obj=1,
    direction=np.array([-1.0]),
    lb=LB,
    ub=UB,
    constraints=[constraint],
    handler=EpsilonConstraintHandler(schedule),
)
result = minimize(problem, max_fe=1000, seed=0)

関連するConceptとReference#