Adapter 固有 diagnostics#
Adapter diagnostics は、portable な Solution には入らない
solver 側の情報を残すための仕組みです。decode 済みの OMMX 結果を見るときは
Solution を使います。backend solver が何を観測し、何を報告し、
どこまで証明したかを確認したい場合に diagnostics を使います。
PySCIPOpt で diagnostics を記録する#
PySCIPOpt Adapter は、solve() に DiagnosticCollector を渡すと
SCIP の progress と termination 情報を記録します。通常は
SCIPDiagnosticsAnalyzer を通して読みます。
from ommx import adapter, dataset
from ommx_pyscipopt_adapter import (
OMMXPySCIPOptAdapter as Adapter,
SCIPDiagnosticsAnalyzer,
)
instance = dataset.miplib2017("air05")
diag = adapter.DiagnosticCollector()
solution = Adapter.solve(instance, diagnostics=diag)
analyze = SCIPDiagnosticsAnalyzer(diag.diagnostics)
analyze.progress_history_df[["primal_bound", "dual_bound"]].loc[5:].plot()
SCIPDiagnosticsAnalyzer で読み出した
SCIP の primal / dual bound の推移。#
progress_history_df は solving_time_sec を index にした pandas DataFrame です。
dual_bound、gap、incumbent_objective などの Series property も同じ time index を使うので、
そのまま時間軸の plot に使えます。termination_result は最終的な SCIP report を表す dictionary です。
dual_bound = analyze.dual_bound
gap = analyze.gap
incumbents = analyze.incumbent_objective
termination = analyze.termination_result
DataFrame / Series helper は pandas を必要とします。pandas が使えない環境では、
progress sample には progress_history_records、最終 report には termination_result を使ってください。
PySCIPOpt が記録するもの#
PySCIPOpt Adapter は 2 種類の SCIP diagnostics を記録します。
SCIPProgressSnapshot は、SCIP event callback から記録される
progress sample です。現在は BESTSOLFOUND と DUALBOUNDIMPROVED を監視しています。
progress snapshot には solving_time_sec、node_count、primal_bound、dual_bound、
gap、incumbent_objective などが含まれます。
SCIPTerminationReport は、model.optimize() が終了した後、
PySCIPOpt model を OMMX Solution に decode する前に記録される最終 report です。
status、primal_bound、dual_bound、gap、objective_value、node 数、LP / cut counter、
primal-dual integral、timing、SCIP / PySCIPOpt version metadata などが含まれます。
progress snapshot は callback 時点の観測値です。SCIP は BESTSOLFOUND callback を、
集計済みの統計がすべて更新される前に呼ぶことがあります。終了時点の値は termination report を参照してください。
完全な member list は API Reference の
SCIPProgressSnapshot、
SCIPTerminationReport、
SCIPDiagnosticsAnalyzer を参照してください。
失敗時の処理#
直接取得は、OMMX Solution への decode が失敗する場合にも有用です。PySCIPOpt Adapter は
decode の前に termination report を記録するため、InfeasibleDetected や
UnboundedDetected などの adapter exception が発生しても、collector には
SCIP の最終 status や bound が残ります。
from ommx.adapter import DiagnosticCollector, UnboundedDetected
from ommx_pyscipopt_adapter import OMMXPySCIPOptAdapter, SCIPDiagnosticsAnalyzer
collector = DiagnosticCollector()
try:
OMMXPySCIPOptAdapter.solve(instance, diagnostics=collector)
except UnboundedDetected:
analysis = SCIPDiagnosticsAnalyzer(collector.diagnostics)
print(analysis.termination_result)
Experiment 連携#
log_solve() を使う場合、ユーザー側から diagnostics keyword を
渡さないでください。この keyword は Run.log_solve が予約しています。diagnostics 収集は
デフォルトでは無効なので、Experiment Artifact の Solve entry に diagnostics を保存したい場合は
store_diagnostics=True を指定してください。このとき adapter に diagnostics sink が渡されます。
from ommx.experiment import Experiment
from ommx_pyscipopt_adapter import OMMXPySCIPOptAdapter, SCIPDiagnosticsAnalyzer
with Experiment() as experiment:
with experiment.run() as run:
solution = run.log_solve(
OMMXPySCIPOptAdapter,
instance,
store_diagnostics=True,
)
solve = experiment.runs[0].solves[0]
analysis = SCIPDiagnosticsAnalyzer(solve.diagnostics)
print(analysis.dual_bound)
print(analysis.termination_result)
Experiment から diagnostics で読み出した diagnostics は、
元の dataclass instance ではなく dictionary です。これにより、保存済み Artifact は求解時に使われた
Python class 定義から独立して読めます。直接取得した場合と同じ records / DataFrame / Series view が
必要な場合は、その list をそのまま SCIPDiagnosticsAnalyzer に渡してください。
solve() が OMMX Solution を返す前に例外を投げた場合でも、
可能な限り Run.log_solve は failed Solve entry を記録します。この entry は
status == "failed" または "interrupted"、output Solution なし、失敗前に収集済みの
diagnostics あり、という形で保存されます。diagnostics が保存されるのは store_diagnostics=True
の場合です。
adapter diagnostics の contract は API Reference の
DiagnosticsSink、
DiagnosticCollector、
solve() を参照してください。