探索アルゴリズム#
saealib は候補生成とフィードバックの消費を分離し、その契約を Algorithm にまとめます。 Algorithm は Optimizer.set_algorithm(algorithm) で差し替えられます。
現行の Algorithm は、候補を生成するProposerと、評価結果を消費するFeedbackConsumerを兼ねる契約です。 通常の利用では、利用者がこの契約を意識する必要はありません。 独自の探索アルゴリズムを実装するときだけ、次の状態境界を実装します。
Algorithmの役割#
Algorithm は、読み取り専用の StateView を受け取り、状態を直接変更せずに結果を返します。
ask(request, state) -> ProposalBatch:ProposalRequest と StateView から候補のバッチを生成します。 request.n_offspring が None の場合の候補数は、アルゴリズムが現在のPopulationから決定します。
tell(feedback, state) -> StatePatch:FeedbackBatch を消費し、適用すべき状態変更を StatePatch として返します。 tell() は OptimizationState や Population を直接変更しません。
この分離によって、どの候補を高コストな真の評価へ送るかを Algorithm の外側に置けます。 その判断は OptimizationStrategy と評価計画、FeedbackPolicyの組み合わせが担います。
組み込みAlgorithm#
クラス |
探索手法 |
|---|---|
|
交叉と突然変異による遺伝的アルゴリズム |
|
速度と位置の更新による粒子群最適化 |
|
|
GenomeGA は、GenomeBatch と SearchSpace の契約を使うGenome-native経路のAlgorithmです。 固定幅のdense vectorを前提とする GA と同じ x 列の経路へ暗黙に変換せず、Genomeの表現と空間サービスを維持します。
GA:演算子を組み合わせるコンテナ#
GA自体には探索ロジックがなく、crossover/mutation/parent_selection/survivor_selectionという4つの演算子を注入して組み立てるコンテナです。
GA(crossover, mutation, parent_selection, survivor_selection, *,
duplicate_elimination=None,
variation_execution="batch",
integer_crossover=None, integer_mutation=None,
categorical_crossover=None, categorical_mutation=None)
各演算子の動作とパラメータは、それぞれの専用ページで説明します。
Crossover:親個体から子個体を生成します。
Mutation:子個体に摂動を加えます。
ParentSelection:交叉に使う親個体を選びます。
SurvivorSelection:次世代に残す個体を選びます。
variation_executionは交叉と突然変異のバッチ実行または逐次実行を選択し、辞書でそれぞれを個別に制御できます。既定値はバッチです。操作・フックの完全な順序と再現性の仕様については、下記のGA APIリファレンスを参照してください。
GA.tell()は現在の個体群とask()が生成した候補を1つのプールにまとめ、survivor_selectionで次の個体群を選びます。これは\((\mu+\lambda)\)型の更新に相当します。プールに何を入れるか(親を含めるかどうか)はGA自身の責務であり、SurvivorSelectionのインターフェース自体には現れません。
GA:混合変数問題#
設計変数に整数変数とカテゴリ変数が混在する問題では、GAは連続変数用のcrossover/mutationに加えて、型ごとの演算子を使います。integer_crossover/integer_mutation/categorical_crossover/categorical_mutationを省略すると、それぞれCrossoverIntegerSBX/MutationIntegerUniform/CrossoverCategorical/MutationCategoricalが自動的に設定されます。自動設定された演算子の確率パラメータ(prob/prob_var)は、連続変数用のcrossover/mutationから引き継がれます。
型ごとに設定する演算子でも、n_children/n_parentsは連続変数用のcrossoverと一致しなければなりません。一致しない場合はConfigurationErrorを送出します。これは、GAが親個体を変数型ごとの列に分割し、対応する列にだけ各演算子を適用してから、結果を1つの個体に再構成するためです。この実装では、型によって子個体数と親個体数を変えられません。既定のバッチモードでは、各型専用演算子のバッチメソッドをその列に対して呼び出します。variation_execution="sequential"では従来のペア単位・個体単位の振り分けを使い、乱数列を維持します。
Variable types は defined via Problem's variables argument.。
GA:補助ユーティリティ#
**duplicate_elimination**引数にDuplicateElimination(atol=1e-16, rtol=0.0, max_retries=100)を渡すと、現在の個体群と重複する子個体を再生成して置き換えます。重複検出の許容誤差はatol/rtolで、再生成の最大試行回数はmax_retriesで指定します。省略した場合(既定値はNone)、重複除去は行われません。
saealib.repair_clipping(candidates, bounds)は、候補集合を(lb, ub)に単純にnp.clipする独立したユーティリティ関数です。GA自身はConstraintHandlerのrepair()(これも既定ではnp.clip)とProblem.repair()(Variableごとの射影)を通じて候補を修復します。repair_clippingはGAの内部処理に自動接続されません。ConstraintHandlerを通らないカスタム評価パイプラインを書く場合など、同じクリッピング動作を単独で使いたいときのために公開されています。
PSO法#
PSO(w=0.7, c1=1.5, c2=1.5, v_max=None)は、慣性項w、個体最良値への誘引c1、群最良値への誘引c2の重みを使って速度を更新し、その速度で位置を進めます。v_maxを指定すると、各次元の速度の大きさをその値に制限します。
GAと異なり、演算子を注入して組み立てるものではありません。ask()/tell()が速度と位置の更新を直接行い、各粒子のpbest(個体最良値)を追跡します。群最良値(リーダー)はctx.comparatorを使って全粒子のpbestから選ぶため、任意の単目的Comparatorを自動的に利用できます。多目的PSO(MOPSO)には、非劣解集合を管理する専用サブクラスが必要です。組み込みのPSOは単目的問題を対象とします。
外部ライブラリアダプタ#
PymooAlgorithm(pymoo_algorithm, *, allow_partial_tell=False)は、構築済みのpymooアルゴリズム(例:NSGA2()、DE())をラップし、既存のpymooアルゴリズムでsaealibのask-tellループとサロゲート支援戦略を変更せずに動かせるようにします。
GA/PSOはctx.populationを正本とみなしますが、PymooAlgorithmは「エンジンモード」で動作します。ラップしたpymooアルゴリズムが独自の個体群と内部の生存状態を保持し、各 tell() の終了時にctx.populationがそこから更新されます。これは正本ではなくミラーです。pymooアルゴリズム自身の検証済みの生存選択ロジックを変更せずに再利用する唯一の方法ですが、利用前に知っておくべき実際の制約があります。
チェックポイントからの保存・再開には対応しません —
OptimizationState.save()はsaealib独自の配列だけをシリアライズし、ラップしたアルゴリズムの内部状態は対象にしません。ask()に渡したn_offspringは無視されます。子個体数はラップしたアルゴリズム自身の設定(例:pop_size)で固定されます。PreSelectionStrategyの上位k件への打ち切りでは、ask()が生成したものの一部だけをtell()に渡します。既定ではConfigurationErrorを送出します。インデックスに依存するアルゴリズム(例:差分進化)では、部分的なtell()により親と子個体の対応が気付かないままずれるためです。それでも許可する場合はallow_partial_tell=Trueを渡します。
pymoo extraのインストールについてはインストールを参照してください。
独自Algorithmの実装方法#
独自の探索アルゴリズムを実装するときは、Algorithm を継承して ask() と tell() を実装します。 get_required_attrs()、population_class、archive_class も、既存のPopulation互換経路を使う場合には実装が必要です。
新しい実装では、ask() は ProposalBatch を返し、tell() は StatePatch を返します。 state は宣言された状態キーだけを参照できる StateView です。 候補数、候補ID、評価結果の対応付けは ProposalBatch と FeedbackBatch の契約に従います。
次の骨格は、独自Algorithmが実装する境界だけを示しています。
from saealib import Algorithm, Archive, Population
from saealib.algorithms import ProposalRequest
from saealib.core.contracts import FeedbackBatch, ProposalBatch
from saealib.core import StatePatch, StateView
class CustomAlgorithm(Algorithm):
"""Skeleton for an Algorithm that generates candidates and consumes Feedback."""
def get_required_attrs(self, problem):
return []
@property
def population_class(self):
return Population
@property
def archive_class(self):
return Archive
def ask(self, request: ProposalRequest, state: StateView) -> ProposalBatch:
# Read the current Population from state.context and declared StateKeys.
# Return a ProposalBatch with candidate IDs and FeedbackRequirement.
...
def tell(self, feedback: FeedbackBatch, state: StateView) -> StatePatch:
# Read the FeedbackBatch and collect changes to Population and other state in a StatePatch.
...
tell() が受け取るFeedbackは、どの評価経路から届いたかと、候補IDとの対応を保持します。 真の評価だけを必要とするか、予測値を受け入れるかは、Algorithmの契約とFeedbackPolicyで宣言します。 PSOのpbestのような永続的な補助情報は、Population属性または宣言済みのStateKeyとして扱います。
関連コンポーネント#
Crossover / Mutation / ParentSelection / SurvivorSelection:
GAが組み合わせる4つの演算子。Problem:
variablesによる混合変数の定義ConstraintHandler:
repair()thatGAuses へrepair 候補s。OptimizationStrategy: Decides whether へperform 真の評価 between
ask()/tell()。Population: Algorithmの互換性経路で利用するPopulationと
GenomeBatch