C接口
本章通过一个简单的示例演示如何使用杉数求解器的C接口,待求解的问题数学形式如 公式 1 所示:
示例解析
使用杉数求解器的C接口求解与分析上述问题的代码见 代码 2 :
1/*
2 * This file is part of the Cardinal Optimizer, all rights reserved.
3 */
4
5/*
6 * The problem to solve:
7 *
8 * Maximize:
9 * 1.2 x + 1.8 y + 2.1 z
10 *
11 * Subject to:
12 * 1.5 x + 1.2 y + 1.8 z <= 2.6
13 * 0.8 x + 0.6 y + 0.9 z >= 1.2
14 *
15 * where:
16 * 0.1 <= x <= 0.6
17 * 0.2 <= y <= 1.5
18 * 0.3 <= z <= 2.8
19 */
20
21#include "copt.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25
26int main(int argc, char* argv[])
27{
28 int errcode = 0;
29
30 copt_env* env = NULL;
31 copt_prob* prob = NULL;
32
33 // Create COPT environment
34 errcode = COPT_CreateEnv(&env);
35 if (errcode)
36 goto COPT_EXIT;
37
38 // Create COPT problem
39 errcode = COPT_CreateProb(env, &prob);
40 if (errcode)
41 goto COPT_EXIT;
42
43 /*
44 * Add variables
45 *
46 * obj: 1.2 C0 + 1.8 C1 + 2.1 C2
47 *
48 * var:
49 * 0.1 <= C0 <= 0.6
50 * 0.2 <= C1 <= 1.5
51 * 0.3 <= C2 <= 2.8
52 *
53 */
54 int ncol = 3;
55 double colcost[] = {1.2, 1.8, 2.1};
56 double collb[] = {0.1, 0.2, 0.3};
57 double colub[] = {0.6, 1.5, 1.8};
58
59 errcode = COPT_AddCols(prob, ncol, colcost, NULL, NULL, NULL, NULL, NULL, collb, colub, NULL);
60 if (errcode)
61 goto COPT_EXIT;
62
63 /*
64 * Add constraints
65 *
66 * r0: 1.5 C0 + 1.2 C1 + 1.8 C2 <= 2.6
67 * r1: 0.8 C0 + 0.6 C1 + 0.9 C2 >= 1.2
68 */
69 int nrow = 2;
70 int rowbeg[] = {0, 3};
71 int rowcnt[] = {3, 3};
72 int rowind[] = {0, 1, 2, 0, 1, 2};
73 double rowelem[] = {1.5, 1.2, 1.8, 0.8, 0.6, 0.9};
74 char rowsen[] = {COPT_LESS_EQUAL, COPT_GREATER_EQUAL};
75 double rowrhs[] = {2.6, 1.2};
76
77 errcode = COPT_AddRows(prob, nrow, rowbeg, rowcnt, rowind, rowelem, rowsen, rowrhs, NULL, NULL);
78 if (errcode)
79 goto COPT_EXIT;
80
81 // Set parameters and attributes
82 errcode = COPT_SetDblParam(prob, COPT_DBLPARAM_TIMELIMIT, 10);
83 if (errcode)
84 goto COPT_EXIT;
85 errcode = COPT_SetObjSense(prob, COPT_MAXIMIZE);
86 if (errcode)
87 goto COPT_EXIT;
88
89 // Solve problem
90 errcode = COPT_SolveLp(prob);
91 if (errcode)
92 goto COPT_EXIT;
93
94 // Analyze solution
95 int lpstat = COPT_LPSTATUS_UNSTARTED;
96 double lpobjval;
97 double* lpsol = NULL;
98 int* colstat = NULL;
99
100 errcode = COPT_GetIntAttr(prob, COPT_INTATTR_LPSTATUS, &lpstat);
101 if (errcode)
102 goto COPT_EXIT;
103
104 if (lpstat == COPT_LPSTATUS_OPTIMAL)
105 {
106 lpsol = (double*)malloc(ncol * sizeof(double));
107 colstat = (int*)malloc(ncol * sizeof(int));
108
109 errcode = COPT_GetLpSolution(prob, lpsol, NULL, NULL, NULL);
110 if (errcode)
111 goto COPT_EXIT;
112
113 errcode = COPT_GetBasis(prob, colstat, NULL);
114 if (errcode)
115 goto COPT_EXIT;
116
117 errcode = COPT_GetDblAttr(prob, COPT_DBLATTR_LPOBJVAL, &lpobjval);
118 if (errcode)
119 goto COPT_EXIT;
120
121 printf("\nObjective value: %.6f\n", lpobjval);
122
123 printf("Variable solution: \n");
124 for (int i = 0; i < ncol; ++i)
125 printf(" x[%d] = %.6f\n", i, lpsol[i]);
126
127 printf("Variable basis status: \n");
128 for (int i = 0; i < ncol; ++i)
129 printf(" x[%d]: %d\n", i, colstat[i]);
130
131 free(lpsol);
132 free(colstat);
133 }
134
135 // Write problem, solution and modified parameters to files
136 errcode = COPT_WriteMps(prob, "lp_ex1.mps");
137 if (errcode)
138 goto COPT_EXIT;
139 errcode = COPT_WriteBasis(prob, "lp_ex1.bas");
140 if (errcode)
141 goto COPT_EXIT;
142 errcode = COPT_WriteSol(prob, "lp_ex1.sol");
143 if (errcode)
144 goto COPT_EXIT;
145 errcode = COPT_WriteParam(prob, "lp_ex1.par");
146 if (errcode)
147 goto COPT_EXIT;
148
149 // Error handling
150COPT_EXIT:
151 if (errcode)
152 {
153 char errmsg[COPT_BUFFSIZE];
154
155 COPT_GetRetcodeMsg(errcode, errmsg, COPT_BUFFSIZE);
156 printf("ERROR %d: %s\n", errcode, errmsg);
157
158 return 0;
159 }
160
161 // Delete problem and environment
162 COPT_DeleteProb(&prob);
163
164 COPT_DeleteEnv(&env);
165
166 return 0;
167}
接下来我们将基于上述代码分步骤讲解求解与分析过程,详细的C接口使用说明请用户查阅 C API 参考手册 。
创建环境
对于任意求解任务,杉数求解器要求首先创建求解环境,并通过检查返回值判断环境是否创建成功。
// Create COPT environment
errcode = COPT_CreateEnv(&env);
if (errcode)
goto COPT_EXIT;
若返回值非0,则跳转至错误处理步骤并反馈相应的错误代号和错误信息,然后退出程序。
创建问题
创建求解环境成功后,用户需要创建问题,问题中将包括待求解的变量、约束等信息,通过检查返回值判断 问题是否创建成功。
// Create COPT problem
errcode = COPT_CreateProb(env, &prob);
if (errcode)
goto COPT_EXIT;
若返回值非0,则跳转至错误处理步骤并反馈相应的错误代号和错误信息,然后退出程序。
添加变量
对于线性模型,在创建变量时允许同时指定变量在目标函数中的系数、变量上下界等信息。对于本章节的示例 问题,通过下述代码创建待求解变量:
/*
* Add variables
*
* obj: 1.2 C0 + 1.8 C1 + 2.1 C2
*
* var:
* 0.1 <= C0 <= 0.6
* 0.2 <= C1 <= 1.5
* 0.3 <= C2 <= 2.8
*
*/
int ncol = 3;
double colcost[] = {1.2, 1.8, 2.1};
double collb[] = {0.1, 0.2, 0.3};
double colub[] = {0.6, 1.5, 1.8};
errcode = COPT_AddCols(prob, ncol, colcost, NULL, NULL, NULL, NULL, NULL, collb, colub, NULL);
if (errcode)
goto COPT_EXIT;
参数 ncol
指定了待创建变量的数目为3,参数 colcost
、collb
和 colub
分别
指定了待添加变量在目标函数中的系数、上下界信息。对于函数 COPT_AddCols
的其它参数如:
变量类型、变量名称均传入 NULL
表示默认为连续变量且变量名称由求解器自动生成。对于约束矩阵
系数相关参数,传入 NULL
表示留待后续其它函数调用进行添加。
类似地,若返回值非0,则跳转至错误处理步骤并反馈相应的错误代码和错误信息,然后退出程序。
添加约束
添加变量成功后,进一步添加作用于变量的约束条件。对于本章节的示例问题,其代码实现如下:
/*
* Add constraints
*
* r0: 1.5 C0 + 1.2 C1 + 1.8 C2 <= 2.6
* r1: 0.8 C0 + 0.6 C1 + 0.9 C2 >= 1.2
*/
int nrow = 2;
int rowbeg[] = {0, 3};
int rowcnt[] = {3, 3};
int rowind[] = {0, 1, 2, 0, 1, 2};
double rowelem[] = {1.5, 1.2, 1.8, 0.8, 0.6, 0.9};
char rowsen[] = {COPT_LESS_EQUAL, COPT_GREATER_EQUAL};
double rowrhs[] = {2.6, 1.2};
errcode = COPT_AddRows(prob, nrow, rowbeg, rowcnt, rowind, rowelem, rowsen, rowrhs, NULL, NULL);
if (errcode)
goto COPT_EXIT;
参数 nrow
指定了待创建的约束数目为2,参数 rowbeg
、rowcnt
、rowind
和
rowelem
分别表示以CSR格式表示的约束系数对应的稀疏矩阵。参数 rowsen
表示约束的方向,
参数 rowrhs
表示约束的右端项。对于函数 COPT_AddRows
中的其它参数均传入 NULL
。
同样,若返回值为0,则跳转至错误处理步骤并进行后续处理。
设置参数和属性
用户可以在求解问题前设置求解参数和问题相关属性,如设置求解时间限制为10秒,设置问题求解方向为 最大化,代码如下:
// Set parameters and attributes
errcode = COPT_SetDblParam(prob, COPT_DBLPARAM_TIMELIMIT, 10);
if (errcode)
goto COPT_EXIT;
errcode = COPT_SetObjSense(prob, COPT_MAXIMIZE);
if (errcode)
goto COPT_EXIT;
若设置失败,则返回非0值,并跳转至错误处理步骤进行后续处理。
求解问题
当前文所述的步骤都完成后,调用下述函数求解问题:
// Solve problem
errcode = COPT_SolveLp(prob);
if (errcode)
goto COPT_EXIT;
若返回值非0,则跳转至错误处理步骤进行后续处理。
分析结果
求解完成后,首先获取问题求解状态,若状态为找到了最优解,则进一步获取目标函数值、各变量的取值及其 基状态信息,代码实现如下:
// Analyze solution
int lpstat = COPT_LPSTATUS_UNSTARTED;
double lpobjval;
double* lpsol = NULL;
int* colstat = NULL;
errcode = COPT_GetIntAttr(prob, COPT_INTATTR_LPSTATUS, &lpstat);
if (errcode)
goto COPT_EXIT;
if (lpstat == COPT_LPSTATUS_OPTIMAL)
{
lpsol = (double*)malloc(ncol * sizeof(double));
colstat = (int*)malloc(ncol * sizeof(int));
errcode = COPT_GetLpSolution(prob, lpsol, NULL, NULL, NULL);
if (errcode)
goto COPT_EXIT;
errcode = COPT_GetBasis(prob, colstat, NULL);
if (errcode)
goto COPT_EXIT;
errcode = COPT_GetDblAttr(prob, COPT_DBLATTR_LPOBJVAL, &lpobjval);
if (errcode)
goto COPT_EXIT;
printf("\nObjective value: %.6f\n", lpobjval);
printf("Variable solution: \n");
for (int i = 0; i < ncol; ++i)
printf(" x[%d] = %.6f\n", i, lpsol[i]);
printf("Variable basis status: \n");
for (int i = 0; i < ncol; ++i)
printf(" x[%d]: %d\n", i, colstat[i]);
free(lpsol);
free(colstat);
}
问题与结果文件输出
用户可以将当前求解的问题保存为标准的MPS模型文件,以及输出变量结果文件、基状态信息文件和修改过的 参数文件,代码实现如下:
// Write problem, solution and modified parameters to files
errcode = COPT_WriteMps(prob, "lp_ex1.mps");
if (errcode)
goto COPT_EXIT;
errcode = COPT_WriteBasis(prob, "lp_ex1.bas");
if (errcode)
goto COPT_EXIT;
errcode = COPT_WriteSol(prob, "lp_ex1.sol");
if (errcode)
goto COPT_EXIT;
errcode = COPT_WriteParam(prob, "lp_ex1.par");
if (errcode)
goto COPT_EXIT;
错误处理
若在调用C接口的过程中返回值非0,则跳转至该步骤,输出错误代号与错误信息:
goto COPT_EXIT;
errcode = COPT_WriteParam(prob, "lp_ex1.par");
if (errcode)
goto COPT_EXIT;
// Error handling
COPT_EXIT:
if (errcode)
{
char errmsg[COPT_BUFFSIZE];
删除环境与问题
求解完成后,分别删除问题与环境:
// Delete problem and environment
COPT_DeleteProb(&prob);
COPT_DeleteEnv(&env);
编译与运行
为了便于用户在不同的操作系统下编译与运行示例程序,我们在Windows、Linux和MacOS平台分别提供了 Visual Studio工程和Makefile文件,具体说明如下。
Windows
对于Windows平台的用户,我们提供了Visual Studio工程文件,请用户确保计算机中已下载与安装
Visual Studio 2017。假定杉数求解器的安装路径为:'<instdir>'
,对于下载可执行安装程序
进行安装的用户,则可以直接打开路径 '<instdir>\examples\c\vsprojects'
下的
Visual Studio工程文件 lp_ex1.vcxproj
生成解决方案即可。对于下载ZIP包安装的用户,请确保
已正确配置相关环境变量,详情见 杉数求解器安装说明 。
此外,对于Windows系统,COPT还支持MinGW-w64编译器。请参考 '<instdir>\examples\c'
路径下 Makefile 文件中的相应说明使用。
Linux和MacOS
对于Linux和Mac平台的用户,我们提供了Makefile文件,请用户确保计算机中已分别安装GCC和Clang
编译器工具链,以及make工具,并正确配置杉数求解器的必要的环境变量,详情见
杉数求解器安装说明 。假定杉数求解器的安装路径为:<instdir>
,
则请用户切换到路径 '<instdir>\examples\c'
下,并在终端输入命令 make
即可编译成功。