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// The problem to solve:
6//
7// Maximize:
8// 1.2 x + 1.8 y + 2.1 z
9//
10// Subject to:
11// 1.5 x + 1.2 y + 1.8 z <= 2.6
12// 0.8 x + 0.6 y + 0.9 z >= 1.2
13//
14// where:
15// 0.1 <= x <= 0.6
16// 0.2 <= y <= 1.5
17// 0.3 <= z <= 2.8
18
19#include "copt.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23
24int main(int argc, char *argv[]) {
25 int errcode = 0;
26
27 copt_env *env = NULL;
28 copt_prob *prob = NULL;
29
30 // Create COPT environment
31 errcode = COPT_CreateEnv(&env);
32 if (errcode) goto COPT_EXIT;
33
34 // Create COPT problem
35 errcode = COPT_CreateProb(env, &prob);
36 if (errcode) goto COPT_EXIT;
37
38 // Add variables
39 //
40 // obj: 1.2 C0 + 1.8 C1 + 2.1 C2
41 //
42 // var:
43 // 0.1 <= C0 <= 0.6
44 // 0.2 <= C1 <= 1.5
45 // 0.3 <= C2 <= 2.8
46 //
47 int ncol = 3;
48 double colcost[] = {1.2, 1.8, 2.1};
49 double collb[] = {0.1, 0.2, 0.3};
50 double colub[] = {0.6, 1.5, 1.8};
51
52 errcode = COPT_AddCols(prob, ncol, colcost, NULL, NULL, NULL, NULL,
53 NULL, collb, colub, NULL);
54 if (errcode) goto COPT_EXIT;
55
56 // Add constraints
57 //
58 // r0: 1.5 C0 + 1.2 C1 + 1.8 C2 <= 2.6
59 // r1: 0.8 C0 + 0.6 C1 + 0.9 C2 >= 1.2
60 //
61 int nrow = 2;
62 int rowbeg[] = {0, 3};
63 int rowcnt[] = {3, 3};
64 int rowind[] = {0, 1, 2, 0, 1, 2};
65 double rowelem[] = {1.5, 1.2, 1.8, 0.8, 0.6, 0.9};
66 char rowsen[] = {COPT_LESS_EQUAL, COPT_GREATER_EQUAL};
67 double rowrhs[] = {2.6, 1.2};
68
69 errcode = COPT_AddRows(prob, nrow, rowbeg, rowcnt, rowind, rowelem,
70 rowsen, rowrhs, NULL, NULL);
71 if (errcode) goto COPT_EXIT;
72
73 // Set parameters and attributes
74 errcode = COPT_SetDblParam(prob, COPT_DBLPARAM_TIMELIMIT, 10);
75 if (errcode) goto COPT_EXIT;
76 errcode = COPT_SetObjSense(prob, COPT_MAXIMIZE);
77 if (errcode) goto COPT_EXIT;
78
79 // Solve problem
80 errcode = COPT_SolveLp(prob);
81 if (errcode) goto COPT_EXIT;
82
83 // Analyze solution
84 int lpstat = COPT_LPSTATUS_UNSTARTED;
85 double lpobjval;
86 double *lpsol = NULL;
87 int *colstat = NULL;
88
89 errcode = COPT_GetIntAttr(prob, COPT_INTATTR_LPSTATUS, &lpstat);
90 if (errcode) goto COPT_EXIT;
91
92 if (lpstat == COPT_LPSTATUS_OPTIMAL) {
93 lpsol = (double *) malloc(ncol * sizeof(double));
94 colstat = (int *) malloc(ncol * sizeof(int));
95
96 errcode = COPT_GetLpSolution(prob, lpsol, NULL, NULL, NULL);
97 if (errcode) goto COPT_EXIT;
98
99 errcode = COPT_GetBasis(prob, colstat, NULL);
100 if (errcode) goto COPT_EXIT;
101
102 errcode = COPT_GetDblAttr(prob, COPT_DBLATTR_LPOBJVAL, &lpobjval);
103 if (errcode) goto COPT_EXIT;
104
105 printf("\nObjective value: %.6f\n", lpobjval);
106
107 printf("Variable solution: \n");
108 for (int i = 0; i < ncol; ++i)
109 printf(" x[%d] = %.6f\n", i, lpsol[i]);
110
111 printf("Variable basis status: \n");
112 for (int i = 0; i < ncol; ++i)
113 printf(" x[%d]: %d\n", i, colstat[i]);
114
115 free(lpsol);
116 free(colstat);
117 }
118
119 // Write problem, solution and modified parameters to files
120 errcode = COPT_WriteMps(prob, "lp_ex1.mps");
121 if (errcode) goto COPT_EXIT;
122 errcode = COPT_WriteBasis(prob, "lp_ex1.bas");
123 if (errcode) goto COPT_EXIT;
124 errcode = COPT_WriteSol(prob, "lp_ex1.sol");
125 if (errcode) goto COPT_EXIT;
126 errcode = COPT_WriteParam(prob, "lp_ex1.par");
127 if (errcode) goto COPT_EXIT;
128
129 // Error handling
130COPT_EXIT:
131 if (errcode) {
132 char errmsg[COPT_BUFFSIZE];
133
134 COPT_GetRetcodeMsg(errcode, errmsg, COPT_BUFFSIZE);
135 printf("ERROR %d: %s\n", errcode, errmsg);
136
137 return 0;
138 }
139
140 // Delete problem and environment
141 COPT_DeleteProb(&prob);
142
143 COPT_DeleteEnv(&env);
144
145 return 0;
146}
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.