Gurobi教程——C++ API概览

C++ API 概述

  本节介绍 Gurobi C++ 接口。本手册首先简要概述了接口中公开的类以及这些类中最重要的方法。然后继续全面介绍所有可用的类和方法。

  如果您是 Gurobi Optimizer 的新手,我们建议您从快速入门指南或示例教程开始。这些文档提供了如何使用此处描述的类和方法的具体示例。

环境 Environments

  使用 Gurobi C++ 接口的第一步是创建一个环境对象。使用 GRBEnv 类表示环境。环境充当与一组优化运行相关的所有数据的容器。您的程序中通常只需要一个环境对象。

  对于更高级的用例,您可以使用一个空环境来创建一个未初始化的环境,然后以编程方式为您的特定要求设置所有必需的选项。有关详细信息,请参阅环境部分。

模型 Models

  您可以在环境中创建一个或多个优化模型。每个模型都表示为 GRBModel 类的一个对象。一个模型由一组决策变量(GRBVar 类的对象)、这些变量的线性或二次目标函数(使用 GRBModel::setObjective 指定)和这些变量的一组约束(GRBConstr、GRBQConstr 类的对象、 GRBSOS 或 GRBGenConstr)。每个变量都有一个关联的下限、上限和类型(连续、二进制等)。每个线性或二次约束都有一个关联的意义(小于或等于、大于或等于或等于)和右侧值。有关变量、约束和目标的更多信息,请参阅本节。

  通过构建线性表达式(GRBLinExpr 类的对象),然后指定这些表达式之间的关系(例如,要求一个表达式等于另一个)来指定线性约束。二次约束以类似的方式构建,但使用二次表达式(GRBQuadExpr 类的对象)。

  可以通过从文件一次性加载指定优化模型(使用适当的 GRBModel 构造函数),或者通过首先构造 GRBModel 类的空对象然后调用 GRBModel::addVar 或 GRBModel::addVars 添加其他变量,GRBModel::addConstr、GRBModel::addQConstr、GRBModel::addSOS 或任何 GRBModel::addGenConstrXxx 方法添加约束。模型是动态实体;您始终可以添加或删除变量或约束。

  我们经常提到优化模型的类。具有线性目标函数、线性约束和连续变量的模型是线性规划 (LP)。如果目标是二次的,则模型是二次规划 (QP)。如果任何约束是二次的,则该模型是二次约束程​​序 (QCP)。我们有时会提到 Q​​CP 的一些特殊情况:具有凸约束的 QCP、具有非凸约束的 QCP、双线性规划和二阶锥规划 (SOCP)。如果模型包含任何整数变量、半连续变量、半整数变量、特殊有序集 (SOS) 约束或一般约束,则该模型是混合整数程序 (MIP)。我们有时还会讨论 MIP 的特殊情况,包括混合整数线性规划 (MILP)、混合整数二次规划 (MIQP)、混合整数二次约束规划 (MIQCP) 和混合整数二阶锥规划 (MISOCP)。 Gurobi 优化器处理所有这些模型类。

求解模型 Solving a Model

  建立模型后,您可以调用 GRBModel::optimize 来计算解决方案。默认情况下,optimize 将使用并发优化器求解 LP 模型,使用障碍算法求解具有凸目标的 QP 模型和具有凸约束的 QCP 模型,否则使用分支切割算法。解决方案存储在模型的一组属性中。可以使用 GRBModel、GRBVar、GRBConstr、GRBQConstr、GRBSOS 和 GRBGenConstr 类上的一组属性查询方法来查询这些属性。

  Gurobi 算法会仔细跟踪模型的状态,因此对 GRBModel::optimize 的调用只会在模型上次优化后相关数据发生变化时执行进一步优化。如果您想丢弃之前计算的解决方案信息并从头开始优化而不更改模型,您可以调用 GRBModel::reset。

  求解 MIP 模型后,您可以调用 GRBModel::fixedModel 来计算关联的固定模型。此模型与原始模型相同,不同之处在于整数变量在 MIP 解决方案中固定为它们的值。如果您的模型包含 SOS 约束,则这些约束中出现的一些连续变量也可能是固定的。在某些应用程序中,计算有关此固定模型的信息(例如,对偶变量、灵敏度信息等)可能很有用,尽管您应该小心如何解释这些信息。

多种解决方案、目标和场景 Multiple Solutions, Objectives, and Scenarios

  默认情况下,Gurobi 优化器假定您的目标是为具有单个目标函数的单个模型找到一个经过验证的最优解。 Gurobi 提供了以下功能,可让您放松这些假设:

  • 解决方案池:允许您找到更多解决方案。
  • 多个场景:允许您找到多个相关模型的解决方案。
  • 多个目标:允许您指定多个目标函数并控制它们之间的权衡。

