拡張ガイドライン#

Cross-layer

このページは、既存コンポーネントの軽量な差し替えと互換性用Pipelineの構成を扱います。 拡張は、既存コンポーネントの差し替えとフレームワークの拡張に分かれます。

import経路の規則は Canonical Imports にまとめています。

拡張点を選ぶ#

変更したい責務に対応する最も浅い境界を選びます。 新しい実行契約が必要な場合だけFrameworkへ進み、既存の契約で表現できる差し替えはチュートリアルの手順で実装します。

変更したいこと

主な境界

概念

APIリファレンス

候補生成や個体更新を変更する

Algorithm

Algorithm

アルゴリズム

交叉、突然変異、選択を変更する

演算子

Crossover、Mutation

演算子

予測モデルや学習データを変更する

Surrogate、SurrogateManager

Surrogate

代理モデル

候補の選択や評価順序を変更する

OptimizationStrategy、Stage

Strategy

戦略

外部ライブラリを接続する

明示的なAdapter

Evaluation

評価

候補表現や変数制約を追加する

SearchSpace

SearchSpace

Space

入出力、状態、ライフサイクルを宣言する

ComponentContract

Contract

Coreリファレンス

接続や互換性検査を変更する

ComponentGraph、Compiler rule

Graph、Compiler

Coreリファレンス

実行、再開、非同期待機の意味を変更する

ExecutionRuntime

Runtime

Executionリファレンス

フレームワーク契約は saealib.core から取得し、実装モジュールを安定した拡張APIとして扱いません。

Algorithm、OptimizationStrategy、Surrogate、AcquisitionFunction、SurrogateManagerには、それぞれ差し替え可能な契約があります。 独自の探索や予測を実装するときは、対象のドメインコンポーネントを実装して Optimizer.set_*() から登録します。 既存コンポーネントの一部だけを変更する場合は、次の軽量な拡張手段を使えます。

with_post / with_post_fitによる後処理#

Crossover/Mutation の with_post(fn) と Surrogate の with_post_fit(fn) は、サブクラス化せずに既存インスタンスへ後処理を追加します。元のインスタンスは変更せず、フックを追加したコピーを返します。

用例は、Crossover/Mutationへの修復関数の追加や、Surrogateのフィット後処理です。 各コンポーネントページの「拡張フック」節に、コンポーネントごとの具体例があります。

PipelineとStage#

Stage と Pipeline には、互換性用の実行経路と構造化DSLの二つの境界があります。 Stageは OptimizationState を受け取り、更新した状態を返す互換性用の実行単位です。 一方、構造化Pipelineはgraph-native component、入れ子のPipeline、Repeat、Loop、Branchを保持し、状態を直接実行しません。

構造化PipelineへStageを入れるときは、stage_component(stage) で互換性境界を明示します。 Optimizerは内部でこのDSLをGraphへlowerし、構造化Runtimeで実行できる計画へ変換します。 通常の利用者がCompilerを直接呼び出す必要はありません。

pipeline.replace("name", entry) は構造上のエントリを置き換え、pipeline.find("name", recursive=False) は名前でエントリを検索します。

from saealib import Pipeline, Stage
from saealib.stages import stage_component
from saealib.stages import CountGenerationStage


class DoubleCountStage(Stage):
    name = "count_generation"

    def execute(self, state):
        return state.replace(gen=state.gen + 2)


pipeline = Pipeline(
    steps=[
        stage_component(CountGenerationStage()),
        # Add a graph-native component or another Stage adapter next.
    ]
)
pipeline.replace("count_generation", stage_component(DoubleCountStage()))

Stage はStageの契約、組み込みStage、互換性用のカスタムStageを説明します。 このページでは、既存の構成を置き換えるときにどの境界を選ぶかを説明します。

CallbackManager#

CallbackManager はEvent発火時にハンドラを呼び出す観測機構です。 candidates などのEventフィールドは観測用であり、再代入してもPipelineの入力は変わりません。 データフローの後処理には with_post() / with_post_fit()、実行単位の置換には Stage、実行構成の変更には Runtime を選びます。

iterate() / run() のステップ境界で optimizer.set_*() やコンポーネント属性を変更すると、実行環境が変化を検出してplanを再コンパイルし、次世代から反映します。 この手順はStage互換経路とgraph-native経路のどちらでも利用できます。 Component側から再コンパイルを要求する経路については OptimizationStrategy の「Behavior of runtime swapping」を参照してください。 Callbackは観測専用で、ハンドラから RuntimeCommand を返すことはできません。 Callbackのクロージャで optimizer.set_*() を呼ぶ変更は上記の経路として反映されますが、構成変更は iterate() 側の手順として案内します。

レジストリ#

saealib.registryは、名前(文字列)またはspec({"type": "Name", "params": {...}})から実インスタンスを構築する仕組みです。 with_post/Pipeline-Stage/CallbackManagerが「実行時の挙動を変える」ための機構であるのに対し、Registryは「コンポーネントを文字列や設定ファイルから組み立てる」ための機構であり、目的が異なります。 プリセットYAML経由の設定駆動構築(Optimizer.set_preset())やチェックポイント再開のように、クラスを直接importしない場面で使います。

register(name=None)(デコレータ):クラスや関数をレジストリへ登録します。 独自のAlgorithm/Surrogate等のサブクラスに@register()を付けるだけで、組み込みコンポーネントと同じように短い名前で参照できるようになります。

from saealib import register
from saealib.surrogate import Surrogate


@register()
class MyCustomSurrogate(Surrogate):
    def __init__(self, alpha: float = 1.0):
        self.alpha = alpha

    ...

get/build/to_specはsaealibのトップレベルからは公開されておらず、saealib.registryから直接importします。

get(name):登録名、または未登録なら"module.submodule.ClassName"形式のドットパスとして解決します。

build(spec):specを再帰的に実インスタンスへ構築します。 spec内の値がさらに入れ子のspecであれば再帰的に構築されます。 {"callable": "dotted.path"}という形式は、関数や組み込み関数そのものを(呼び出さずに)解決します。

to_spec(obj):build()の逆操作。 コンストラクタシグネチャを反映し、同名属性を読んでspecへ再帰的にシリアライズします。 _registry_spec属性を持つクラス(TerminationConditionなど)はこの汎用リフレクションを使わず、その属性を直接返します。 Optimizer.save_preset()が使う経路です。

from saealib.registry import build, get, to_spec

obj = build({"type": "MyCustomSurrogate", "params": {"alpha": 2.0}})
get("MyCustomSurrogate")  # -> the MyCustomSurrogate class
to_spec(obj)  # -> {"type": "MyCustomSurrogate", "params": {"alpha": 2.0}}

各コンポーネントページで「◯◯クラスは@register()未登録」という注記が複数箇所にありますが、いずれもRegistry経由でクラス名から解決する使い方をする場合にのみ影響します。

使い分けの指針#

やりたいこと

使う機構

既存の演算子やサロゲートに後処理を足すだけ

with_post / with_post_fit

ステージの並び自体を変えたい

Pipeline / Stage

外部観測、ログ、条件判断

CallbackManager

設定ファイルやプリセットから組み立てたい

Registry

関連コンポーネント#

参照#