需求
用C#调用 iLog Cplex 的API 求解规划问题,如果模型不收敛(infeasible),需要知道是哪里出了问题,根据IBM管网的Java示例,写一个C#版本。
步骤
首先,在构造好模型后需要保存模型文件(*.lp)
cplex.ExportModel("optmodel.lp");
然后,判断求解是否成功,如果不成功则执行下文的逻辑
if (cplex.Solve())
{
...
}
else
{
//下方代码
}
导入optmodel.lp,这样才能获取到矩阵,遍历约束判断是否有冲突,打印冲突的约束,需要时可以打开 optmodel.lp 文件,对照分析约束不满足的原因。
cplex.ImportModel("optmodel.lp");
var matrixEnum = cplex.GetLPMatrixEnumerator();
matrixEnum.MoveNext();
var lp = (ILPMatrix)matrixEnum.Current;
Console.WriteLine($"Solution status = {cplex.GetStatus()}");
Console.WriteLine("Model Infeasible, Calling CONFLICT REFINER");
var rng = lp.Ranges;
int numVars = 0;
//calculate the number of non-boolean variables
for (int c1 = 0; c1 < lp.NumVars.Length; c1++)
if (lp.NumVars[c1].Type != NumVarType.Bool)
numVars++;
//find the number of SOSs in the model
int numSOS = cplex.NSOSs;
Console.WriteLine("Number of SOSs=" + numSOS);
int numConstraints = rng.Length + 2 * numVars + numSOS;
var constraints = new IConstraint[numConstraints];
for (int c1 = 0; c1 < rng.Length; c1++)
{
constraints[c1] = rng[c1];
}
int numVarCounter = 0;
//add variable bounds to the constraints array
for (int c1 = 0; c1 < lp.NumVars.Length; c1++)
{
if (lp.NumVars[c1].Type != NumVarType.Bool)
{
constraints[rng.Length + 2 * numVarCounter] = cplex.AddLe(lp.NumVars[c1].LB, lp.NumVars[c1]);
constraints[rng.Length + 2 * numVarCounter].Name = lp.NumVars[c1].ToString() + "_LB";
constraints[rng.Length + 2 * numVarCounter + 1] = cplex.AddGe(lp.NumVars[c1].UB, lp.NumVars[c1]);
constraints[rng.Length + 2 * numVarCounter + 1].Name = lp.NumVars[c1].ToString() + "_UB";
numVarCounter++;
}
}
//add SOSs to the constraints array
if (numSOS > 0)
{
int s1Counter = 0;
var s1 = cplex.GetSOS1Enumerator();
while (s1.MoveNext())
{
var cur = (ISOS1)s1.Current;
Console.WriteLine(cur);
constraints[rng.Length + numVars * 2 + s1Counter] = cur;
s1Counter++;
}
int s2Counter = 0;
var s2 = cplex.GetSOS2Enumerator();
while (s2.MoveNext())
{
var cur = (ISOS2)s2.Current;
Console.WriteLine(cur);
constraints[rng.Length + numVars * 2 + s1Counter + s2Counter] = cur;
s2Counter++;
}
}
double[] prefs = new double[constraints.Length];
for (int c1 = 0; c1 < constraints.Length; c1++)
{
//Console.WriteLine(constraints[c1]);
prefs[c1] = 1.0;//change it per your requirements
}
if (cplex.RefineConflict(constraints, prefs))
{
Console.WriteLine("Conflict Refinement process finished: Printing Conflicts");
var conflict = cplex.GetConflict(constraints);
int numConConflicts = 0;
int numBoundConflicts = 0;
int numSOSConflicts = 0;
for (int c2 = 0; c2 < constraints.Length; c2++)
{
if (conflict[c2] == Cplex.ConflictStatus.Member)
{
Console.WriteLine(" Proved : " + constraints[c2]);
if (c2 < rng.Length)
numConConflicts++;
else if (c2 < rng.Length + 2 * numVars)
numBoundConflicts++;
else
numSOSConflicts++;
}
else if (conflict[c2] == Cplex.ConflictStatus.PossibleMember)
{
Console.WriteLine(" Possible : " + constraints[c2]);
if (c2 < rng.Length)
numConConflicts++;
else if (c2 < rng.Length + 2 * numVars)
numBoundConflicts++;
else
numSOSConflicts++;
}
}
Console.WriteLine("Conflict Summary:");
Console.WriteLine(" Constraint conflicts = " + numConConflicts);
Console.WriteLine(" Variable Bound conflicts = " + numBoundConflicts);
Console.WriteLine(" SOS conflicts = " + numSOSConflicts);
}
else
{
Console.WriteLine("Conflict could not be refined");
}
还可以进一步打印 Cplex 给出的建议,不过这部分我没用
Console.WriteLine("Calling FEASOPT");
// cplex.SetParam(Cplex.IntParam.FeasOptMode, 0);//change per feasopt requirements
// Relax contraints only, modify if variable bound relaxation is required
double[] lb_pref = new double[rng.Length];
double[] ub_pref = new double[rng.Length];
for (int c1 = 0; c1 < rng.Length; c1++)
{
lb_pref[c1] = 1.0;//change it per your requirements
ub_pref[c1] = 1.0;//change it per your requirements
}
if (cplex.FeasOpt(rng, lb_pref, ub_pref))
{
Console.WriteLine("Finished Feasopt");
double[] infeas = cplex.GetInfeasibilities(rng);
//Print bound changes
Console.WriteLine("Suggested Bound changes:");
for (int c3 = 0; c3 < infeas.Length; c3++)
if (infeas[c3] != 0)
Console.WriteLine(" " + rng[c3] + " : Change=" + infeas[c3]);
Console.WriteLine("Relaxed Model's obj value=" + cplex.ObjValue);
Console.WriteLine("Relaxed Model's solution status:" + cplex.GetCplexStatus());
double[] x = cplex.GetValues(lp);
for (int j = 0; j < x.Length; ++j)
Console.WriteLine("Relaxed Model's Variable Name:" + lp.NumVars[j].Name + "; Value = " + x[j]);
}
else
{
Console.WriteLine("FeasOpt failed- Could not repair infeasibilities");
}