不可行模型 Infeasible Models

  如果发现模型不可行,您有几个选择。您可以尝试诊断不可行的原因,尝试修复不可行,或两者兼而有之。要获取可用于诊断不可行原因的信息,请调用 GRBModel::computeIIS 来计算不可约不一致子系统 (IIS)。此方法可用于连续模型和 MIP 模型,但您应该知道 MIP 版本可能非常昂贵。此方法填充一组 IIS 属性。

  要尝试修复不可行性,请调用 GRBModel::feasRelax 来计算模型的可行性松弛。这种放松使您可以找到一个解决方案,以最大限度地减少违反约束的幅度。

查询和修改属性 Querying and Modifying Attributes

  与 Gurobi 模型相关的大部分信息都存储在一组属性中。一些属性与模型的变量相关联,一些与模型的约束相关,还有一些与模型本身相关。举一个简单的例子,求解优化模型会导致 X 变量属性被填充。由 Gurobi 优化器计算的 X 等属性不能由用户直接修改,而其他属性,例如变量下限(LB 属性)可以。

  使用 GRBVar::get、GRBConstr::get、GRBQConstr::get、GRBSOS::get、GRBGenConstr::get 或 GRBModel::get 查询属性,并使用 GRBVar::set、GRBConstr::set、GRBQConstr 进行修改: :set、GRBGenConstr::set 或 GRBModel::set。属性按类型分组为一组枚举(GRB_CharAttr、GRB_DoubleAttr、GRB_IntAttr、GRB_StringAttr)。 get() 和 set() 方法被重载,所以属性的类型决定了返回值的类型。因此, constr.get(GRB.DoubleAttr.RHS) 返回一个双精度,而 constr.get(GRB.CharAttr.Sense) 返回一个字符。

  如果您希望检索一组变量或约束的属性值,通常在关联的 GRBModel 对象上使用数组方法更有效。方法 GRBModel::get 包括允许您查询或修改变量或约束数组的属性值的签名。

属性的完整列表可以在属性部分中找到 Additional Model Modification Information

  对现有模型的大多数修改都是通过属性接口完成的(例如,更改变量边界、约束右侧等)。主要的例外是对约束矩阵和目标函数的修改。

  可以通过几种方式修改约束矩阵。第一种是在 GRBModel 对象上调用 chgCoeffs 方法来更改各个矩阵系数。此方法可用于修改现有非零的值,将现有非零设置为零,或创建新的非零。当您从模型中删除变量或约束时(通过 GRBModel::remove 方法),约束矩阵也会被修改。与已删除的约束或变量关联的非零值与约束或变量本身一起被删除。

  模型目标函数也可以通过几种方式进行修改。最简单的方法是构建一个捕获目标函数的表达式(GRBLinExpr 或 GRBQuadExpr 对象),然后将该表达式传递给方法 GRBModel::setObjective。如果您想修改目标,您可以简单地使用新的 GRBLinExpr 或 GRBQuadExpr 对象再次调用 setObjective。

  对于线性目标函数,setObjective 的替代方法是使用 Obj 变量属性来修改各个线性目标系数。

  如果您的变量具有分段线性目标,您可以使用 GRBModel::setPWLObj 方法指定它们。为每个相关变量调用一次此方法。 Gurobi 单纯形求解器包括对凸分段线性目标函数的算法支持,因此对于连续模型,您应该会看到使用此功能可显着提高性能。要清除先前指定的分段线性目标函数,只需将相应变量的 Obj 属性设置为 0。

延迟更新 Lazy Updates

  关于 Gurobi 优化器中的模型修改需要注意的一个重要事项是它是以惰性方式执行的,这意味着修改不会立即影响模型。相反,它们会排队并稍后应用。如果您的程序只是简单地创建一个模型并解决它,您可能永远不会注意到这种行为。但是,如果您在应用修改之前询问有关模型的信息,则延迟更新方法的详细信息可能与您相关。

  正如我们刚刚提到的,模型修改(绑定更改、右侧更改、目标更改等)被放置在队列中。这些排队的修改可以三种不同的方式应用于模型。第一个是通过显式调用 GRBModel::update。第二个是调用 GRBModel::optimize。第三个是调用 GRBModel::write 来写出模型。第一种情况为您提供了对何时应用修改的细粒度控制。第二个和第三个假设您希望在优化模型或将其写入磁盘之前应用所有待处理的修改。

  为什么 Gurobi 界面会以这种方式运行?有几个原因。首先是这种方法使得对模型执行多次修改变得更加容易,因为模型在修改之间保持不变。第二个是处理模型修改可能很昂贵,特别是在计算服务器环境中,修改需要机器之间的通信。因此,准确了解何时应用这些修改很有用。一般来说,如果您的程序需要对模型进行多次修改,您应该分阶段进行,在其中进行一组修改,然后更新,然后进行更多修改,然后再次更新,等等。在每个人之后更新修改可能非常昂贵。

  如果您忘记调用更新,您的程序不会崩溃。您的查询将仅返回上次更新时所请求数据的值。如果您尝试查询的对象不存在,您将获得 NOT_IN_MODEL 异常。

  自早期 Gurobi 版本以来,延迟更新的语义发生了变化。虽然绝大多数程序不受此更改的影响,但如果遇到问题,您可以使用 UpdateMode 参数恢复到早期的行为。

