# 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:

(1)$\begin{split}\text{Maximize: } & \\ & 1.2 x + 1.8 y + 2.1 z \\ \text{Subject to: } & \\ & 1.5 x + 1.2 y + 1.8 z \leq 2.6 \\ & 0.8 x + 0.6 y + 0.9 z \geq 1.2 \\ \text{Bounds: } & \\ & 0.1 \leq x \leq 0.6 \\ & 0.2 \leq y \leq 1.5 \\ & 0.3 \leq z \leq 2.8\end{split}$

## Example details

The source code for solving the above problem using C API of Cardinal Optimizer is shown in Listing 2:

Listing 2 lp_ex1.c
  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.

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.

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.