51 个版本 (21 个稳定版)
1.8.1 | 2024 年 4 月 4 日 |
---|---|
1.7.0 | 2023 年 10 月 6 日 |
1.4.1 | 2023 年 7 月 18 日 |
1.4.0 | 2023 年 3 月 12 日 |
0.4.3 | 2021 年 3 月 19 日 |
#16 in 数学
每月下载量 28,825
在 50 个 软件包中使用 (6 个直接使用)
135KB
2.5K SLoC
good_lp
一个易于使用、性能优异且类型良好的混合整数线性规划模型器。
use std::error::Error;
use good_lp::{constraint, default_solver, Solution, SolverModel, variables};
fn main() -> Result<(), Box<dyn Error>> {
variables! {
vars:
a <= 1;
2 <= b <= 4;
} // variables can also be added dynamically
let solution = vars.maximise(10 * (a - b / 5) - b)
.using(default_solver) // multiple solvers available
.with(constraint!(a + 2 <= b))
.with(constraint!(1 + a >= 4 - b))
.solve()?;
println!("a={} b={}", solution.value(a), solution.value(b));
println!("a + b = {}", solution.eval(a + b));
Ok(())
}
特性和限制
- 线性规划。该软件包目前仅支持线性规划的定义。不能与二次函数一起使用。例如:可以最大化
3 * x + y
,但不能最大化3 * x * y
。 - 连续和整数变量。good_lp 本身支持混合整数线性规划(MILP),但并非所有底层求解器都支持整数变量。(参见 变量类型)
- 非求解器。该软件包使用其他 Rust 软件包提供求解器。good_lp 本身没有求解算法。如果遇到求解器问题,请直接向求解器报告。以下为支持的求解器列表。
贡献
欢迎拉取请求!如果您需要尚未实现的功能,请与我们联系。还可以毫不犹豫地提出问题以讨论实现。
替代方案
如果您需要非线性规划,可以使用 lp-modeler。然而,它目前在大规模问题上非常慢。
如果您不需要使用 Rust 习惯用法表达目标函数和约束条件,也可以直接使用底层求解器库,例如 coin_cbc 或 minilp。
使用示例
您可以在 resource_allocation_problem.rs
中找到资源分配问题的示例。
求解器
这个库为多个求解器提供抽象。默认情况下,它使用cbc,但您也可以使用cargo特性激活其他求解器。
求解器特性名称 | 整数变量 | 没有C编译器* | 没有额外库** | 快速 |
---|---|---|---|---|
coin_cbc |
✅ | ✅ | ❌ | ✅ |
highs |
✅ | ❌ | ✅+ | ✅ |
lpsolve |
✅ | ❌ | ✅ | ❌ |
minilp |
❌ | ✅ | ✅ | ❌ |
lp-solvers |
✅ | ✅ | ✅ | ❌ |
scip |
✅ | ✅ | ❌ | ✅ |
cplex-rs |
✅ | ❌ | ✅++ | ✅ |
clarabel |
❌ | ✅ | ✅ | ✅ |
- * 没有C编译器:仅使用cargo构建,无需安装C编译器
- ** 没有额外库:运行时无需额外库,所有依赖项都是静态链接的
- + highs自身是静态链接的,无需手动安装。但是,在某些系统上,您可能需要安装highs自身的依赖项。
- ++ cplex_rs包静态链接到本地安装的IBM ILOG CPLEX Optimizer。
要使用替代求解器,请在您的Cargo.toml
中放入以下内容
good_lp = { version = "*", features = ["your solver feature name"], default-features = false }
请注意,lpsolve
和cplex-rs
特性是互斥的,同时激活它们将导致编译错误。特别是,这意味着使用--all-features
选项构建将产生编译错误。
cbc
默认使用,性能良好,但需要在构建机器上提供cbc C库头文件,并在您想要运行程序的任何机器上提供cbc动态库。
在Ubuntu上,您可以使用以下命令安装
sudo apt-get install coinor-cbc coinor-libcbc-dev
在MacOS上,使用homebrew
brew install cbc
如果您禁用了此crate的默认特性并手动激活cbc特性,请小心。在这种情况下,您还必须激活singlethread-cbc
,除非您使用CBC_THREAD_SAFE
选项自己编译了Cbc。否则,使用多线程的Cbc将是不安全的。
minilp
minilp是一个纯Rust求解器,这意味着它无需安装任何其他内容即可直接使用。
Minilp是用纯Rust编写的,因此您可以在不安装C编译器或任何外部库的情况下使用它,但它的速度比其他求解器慢。
在调试模式下编译时表现非常差,因此当解决大型问题时,请务必以--release
模式编译您的代码。
HiGHS
HiGHS是一个免费(MIT)的并行混合整数线性规划求解器,用C++编写。它能够充分利用所有可用的处理器核心来解决一个问题。
good_lp使用highs crate来调用HiGHS。您需要C编译器,但在Linux上您不需要安装任何额外的库(它仅依赖于C++标准库)。更多信息请参阅highs-sys crate。
lpsolve
lp_solve是一个免费(LGPL)的基于改进单纯形法的线性(整数)规划求解器,用C编写。
good_lp使用lpsolve crate来调用lpsolve。您需要C编译器,但不需要安装任何额外的库。
lp-solvers
lp-solvers
特性特别之处在于它不包含任何求解器。相反,它在运行时调用其他求解器。它将给定的问题写入一个.lp
文件,并启动外部求解器命令(如gurobi
,cplex
,cbc
或glpk
)来解决问题。
此方法存在一些开销:将问题写入文件、启动外部求解器、等待其完成,然后解析其解可能需要几百毫秒。如果您不是解决几个大问题,而是解决许多小问题(例如在Web服务器中),则此方法可能不合适。
此外,您的程序最终用户将必须自行安装所需的求解器。
SCIP
SCIP是目前最快的开源混合整数规划(MIP)和混合整数非线性规划(MINLP)求解器之一。它也是一个约束整数规划和分支切割定价的框架。它允许对求解过程进行完全控制,并访问求解器内部的详细信息。
good_lp
通过其Rust接口russcip使用SCIP。要使用此功能,您需要安装SCIP。最简单的方法是从此处安装预编译包或通过运行
conda install --channel conda-forge scip
cplex-rs
IBM ILOG CPLEX Optimizer是一款商业高性能优化求解器,适用于线性、混合整数和二次规划。
good_lp使用cplex-rs crate通过安全的Rust绑定调用CPLEX,而后者又通过cplex-rs-sys crate调用CPLEX C API的原始绑定。
要使用此功能,您需要一个有效的CPLEX安装。CPLEX应安装在其默认安装目录中,或者您也可以在编译时通过CPLEX_PATH
环境变量指定其安装目录
由于cplex-rs-sys使用bindgen生成原始C绑定,因此您还需要安装clang和llvm,如bindgen要求中所示。
Clarabel
Clarabel是由Oxford Control组用Rust编写的免费(Apache 2.0许可协议)线性规划求解器。
它不支持整数变量,但速度快,易于安装。它实现了SolutionWithDual特质,允许您访问约束的影子价格(对偶值)。
变量类型
good_lp
内部将所有变量值和系数表示为f64
。它允许您使用f64
或i32
(在后一种情况下,整数将无损转换为浮点数)来表示约束。解的值也是f64
。
例如
// Correct use of f64 and i32 for Variable struct and constraints
variables! {
problem:
a <= 10.0;
2 <= b <= 4;
};
let model = problem
.maximise(b)
.using(default_solver)
.with(constraint!(a + 2 <= b))
.with(constraint!(1 + a >= 4.0 - b));
在此,a
和b
是Variable
实例,可以取连续(浮点)或整数值。约束可以使用f64
或i32
表示,如示例所示(但将例如4.0
替换为usize
变量将失败,因为usize
无法无损转换为f64
)。
解决方案的值始终是 f64
类型,无论变量是否定义为 f64
或 i32
。因此,即使您使用整数变量,解决方案对象也将存储整数变量的值作为 f64
。
例如,当打印解决方案时
// Correct use of f64 for solution values
println!("a={} b={}", solution.value(a), solution.value(b));
println!("a + b = {}", solution.eval(a + b));
// Incorrect use of i32 in combination with solution value (Will fail!)
println!("a + 1 = {}", solution.value(a) + 1); // This will cause a compilation error!
调用 solution.value(a)
和 solution.value(b)
将返回 f64
类型的值,而 solution.eval(a + b)
也将提供一个 f64
类型的值。
许可证
本库在 MIT 许可下发布。求解器本身有不同的许可证,请参阅它们的个别文档。
依赖项
~0–11MB
~116K SLoC