在主流程控制循环中使用 opl.end() 时如何修复错误

How to fix errors when using opl.end() in main flow control loop

我在 main 流控制块内使用循环批处理 运行 数以万计的测试。循环的每个 运行 使用指定的 .dat 数据文件和 .mod 模型文件(与批处理 运行ner .mod 分开生成一个新的 OPL 模型仅包含 main 流控制块的文件)。

因为循环的每个 运行 都会创建一个新模型,所以内存使用会不断增加,直到我不使用 .end() 语句关闭时最终导致 IDE 崩溃为每个测试创建的所有数据结构。虽然大多数数据结构都可以很好地关闭,但我发现 opl.end(); 会使测试仪崩溃。

如何在不使测试仪崩溃的情况下关闭 OPL 模型?

我已经在 macOS 和 Windows 上尝试了 oplide 应用程序和 oplrun 命令行界面(尽管我需要 运行 在 [=54= 上进行测试] 到底)。我正在使用 CPLEX Optimization Studio 12.9。

我使用以下方法创建对象:

// Create new model
var source = new IloOplModelSource("model-file.mod");
var def = new IloOplModelDefinition(source);
var cp = new IloCP();
var opl = new IloOplModel(def,cp);

// Set next test case as data source
var testCasePath = thisOplModel.TestCaseFilenames[testNumber];
var data = new IloOplDataSource(testCasePath);
opl.addDataSource(data);

// Generate model
opl.generate();

我尝试在完成后使用以下方法关闭它们:

data.end();
opl.end(); // Causes it to crash
cp.end();
def.end();
source.end();

编辑以添加更具体的信息: 我正在 运行解决 RCPSP 调度问题,使用我自己的模型,它是内置示例的修改版本。

这是我正在使用的代码的精简版。我创建了一个 运行 配置,只包含这批 运行ner .mod 文件,模型文件和它引用的数据文件存储在别处:

using CP;

// Import test case filename data
int NumberOfFiles = 3;
range FileIDs = 0..NumberOfFiles-1;
string TestCaseFilenames[FileIDs] = ["TestCase-01.dat", "TestCase-02.dat", "TestCase-03.dat"];

main {
  var testCaseDirectory = "~/Desktop/TestCases/";

  // Solve each test case in the list
  for (var testNumber = 0; testNumber < thisOplModel.NumberOfFiles; testNumber++) {
    // Create new model
    var source = new IloOplModelSource("RCPSP.mod");
    var def = new IloOplModelDefinition(source);
    var cp = new IloCP();
    var opl = new IloOplModel(def,cp);

    // Set CP parameters
    cp.param.TimeLimit = 5; // Number is in seconds
    cp.param.Workers = 4; // Number of computer cores used
    cp.param.TimeMode = "ElapsedTime"; // How to report time 
    cp.param.LogVerbosity = "Quiet"; // How much to write to the engine log

    // Set next test case as data source
    var testCaseFilename = thisOplModel.TestCaseFilenames[testNumber];
    var testCasePath = testCaseDirectory + testCaseFilename;
    var data = new IloOplDataSource(testCasePath);
    opl.addDataSource(data);

    // Generate model
    opl.generate();

    // Report test case name to script log for progress visibility
    writeln(testNumber+1 + ") Solving " + opl.TestCaseFilename + "...");

    // Solve model
    if (cp.solve()) { // Successfully found solution
      // Run solution validation code in model file's post-processing execute statement
        // If a solution is invalid, the entire bath runner will fail with an error at that test and line
      opl.postProcess();

      // Report some solve-dependent results to oplide log for visibility
      writeln(" - UB = " + cp.getObjValue() + ", LB = " + cp.getObjBound()); // Makespan
    } else { // Failed to find solution
      // Report no solution to oplide log for visibility
      writeln(" - No solution found.")
      writeln(" - Lower Bound = " + cp.getObjBound());
    }

    // Report some results to script log for visibility
    writeln(" - Status = " + cp.status); // Solver status
    writeln(" - Solve Time = " + cp.info.SolveTime + " sec"); // Time spent solving

    // End processes to prevent memory leaks
    data.end();
    opl.end(); // Causes it to crash
    cp.end();
    def.end();
    source.end();
  }

  // Confirm to user that tests are complete
  writeln();
  writeln("All done!");
}

输出应该是这样的(注释掉 opl.end() 行):

1) Solving TestCase-01.dat...
 - UB = 48, LB = 48
 - Status = 2
 - Solve Time = 1.299999952 sec
2) Solving TestCase-02.dat...
 - UB = 65, LB = 36
 - Status = 1
 - Solve Time = 5.019999981 sec
3) Solving TestCase-03.dat...
 - No solution found.
 - LB = 1
 - Status = 0
 - Solve Time = 5.010000229 sec

All done!

相反,它只记录此输出:

1) Solving TestCase-01.dat...
 - UB = 48, LB = 48
 - Status = 2
 - Solve Time = 1.299999952 sec

然后弹出一个 window 错误信息:

Oplrun process is not responding, you must relaunch the Run Configuration.

我们通过电子邮件解决了这个问题。崩溃的原因是 OPL 中的错误。代码中有问题的部分是这样的:

tuple T {
  key int id;
  int intArray[0..0];
}

{T} tuples = ...;

T tupleById[1..1];
execute {
  for (var t in tuples)
    tupleById[t.id] = t;
}

问题是在脚本中分配包含数组或集合成员的元组。这会触发 OPL 中的错误,最终导致崩溃。解决方法是初始化 tupleById 而无需通过

编写脚本
T tupleById[id in 1..1] = first({ t | t in tuples : t.id == id});

这里的技巧是所有生成的集合都是单例,因为 id 是唯一的。然后我们使用 first() 函数从单例集中提取第一个元素以获得普通元组。