管理参数 Managing Parameters

  Gurobi 优化器提供了一组参数,允许您控制优化过程的许多细节。可行性和最优性容差、算法选择、探索 MIP 搜索树的策略等因素可以通过在开始优化之前修改 Gurobi 参数来控制。参数的类型可以是 int、double 或 string。

  设置参数的最简单方法是通过模型对象上的 GRBModel::set 方法。类似地,可以使用 GRBModel::get 查询参数值。

  也可以使用 GRBEnv::set 在 Gurobi 环境对象上设置参数。请注意,每个模型在创建时都会获得自己的环境副本,因此对原始环境的参数更改对现有模型没有影响。

  您可以使用 GRBEnv::readParams 从文件中读取一组参数设置,或使用 GRBEnv::writeParams 写入一组更改的参数。

  我们还包括一个自动参数调整工具,该工具探索许多不同的参数更改集,以找到提高性能的集。您可以调用 GRBModel::tune 来调用模型上的调整工具。有关详细信息,请参阅参数调整工具部分。

  Gurobi 参数的完整列表可以在参数部分找到。

内存管理 Memory Management

  在 C++ 程序中必须始终考虑内存管理。特别是,Gurobi 库和用户程序共享同一个 C++ 堆,因此用户必须了解 Gurobi 库如何使用该堆的某些方面。使用 Gurobi 优化器时管理内存的基本规则如下:

  • 与其他动态分配的 C++ 对象一样,GRBEnv 或 GRBModel 对象应该使用关联的析构函数来释放。换句话说,给定一个 GRBModel 对象 m,当您不再使用 m 时,您应该调用 delete m。
  • 与模型关联的对象(例如 GRBConstr、GRBQConstr、GRBSOS、GRBGenConstr 和 GRBVar 对象)由模型管理。特别是,删除模型将删除所有关联的对象。同样,从模型中移除对象(使用 GRBModel::remove)也会删除该对象。
  • 一些 Gurobi 方法返回一个对象或值数组。例如,GRBModel::addVars 返回一个 GRBVar 对象数组。释放返回的数组是用户的责任(使用 delete[])。参考手册指出方法何时返回堆分配的结果。

  这些规则的一个结果是,一旦对象被释放,您必须小心不要使用它。对于显式调用析构函数的环境和模型,这无疑是非常清楚的,但对于约束和变量可能不太清楚,因为在删除关联模型时会隐式删除它们。

监控进度 - 记录和回调 Monitoring Progress - Logging and Callbacks

  可以通过 Gurobi 日志记录优化的进度。默认情况下,Gurobi 会将输出发送到屏幕。一些简单的控件可用于修改默认的日志记录行为。如果您想将输出定向到文件以及屏幕,请在 GRBEnv 构造函数中指定日志文件名。如果您希望在创建环境对象后将日志重定向到不同的文件,则可以修改 LogFile 参数。可以使用 DisplayInterval 参数控制记录输出的频率,并且可以使用 OutputFlag 参数完全关闭记录。 Gurobi 日志文件的详细描述可以在日志部分找到。

  更详细的进度监控可以通过 GRBCallback 类来完成。 GRBModel::setCallback 方法允许您从 Gurobi 优化器接收定期回调。为此,您可以对 GRBCallback 抽象类进行子类化,并在该类上编写自己的 callback() 方法。您可以在回调中调用 GRBCallback::getDoubleInfo、GRBCallback::getIntInfo、GRBCallback::getStringInfo 或 GRBCallback::getSolution,以获取有关优化状态的其他信息。

修改求解器行为 - 回调 Modifying Solver Behavior - Callbacks

  回调也可用于修改 Gurobi 优化器的行为。最简单的控制回调是 GRBCallback::abort,它要求优化器在最早的方便点终止。方法 GRBCallback::setSolution 允许您在 MIP 模型的求解过程中注入可行的解决方案(或部分解决方案)。方法 GRBCallback::addCut 和 GRBCallback::addLazy 允许您分别在 MIP 优化期间添加切割平面和惰性约束。方法 GRBCallback::stopOneMultiObj 允许您在不停止分层优化过程的情况下中断多目标 MIP 问题中优化步骤之一的优化过程。

批量优化 Batch Optimization

  Gurobi Compute Server 使程序能够将优化计算卸载到专用服务器上。 Gurobi Cluster Manager 在此基础上添加了许多附加功能。一个重要的,批量优化,允许您使用您的客户端程序构建优化模型,将其提交到计算服务器集群(通过集群管理器),然后检查模型的状态并检索其解决方案。您可以使用 Batch 对象来更轻松地处理批处理。有关批次的详细信息,请参阅批次优化部分。

错误处理 Error Handling

  Gurobi C++ 库中的所有方法都可以抛出 GRBException 类型的异常。发生异常时,可以通过检索错误代码(使用方法 GRBException::getErrorCode)或检索异常消息(使用方法 GRBException::getMessage)来获取有关错误的附加信息。可以在错误代码部分找到可能的错误返回代码列表。

猜你喜欢

转载自blog.csdn.net/qq_16775293/article/details/122707172