最佳化求解器的使用者手冊,介紹求解器的調用方式、如何輸入問題和求解器的APIs清單。
由於本軟體APIs比較多,且還在高頻增加功能中,因此APIs的介紹會較多地引導至另一個《MindOpt使用者使用手冊——完整版》上查看細節,給您帶來的不連貫閱讀體驗敬請諒解。該文庫可以在頁面切換文檔語言和文檔版本。
最佳化求解器調用方式
在使用之前,請先下載和安裝求解器SDK,並擷取使用許可權,見快速入門(開通和使用)和求解器SDK下載和安裝。
下面列出簡單的樣本,求解器細節的調用方式和完整案例可查看更多。
命令列調用樣本:
Linux以及macOS下,假設使用者根據安裝文檔將MindOpt安裝到環境變數$MINDOPT_HOME指定目錄:
mindopt $MINDOPT_HOME/examples/data/afiro.mps
Windows下
mindopt %MINDOPT_HOME%\examples\data\afiro.mps
C/C++/C#/Java/Python/MATLAB語言調用樣本:
V1.x的API與V0.x的API大部分都不相同。請關注自己使用的版本。
V1.x.x
env = mindoptpy.Env()
env.start()
model = mindoptpy.read(filename, env)
model.optimize()
print(f"Optimal objective value is: {model.objval}")
for v in x:
print(f"{v.VarName} = {v.X}")
MDOEnv env = new MDOEnv();
MDOModel model = new MDOModel(env, filename);
model.optimize();
System.out.println("Optimal objective value is: " + model.get(MDO.DoubleAttr.ObjVal));
MDOVar[] x = model.getVars();
for (int i = 0; i < x.length; ++i) {
System.out.println(x[i].get(MDO.StringAttr.VarName) + " = " + x[i].get(MDO.DoubleAttr.X));
}
MDOEnv env = MDOEnv();
MDOModel model = MDOModel(env, filename);
model.optimize();
std::cout << "Optimal objective value is: " << model.get(MDO_DoubleAttr_ObjVal) << std::endl;
std::vector<MDOVar> vars = model.getVars();
for (auto v : vars) {
std::cout << v.get(MDO_StringAttr_VarName) << " = " << v.get(MDO_DoubleAttr_X) << std::endl;
}
MDOenv* env;
MDOemptyenv(&env);
MDOstartenv(env);
MDOmodel* model;
double obj;
MDOreadmodel(env, argv[1], filename);
MDOoptimize(model);
MDOgetdblattr(model, MDO_DBL_ATTR_OBJ_VAL, &obj);
printf("Optimal objective value is: %f\n", obj);
/* Free the environment. */
MDOfreemodel(model);
MDOfreeenv(env);
MDOEnv env = new MDOEnv();
MDOModel model =new MDOModel(env, filename);
model.optimize();
Console.WriteLine($"Optimal objective value is: {model.Get(MDO.DoubleAttr.ObjVal)}");
MDOVar[] vars = model.GetVars();
for (int i = 0; i < vars.Length; i++)
{
Console.WriteLine($"{vars[i].Get(MDO.StringAttr.VarName)} = {vars[i].Get(MDO.DoubleAttr.X)}");
}
model = mindopt_read(file);
result = mindopt(model);
for v=1:length(result.X)
fprintf('%s %d\n', model.varnames{v}, result.X(v));
end
fprintf('Optimal objective value is: %e\n', result.ObjVal);
objval = result.ObjVal;
完整例子的代碼請查看更多。
V0.x.x
# 方法1:從0.19.0版本開始引入新式的建立模型方式,雲鑒權更快,消耗的並發度少
env = mindoptpy.MdoEnv()
model = mindoptpy.MdoModel(env)
model.read_prob(filename)
model.solve_prob()
model.display_results()
# 方法2:舊式的建立模型方式仍然支援
# model = mindoptpy.MdoModel()
# model.read_prob(filename)
# model.solve_prob()
# model.display_results()
//<dependency>
// <groupId>com.alibaba.damo</groupId>
// <artifactId>mindoptj</artifactId>
// <version>[0.20.0,)</version>
//</dependency>
// load動態連結程式庫,如下:
Mdo.load("c:\\mindopt\\0.20.0\\win64_x86\\lib\\mindopt_0_20_0.dll");
// 方法1:從0.19.0版本開始引入新式的建立模型方式,雲鑒權更快,消耗的並發度少
//Set up environment,放在程式初始化的時候進行,例如MapReduce裡的setup階段
MdoEnv env = new MdoEnv();
//create a model
MdoModel model = env.createModel();
model.readProb(filename)
model.solveProb();
model.displayResult();
model.free();
//JAVA SDK 需要手動釋放 env,放在程式結束的時候進行,例如MapReduce裡的cleanup階段
env.free();
// 方法2:舊式的建立模型方式仍然支援,但被標記為過時,將在以後的版本中移除
/*
MdoModel model = new MdoModel();
model.readProb(filename)
model.solveProb();
model.displayResult();
*/
// 方法1:從0.19.0版本開始引入新式的建立模型方式,雲鑒權更快,消耗的並發度少
using mindopt::MdoEnv;
MdoEnv env;
MdoModel model(env);
model.readProb(filename);
model.solveProb();
model.displayResults();
// 方法2:舊式的建立模型方式仍然支援
/*
MdoModel model;
model.readProb(filename);
model.solveProb();
model.displayResults();
*/
// 方法1:從0.19.0版本開始引入新式的建立模型方式,雲鑒權更快,消耗的並發度少
MdoEnvPtr env;
MdoMdlPtr model;
Mdo_createEnv(&env);
Mdo_createMdlWithEnv(&model, env);
Mdo_readProb(model, filename);
Mdo_solveProb(model);
Mdo_displayResults(model);
Mdo_freeMdl(&model);
// C SDK 需要手動釋放 env
Mdo_freeEnv(&env);
// 方法2:舊式的建立模型方式仍然支援
/*
MdoMdlPtr model;
Mdo_createMdl(&model);
Mdo_readProb(model, filename);
Mdo_solveProb(model);
Mdo_displayResults(model);
Mdo_freeMdl(&model);
*/
完整例子的代碼請查看V0.x版本文檔。
支援求解的最佳化問題
目前的版本的求解器支援:
線性規劃:求解線性規劃(LP)的能力已完整。支援原始/對偶單純形法 (simplex) 和內點法 (interior point)、支援求解大規模網路流最佳化問題。
整數規劃:支援求解混合整數線性規劃(MILP)問題的分支定界演算法 (branch-and-cut solver)。
最佳化問題的輸入方式
最佳化問題支援3種輸入方式:檔案輸入、資料建模APIs輸入、外部建模工具調用。
方式1:檔案輸入
支援
.dat-s
格式, 如SDP問題樣本。支援
.qps
格式,如QP問題資料。mindoptampl模組支援
.nl
格式檔案輸入,如在命令列中輸入mindoptampl filename.nl
。
更詳細的介紹請查看更多。
方式2:建模APIs輸入
V1.x的API與V0.x的API大部分都不相同。請關注自己使用的版本。
V1.x.x
相關聯的APIs有多種,同一個資料也可以有多種輸入方式。
按行輸入Python樣本簡述:
用
mindoptpy.Model.modelsense = MDO.MINIMIZE
將目標函數設定為最小化;用
mindoptpy.Model.addVar()
來添加最佳化變數,並分別定義其下界、上界、目標係數、名稱和類型;用
mindoptpy.Model.addConstrs()
來添加約束。
按列輸入Python樣本簡述:
用
mindoptpy.Model.modelsense = MDO.MAXIMIZE
將目標函數設定為最小化;開始時調用
mindoptpy.Model.addConstrs()
來建立帶有指定的左側和右側值的約束(無非零元素);建立臨時的列對象
mindoptpy.Model.Column()
來按順序地儲存約束和非零元素的值;最後調用
mindoptpy.Model.addVar()
來建立新的變數,及其相應的目標函數係數、列向量中的非零元、下界和上界、變數名以及變數類型。
更詳細的介紹請查看更多。其中,屬性描述(Attributes)列表見更多,且API章節有對應語言的完整例子。
V0.x.x
相關聯的APIs有多種,同一個資料也可以有多種輸入方式。
按行輸入Python樣本簡述:
用
mindoptpy.MdoModel.set_int_attr()
將目標函數設定為最小化;用
mindoptpy.MdoModel.add_var()
來添加四個最佳化變數,並分別定義其下界、上界、名稱和類型;用
mindoptpy.MdoModel.add_cons()
來添加約束。
按列輸入Python樣本簡述:
用
mindoptpy.MdoModel.set_int_attr()
將目標函數設定為最小化;開始時調用
mindoptpy.MdoModel.add_cons()
來建立帶有指定的左側和右側值的約束(無非零元素);建立臨時的列對象
mindoptpy.MdoCol()
來按順序地儲存約束和非零元素的值;最後調用
mindoptpy.MdoModel.add_var()
來建立新的變數,及其相應的目標函數係數、列向量中的非零元、下界和上界、變數名以及變數類型。
方式3:建模語言 MindOpt APL、 AMPL、Pyomo、PuLP、JuMP
採用建模語言的好處是用建模語言的API來建模,可以方便切換不同版本求解器。
MindOpt支援一些常見的建模工具,當前支援以下幾種:
1. MindOpt APL
2022年開始支援MindOpt APL建模語言,是MindOpt團隊自研的一款建模語言。
2. AMPL
使用 AMPL 調用 MindOpt 之前需要先安裝 MindOpt 和 AMPL 。mindoptampl 應用位於安裝包的\bin\mindoptampl
。mindoptampl 提供了一些可配置的參數,使用者可以通過 AMPL 的option
命令設定 mindoptampl_options
參數,如:
ampl: option mindoptampl_options 'numthreads=4 maxtime=1e+4';
更詳細的介紹和案例請查看更多。
3. Pyomo
使用 Pyomo 調用 MindOpt 之前需要先安裝 MindOpt 和 Pyomo。調用 MindOpt 求解器的 Pyomo API 需要使用介面檔案 mindopt_pyomo.py
。 MindOpt 的 Pyomo 介面是繼承自 Pyomo 的 DirectSolver
類,實現代碼在安裝包的\lib\pyomo\mindopt_pyomo.py
。在 Python 代碼中匯入該檔案:
from mindopt_pyomo import MindoDirect
更詳細的介紹和案例請查看更多。
4. PuLP
使用 PuLP 調用 MindOpt 之前需要先安裝 MindOpt 和 PuLP。調用 MindOpt 求解器的 PuLP API 需要使用介面檔案 mindopt_pulp.py
。 MindOpt 的 PuLP 介面繼承自 PuLP 的 LpSolver
類,實現代碼在安裝包的\lib\pulp\mindopt_pulp.py
。在 Python 代碼中匯入該檔案:
from mindopt_pulp import MINDOPT
更詳細的介紹和案例請查看更多。
5. JuMP
JuMP 是一個基於 Julia 程式設計語言的開源建模工具。使用 JuMP 調用 MindOpt 之前需要先安裝 MindOpt、Julia、JuMP、AmplNLWriter。調用JuMP API來建立建模對象,並指定使用mindoptampl作為求解器。
model = Model(() -> AmplNLWriter.Optimizer(mindoptampl))
更詳細的介紹和案例請查看更多。
求解時的參數設定
運行求解器可以設定輸入參數,V1版文檔請參考參數章節。如設定求解器的最大求解時間"MaxTime"
。V0版本文檔請參考可選輸入參數章節。
計算裝置配置參考:LP求解時不同演算法特性
配置機器資源時候,不同問題結構、演算法選擇,耗費的機器資源會有差異,請根據需要測試選擇。
關於LP求解,當前我們線性規劃(LP)的求解提供了Simplex(單純形法)、IPM(Interior Point Method,內點法)、Concurrent(並發最佳化)演算法。在求解時候,執行流程如下文的“執行流程”圖片所示,會預設選Concurrent,您可以通過設定“Method”參數來選擇演算法。
這3種演算法的區別如下:
Simplex(單純形法) | IPM(Interior Point Method,內點法) | Concurrent(同時最佳化) | |
特性 | - 通常情況下對數值敏感低 - 耗費記憶體更少 - 支援Warm-start | - 對數值更敏感 - 比Simplex方法要多2~10倍的記憶體 - 不支援Warm-start - 對大規模的問題可能更適用 | - 同時進行兩個方法的最佳化,耗費的記憶體更多 - 更魯棒 - 在求解新類別問題的時候,建議先用本方法來嘗試求解,協助分辨Simplex或IPM方法哪種更合適,輔助後續演算法選取 |
計算裝置需求 不同問題可能有明顯差異,請以實測為準。以下實驗室的測試值供參考: | |||
當問題約束量為43200,非零元素為1038761時 | 測試最大記憶體佔用為350 MB | 測試最大記憶體佔用為620 MB | 測試最大記憶體佔用為920 MB |
當問題約束量為986069,非零元素為4280320時 | 測試最大記憶體佔用為1250 MB | 測試最大記憶體佔用為1500 MB | 測試最大記憶體佔用為1650 MB |
當問題約束量為4284,非零元素為11279748時 | 測試最大記憶體佔用為2050 MB | 測試最大記憶體佔用為5200 MB | 測試最大記憶體佔用為5200 MB |
當問題約束量為22117,非零元素為20078717時 | 測試最大記憶體佔用為3400 MB | 測試最大記憶體佔用為5600 MB | 測試最大記憶體佔用為8300 MB |
- 記憶體的消耗取決於問題的形式、規模以及稀疏程度,如需提前預估記憶體資源時,建議先通過問題的規模和稀疏程度來推算記憶體消耗,再乘以一定的倍數作為記憶體資源預留的預估值。
求解結果擷取
命令列運行
命令列運行求解時,會列印輸出求解過程輸出和結果的summary,同時會在求解的檔案處產生同名結果文檔.bas
和.sol
。
如下圖示意運行mindopt afiro.mps
後,檔案夾中產生了afiro.bas
和afiro.sol
檔案,.bas
檔案儲存的是基,.sol
檔案儲存的是求解的最優目標值和變數值的解。
API求解和擷取結果
V1.x的API與V0.x的API大部分都不相同。請關注自己使用的版本。
V1.x.x
待求解的問題輸入後,可以調用optimize()
的API來求解,如python的求解指令model.optimize()
。
求解完後,可以調用API來擷取結果,以Python為例:
可以調用
model.status
擷取求解的狀態。有UNKNOWN、OPTIMAL、INFEASIBLE、UNBOUNDED、INF_OR_UBD、SUB_OPTIMAL。如Python裡的MDO.OPTIMAL
可以用
model.objval
擷取目標值。還可以調用
model.getAttr(MDO.Attr.PrimalObjVal)
來擷取其他的模型屬性值,類似地:SolutionTime
對應:總執行時間(秒)DualObjVal
對應:對偶目標值ColBasis
對應:原始解的基
可以調用
var.X
來變數的解。更多解的屬性描述(Attributes)列表見更多。
完整的程式碼範例可參考安裝包的example檔案夾,或文檔更多,和各個語言API文檔最後一章,如Python的examples。
V0.x.x
待求解的問題輸入後,可以調用solveProb
的API來求解,如python的求解指令model.solve_prob()
。
求解完後,可以調用API來擷取結果,以Python為例:
可以調用
model.display_results()
來列印結果。可以調用
model.get_status()
擷取求解的狀態。可以調用
model.get_real_attr("PrimalObjVal")
來擷取原始目標值,類似地:"DualObjVal"
對應:對偶目標值"PrimalSoln"
對應:原始解"ColBasis"
對應:原始解的基更多解的屬性描述(Solution Attributes)列表在更多V0版本文檔
完整的程式碼範例可參考安裝包的example檔案夾或文檔。
求解輔助分析工具、進階建模技巧
約束不可行性分析
在建模求解過程中,會遇到由於某些約束互相衝突導致問題不可行(infeasible)的情況,分析不可行問題並識別出導致約束衝突的關鍵約束能有效協助建模。這類導致問題不可行的最小約束子集被稱為不可約不可行系統(IIS, irreduciable infeasible system)。
MindOpt 設計了用來計算IIS的API,當問題"INFEASIBLE"
時,使用者可以通過 compute IIS 的API來對不可行問題進行分析,如可依據此對IIS中的約束進行修正或移除,使得最佳化問題變得可行。
IIS的Python介面調用樣本如下:
V1.x.x
if model.status == MDO.INFEASIBLE or model.status == MDO.INF_OR_UBD:
idx_rows, idx_cols = model.compute_iis()
完整的 compute IIS 的說明請參閱更多。
V0.x.x
status_code, status_msg = model.get_status()
if status_msg == "INFEASIBLE":
idx_rows, idx_cols = model.compute_iis()
完整的 compute IIS 的說明請參閱更多V0版文檔。
回調功能 (Callback)
MindOpt使用經典的分支定界法來求解MIP問題。MindOpt為使用者提供回調功能,協助使用者對MIP問題的求解過程進行跟蹤與修改。利用回調功能,使用者可以提供一些決策以修改求解過程,包括:
添加割平面,裁剪不會出現最優解的分支;
幹預MindOpt的分支選擇策略,控制節點二分方法及遍曆順序;
添加自訂可行解(比如通過某種啟發學習法演算法得到的解),一個較好的可行解可以提高MindOpt的求解效率。
可以如下方式建立回呼函數:
def callback(model, where):
完整的 Callback 說明請參閱更多。
求解器調參
MindOpt Tuner也可以對求解器超參調優,提供業務情境的資料,採用MindOpt Tuner,經過調參後,可以得到一個業務情境定製的更快的求解器。目前部分功能在MindOpt雲上平台中可以使用。
AI工程師輔助建模
AI工程師是在LLM大模型的基礎上,結合特定領域軟體工具的知識,用於該領域基礎技術諮詢的一個AI機器人。MindOpt Copilot 是一款協助使用者使用MindOpt技術解決“數學最佳化”問題的AI工程師。可用於“數學最佳化”領域的技術諮詢。
當前,MindOpt Copilot AI工程師支援使用MindOpt Solver最佳化求解器、MindOpt APL建模語言等工具,通過自然語言和表格式資料溝通,自動根據使用者的業務問題進行數學建模、列數學公式、碼代碼、調用MindOpt軟體來求解等工序。並且可以將機器人產生的文字、數學公式、代碼等,產生專案,快捷地匯入開發專案進行開發。
2023年9月首次上線,可在MindOpt雲上平台中可以使用。
其他功能
資料脫敏
MindOpt的命令列功能裡有--sanitize
可進行資料脫敏,方便外發資料用於技術交流或求解器調參改進。其脫敏原理可以參考部落格。
求解器的執行流程
MindOpt的執行流程:
不同的最佳化問題會有不同的求解方法,整體流程大致相同。以下是線性規劃LP問題的調用樣本,過程中有參數可以由使用者選擇設定。
完整APIs和調用方式
完整的說明請查閱《MindOpt使用者使用手冊——完整版》
目錄示意如下: