C Interface
This chapter illustrate the use of C interface of Cardinal Optimizer through a simple C example. The problem to solve is shown in Eq. 1:
Example details
The source code for solving the above problem using C API of Cardinal Optimizer is shown in Listing 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}
We will explain how to use the C API step by step based on code above, please refer to C API Reference for detailed usage of C API.
Creating the environment
To solve any problem with Cardinal Optimizer, users are required to create optimization environment first, and check if it was created successfully by checking the return value:
// Create COPT environment
errcode = COPT_CreateEnv(&env);
if (errcode)
goto COPT_EXIT;
If non-zero value was returned, it will jump to error reporting code block for detailed information and exit.
Creating the problem
Once the optimization environment was successfully created, users will need to create problem then, the problem is the main structure that consists of variables, constraints etc. Users need to check the return value too.
// Create COPT problem
errcode = COPT_CreateProb(env, &prob);
if (errcode)
goto COPT_EXIT;
If non-zero value was returned, it will jump to error reporting code block for detailed information and exit.
Adding variables
For linear problem, C API allows users to specify costs of variables in objective, and lower and upper bound simultaneously. For the problem above, we use code below to create variables:
/*
* 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;
The argument ncol
specify that the number of variables to create is 3, while
the argument colcost
, collb
and colub
specify the costs in objective,
lower and upper bound respectively. Regarding other arguments of COPT_AddCols
for specifing variables types and names, we just pass NULL
to them, which
means all variables are continuous and names are automatically generated by
the Cardinal Optimizer. For the remaining arguments, we passed NULL
too
for further action.
Similarly, if non-zero value was returned, it will jump to error reporting code block for detailed information and exit.
Adding constraints
The next step to do after adding variables successfully is to add constraints to problem. For the problem above, the implementation is shown below:
/*
* 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;
The argument nrow
specifies that the number of constraints to create is 2,
while argument rowbeg
, rowcnt
, rowind
and rowelem
define the
coefficient matrix in CSR format. The argument rowsen
represents the sense
of constraints, while argument rowrhs
specifies the right hand side of
constraints. For remaining arguments in COPT_AddRows
, we simply pass NULL
to them.
If the return value is non-zero, then it jump to error reporting code block for detailed information and exit.
Setting parameters and attributes
Users are allowed to set parameters and attributes of problem before solving. For example, to set the time limit to 10 seconds, and to set the optimization direction to maximization, the code is shown below:
// 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;
If non-zero value was returned, then it will jump to error reporting code block for detailed information and exit.
Solve the problem
The next step to do is to solve the problem using code beblow:
// Solve problem
errcode = COPT_SolveLp(prob);
if (errcode)
goto COPT_EXIT;
Non-zero return value indicates unsuccessful solve and jump to error reporting code block for detailed information and exit.
Analyze the solution
Once the solving process was finished, check the solution status first. If it claimed to have found the optimal solution, then use code below to obtain objective value, variables’ solution and basis status:
// 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);
}
Write problem and solution
Users are allowed not only to save the problem to solve to standard MPS file, but also the solution, basis status and modified parameters to files:
// 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;
Error handling
The error handling block report error code and message by checking if the return value was non-zero:
// Error handling
COPT_EXIT:
if (errcode)
{
char errmsg[COPT_BUFFSIZE];
COPT_GetRetcodeMsg(errcode, errmsg, COPT_BUFFSIZE);
printf("ERROR %d: %s\n", errcode, errmsg);
return 0;
}
Delete environment and problem
Before exiting, delete problem and environment respectively:
// Delete problem and environment
COPT_DeleteProb(&prob);
COPT_DeleteEnv(&env);
Build and run
To ease the work for running the example for users on different operating systems, we provide Visual Studio project and Makefile for Windows, Linux and MacOS respectively, details are shown below.
Windows
For users on Windows platform, we provide Visual Studio project, all users are
required to install Visual Studio 2017 beforehand. Assume that the installation
directory is: '<instdir>'
, users that install the Cardinal Optimizer with
executable installer can change directory to '<instdir>\examples\c\vsprojects'
and open the Visual Studio project lp_ex1.vcxproj
to build the solution.
Users that install the Cardinal Optimizer using ZIP-format archive should make
sure that all required environment variables are set correctly, see
Install Guide for Cardinal Optimizer for details.
Linux and MacOS
For users on Linux or MacOS, we provide Makefile to build the example. Please
install GCC toolchain for Linux and Clang toolchain for MacOS, together with
the make
utility beforehand. What’s more, users should make sure also that
all required environment variables are set correctly, see
Install Guide for Cardinal Optimizer for details. Let’s
assume that the installation directory of Cardinal Optimizer is '<instdir>'
,
then users need to change directory to '<instdir>\examples\c'
and execute
command make
in terminal.