使用 SCIP 获得 MIP 的 LP 松弛的最佳方法

Best way to obtain the LP relaxation of a MIP using SCIP

我有一个 C++ 应用程序,可以无差别地使用 CPLEXCOIN,现在我想添加 SCIP 作为一个选择。作为功​​能的一部分,需要 运行 MIP 问题的 LP 松弛。在 CPLEX 或 COIN 中很简单,我只是创建问题,然后使用以下方法解决 LP:

并且该 LP 的解决方案与常规 MIP 的解决方案在同一位置,没有 “特殊” 也没有 “单独” 我需要查看的解决方案对象,只是常规的。

但是,我无法使用 SCIP 计算出等效项。到目前为止,我已经看到了实现这一目标的两种可能的解决方案:

    int nVars = SCIPgetNVars(m_scip);
    std::vector<SCIP_VARTYPE> varTypes(nVars, SCIP_VARTYPE_CONTINUOUS);
    SCIP_VAR **pVars = SCIPgetVars(m_scip);
    
    /* Move all binary and integral variables to continuous */
    for (size_t i = 0; i < static_cast<size_t>(nVars); i++)
    {
        SCIP_VARTYPE const varType = SCIPvarGetType(pVars[i]);
        if (varType == SCIP_VARTYPE_CONTINUOUS)
            continue;

        varTypes[i] = varType;
    
        SCIP_Bool infeasible;
        if (SCIPchgVarType(m_scip, pVars[i], SCIP_VARTYPE_CONTINUOUS, &infeasible) != SCIP_OKAY)
            throw std::runtime_error("Error changing integrality type to SCIP variable " + std::to_string(i) + " to continuos to solve LP relaxation");
    }

    /* Store parameters current value */
    SCIP_Longint oldNodes;
    if (SCIPgetLongintParam(m_scip, "limits/nodes", &oldNodes) != SCIP_OKAY)
        throw std::runtime_error("Couldn't get limits/nodes parameter in SCIP");

    int oldMaxRounds;
    if (SCIPgetIntParam(m_scip, "presolving/maxrounds", &oldMaxRounds) != SCIP_OKAY)
        throw std::runtime_error("Couldn't get presolving/maxrounds parameter in SCIP");
        
    /* Change parameters to create the "LP-like" scenario */
    if (SCIPsetLongintParam(m_scip, "limits/nodes", 1) != SCIP_OKAY)
        throw std::runtime_error("Couldn't set limits/nodes parameter in SCIP");

    if (SCIPsetIntParam(m_scip, "presolving/maxrounds", 0) != SCIP_OKAY)
        throw std::runtime_error("Couldn't set presolving/maxrounds parameter in SCIP");

    /* Solve the supposedly now LP problem */
    if (SCIPsolve(m_scip) != SCIP_OKAY)
        throw std::runtime_error("Couldn't solve LP problem in SCIP");

    /* Restore the original state of the parameters */
    if (SCIPsetLongintParam(m_scip, "limits/nodes", oldNodes) != SCIP_OKAY)
        throw std::runtime_error("Couldn't restore limits/nodes parameter in SCIP");

    if (SCIPsetIntParam(m_scip, "presolving/maxrounds", oldMaxRounds) != SCIP_OKAY)
        throw std::runtime_error("Couldn't restore presolving/maxrounds parameter in SCIP");

    /* Restore the original state of the variables */
    for (size_t i = 0; i < static_cast<size_t>(nVars); i++)
    {
        SCIP_VARTYPE const varType = varTypes[i];
        if (varType == SCIP_VARTYPE_CONTINUOUS)
            continue;

        SCIP_Bool infeasible;
        if (SCIPchgVarType(m_scip, pVars[i], varType, &infeasible) != SCIP_OKAY)
            throw std::runtime_error("Error restoring integrality type to SCIP variable " + std::to_string(i) + " from continuos after solving LP relaxation");
    }
    /* Presolve the original MIP problem. Without this step, the following SCIPgetLPI() call throws a SIGSEGV */
    if (SCIPpresolve(m_scip) != SCIP_OKAY)
        throw std::runtime_error("Couldn't presolve SCIP");

    /* Access the LP interface of SCIP */
    SCIP_LPI *pLpi = nullptr;
    if (SCIPgetLPI(m_scip, &pLpi) != SCIP_OKAY)
        throw std::runtime_error("Couldn't obtain access to LP interface of SCIP");

    /* And solve it */
    if (SCIPlpiSolvePrimal(pLpi) != SCIP_OKAY)
        throw std::runtime_error("Couldn't solve LP interface of SCIP");

虽然方法 1 似乎可行,但我有(未经证实的)感觉方法 2 更正确。但是,我不确定用于解决问题的方法 (SCIPlpiSolvePrimal()) 是否确实是正确的,或者该方法提供的解决方案是否位于我可以简单地使用 SCIPgetBestSol()(我的库没有单独的方法来获取 IP 或 LP 解决方案,因为 CPLEX 和 COIN 都不需要它)。

所以我的主要问题是:我的方法 #1 正确吗?或者它也是 slow/not 最合适的,而不是 #2 是应该使用的那个吗?如果是这样,SCIPlpiSolvePrimal() 提供的解决方案是否可以从常规 MIP 界面访问?或者,如果没有,是否有办法将其移入其中?

或者甚至还有第三种方法来解决这个问题?我读过 here,也许使用 SCIP*Dive() 是更好的方法,但我还没有找到任何相关样本。

非常感谢您的帮助。

如果您可以在 SCIP 插件中执行代码,那将是最 efficient/elegant 的方式。 (例如,您可以添加一个事件处理程序,该处理程序在第一个 LP 求解之后执行,并且只获取 LP 解)。这也是您可以使用潜水模式更改问题的某些方面的地方,只需解决(更改的)LP,然后恢复解决原始问题。 你不能不使用插件,因为你需要在解决阶段才能执行 diving mdoe。您提到的方法 2 也有同样的问题,只有在您已经处于求解模式时,LPI 才会有正确的信息。当然你可以创建一个新的 LPI 结构并基本上复制整个问题,但如果你正在解决大问题,那可能效率太低了。

如果你需要你的代码结构和现在一样(调用 LP 解决,对信息做一些事情,继续解决)你现在被选项 1 困住了(变量类型的改变不会是性能问题,但您实际上会在更改变量类型后重新开始求解)。

遗憾的是,SCIP 目前没有解决松弛问题然后停止的功能,可以选择继续整个 MIP 解决方案。

希望对您有所帮助,如果您对我的回答有任何不清楚的地方,请告诉我。