Java接口
本章通过一个简单的示例演示如何使用杉数求解器的Java接口。简单来说,本例演示如何 创建环境和建立模型,添加变量和约束,然后求解的过程。最后求解后分析的步骤也包括在内。
待求解的线性问题数学公式如下所示:
注意到上述问题其实和 C接口 里通过建模和求解来演示的问题是同一个。
示例解析
使用杉数求解器的 Java 接口求解与分析上述问题的代码如下。
1/*
2 * This file is part of the Cardinal Optimizer, all rights reserved.
3 */
4import copt.*;
5
6/*
7 * This Java example solves the following LP model:
8 *
9 * Maximize:
10 * 1.2 x + 1.8 y + 2.1 z
11 *
12 * Subject to:
13 * 1.5 x + 1.2 y + 1.8 z <= 2.6
14 * 0.8 x + 0.6 y + 0.9 z >= 1.2
15 *
16 * where:
17 * 0.1 <= x <= 0.6
18 * 0.2 <= y <= 1.5
19 * 0.3 <= z <= 2.8
20 */
21public class Lp_ex1 {
22 public static void main(final String argv[]) {
23 try {
24 Envr env = new Envr();
25 Model model = env.createModel("lp_ex1");
26
27 /*
28 * Add variables x, y, z
29 *
30 * obj: 1.2 x + 1.8 y + 2.1 z
31 *
32 * var:
33 * 0.1 <= x <= 0.6
34 * 0.2 <= y <= 1.5
35 * 0.3 <= z <= 2.8
36 */
37 Var x = model.addVar(0.1, 0.6, 1.2, copt.Consts.CONTINUOUS, "x");
38 Var y = model.addVar(0.2, 1.5, 1.8, copt.Consts.CONTINUOUS, "y");
39 Var z = model.addVar(0.3, 2.8, 2.1, copt.Consts.CONTINUOUS, "z");
40
41 /*
42 * Add two constraints using linear expression
43 *
44 * r0: 1.5 x + 1.2 y + 1.8 z <= 2.6
45 * r1: 0.8 x + 0.6 y + 0.9 z >= 1.2
46 */
47 Expr e0 = new Expr(x, 1.5);
48 e0.addTerm(y, 1.2);
49 e0.addTerm(z, 1.8);
50 model.addConstr(e0, copt.Consts.LESS_EQUAL, 2.6, "r0");
51
52 Expr e1 = new Expr(x, 0.8);
53 e1.addTerm(y, 0.6);
54 e1.addTerm(z, 0.9);
55 model.addConstr(e1, copt.Consts.GREATER_EQUAL, 1.2, "r1");
56
57 // Set parameters and attributes
58 model.setDblParam(copt.DblParam.TimeLimit, 10);
59 model.setObjSense(copt.Consts.MAXIMIZE);
60
61 // Solve problem
62 model.solve();
63
64 // Output solution
65 if (model.getIntAttr(copt.IntAttr.HasLpSol) != 0) {
66 System.out.println("\nFound optimal solution:");
67 VarArray vars = model.getVars();
68 for (int i = 0; i < vars.size(); i++) {
69 Var x = vars.getVar(i);
70 System.out.println(" " + x.getName() + " = " + x.get(copt.DblInfo.Value));
71 }
72 System.out.println("Obj = " + model.getDblAttr(copt.DblAttr.LpObjVal));
73 }
74
75 System.out.println("\nDone");
76 } catch (CoptException e) {
77 System.out.println("Error Code = " + e.getCode());
78 System.out.println(e.getMessage());
79 }
80 }
81}
接下来我们将基于上述代码分步骤讲解求解与分析过程。
导入COPT的Java类
使用杉数求解器的 Java 接口,需要首先导入 COPT 的 Java 类
import copt.*;
创建求解环境和建模
对于任意使用杉数求解器 Java 接口的应用程序,求解步骤的第一步是创建求解环境。从求解环境 出发,用户可以创建一个或者多个模型。注意到每个模型都对应了一个实际的求解问题以及 相关数据,比如参数设置等。
如果用户需要求解多个问题,用户即可以在同一个模型里对多个问题逐个依次求解,也可以在同一 求解环境里创建多个模型来求解。
Envr env = new Envr();
Model model = env.createModel("lp_ex1");
上述代码对 Java 求解环境实例化,并创建了一个名为 “lp_ex1” 的模型对象。
添加变量
接下来,我们演示在模型里添加多个变量。杉数求解器提供包括addVar()和addVars()在内的 多种途径来添加变量。注意到变量不是独立存在,总是和某个模型关联的。
/*
* Add variables x, y, z
*
* obj: 1.2 x + 1.8 y + 2.1 z
*
* var:
* 0.1 <= x <= 0.6
* 0.2 <= y <= 1.5
* 0.3 <= z <= 2.8
*/
Var x = model.addVar(0.1, 0.6, 1.2, copt.Consts.CONTINUOUS, "x");
Var y = model.addVar(0.2, 1.5, 1.8, copt.Consts.CONTINUOUS, "y");
Var z = model.addVar(0.3, 2.8, 2.1, copt.Consts.CONTINUOUS, "z");
上述代码中addVar()的第一和第二个参数分别是指变量的下界和上界;第三个参数是指目标函数中的系数; 第四个参数是指变量的类型。例子里所有变量的类型都是连续形的。最后一个参数是指变量名。
为了适应不同的调用需求,杉数求解器实现了addVar()的不同形参的几种重载。请参看 Java API 参考手册里的章节 Java建模类 。
添加约束
接下来,我们演示在模型里添加多个约束。和变量一样,约束也总是和某个模型关联的。 杉数求解器提供包括addConstr()和addConstrs()在内的多种途径来添加约束。
/*
* Add two constraints using linear expression
*
* r0: 1.5 x + 1.2 y + 1.8 z <= 2.6
* r1: 0.8 x + 0.6 y + 0.9 z >= 1.2
*/
Expr e0 = new Expr(x, 1.5);
e0.addTerm(y, 1.2);
e0.addTerm(z, 1.8);
model.addConstr(e0, copt.Consts.LESS_EQUAL, 2.6, "r0");
Expr e1 = new Expr(x, 0.8);
e1.addTerm(y, 0.6);
e1.addTerm(z, 0.9);
model.addConstr(e1, copt.Consts.GREATER_EQUAL, 1.2, "r1");
上述代码的约束都是通过先建立线性表达式的对象,然后再通过addTerm()添加变量和对应 系数的方式来逐步建立最终的线性表达式。
设置参数和属性
在求解问题之前,还可以通过setDblParam()等方法设置模型的参数和属性。
// Set parameters and attributes
model.setDblParam(copt.DblParam.TimeLimit, 10);
model.setObjSense(copt.Consts.MAXIMIZE);
这里,调用setDblParam()来设置copt.DblParam.TimeLimit参数值,使得求解器在最多执行10秒 后超时退出;调用setObjSense()并使用copt.Consts.MAXIMIZE参数,设置求解目标为最大化。
求解问题
到此为止,我们已经建好了模型。接下来,可以求解问题。
// Solve problem
model.solve();
这一步之后,问题已经被求解,并在内部保存了结果,包括求解状态,最优解等属性值。
输出结果
在问题被求解之后,可以通过查询不同的属性值来实现不同的目的。
// Output solution
if (model.getIntAttr(copt.IntAttr.HasLpSol) != 0) {
System.out.println("\nFound optimal solution:");
VarArray vars = model.getVars();
for (int i = 0; i < vars.size(); i++) {
Var x = vars.getVar(i);
System.out.println(" " + x.getName() + " = " + x.get(copt.DblInfo.Value));
}
System.out.println("Obj = " + model.getDblAttr(copt.DblAttr.LpObjVal));
}
System.out.println("\nDone");
上述代码中,我们首先查询模型的属性值copt.IntAttr.HasLpSol来知道是否生成了LP最优解; 再查询变量的属性值copt.DblInfo.Value来获得这个变量的值;然后查询模型的属性值 copt.DblAttr.LpObjVal来获得目标函数的最优值。
关于模型、变量、约束的属性名和类型,请参看 Java API 参考手册里的章节 Java常量类 。
错误处理
杉数求解器Java接口的错误处理使用Java自身的异常处理机制。例子中,整个求解过程都嵌入在 try块里,中间的任意求解错误都会被catch块捕获并显示。
} catch (CoptException e) {
System.out.println("Error Code = " + e.getCode());
System.out.println(e.getMessage());
}
编译与运行
本章节演示的例子,我们已经放入在COPT安装包里,方便用户尝试编译并运行。具体请参看
安装路径下的文件夹 $COPT_HOME/examples/java
。这个文件夹包括了例子的Java代码,
以及一个脚本文件,可以用来直接编译并运行。
本例子可以在任意支持Java的平台上运行。首先需要在使用平台上安装Java 8或者更高版本。
Java例子细节
运行Java例子最简单的方式是先在console或者terminal窗口下进入安装路径的Java例子文件夹
$COPT_HOME/examples/java
,然后执行命令 'sh run.sh'
。
具体来说,编译例子依赖于COPT的Java包 copt_java.jar
。它不仅定义了杉数求解器的所有Java类,
还间接加载了两个相关的COPT动态库,分别是Windows下的 coptjniwrap.dll
和 copt_cpp.dll
,
Linux下的 libcoptjniwrap.so
和 libcopt_cpp.so
,Mac下的 libcoptjniwrap.dylib
和 libcopt_cpp.dylib
。这里,动态库 coptjniwrap
是使用swig对JNI进行包装,用来衔接
COPT的Java包和COPT的C++核心库 copt_cpp
。这个核心库真正定义了杉数求解器的所有类,接口以及
属性和参数常量。所以用户需要确保上述动态库已经安装在合适的路径下,可以在运行中被加载。
一般来说,用户只要正确安装了COPT安装包(设置好了动态库路径),并配置了有效的授权文档,就可以 正常执行这个Java例子。安装和授权细节请参考 安装说明 。