IIS機能とは、
結論から言うと、IIS = Irreducible Infeasible Subsystem(最小非実行可能部分系) で、
「モデルが実行不可能になる原因となっている最小の制約セット」 を指します。
◆ 一言でまとめると
IIS = “この制約グループが原因で infeasible になっている最小セット” どれか 1 つでも外すと実行可能になる可能性がある、という“最小の矛盾の塊”です。
◆ もう少し丁寧に説明すると
最適化モデル(LP/MIP)が infeasible(実行不可能) になるとき、 原因は通常「複数の制約の組み合わせ」にあります。
しかし、モデル全体を見ても原因は分かりにくい。
そこでソルバーは IIS を抽出 します:
IIS は 実行不可能な制約の最小集合
IIS 内の制約を 1 つでも外すと、実行可能になる(可能性が高い)
モデルのデバッグに非常に有効
◆ 例(イメージ)
x ≥ 10
x ≤ 5
この 2 つだけで infeasible。 これが IIS。
もし制約が 100 個あっても、IIS は 2〜5 個程度の小さなセットになることが多い。
◆ COPT での IIS
COPT には Compute IIS 機能があり、 モデルが infeasible のときに IIS を抽出できます。
<例>
次は、COPTがInfeasibleを検出したときの解析コードです。
この中で、
COPT_ComputeIISで、IISを計算、COPT_WriteIISで、その原因をファイルに書き出しています。API的には、問題となる制約ROWと変数を列挙しています。結果的には、以下のファイルに書き出した内容を得ることが出来ます。
このファイルが言っていることは、制約267でX15変数が1,一方で、X15変数範囲は[0:0]になっていて、これが矛盾である、ということが分かります。商用ソルバであるCOPTは、このように、IIS機能が充実しています。現在は、評価ライセンスでCOPTを使用できので、IIS機能を使って開発デバッグの一助とすることが出来ます。
製品版では、これらをHIGHSに置き換える必要があります。実装は、HIGHSがどの程度IISをサポートするかにも依存します。さらにGUIモデル系に変換することも必要となります。実装には、結構な時間を要すると思います。
<書き出しファイル内容>
\Generated by Cardinal Operations Minimize x15 Subject To r267: x15 = 1 Bounds x15 = 0 END
<APIコード>
if (lpstat== COPT_LPSTATUS_INFEASIBLE){ //assert(0); stringstream stm; stm << "Fatal COPT Infeasible\n" << ends; send_status_to_gui(stm.str()); errcode = COPT_ComputeIIS(prob->get()); if (errcode) { error_handling(errcode); return; } COPT_WriteIIS(prob->get(),"model.iis"); // === 1. 制約(行:ROW)のチェック === int numRows = getNumRows(); int* rowLowerIIS = (int*)malloc(sizeof(int) * numRows); int* rowUpperIIS = (int*)malloc(sizeof(int) * numRows); COPT_GetRowLowerIIS(prob->get(), numRows, NULL, rowLowerIIS); COPT_GetRowUpperIIS(prob->get(), numRows, NULL, rowUpperIIS); stm<<"--- 矛盾の原因となっている制約(ROW) ---\n"; for (int i = 0; i < numRows; i++) { if (rowLowerIIS[i] == 1 || rowUpperIIS[i] == 1) { char rowName[256]; COPT_GetRowName(prob->get(), i, rowName, sizeof(rowName), NULL); stm<< " - 制約名: "<< rowName<< " "<< i; if (rowLowerIIS[i] == 1) stm<< " [下限値が矛盾]"; if (rowUpperIIS[i] == 1) stm<<" [上限値が矛盾]"; stm<<"\n"; } } // === 2. 変数(列:COL)のチェック === int numCols = getNumCols(); int* colLowerIIS = (int*)malloc(sizeof(int) * numCols); int* colUpperIIS = (int*)malloc(sizeof(int) * numCols); // 変数の「下限」と「上限」のIISステータスを取得 COPT_GetColLowerIIS(prob->get(), numCols, NULL, colLowerIIS); COPT_GetColUpperIIS(prob->get(), numCols, NULL, colUpperIIS); stm<<"\n--- 矛盾の原因となっている変数限界(COL) ---\n"; for (int j = 0; j < numCols; j++) { if (colLowerIIS[j] == 1 || colUpperIIS[j] == 1) { char colName[256]; COPT_GetColName(prob->get(), j, colName, sizeof(colName), NULL); stm<<" - 変数名:"<< colName<< " "<< j; if (colLowerIIS[j] == 1) stm<< " [変数の下限(LB)が矛盾]"; if (colUpperIIS[j] == 1) stm<<" [変数の上限(UB)が矛盾]"; stm<<"\n"; } } // メモリ解放 free(rowLowerIIS); free(rowUpperIIS); free(colLowerIIS); free(colUpperIIS); stm << ends; send_status_to_gui(stm.str()); return; }