C++ Interface
This chapter walks through a simple C++ example to illustrate the use of the COPT C++ interface. In short words, the example creates an environment, builds a model, add variables and constraints, optimizes it, and then outputs the optimal objective value.
The example solves the following linear problem:
Note that this is the same problem that was modelled and optimized in chapter of C Interface.
Example details
Below is the source code solving the above problem using COPT C++ interface.
1/*
2 * This file is part of the Cardinal Optimizer, all rights reserved.
3 */
4#include "coptcpp_pch.h"
5
6using namespace std;
7
8/*
9 * This example solves the following LP model:
10 *
11 * Maximize:
12 * 1.2 x + 1.8 y + 2.1 z
13 *
14 * Subject to:
15 * R0: 1.5 x + 1.2 y + 1.8 z <= 2.6
16 * R1: 0.8 x + 0.6 y + 0.9 z >= 1.2
17 *
18 * Where:
19 * 0.1 <= x <= 0.6
20 * 0.2 <= y <= 1.5
21 * 0.3 <= z <= 2.8
22 */
23int main(int argc, char* argv[])
24{
25 try
26 {
27 Envr env;
28 Model model = env.CreateModel("lp_ex1");
29
30 // Add variables
31 Var x = model.AddVar(0.1, 0.6, 0.0, COPT_CONTINUOUS, "x");
32 Var y = model.AddVar(0.2, 1.5, 0.0, COPT_CONTINUOUS, "y");
33 Var z = model.AddVar(0.3, 2.8, 0.0, COPT_CONTINUOUS, "z");
34
35 // Set objective
36 model.SetObjective(1.2 * x + 1.8 * y + 2.1 * z, COPT_MAXIMIZE);
37
38 // Add linear constraints using linear expression
39 model.AddConstr(1.5 * x + 1.2 * y + 1.8 * z <= 2.6, "R0");
40
41 Expr expr(x, 0.8);
42 expr.AddTerm(y, 0.6);
43 expr += 0.9 * z;
44 model.AddConstr(expr >= 1.2, "R1");
45
46 // Set parameters
47 model.SetDblParam(COPT_DBLPARAM_TIMELIMIT, 10);
48
49 // Solve problem
50 model.Solve();
51
52 // Output solution
53 if (model.GetIntAttr(COPT_INTATTR_HASLPSOL) != 0)
54 {
55 cout << "\nFound optimal solution:" << endl;
56 VarArray vars = model.GetVars();
57 for (int i = 0; i < vars.Size(); i++)
58 {
59 Var var = vars.GetVar(i);
60 cout << " " << var.GetName() << " = " << var.Get(COPT_DBLINFO_VALUE) << endl;
61 }
62 cout << "Obj = " << model.GetDblAttr(COPT_DBLATTR_LPOBJVAL) << endl;
63 }
64 }
65 catch (CoptException e)
66 {
67 cout << "Error Code = " << e.GetCode() << endl;
68 cout << e.what() << endl;
69 }
70 catch (...)
71 {
72 cout << "Unknown exception occurs!";
73 }
74}
Let’s now walk through the example, line by line, to understand how it achieves the desired result of optimizing the model. Note that the example must include header coptcpp_pch.h.
Creating environment and model
Essentially, any C++ application using Cardinal Optimizer should start with a COPT environment, where user could add one or more models. Note that each model encapsulates a problem and corresponding data.
Furthermore, to create multiple problems, one can load them one by one in the same model, besides the naive option of creating multiple models in the environment.
Envr env;
Model model = env.CreateModel("lp_ex1");
The above call instantiates a COPT environment and a model with name “lp_ex1”.
Adding variables
The next step in our example is to add variables to the model. Variables are added through AddVar() or AddVars() method on the model object. A variable is always associated with a particular model.
// Add variables
Var x = model.AddVar(0.1, 0.6, 0.0, COPT_CONTINUOUS, "x");
Var y = model.AddVar(0.2, 1.5, 0.0, COPT_CONTINUOUS, "y");
Var z = model.AddVar(0.3, 2.8, 0.0, COPT_CONTINUOUS, "z");
The first and second arguments to the AddVar() call are the variable lower and upper bounds, respectively. The third argument is the linear objective coefficient (zero here - we’ll set the objective later). The fourth argument is the variable type. Our variables are all continuous in this example. The final argument is the name of the variable.
The AddVar() method has been overloaded to accept several different argument lists. Please refer to C++ API Reference for further details.
The objective is built here using overloaded operators. The C++ API overloads the arithmetic operators to allow you to build linear expressions by COPT variables. The second argument indicates that the sense is maximization.
Adding constraints
The next step in the example is to add the linear constraints. As with variables, constraints are always associated with a specific model. They are created using AddConstr() or AddConstrs() methods on the model object.
// Add linear constraints using linear expression
model.AddConstr(1.5 * x + 1.2 * y + 1.8 * z <= 2.6, "R0");
Expr expr(x, 0.8);
expr.AddTerm(y, 0.6);
expr += 0.9 * z;
model.AddConstr(expr >= 1.2, "R1");
The first constraint is to use overloaded arithmetic operators to build the linear expression. The comparison operators are also overloaded to make it easier to build constraints.
The second constraint is created by building a linear expression incrementally. That is, an expression can be built by constructor of a variable and its coefficient, by AddTerm() method, and by overloaded operators.
Setting parameters and attributes
The next step in the example is to set parameters and attributes of the problem before optimization.
// Set parameters
model.SetDblParam(COPT_DBLPARAM_TIMELIMIT, 10);
The SetDblParam() call here with COPT_DBLPARAM_TIMELIMIT argument sets solver to optimize up to 10 seconds.
Solving problem
Now that the model has been built, the next step is to optimize it:
// Solve problem
model.Solve();
This routine performs the optimization and populates several internal model attributes (including the status of the optimization, the solution, etc.).
Outputting solution
After solving the problem, one can query the values of the attributes for various of purposes.
// Output solution
if (model.GetIntAttr(COPT_INTATTR_HASLPSOL) != 0)
{
cout << "\nFound optimal solution:" << endl;
VarArray vars = model.GetVars();
for (int i = 0; i < vars.Size(); i++)
{
Var var = vars.GetVar(i);
cout << " " << var.GetName() << " = " << var.Get(COPT_DBLINFO_VALUE) << endl;
}
cout << "Obj = " << model.GetDblAttr(COPT_DBLATTR_LPOBJVAL) << endl;
}
Spcifically, one can query the COPT_INTATTR_HASLPSOL attribute on the model to know whether we have optimal LP solution; query the COPT_DBLINFO_VALUE attribute of a variable to obtain its solution value; query the COPT_DBLATTR_LPOBJVAL attribute on the model to obtain the objective value for the current solution.
The names and types of all model, variable, and constraint attributes can be found in Attributes of C API reference.
Error handling
Errors in the COPT C++ interface are handled through the C++ exception mechanism. In the example, all COPT statements are enclosed inside a try block, and any associated errors would be caught by the catch block.
catch (CoptException e)
{
cout << "Error Code = " << e.GetCode() << endl;
cout << e.what() << endl;
}
catch (...)
{
cout << "Unknown exception occurs!";
}
Build and Run
To build and run the example, users may refer to files under
$COPT_HOME/examples/cpp
. Specifically, We provide visual studio
project on Windows, as well as makefile project on Linux and Mac platforms.
Windows
1. Visual Studio project
For Windows platform, Visual Studio project is located at
$COPT_HOME/examples/cpp/vsprojects
. Double-clicking the project file lp_ex1.vcxproj
will bring Visual Studio. Note that it requires Visual studio 2017 or 2019 installed
on Windows 10 to build.
The Visual Studio project has dependency on COPT cpp shared libary copt_cpp.dll
, which is
refered in project file, along with its import library copt_cpp.lib
.
The required headers are copt.h
, coptcpp.h
and coptcpp.idl.h
, which declare COPT
constants, interfaces and methods exported from copt_cpp.dll
. In addition,
the example provides class header files under $COPT_HOME/include/coptcpp_inc
,
which wraps COPT cpp interfaces and redefines
overloaded operators.
In simple terms, users only need to include the header file coptcpp_pch.h
as shown in the example, configure additional dependencies as copt_cpp.lib
, and set the directory of additional link libraries as $(COPT_HOME)/lib
, and make sure that the dynamic library copt_cpp.dll
has been installed in the appropriate path, and can be loaded at runtime.
To run the example, users should have COPT installed. Specifically, it requires
COPT cpp libary, copt_cpp.dll
, and valid license files to run. Please refer to
Install Guide for Cardinal Optimizer for further details.
In addition, on Windows systems, the Clang and Intel compilers, like the aforementioned MSVC compiler, all support compiling the C++ examples of COPT.
2. MinGW-w64 Toolchain
For Windows systems, COPT also supports the MinGW-w64 toolchain. For the two variants of the MinGW-w64, we provide
the dynamic library copt_mmingwcpp.dll
supporting MSVCRT and the dynamic library copt_umingwcpp.dll
supporting UCRT.
Please refer to the corresponding instructions in the Makefile under the path $COPT_HOME/examples/cpp
for usage.
We recommend using copt_mmingwcpp.dll
supporting MSVCRT by default, and trying the UCRT if any issues arise.
Linux and MacOS
We provide Makefile to build the example for Linux and MacOS platforms. Please
make sure tools, gcc
and make
, are already installed on the platforms.
To build the example, change directory to $COPT_HOME/examples/cpp
and
execute command 'make'
on unix terminal.
The project has dependency on COPT cpp shared libary, that is, libcopt_cpp.so
on Linux platform and libcopt_cpp.dylib
on MacOS. Similar to Windows VS project,
user should refer to coptcpp_pch.h
under $COPT_HOME/include/coptcpp_inc'
to
include all necessary headers, as shown in the example.
To run the example, users should have COPT installed. Specifically, it requires
COPT cpp libary, libcopt_cpp.so
on Linux, libcopt_cpp.dylib
on MacOS,
and valid license files to run. Alternatively, user might set LD_LIBRARY_PATH
and COPT_LICENSE_DIR
properly to work around. Please refer to
Install Guide for Cardinal Optimizer for further details.