Population#
Populationは、個体と評価結果を扱うドメインデータ構造です。
旧来のベクトル経路では、x、f、g、cv列を持つ構造化配列です。
現在のグラフネイティブ経路では、GenomeBatchとSearchSpaceが個体の表現を定義し、Population.xは可能な場合にだけ利用できる互換性ビューです。
Initializer はProblemのSearchSpaceからGenomeを生成し、候補IDとともに状態やArchiveへ登録します。
したがって、すべてのProblemを浮動小数点の x 配列へ変換できるとは限りません。
Populationの役割#
Populationは、Genome、目的値、制約値、制約違反、Algorithm固有の補助属性を保持します。
Genomeは、DenseVectorBatch、ObjectBatch、PermutationBatchなどのGenomeBatch実装で表現できます。
値を安全に数値ベクトルとして取得できる場合だけ、xまたは密な数値ビューを使います。
Population.genomesは、実行中のPopulationのGenome格納領域から借用する読み取り専用ビューであり、不変スナップショットではありません。
update_arrayなど後続のPopulation操作はその格納領域をインプレースで更新できるため、既存のgenomesビューから観測される内容も変化する場合があります。
Population は通常インスタンスとして使い、独自の個体管理が必要な場合だけサブクラス化します。
属性スキーマは PopulationAttribute(name, dtype, shape, default) のリストで定義します。
旧来のベクトル経路では x、f、g、cv と、Algorithm.get_required_attrs(problem) が返す補助属性をこのスキーマへ追加します。
Genome-native経路では、GenomeBatchとSearchSpaceの契約が表現の構造を担い、列スキーマだけで個体を表しません。
Populationが保持するもの#
メソッド |
役割 |
|---|---|
|
属性の生配列を取得する。書き込み禁止のviewを返す |
|
属性配列を一括更新する |
|
存在しない属性なら |
|
個体を1件追加する |
|
別の |
|
インデックス配列/スライスで部分集合を新しい |
|
サイズ変更と削除 |
|
並べ替え |
|
同じスキーマの空 |
|
計算結果をPopulation変更まで有効なキャッシュとして保持する |
|
単一intなら |
|
個体数 |
set_cache/get_cacheによるキャッシュは、append/delete/update_arrayなど個体群を変更する操作を呼ぶたびに自動的に無効化されます。
NSGA2Comparatorがフロント分割と混雑度距離の計算結果を世代内で使い回す際に、この仕組みを利用しています。
import numpy as np
from saealib import Population, PopulationAttribute
attrs = [
PopulationAttribute("x", np.float64, (2,)),
PopulationAttribute("f", np.float64, (1,)),
PopulationAttribute("cv", np.float64, ()),
]
pop = Population(attrs, init_capacity=4)
pop.append(x=np.array([0.1, 0.2]), f=np.array([1.0]), cv=0.0)
pop.append(x=np.array([0.3, 0.4]), f=np.array([2.0]), cv=0.0)
pop.x # design-variable array with shape (2, 2)
pop[0] # an Individual view of the first individual
pop[0:1] # a Population containing only the first individual
Individual#
Individualはpop[i]で得られる、単一個体への軽量なビューです。
実データを複製せず、参照元のPopulationと自分のインデックスだけを保持します。
get_readonly_value(key)/update_value(key, value)で値の読み書きができるほか、ind.x/ind.f = ...のような属性アクセスでも同じ読み書きができます。
参照元のPopulationの構造(個体数や並び順)が変わった後に古いIndividualを使うと、無効な参照として例外になります。
不変条件#
個体属性の配列は同じ個体数とスキーマを共有し、変更操作はキャッシュを無効化します。行数やスキーマが一致しないまま更新すると、個体と評価結果の対応が壊れます。
Archive#
Archiveは、ArchiveMixinをPopulationにミックスインした具象クラスで、評価済み解を重複なく蓄積する目的で使います。
add(element, **kwargs)はappendとほぼ同じ引数を取りますが、重複解を無視する点が異なります。
重複判定に使う属性はkey_attr引数(既定"x")で指定し、atol/rtolで許容誤差を調整します。
get_knn(x, k)はkd-tree(初回呼び出し時に遅延構築される)による近傍検索を提供し、LocalSurrogateManagerの既定training_setが候補ごとの局所学習データを集める際に使います。
from saealib import Archive
arc = Archive(attrs, init_capacity=4, key_attr="x")
arc.add(x=np.array([0.1, 0.2]), f=np.array([1.0]), cv=0.0)
arc.add(
x=np.array([0.1, 0.2]), f=np.array([1.0]), cv=0.0
) # the duplicate solution is ignored
idx, dist = arc.get_knn(np.array([0.1, 0.2]), k=1)
ParetoArchive#
ParetoArchiveは、ParetoMixinをPopulationにミックスインした具象クラスで、非優越解集合を常時維持します。
新規解を追加するたびに、その解に支配される既存解を削除し、新規解自体が既存解に支配されている場合はその新規解を破棄します。
支配関係の判定は実行可能性優先方式で行われます。
実行可能解(cv <= eps_cv)は常に実行不可能解を支配し、両方が実行可能な場合にのみDominatorのdominatesが使われます。
dominator引数で支配関係の定義を差し替えられます。
eps_cvの既定値は0.0(厳密に実行可能な解のみを許容可能とみなす)ですが、Optimizer実行中はこの値が毎世代problem.handler.feasibility_thresholdで上書きされます。
0.0という既定値は、ParetoArchiveをOptimizerから切り離して単体で使う場合にのみ意味を持ちます。
Populationの拡張#
ArchiveMixin/ParetoMixinは、Populationのサブクラスに多重継承でミックスインするという前提で設計されています。
独自の個体群管理ロジックが必要な場合、これらのMixinを組み合わせた新しいクラス(class MyArchive(ArchiveMixin, Population): ...)を定義できます。
またAlgorithmのpopulation_class/archive_classをオーバーライドすれば、Initializerが生成するPopulation/Archiveを独自サブクラスに差し替えられます。
関連コンポーネント#
Initializer:
Population/Archive/ParetoArchiveを実行開始時に構築するOptimizationState:構築後の
Population/Archive/ParetoArchiveを保持する状態オブジェクトAlgorithm:
population_class/archive_classで具象クラスを差し替えるComparator:
set_cache/get_cacheによるソート結果の使い回しDominance:
ParetoArchiveが非優越解判定に使うDominatorSurrogateManager:
Archive.get_knnを使う局所学習データの収集